实操指南9 分钟阅读
如何搭一条撑得过第二次运行的地理空间数据管道
大多数空间数据管道都能完美地跑通一次。真正重要的设计决策,是让它跑到第二十次时依然可靠。
- 作者
- HuiTu Technology
- 发布时间
只跑一次的地理空间流程,只能算脚本。只有当同一套流程下个月面对变化了的源数据还能跑通,并且你能立刻判断结果是否可信时,它才算一条数据管道。
把三层拆开
接入、转换、校验三层必须严格分离。混在一起时,源数据格式一变就得改分析逻辑;而校验一旦失败,你也判断不出是哪一层出的问题。
- 接入层:抓取并原样存储源数据,不做任何修改,并打上时间戳。这一层绝不做转换。
- 转换层:解析、标准化、重投影、关联。过程必须是确定性的,并且可以从存好的原始数据重新跑出来。
- 校验层:断言结果满足事先写明的条件。每次运行都执行,而不是等到看着不对劲才跑。
每一处都把坐标系写明白
空间数据管道最常见的故障是悄无声息的投影不匹配。程序不报错,点只是落错了地方,或者算出来的距离单位其实是度——而结果看上去足够合理,于是就这么交付出去了。
-- Constrain the CRS at the table level so bad data cannot be inserted
ALTER TABLE poi
ADD CONSTRAINT poi_geom_srid CHECK (ST_SRID(geom) = 4326);
-- And measure in a projected system, never in degrees
SELECT id,
ST_Distance(
ST_Transform(geom, 32633),
ST_Transform(:target, 32633)
) AS metres
FROM poi;校验要写成断言,而不是靠肉眼看
每次运行都应当检查一组条件,不满足就大声报错并中断。目的是让你从管道本身得知问题,而不是三周后从使用数据的人那里得知。
| 校验项 | 触发失败的情况 |
|---|---|
| 行数与上一次运行的差异在容差范围内 | 某个来源悄悄改了分页逻辑 |
| 所有几何对象都有效 | 自相交或畸形的多边形混进了数据 |
| 所有要素都落在研究区域的外接矩形内 | 投影不匹配,或者经纬度被写反了 |
| 必填字段的非空率高于阈值 | 页面模板变了,解析程序被打断 |
| 重复率落在预期区间内 | 去重主键失效了 |
| 没有异常的坐标堆叠 | 地理编码开始回退到中心点 |
让每一层都是幂等的
同一层跑两次,结果必须和跑一次完全一样。这意味着用 upsert 而不是直接 insert,用确定性的标识符而不是自增序号,除非明确设计,否则不在两次运行之间携带任何状态。没有幂等性,一次跑到一半的失败会把数据留在谁也说不清的状态里。
要做版本管理的不只是代码,还有产出物
- 为每次运行打上版本号与时间戳,并保留历史产出。
- 记录每一个输入图层的数据版本,包括那些很少变动的图层。
- 保存本次使用的参数,让结果可以被精确复现,而不是大致复现。
- 每次运行输出一份变更日志:新增了什么、删除了什么、修改了什么。
这里说的可复现不是学术上的讲究。当两份分析结论打架时,正是这些版本记录让你能在一小时内查清原因,而不是把两份都弃之不用。
一套不跟你对着干的技术栈
| 用途 | 工具 | 理由 |
|---|---|---|
| 存储与分析 | PostgreSQL + PostGIS | 有空间索引、有约束,而且 SQL 是可以逐句审的 |
| 格式转换 | GDAL / OGR | 几乎什么格式都能稳定地读和写 |
| 数据处理 | Python + GeoPandas | 转换逻辑好读,测试也好写 |
| 调度编排 | 你手上现成的任何调度器 | 调度器本身很少是真正值得纠结的问题 |
| 制图与复核 | QGIS | 肉眼快速核查,能抓到断言漏掉的问题 |
| 网页端交付 | 矢量瓦片(PMTiles) | 让大体量图层在浏览器里依然流畅 |