坐标转换最危险的错误不是程序崩溃,而是程序给出一个数值合理、位置却偏移数十公里的结果。OxiGeo 0.2.4 的官方发布把这一风险写得很直白:TransverseMercator 是球面模型,不适用于 UTM 和国家格网;该版本新增明确的 SphericalTransverseMercatorEllipsoidalTransverseMercator 类型别名,并以一个真实的 UTM 33N 参考点把两者的差异固定进回归测试。发布说明称,在北纬 48°,把球面模型用于 UTM/国家格网会造成约 24.9 km 的北向偏差。

对 GIS 开发者,这不是某个 Rust 命名优化,而是一个数据合同问题:坐标的地球模型、EPSG 定义、线性单位、坐标轴、feature flag 与输入输出精度必须随结果可追溯。若这些假设只藏在默认类型或构建特性里,程序即使“成功完成转换”,下游空间 join、瓦片定位和面积统计也可能基于错误坐标运行。

可核查事实

  • OxiGeo 0.2.4 于 2026 年 8 月 18 日发布,Release 提供从 v0.2.3 到 v0.2.4 的完整 changelog 链接。

  • oxigeo-proj 新增 SphericalTransverseMercator(等同 TransverseMercator)和 EllipsoidalTransverseMercator(等同 GaussKruger)的明确类型别名,并在 crate root 重新导出。

  • Release 明确警告 TransverseMercator 是基于球面的实现;用于 UTM 或国家格网是错误的,在北纬 48° 会产生约 24.9 km northing 偏差。

  • 版本新增 regression test,在真实 UTM 33N 参考点固定并区分两种模型。

  • oxigeo-geoparquet 新增 GeoParquetReader::from_bytes,让内存中的 GeoParquet image 使用与磁盘输入相同的 read_geometriesread_row_groupread_allread_pushdown 路径。

  • Release 为 GeoParquet 增加保留每行索引对齐的 optional geometry 读取:null geometry 以 None 保留,而不是让 geometry 与 property row 脱节;同时暴露 geometry column 的 declared encoding。

  • oxigeo-geotiff 修复 GDAL internal mask IFD 被误算为 overview level 的问题;Release 说明此前可能让后续 level index 指向错误分辨率,现以 mask-free level mapping 处理。

  • oxigeo-gpkg 修复 SQLite REAL type affinity 的读取:声明为 REAL/DOUBLE/FLOAT 的无损 40.0 若底层存为整数 40,typed scan 会还原为 Float。

  • Release 还修复多个 EPSG/投影正确性问题,例如 State Plane feet 单位、JGD2011 日本平面直角坐标注册、以及 SIMD fast path 在不适用时应回退标量路径。

这些事实表明版本改变的核心是“避免无声的空间语义偏移”。每一项都需要在采用者自己的数据、目标 CRS 和构建模式上重新验证。

核心机制

可靠坐标转换有四个不能互相替代的层。第一层是地球模型:球面与椭球的横轴墨卡托公式并不是可随意互换的实现细节。第二层是 CRS 定义:EPSG 代码对应投影参数、datum、prime meridian、axis 和单位;错误地把 feet 定义为 metres,坐标会按比例失真。第三层是数据表示:null geometry、GeoArrow/WKB encoding、GeoTIFF overview/mask 的索引关系决定读取到的要素和像元是否仍与属性/分辨率对齐。第四层是构建特性:stdno_std、proj database 或 SIMD 路径可能改变可用依赖和实际执行分支。

类型别名的意义在于让调用点显式表达第一层选择。若函数签名或变量名只写 TransverseMercator,审阅者无法判断使用的是球面还是椭球模型;明确的 Spherical...Ellipsoidal... 让误用更容易在代码审查中发现。但类型名称本身不足以保证正确:仍要针对业务 CRS 用权威控制点和独立工具验证数值。

GIS 场景

假设一个市政设施团队将 GNSS 点位转换到 UTM 33N,再与道路、地籍和工单图层做空间 join。发布前选择一组已知控制点,固定输入经纬度、目标 EPSG、期望 easting/northing 和容差。测试既覆盖单点转换,也覆盖点集转换;若 batch 路径启用 SIMD,则结果还要与标量路径逐点比较。任何超出容差的结果都暂停写入,而不是让图层看似落在城市附近就通过。

同一项目若读取 GeoParquet 的资产表和 COG 背景图,也需要交叉验收:GeoParquet 的每个 null geometry 仍与原 property row 对应;记录声明的 geometry encoding;COG level 选择对应实际 overview,而非 internal mask。否则空间 join 可能把属性错配给几何,或请求一个 overview 时意外读取 mask 像元;二者都可能生成形式完整、内容错误的可视化和统计。

技术路径

建立 CRS regression fixture,至少包含一个 UTM、一个国家/State Plane CRS、一个不同线性单位的 CRS 与一个非 Greenwhich 或带 datum 转换的案例。每项记录输入经纬度/高度、源与目标 CRS、地球模型、预期输出、容差和权威来源。构建矩阵覆盖默认 feature、必要的 no_default_features 配置以及启用 SIMD/数据库特性后的路径。只在所有矩阵中与权威值和标量路径一致时,才升级库版本。

数据读取另建 fixture。GeoParquet 同时包含非空与 null geometry、至少一种实际声明 encoding 和与属性可核对的行 ID;断言读回的 collection 长度、每行 geometry optional 状态和属性索引不变。COG fixture 应含 full-resolution、overview 和 GDAL internal mask,断言每个 public level 的尺寸、像元范围与 mask-free index。GeoPackage fixture 包含整数外观的 REAL 值和溢出页面/宽表场景,核对类型与数值。

生产运行记录转换器版本、crate feature、EPSG/PROJ 数据版本、控制点 fixture 版本和输入数据 hash。输出数据写入前保存 CRS、axis order、单位和处理日志;当输入 metadata 与项目合同冲突时直接拒绝或要求人工指定,不能猜测。这样,发布说明中“修复正确性”的改变才能被转化为可审计的结果差异。

风险边界

Release 中的 24.9 km 数值属于列出的模型和纬度情形,不能作为其他 CRS、地点或坐标操作的通用误差估计。类型别名和回归测试也不能替代权威 CRS 定义、现场控制点、数据质量和业务容差。内存读取、mask-free overview 映射和 SQLite affinity 修复解决的是特定库路径,其他 GDAL、PROJ、数据库或浏览器实现仍须独立验证。对受监管或安全关键的测绘成果,应采用组织批准的坐标操作链和签核流程。

检查清单

  1. 为每个转换明确球面/椭球模型、源/目标 CRS、datum、axis order 和线性单位。

  2. 用权威控制点覆盖 UTM、国家格网、不同单位与 datum/prime-meridian 边界,并设置容差。

  3. 比较批处理/SIMD 与标量路径;任一差异超出容差即阻断发布。

  4. GeoParquet 测试必须含 null geometry,断言 geometry 与 property row 的索引持续对齐。

  5. COG 测试必须区分 overview 和 GDAL internal mask,逐 level 核对尺寸与像元来源。

  6. 记录 crate 版本、feature、EPSG/PROJ 数据版本、fixture 和输入数据 hash。

  7. metadata 缺失或与合同冲突时拒绝、提示或要求人工指定,不让转换器猜测 CRS。

结论

OxiGeo 0.2.4 把坐标模型、文件编码和栅格层级中的多种无声偏差显式化。GIS 团队的任务是进一步把这些修复落到控制点、数据 fixture、构建矩阵和运行记录上:先证明每个坐标与像元来自正确的模型和层级,再让它进入空间分析。

参考来源

  1. OxiGeo v0.2.4 Release:https://github.com/cool-japan/oxigdal/releases/tag/v0.2.4