maptoposter 版本演进解析:从 0.1.0 到 Unreleased 的变更记录与源码印证

原创2026-10-13 16:41:051,184 阅读
文章标签:数据可视化GIS图像处理CLI

maptoposter 版本演进解析:从 0.1.0 到 Unreleased 的变更记录与源码印证

maptoposter 是一个将城市 OpenStreetMap 数据转化为极简风格海报的工具,其 CHANGELOG.md 按 Keep a Changelog 规范完整记录了项目从 0.1.0 初始发布到 Unreleased 社区贡献阶段的每一次功能新增、默认参数调整与缺陷修复。本文以这份变更记录为骨架,逐版本结合当前仓库中的源码、主题配置与 CI 工作流进行印证,帮助你理解 uv 包管理接入、渲染层级(z-order)修复、横向画布文字缩放、本地缓存与 API 限速等关键变更背后的实现细节,以及如何在本仓库中复核这些变更。

maptoposter 使用 emerald 主题生成的西雅图海报(0.3.0 版本新增 emerald 主题的真实输出)

maptoposter 使用默认 terracotta 主题生成的马拉喀什海报(自 0.3.0 起 terracotta 成为默认主题)

变更记录的编写规范与版本总览

CHANGELOG.md 开头声明了两条约定:格式基于 Keep a Changelog 规范,版本管理遵循语义化版本(Semantic Versioning)。全文按版本倒序组织,每个版本分 Added / Changed / Removed / Fixed 小节,0.2.1 起每个版本还标注了日期与维护者(Maintainer: @originalankur),关键条目附带对应的 PR/Issue 编号(如 PR #106、Issue #39)。

文件中共包含五个版本区块,覆盖约两周的密集迭代:

版本 日期 核心主题
Unreleased 未发布 社区贡献:uv 支持、z-order 修复、横向文字缩放
0.3.0 2026-01-27 自定义坐标、emerald 主题、CI 工作流、默认参数调整
0.2.1 2026-01-18 SVG/PDF 导出、可变尺寸、本地缓存、API 限速
0.2.0 2026-01-17(Tag: v0.2) README 示例图片、初始主题集合
0.1.0 2026-01-17 初始源码、17 个内置主题、核心渲染能力

v0.1.0:初始发布,奠定核心渲染管线

0.1.0 交付了项目的完整初始形态,CHANGELOG 记录的内容包括:

  • 初始 maptoposter 源码,即当前的单脚本入口 create_map_poster.py;
  • 带使用说明的 README.md;
  • 17 个内置主题:autumn、blueprint、contrast_zones、copper_patina、forest、gradient_roads、japanese_ink、midnight_blue、monochrome_blue、neon_cyberpunk、noir、ocean、pastel_dream、sunset、terracotta、warm_beige 等;
  • 核心能力清单:基于城市/国家的地图生成、JSON 自定义主题、按 OSM 道路等级着色、水系与公园要素渲染、Roboto 字体排版、坐标显示、OpenStreetMap 版权署名。

当前仓库的 themes/ 目录中正好存在 17 个主题 JSON 文件,与变更记录的规模相符。不过初始版本的 17 个主题与当前目录并非完全同一批:CHANGELOG 记载 0.3.0 移除了 feature_based 主题并新增了 emerald 主题,一进一出后总数保持 17 不变,这也解释了为何当前目录中没有 feature_based.json 却有 themes/emerald.json。

v0.2.0 与 v0.2.1:矢量输出、可变尺寸与本地缓存

v0.2.0:补齐文档与主题集合

0.2.0(2026-01-17,Tag: v0.2)的变更较小:在 README 中加入示例海报图片、形成初始主题集合。当前 README.md 顶部的 Examples 表格(多城市、多主题海报缩略图)即为这一条目的延续。

v0.2.1 新增能力:矢量导出、可变尺寸、缓存与限速

0.2.1(2026-01-18)是功能增量最大的版本之一,四项新增能力均可在源码中找到对应实现:

1. SVG/PDF 矢量导出(PR #57)

CHANGELOG 记录通过 --format 标志支持矢量输出。当前源码中该参数注册为 --format / -f,取值限定为 png、svg、pdf,默认 png(见 create_map_poster.py#L951-L957)。保存逻辑中只有 PNG 格式显式指定 dpi=300,SVG/PDF 作为矢量格式不依赖 DPI(见 create_map_poster.py#L758-L769),这与"矢量输出"的定位一致。

2. 可变海报尺寸(PR #59)

-W / -H 分别对应 --width / --height,单位为英寸,默认 12×16(见 create_map_poster.py#L917-L930)。README 中还给出了 300 DPI 分辨率速查表(Instagram 1080×1080 对应 3.6×3.6 英寸、4K 对应 12.8×7.2 英寸、A4 打印对应 8.3×11.7 英寸等)。

3. 本地缓存

下载的 OSM 数据现在会被本地缓存。从源码看,缓存实现为 pickle 对象,存放在 CACHE_DIR(默认 cache/ 目录,可通过环境变量 CACHE_DIR 覆盖,见 create_map_poster.py#L40-L42),缓存键按"坐标 + 半径"等要素拼接,例如路网键为 graph_{lat}_{lon}_{dist}(见 create_map_poster.py#L424-L428),地理编码结果、水系、公园分别独立缓存。缓存读写失败会抛出专用的 CacheError 异常(见 create_map_poster.py#L36-L108),对应 CHANGELOG 中"Improved cache handling with hashed filenames and error handling"与 0.3.0 的"Cache directory handling"修复(PR #109)。

4. API 限速

CHANGELOG 记录"0.3s delay between API requests"。当前源码中限速体现在三处:要素抓取后 time.sleep(0.3)(create_map_poster.py#L471)、路网抓取后 time.sleep(0.5)(create_map_poster.py#L433)、地理编码请求前 time.sleep(1)(create_map_poster.py#L334)。

v0.2.1 的修复与投影变更

CHANGELOG 的 Fixed/Changed 小节记录了多项数据完整性修复,逐项与源码对应:

变更项 说明 源码印证
可变尺寸下的地图变形 变量尺寸导致投影变形 get_crop_limits() 按画布宽高比向内裁剪,保证覆盖半径的同时维持纵横比(create_map_poster.py#L373-L406)
边缘节点保留 保持路网完整 抓图时传入 truncate_by_edge=True(create_map_poster.py#L431)
点状几何过滤 防止点要素在图上渲染成圆点 水系与公园先按 Polygon/MultiPolygon 类型过滤再绘制(create_map_poster.py#L575、create_map_poster.py#L586)
长城市名的动态字号 防止文字截断 城市名长度超过 10 字符时按比例缩小主标题字号(create_map_poster.py#L668-L672)
Nominatim 超时 超时提升至 10 秒 Nominatim(user_agent=..., timeout=10)(create_map_poster.py#L331)
线性投影 保证纵横比正确 路网绘制前经 ox.project_graph() 投影到以米为单位的线性坐标系(create_map_poster.py#L569)

v0.3.0:自定义坐标、emerald 主题与 CI 自动化

坐标覆盖:--latitude / --longitude(PR #106)

新增 --latitude / -lat 与 --longitude / -long 两个参数,用于覆盖地理编码得到的中心点,实现精确的选址控制(例如放大某个街区)。参数以字符串注册,运行时经 lat_lon_parser.parse() 解析为浮点数(见 create_map_poster.py#L876-L889 与 create_map_poster.py#L1016-L1022)。CHANGELOG 特别强调:即使用坐标覆盖,仍须提供 --city 和 --country,因为这两个参数决定海报上的显示名称——源码中对二者的缺失校验是无条件的(create_map_poster.py#L972-L975)。

一个典型用法(来自 README.md):

python create_map_poster.py --city "New York" --country "USA" -lat 40.776676 -long -73.971321 -t noir

从源码结构看有一处值得注意的小不一致:脚本 help 文本中的示例写的是短参 -l(create_map_poster.py#L867),而实际注册的短参是 -lat / -long,帮助示例与参数定义并不同步。

默认参数调整与尺寸上限

0.3.0 对三个默认行为做了调整,均在 argparse 与主题加载逻辑中得到印证:

  • 默认主题:从 feature_based 改为 terracotta(PR #131)。load_theme() 的默认参数与主题缺失时的内嵌回退主题都是 terracotta 调色板(create_map_poster.py#L177-L200),CLI 默认值同为 terracotta(create_map_poster.py#L900);
  • 默认半径:从 12000 米改为 18000 米(PR #128),对应 --distance 默认值 18000(create_map_poster.py#L911-L916),README 的 Distance Guide 也随之给出 4000–20000 米分档建议;
  • 尺寸上限:宽/高强制上限 20 英寸,以支撑 4K 分辨率输出(PR #128、#129)。超限时会打印警告并钳制到 20(create_map_poster.py#L978-L987);
  • 移除:feature_based 主题(PR #131),当前 themes/ 目录中确实不存在该文件。

emerald 主题(PR #114)

新增的 emerald 主题以深绿底色搭配薄荷色点缀,themes/emerald.json 的完整配色为:

{
    "name": "Emerald City",
    "description": "Lush dark green aesthetic with mint accents",
    "bg": "#062C22",
    "text": "#E3F9F1",
    "gradient_color": "#062C22",
    "water": "#0D4536",
    "parks": "#0F523E",
    "road_motorway": "#4ADEB0",
    "road_primary": "#2DB88F",
    "road_secondary": "#249673",
    "road_tertiary": "#1B7559",
    "road_residential": "#155C46",
    "road_default": "#155C46"
}

这体现了主题体系的通用结构:每个主题是一个 11 个颜色槽位的 JSON(背景、文字、渐变、水系、公园、四级道路色加默认道路色),load_theme() 按文件名加载。仓库中 posters/seattle_emerald_20260124_162244.png 即为该主题的实际输出。

GitHub Actions 与冲突自动标记

0.3.0 引入了两条工作流(PR #98):

  • pr-checks.yml:在 ubuntu-latest 与 windows-latest × Python 3.11/3.12/3.13/3.14 矩阵上执行 compileall 语法校验、--help 与 --list-themes 冒烟测试、flake8(含 E9,F63,F7,F82 硬性错误检查)、pylint 与 mypy,另有独立的 pip-audit 依赖安全审计作业;
  • conflicts.yml:基于 eps1lon/actions-label-merge-conflict,给存在合并冲突的 PR 自动打 Conflicts 标签并留言提示。

Unreleased(社区贡献):uv 支持、层级修复与文字缩放

文件顶部的 Unreleased - Community Contributions 区块汇集了尚未发布、来自社区的三项新增与两项修复。

uv 包管理器支持(PR #20)

该条目包含四个配套改动,均可在当前仓库中验证:

  • pyproject.toml:声明项目元数据与锁定版本的全部依赖(osmnx 2.0.7、matplotlib 3.10.8、geopandas 1.1.2 等 30 余项,见 pyproject.toml#L13-L44),构建后端为 setuptools;
  • uv.lock:约 244KB 的锁定文件,保证可复现构建;
  • shebang 行:create_map_poster.py#L1 为 #!/usr/bin/env python3,且文件具备可执行位,支持 uv run ./create_map_poster.py 直接运行;
  • README.md 中的 uv 安装说明:
# First run will automatically install dependencies
uv run ./create_map_poster.py --city "Paris" --country "France"

# Or sync dependencies explicitly first (using locked versions)
uv sync --locked
uv run ./create_map_poster.py --city "Paris" --country "France"

同一区块还记录了 requires-python = ">=3.11" 的声明(修复 Issue #79),对应 pyproject.toml#L6。从当前仓库文件看,pyproject.toml#L3 的 version 字段仍为 0.2.0,与 CHANGELOG 已推进到 0.3.0 + Unreleased 的状态不同步,阅读版本信息时以 CHANGELOG 为准。测试脚本 test/all_variations.sh 中也统一采用 uv run python3 create_map_poster.py ... 的方式调用,印证了 uv 已成为项目的标准运行方式。

渲染层级(z-order)修复(Issue #39 / PR #42)

这是 Unreleased 区块中最具体的一项修复:此前道路被水系和公园盖住,原因是水系层 zorder=1、公园层 zorder=2,与 matplotlib 默认的 2 级道路层冲突。修复后的层级关系为:

由此形成"背景 → 水系(0.5)→ 公园(0.8)→ 道路(2)→ 上下渐变(10)→ 文字(11)"的完整叠放顺序,渐变与文字层的 zorder=10/11 可在 create_map_poster.py#L615-L752 中逐一确认。

横向画布的文字缩放修复(Issue #112)

此前字号只按宽度缩放,导致横向(宽 > 高)海报上文字比例失调。修复后缩放因子改为取宽高较小值,以 12 英寸为基准:

# create_map_poster.py L618-L626
scale_factor = min(height, width) / 12.0
base_main = 60
base_sub = 22
base_coords = 14
base_attr = 8

(见 create_map_poster.py#L618-L626)。该因子同时作用于副标题、坐标、署名文字、装饰线宽与动态主标题,保证竖版 12×16、方形 1080×1080 与横版 4K 等比例画面上的文字观感一致。这与 0.3.0 修复小节中"Dynamic font scaling based on poster width"是同一条演进线:先按宽度缩放,再改为按 min(height, width) 缩放。

其他变更

Unreleased 区块还记录了 .gitignore 的更新,覆盖海报产物、Python 构建产物、IDE 文件与操作系统杂项文件。当前 .gitignore 中可见 cache/*、虚拟环境目录、*.egg-info/、__pycache__/、IDE 配置与 .DS_Store/Thumbs.db 等条目,与该记录相符。

在当前仓库中复核这些变更

以下操作可帮助你在本地逐条验证 CHANGELOG 的记载(仓库为只读资源,以下均为查看/运行类操作):

# 1. 查看当前主题集合(应为 17 个,含 emerald、不含 feature_based)
uv run ./create_map_poster.py --list-themes

# 2. 验证 CLI 参数与默认值(terracotta、18000、-W/-H 上限 20)
uv run ./create_map_poster.py --help

# 3. 验证 z-order 修复:确认水系 0.5、公园 0.8
grep -n "zorder=" create_map_poster.py

# 4. 验证文字缩放修复:确认 min(height, width)
sed -n '618,626p' create_map_poster.py

# 5. 验证 uv 支持:检查项目元数据与 Python 版本约束
sed -n '1,11p' pyproject.toml

# 6. 一键生成全参数变体(针对 Bengaluru 的主题、分辨率、距离矩阵)
bash test/all_variations.sh

其中 test/all_variations.sh 覆盖了基本用法、全部 17 个主题、显示名/国家标签覆盖、-lat/-long 坐标覆盖、印地/卡纳达语 i18n 示例、README 分辨率速查表的五档尺寸以及 Distance Guide 的三档半径,是验证各版本新增 CLI 能力现成可用的回归脚本。

小结

maptoposter 的 CHANGELOG 呈现了一条紧凑的演进主线:0.1.0 确立"地理编码 → OSMnx 抓图 → 主题化分层渲染 → matplotlib 导出"的管线;0.2.1 补齐矢量输出、可变尺寸、缓存与限速等工程能力;0.3.0 引入坐标覆盖、emerald 主题、CI 检查并收敛默认参数(terracotta 主题、18000 米半径、20 英寸上限);Unreleased 则落地了 uv 包管理、渲染层级与横向文字缩放两项渲染修复。上述每一条记录都能在 create_map_poster.py、pyproject.toml、themes/ 与 .github/workflows/ 中找到对应的源码事实,这也是该文档对贡献者与使用者兼具参考价值的原因——它不仅是版本流水账,更是理解当前代码形态的路线图。

登录后查看全文
maptoposter