# maptoposter
**Repository Path**: jtan0213/maptoposter
## Basic Information
- **Project Name**: maptoposter
- **Description**: Transform your favorite cities into beautiful, minimalist designs. MapToPoster lets you create and export visually striking map posters with code.
- **Primary Language**: Python
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-03-05
- **Last Updated**: 2026-03-06
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 城市地图海报生成器
生成精美、极简风格的世界各地城市地图海报。
## 示例
| 国家 | 城市 | 主题 | 海报 |
|:------------:|:--------------:|:---------------:|:------:|
| USA | San Francisco | sunset |
|
| 西班牙 | Barcelona | warm_beige |
|
| 意大利 | Venice | blueprint |
|
| 日本 | Tokyo | japanese_ink |
|
| 印度 | Mumbai | contrast_zones |
|
| 摩洛哥 | Marrakech | terracotta |
|
| 新加坡 | Singapore | neon_cyberpunk |
|
| 澳大利亚 | Melbourne | forest |
|
| 阿联酋 | Dubai | midnight_blue |
|
| USA | Seattle | emerald |
|
## 安装
### 使用 uv(推荐)
请确保已安装 [uv](https://docs.astral.sh/uv/)。在脚本前添加 `uv run` 会自动创建和管理虚拟环境。
```bash
# 首次运行将自动安装依赖
uv run ./create_map_poster.py --city "Paris" --country "France"
# 或者先显式同步依赖(使用锁定版本)
uv sync --locked
uv run ./create_map_poster.py --city "Paris" --country "France"
```
### 使用 pip + venv
```bash
python -m venv .venv
source .venv/bin/activate # Windows 上: .venv\Scripts\activate
pip install -r requirements.txt
```
## 使用方法
### 生成海报
如果使用 `uv`:
```bash
uv run ./create_map_poster.py --city <城市> --country <国家> [选项]
```
否则(pip + venv):
```bash
python create_map_poster.py --city <城市> --country <国家> [选项]
```
### 必需选项
| 选项 | 简写 | 说明 |
|--------|-------|-------------|
| `--city` | `-c` | 城市名称(用于地理编码) |
| `--country` | `-C` | 国家名称(用于地理编码) |
### 可选参数
| 选项 | 简写 | 说明 | 默认值 |
|--------|-------|-------------|---------|
| **可选:** `--latitude` | `-lat` | 覆盖纬度中心点(与 --longitude 配合使用) | |
| **可选:** `--longitude` | `-long` | 覆盖经度中心点(与 --latitude 配合使用) | |
| **可选:** `--country-label` | | 覆盖海报上显示的国家文本 | |
| **可选:** `--theme` | `-t` | 主题名称 | terracotta |
| **可选:** `--distance` | `-d` | 地图半径(米) | 18000 |
| **可选:** `--list-themes` | | 列出所有可用主题 | |
| **可选:** `--all-themes` | | 为所有可用主题生成海报 | |
| **可选:** `--width` | `-W` | 图像宽度(英寸) | 12(最大: 20) |
| **可选:** `--height` | `-H` | 图像高度(英寸) | 16(最大: 20) |
### 多语言支持 - i18n
使用 Google Fonts 自定义字体,以您的语言显示城市和国家名称:
| 选项 | 简写 | 说明 |
|--------|-------|-------------|
| `--display-city` | `-dc` | 城市的自定义显示名称(例如 "东京") |
| `--display-country` | `-dC` | 国家的自定义显示名称(例如 "日本") |
| `--font-family` | | Google Fonts 字体家族名称(例如 "Noto Sans JP") |
| `--custom-font` | | 本地字体文件路径,或 `fonts/custom` 目录中的字体文件名(例如 `NotoSansSC-Regular.ttf`) |
**示例:**
```bash
# 日语
python create_map_poster.py -c "Tokyo" -C "Japan" -dc "東京" -dC "日本" --font-family "Noto Sans JP"
# 韩语
python create_map_poster.py -c "Seoul" -C "South Korea" -dc "서울" -dC "대한민국" --font-family "Noto Sans KR"
# 阿拉伯语
python create_map_poster.py -c "Dubai" -C "UAE" -dc "دبي" -dC "الإمارات" --font-family "Cairo"
```
**注意**:字体会自动从 Google Fonts 下载并缓存在 `fonts/cache/` 目录中。
### 本地自定义字体(`--custom-font`)
如果你想使用自己下载的字体文件(不通过 Google Fonts):
1. 将字体文件放到 `fonts/custom/` 目录(例如:`fonts/custom/MyFont-Regular.ttf`)
2. 使用 `--custom-font` 参数运行
```bash
# 使用 fonts/custom/ 中的字体文件名
python create_map_poster.py -c "Beijing" -C "China" -dc "北京" -dC "中国" --custom-font "MyFont-Regular.ttf"
# 或使用绝对/相对路径
python create_map_poster.py -c "Tokyo" -C "Japan" -dc "東京" -dC "日本" --custom-font "/path/to/your/font.ttf"
```
**优先级**:`--custom-font` > `--font-family` > 默认本地 Roboto 字体。
### 分辨率指南(300 DPI)
使用 `-W` 和 `-H` 参数指定目标分辨率:
| 目标 | 分辨率 (px) | 英寸 (-W / -H) |
|--------|-----------------|------------------|
| **Instagram 帖子** | 1080 x 1080 | 3.6 x 3.6 |
| **手机壁纸** | 1080 x 1920 | 3.6 x 6.4 |
| **HD 壁纸** | 1920 x 1080 | 6.4 x 3.6 |
| **4K 壁纸** | 3840 x 2160 | 12.8 x 7.2 |
| **A4 打印** | 2480 x 3508 | 8.3 x 11.7 |
### 使用示例
#### 基础示例
```bash
# 使用默认主题的简单用法
python create_map_poster.py -c "Paris" -C "France"
# 自定义主题和距离
python create_map_poster.py -c "New York" -C "USA" -t noir -d 12000
```
#### 多语言示例(非拉丁字母)
以原生文字显示城市名称:
```bash
# 日语
python create_map_poster.py -c "Tokyo" -C "Japan" -dc "東京" -dC "日本" --font-family "Noto Sans JP" -t japanese_ink
# 韩语
python create_map_poster.py -c "Seoul" -C "South Korea" -dc "서울" -dC "대한민국" --font-family "Noto Sans KR" -t midnight_blue
# 泰语
python create_map_poster.py -c "Bangkok" -C "Thailand" -dc "กรุงเทพมหานคร" -dC "ประเทศไทย" --font-family "Noto Sans Thai" -t sunset
# 阿拉伯语
python create_map_poster.py -c "Dubai" -C "UAE" -dc "دبي" -dC "الإمارات" --font-family "Cairo" -t terracotta
# 简体中文
python create_map_poster.py -c "Beijing" -C "China" -dc "北京" -dC "中国" --font-family "Noto Sans SC"
# 高棉语
python create_map_poster.py -c "Phnom Penh" -C "Cambodia" -dc "ភ្នំពេញ" -dC "កម្ពុជា" --font-family "Noto Sans Khmer"
```
#### 高级示例
```bash
# 标志性的网格模式
python create_map_poster.py -c "New York" -C "USA" -t noir -d 12000 # 曼哈顿网格
python create_map_poster.py -c "Barcelona" -C "Spain" -t warm_beige -d 8000 # 扩展区
# 滨水区与运河
python create_map_poster.py -c "Venice" -C "Italy" -t blueprint -d 4000 # 运河网络
python create_map_poster.py -c "Amsterdam" -C "Netherlands" -t ocean -d 6000 # 同心运河
python create_map_poster.py -c "Dubai" -C "UAE" -t midnight_blue -d 15000 # 棕榈岛与海岸线
# 放射状模式
python create_map_poster.py -c "Paris" -C "France" -t pastel_dream -d 10000 # 奥斯曼大道
python create_map_poster.py -c "Moscow" -C "Russia" -t noir -d 12000 # 环形公路
# 有机老城
python create_map_poster.py -c "Tokyo" -C "Japan" -t japanese_ink -d 15000 # 密集有机街道
python create_map_poster.py -c "Marrakech" -C "Morocco" -t terracotta -d 5000 # 麦地那迷宫
python create_map_poster.py -c "Rome" -C "Italy" -t warm_beige -d 8000 # 古代布局
# 沿海城市
python create_map_poster.py -c "San Francisco" -C "USA" -t sunset -d 10000 # 半岛网格
python create_map_poster.py -c "Sydney" -C "Australia" -t ocean -d 12000 # 港口城市
python create_map_poster.py -c "Mumbai" -C "India" -t contrast_zones -d 18000 # 沿海半岛
# 河流城市
python create_map_poster.py -c "London" -C "UK" -t noir -d 15000 # 泰晤士河曲线
python create_map_poster.py -c "Budapest" -C "Hungary" -t copper_patina -d 8000 # 多瑙河分叉
# 覆盖中心坐标
python create_map_poster.py --city "New York" --country "USA" -lat 40.776676 -long -73.971321 -t noir
# 列出可用主题
python create_map_poster.py --list-themes
# 为每个主题生成海报
python create_map_poster.py -c "Tokyo" -C "Japan" --all-themes
```
### 距离指南
| 距离 | 适用场景 |
|----------|----------|
| 4000-6000米 | 小型/密集城市(威尼斯、阿姆斯特丹市中心) |
| 8000-12000米 | 中型城市,聚焦市中心(巴黎、巴塞罗那) |
| 15000-20000米 | 大都市,全城视角(东京、孟买) |
## 主题
`themes/` 目录下有 17 个可用主题:
| 主题 | 风格 |
|-------|-------|
| `gradient_roads` | 平滑渐变着色 |
| `contrast_zones` | 高对比城市密度 |
| `noir` | 纯黑背景,白色道路 |
| `midnight_blue` | 深蓝色背景,金色道路 |
| `blueprint` | 建筑蓝图美学 |
| `neon_cyberpunk` | 暗色配霓虹粉/青色 |
| `warm_beige` | 复古棕褐色 |
| `pastel_dream` | 柔和淡雅粉彩 |
| `japanese_ink` | 极简水墨风格 |
| `emerald` | 浓郁深绿美学 |
| `forest` | 深绿与鼠尾草色 |
| `ocean` | 蓝色与蓝绿色,适合沿海城市 |
| `terracotta` | 地中海温暖色调 |
| `sunset` | 暖橙与粉红 |
| `autumn` | 季节性褐橙与红色 |
| `copper_patina` | 氧化铜美学 |
| `monochrome_blue` | 单一蓝色系 |
## 输出
海报保存到 `posters/` 目录,格式如下:
```text
{城市}_{主题}_{YYYYMMDD_HHMMSS}.png
```
## 添加自定义主题
在 `themes/` 目录创建 JSON 文件:
```json
{
"name": "My Theme",
"description": "Description of the theme",
"bg": "#FFFFFF",
"text": "#000000",
"gradient_color": "#FFFFFF",
"water": "#C0C0C0",
"parks": "#F0F0F0",
"road_motorway": "#0A0A0A",
"road_primary": "#1A1A1A",
"road_secondary": "#2A2A2A",
"road_tertiary": "#3A3A3A",
"road_residential": "#4A4A4A",
"road_default": "#3A3A3A"
}
```
## 项目结构
```text
map_poster/
├── create_map_poster.py # 主脚本
├── font_management.py # 字体加载与 Google Fonts 集成
├── themes/ # 主题 JSON 文件
├── fonts/ # 字体文件
│ ├── Roboto-*.ttf # 默认 Roboto 字体
│ └── cache/ # 下载的 Google Fonts(自动生成)
├── posters/ # 生成的海报
└── README.md
```
## 开发者指南
想要扩展或修改脚本的贡献者快速参考。
### 贡献者指南
- 欢迎提交错误修复
- 不要提交用户界面(网页/桌面)
- 暂时不要 Docker 化
- 如果你凭感觉写代码,请测试并查看修复前后的海报版本
- 在开发大型功能前,请先在讨论区/问题区询问是否会合并
### 架构概述
```text
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ CLI 解析器 │────▶│ 地理编码 │────▶│ 数据获取 │
│ (argparse) │ │ (Nominatim) │ │ (OSMnx) │
└─────────────────┘ └──────────────┘ └─────────────────┘
│
┌──────────────┐ ▼
│ 输出 │◀────┌─────────────────┐
│ (matplotlib)│ │ 渲染 │
└──────────────┘ │ (matplotlib) │
└─────────────────┘
```
### 关键函数
| 函数 | 用途 | 修改时机 |
|----------|---------|----------------|
| `get_coordinates()` | 城市 → 经纬度(Nominatim) | 切换地理编码提供商 |
| `create_poster()` | 主渲染流水线 | 添加新地图图层 |
| `get_edge_colors_by_type()` | 按 OSM highway 标签的道路颜色 | 更改道路样式 |
| `get_edge_widths_by_type()` | 按重要性的道路宽度 | 调整线宽 |
| `create_gradient_fade()` | 顶部/底部渐变效果 | 修改渐变叠加 |
| `load_theme()` | JSON 主题 → 字典 | 添加新主题属性 |
| `is_latin_script()` | 检测文字脚本以进行排版 | 支持新文字脚本 |
| `load_fonts()` | 加载自定义/默认字体 | 更改字体加载逻辑 |
### 渲染图层(z-order)
```text
z=11 文本标签(城市、国家、坐标)
z=10 渐变遮罩(顶部和底部)
z=3 道路(通过 ox.plot_graph)
z=2 公园(绿色多边形)
z=1 水体(蓝色多边形)
z=0 背景颜色
```
### OSM 道路类型 → 道路层级
```python
# 在 get_edge_colors_by_type() 和 get_edge_widths_by_type() 中
motorway, motorway_link → 最粗 (1.2),最深色
trunk, primary → 较粗 (1.0)
secondary → 中等 (0.8)
tertiary → 较细 (0.6)
residential, living_street → 最细 (0.4),最浅色
```
### 排版与文字检测
脚本自动检测文字脚本以应用适当的排版:
- **拉丁字母**(英语、法语、西班牙语等):应用字母间距,产生优雅的 "P A R I S" 效果
- **非拉丁字母**(日语、阿拉伯语、泰语、韩语等):自然间距 "东京"(字符之间无间隙)
文字检测使用 Unicode 范围(拉丁字母为 U+0000-U+024F)。如果超过 80% 的字母字符是拉丁字母,则应用间距。
### 添加新功能
**新地图图层(例如铁路):**
```python
# 在 create_poster() 中,获取公园后:
try:
railways = ox.features_from_point(point, tags={'railway': 'rail'}, dist=dist)
except:
railways = None
# 然后在道路之前绘制:
if railways is not None and not railways.empty:
railways = railways.to_crs(g_proj.graph["crs"])
railways.plot(ax=ax, color=THEME['railway'], linewidth=0.5, zorder=2.5)
```
**新主题属性:**
1. 添加到主题 JSON:`"railway": "#FF0000"`
2. 在代码中使用:`THEME['railway']`
3. 在 `load_theme()` 默认字典中添加回退
### 排版定位
所有文本使用 `transform=ax.transAxes`(0-1 归一化坐标):
```text
y=0.14 城市名称(拉丁字母带间距)
y=0.125 装饰线
y=0.10 国家名称
y=0.07 坐标
y=0.02 归属(右下角)
```
### 常用 OSMnx 模式
```python
# 获取所有建筑
buildings = ox.features_from_point(point, tags={'building': True}, dist=dist)
# 获取特定设施
cafes = ox.features_from_point(point, tags={'amenity': 'cafe'}, dist=dist)
# 不同的网络类型
G = ox.graph_from_point(point, dist=dist, network_type='drive') # 仅道路
G = ox.graph_from_point(point, dist=dist, network_type='bike') # 自行车道
G = ox.graph_from_point(point, dist=dist, network_type='walk') # 行人
```
### 性能提示
- 大的 `dist` 值(>20公里)= 下载慢 + 内存占用大
- 本地缓存坐标以避免 Nominatim 速率限制
- 使用 `network_type='drive'` 而非 `'all'` 以加快渲染
- 将 `dpi` 从 300 降低到 150 以快速预览