v0.3.3 —— 一个任务不再能拖垮其他任务;缺瓦片不再静默;区域可以导入、成果可以导出成 MBTiles
先说结论:这一版补的是「多任务同时跑」和「结果不完整」两个长期靠运气的地方 —— 现在四条管线共用一份全局并发与磁盘预算,缺瓦片会被分类记账并在界面上要你拍板,区域可以直接导入 GeoJSON / KML / KMZ / Shapefile,瓦片成果可以导出成单个 .mbtiles 文件。已下载的数据、已切好的地形、配置与历史全部照旧,不必重做任何东西。 升级后首次启动会做一次自动迁移(改缓存目录名、改一列历史状态值),不需要你做任何事,但装回旧版会让缓存全部落空——这一条在下面「升级须知」里说清楚。
一个任务不再能把其它任务拖垮
- 以前四条管线(瓦片 / 高程 / 地形 / 等高线)各管各的并发:配置里的「并发下载数」是每个任务的数字,同时跑两个任务就是两倍连接、两倍内存、两倍磁盘写入,没有任何上限。现在多了一层全局预算,四条管线一起排队。
- 配置页新增五项:同时运行的任务数(出厂 2)、全局网络连接上限(64)、CPU 工作线程(0 = 按机器 CPU 数自己算)、GDAL 并发槽位(2)、缓存总容量上限(0 = 不限,与旧行为一致)。原来的「并发下载数」照旧存在,但它现在会被全局上限压住 —— 两个任务各填 50,实际不会开出 100 条连接。
- 出厂值是保守的。机器好、网络快,把「同时运行的任务数」和「全局网络连接上限」调高即可;这些是可配置的默认值,不是硬限制。
磁盘:先估算,再开工,跑到一半空间不够也能安全停下
- 建任务时会先估这一单要占多少(瓦片、临时文件、拼接产物分别算),减去磁盘预留(出厂 2 GB)再乘一个安全系数(出厂 1.15),不够就直接拦住并告诉你差多少 —— 而不是跑到 80% 时写失败。
- 估算不再用固定的「平均一张瓦片多少 KB」,而是拿你磁盘上已有的瓦片现量。同类工具在这一点上出过 17 倍的偏差,那个坑的成因就是固定均值。
- 临时/工作目录现在跟着输出盘走,不再固定用系统临时目录。跨盘意味着每一次拼接都要把数据完整搬一遍。
- 觉得估算过于保守?配置页里有开关可以整个关掉(
磁盘预算检查)。
缺瓦片不再静默:分类记账 + 由你拍板
- 以前一张瓦片取不到就只是「失败」一个词。现在分五类记账:成功 / 该处本来就没有数据 / 可重试的失败 / 永久失败 / 缓存写入失败。
- 由此多出三个任务状态:补漏中、待决策、已完成(有缺口)。规则只有一条,值得记住:
- 只有「该处本来就没有数据」这一类缺口 —— 比如你框到了大洋深处、或者框出了高程数据集的覆盖范围 —— 任务自动完成,状态是「已完成(有缺口)」,不打扰你。
- 出现任何一类真失败,任务停在待决策,不会给你一张残缺的拼接图。你可以点「补漏」只重跑那些格子,也可以点「接受缺口」让它按现状出图 —— 后者出的成果和历史记录会永久带缺块标记,不会伪装成完整成品。
- 补漏可以反复点,重复点不会重复下载。
- 任务详情里能看到缺口的分类计数和最多 20 个样例格子(层级 / 行列号 / 失败原因)。
每个任务一份自己的日志,出问题可以直接导出
- 以前排障只有一份全局日志,多任务并行时几条管线的输出交织在一起。现在每个任务写一份
logs/tasks/<管线>_<任务号>.log,任务详情里能直接看,也能下载一份诊断文本。 - 日志里的密码和 Token 在落盘前就被抹掉了 —— 这份文件设计上就是可以直接贴进 issue 的。
- 出厂单份上限 4 MB、保留 14 天,超期的在启动时自动清掉。不想要就在配置里关掉。
删任务不再误伤别的任务的缓存
- 以前删任务时清缓存是按样式整片清的:两个任务框的范围有重叠,删掉其中一个,另一个下次恢复要重新下载重叠的那部分。现在只清这个任务独占的那些格子,别人还用得着的一张不动。
- 配置页的缓存管理多了两项:按来源命名空间看占用(见下),以及一键清理「没有任何任务认领」的孤儿缓存。
区域输入:可以导入文件了,也能按地名搜
- 除了在地图上拉框,现在可以直接导入 GeoJSON / KML / KMZ / Shapefile(.zip),支持多边形、多部件与孔洞(挖空的部分不会被下载)。
- 跨 180° 经线的区域从「报错拒绝」改成「自动拆成两段」。以前这种范围要么被拦下、要么被算成绕地球一圈。
- 地名搜索需要你自己填一个服务地址才会出现 —— 出厂是空的,程序不内置任何地名服务,理由见下面「这一版刻意没做什么」。
瓦片成果可以打包成单个 .mbtiles
- 一个
.mbtiles就是一个文件,里面装着整棵瓦片金字塔 —— 拷给别人、丢进 QGIS / ArcGIS,不用再搬几十万个小文件。影像和等高线用的是同一套写入端。 - 两个入口,随你用哪个:建任务时在「输出格式」旁边勾上**「同时导出 MBTiles」,跑完自动打包;或者对已经跑完的任务**事后点一次导出。
- 这是「多给一份产物」,不是「换一种输出格式」。 输出格式的三个选项(只要瓦片 / 只要拼接图 / 两个都要)一个没变,勾了 MBTiles 也不会删掉原来的瓦片目录 —— 那个目录正是打包的原料,也是程序里预览用的那份。同一个任务可以同时留着瓦片目录、拼接图和
.mbtiles。 - 打包出来的库可以直接在本程序里预览(
/mbtiles/...)。 - 打包失败不会把一个已经下载完的任务判成失败 —— 瓦片已经在盘上了,重新点一次导出即可。
- 有缺口的任务打包时会带缺块标记,不会假装完整。
图源向导
- 配置页里粘一条瓦片服务地址,程序会替你认出模板形态(
{z}/{x}/{y}、{s}子域、TMS 行号方向、查询参数),并指出可疑的地方,而不是让你手工拼一遍格式再靠试错。
升级须知(五条,都不需要你动手,但请读一下)
- 缓存目录改名了。 从
cache/<样式>/…改成cache/<样式>-<源指纹>/…,例如cache/s/…→cache/s-3f8a1c2d/…。首次启动自动改名,是重命名不是重新下载,一张瓦片都不会丢。改这个是因为原来的目录名只认样式不认服务器:同一个「卫星」样式换了服务器列表之后,新旧两家的瓦片会混进同一个成品,而且事后无从分辨。注意:改名之后如果你把程序装回旧版本,旧版会按老目录名去找,结果是缓存全部落空、全部重新下载(数据不会损坏,只是白下一遍)。 - 历史任务里的瓦片状态值改了写法。 数据库里旧的
failed会被改写成retryable_failure(可重试的失败)—— 这是保守的读法,意味着这些格子仍然可以用「补漏」重跑。一次性迁移,自动完成。 - 多了三个任务状态。 如果你有自己写的脚本在读任务接口,注意
status现在还可能是retrying/pending_decision/completed_with_gaps。把它们当成「未结束 / 未结束 / 已结束」处理即可。 - 只影响直接调 API 的人:删除本地地形任务的
delete_files默认值从「删」改成「不删」。DELETE /api/terrain/local/tasks/<id>此前不带参数时默认连磁盘产物一起删,而另外三条管线的同名接口默认都是保留。四条现在统一为默认保留,要删就显式带?delete_files=true(这个写法一直有效,没有变)。界面上的删除按钮不受影响 —— 它一直是显式带着这个参数发的,你在界面上看到和勾选的行为与上一版完全一样。会受影响的只有自己写脚本、依赖了那个隐式「默认删」的人:同样的请求现在会把文件留在盘上。改的只有 HTTP 这一层:LocalTerrainTaskManager.delete_task自己的签名默认值仍然是True,从代码里直接调它的地方行为一个字没变。 - 只影响直接调 API 的人:
POST /api/dem/tasks多了一种写区域的方式。 老的north/south/east/west四至照旧可用,没有废弃;新增的region字段(一个RegionSpec)与它二选一,给了region就不必再给四至。跨 180° 经线的 DEM 任务只能用region建 —— 裸四至那条路对east <= west一律回 400,那道校验是有意保留的(它挡的是「填反了四至」这个高频错误)。另外DELETE /api/tasks/<id>新增可选的?clear_cache=1,带上它会顺带清掉只被这个任务引用的共享缓存,响应里多出cache_removed_bytes/cache_removed_files/cache_deferred三个字段;不带就是旧行为。
这一版刻意没做什么(写出来是为了让你不用去找)
- 不内置地名 / 行政区搜索的数据源。 功能本身做好了,但
地名服务地址出厂是空的,你不填就不会在界面上出现。原因有两条:公共地名服务(如 OSM Nominatim)都有明确的批量使用政策,程序替你内置一个等于替你接受了那份政策;中国境内的行政区数据还叠着测绘资质的要求。这两件都不是工程能替用户决定的事。要用就自己填一个 Nominatim 兼容的地址,程序会把它当作不可信的外部 URL 做安全校验后再请求。(依据:docs/notes/external-projects-takeaways.md§11「不内置未经政策审核的公共或商业批量下载源」,以及 §13 末尾把「行政区与地名搜索的数据源与测绘合规」列为仍待产品层决定。) - 没有安装包,仍然是解压即用。 不做 MSI / DMG / DEB,也没有自动更新(§13-6 的决定)。自动更新的前置条件是签名清单、资产哈希与代码签名,一样都还不具备 —— 与其做一个「从某个地址下载 exe 然后直接执行」的更新器,不如不做。
- 没有任何遥测、埋点或使用统计。 一行都没有,将来也不打算加(§11)。程序除了你自己配置的图源、高程源与代理之外不连接任何服务器。
- 可选数据插件(Wayback / MVT / OSM 矢量 / 3D Tiles)一行未写。 那是下一阶段的事,插件契约都还没定稿。
给排障和构建的人
- 新增合同层
src/contracts/(region/region_tiles/source/outcome/artifact/reservation)。region_tiles.py是全仓唯一一处经纬度↔瓦片换算 —— 估算、下载、拼接、MBTiles 四至与界面预览共用它,「预览说的张数」和「实际切的张数」因此不会再各算各的。 - 新增服务:
resource_scheduler/disk_budget/task_logging/cache_exclusive/source_registry/mbtiles/artifact_export/region_import/url_guard/source_wizard/geocoding/artifact_store。架构说明、配置键含义与状态机规则都写进了CLAUDE.md。 - 数据库
user_version推到 6:5 =task_tiles.status的failed→retryable_failure,6 = 缓存目录改名。两条都是幂等的一次性迁移。新增artifacts表,tasks新增export_mbtiles列(与output_format正交,见下条)。 - MBTiles 是「多一份产物」而不是第四种
output_format。OutputFormat一个值没加;打包由独立的布尔列tasks.export_mbtiles驱动,打包器只有一处src/services/artifact_export.py(export_task_mbtiles幂等,按管线查一张布局表,同时覆盖影像与等高线)。做成第四种格式会删掉打包的原料目录 —— 而那个目录正是/tiles/<id>/预览用的那份。 - MBTiles 对外只有两条路由:读走
/mbtiles/<管线>/<任务号>/<z>/<x>/<y>.<扩展名>(影像、等高线与将来的矢量共用,刻意不按数据类型各开一条,它同时登记进 5001 瓦片端口的路径白名单,前后端两份名单由一条相等性断言钉死);写走POST /api/export/<管线>/<任务号>,管线名对着contracts.artifact.PIPELINES校验。 - 日志尾随走 REST 轮询,不走 Socket.IO。 本应用没有 room / namespace,任何 emit 都会发给所有连着的客户端,逐行日志事件等于把一个任务的日志广播给所有人。新增的 socket 事件只有一个:
task_gap_decision。 - CI 三项新门禁:
pytest-cov覆盖率地板--cov-fail-under=55(棘轮起步值,只准往上调)、tag 与Config.APP_VERSION的一致性检查、发布产物的 SHA-256 清单(随包挂在 Release 上,生成后立刻自校验)。 - 高程/地形的规模预告修了两处:速度档下超高分辨率源的最大层级此前少报一级(层级偏移被加在上限截断之后,而实际切片是在之后才截断的);跨 180° 经线的栅格张数此前少报约六成。
/api/raster/inspect因此在每个文件与汇总节点上都多返回一个recommended_maxzoom_by_quality(三档 → 实际会切到的层级),recommended_maxzoom本身没变。
验证
- 本节没有全量测试的通过数 —— 发版时由跑完整套件的那一步填。这里只写已经机器核对过的事实,不给一个编出来的数字。
- 已机器核对:
src/routes/里的 71 条路由与本仓 README「API 端点」一节双向逐条对上(多一条、少一条都会让tests/test_docs_claims.py变红);README 项目结构树里列的每一个仓内路径都真的存在;两个 CI workflow 里python -m pytest tests/仍然是字面子串且仍排在python nuitka_build.py之前;新增的界面文案中英双语齐全、占位符两边一致。 - 打包不需要为新模块加任何 Nuitka 参数,这一条是核对出来的不是猜的:从
app.py出发对src/做了一次导入图可达性遍历,src/contracts/与全部新服务都静态可达(含几处函数体内的 import —— 模块名是编译期常量,静态分析跟得住)。理由与链路记在nuitka_build.py的注释里。 - 没有做的:三平台产物的实机冒烟由 CI 在发版时跑;本轮没有做「快了多少 / 省了多少磁盘」的改前改后计时对比,所以上面任何一条都不带性能倍数。
通用说明
- 下载安装:从下方 Assets 下载对应平台压缩包(
terraforge-windows.zip/terraforge-linux.tar.gz/terraforge-macos.tar.gz),解压即用,无需安装 Python 环境。 - 下载体积:每个平台仍包含 167 MB 的全球底图分卷(自 v0.2.8 起)。
- 首次运行:启动可执行文件后,浏览器访问 http://localhost:5000 ;代理、并发、缓存管理等在「配置」页修改。程序另会监听 5001 出瓦片,不放行也能用。
- 许可证与第三方声明:程序目录下的
LICENSE(MIT)与THIRD_PARTY_NOTICES.md。MIT 只覆盖软件代码,不授予任何数据与在线服务的使用权。 - 历史版本:完整更新历史见仓库 CHANGELOG.md。
- 使用文档:见仓库 README.md 与 docs/guides/QUICKSTART.md。