4. 空间参考系统与坐标变换工程实践
在PostgreSQL地理空间平台中,几何对象的坐标值本身没有意义,只有绑定到某个空间参考系统(SRS)之后,坐标才对应地球上的真实位置。工程上最常见的两类SRS是地理坐标系(GCS)与投影坐标系(PCS):前者以经纬度为单位,单位是度,适合全球范围存储与交换;后者把椭球面投影到平面,单位通常是米,适合量算距离、面积和做空间索引。EPSG编码是这两类坐标系的标准身份证,例如EPSG:4326表示WGS 84经纬度,EPSG:3857表示Web墨卡托,EPSG:4490表示CGCS2000经纬度,EPSG:4547表示CGCS2000的3度带高斯-克吕格投影。
理解EPSG编码的关键,是分清“基准面”和“投影方法”两层含义。EPSG:4326与EPSG:4490都是经纬度,但基准面不同,前者是WGS 84,后者是CGCS2000,两者在国内日常精度下差异很小,但在高精度控制测量中不能混用。EPSG:3857与EPSG:4547都是平面坐标,但前者是球面墨卡托,形变随纬度急剧增大,只适合Web切片展示;后者是高斯-克吕格投影,中央经线附近形变极小,适合工程量算。选错编码,往往不会报错,却会让距离和面积悄悄失真。
入库阶段最稳妥的策略是“同列同SRID”。如果一张表里既有4326又有4547,空间索引会失效,ST_DWithin、ST_Intersects等函数的结果也不可信。常见做法是在INSERT或COPY时用ST_Transform把源数据统一到目标SRID,例如把采集到的CGCS2000经纬度写入投影列:
INSERT INTO poi (name, geom)
VALUES (
'示例点',
ST_Transform(
ST_SetSRID(ST_MakePoint(116.397, 39.908), 4490),
4547
)
);
这里ST_SetSRID只改元数据、不动坐标,ST_Transform才真正做基准面与投影换算。顺序不能颠倒:先给几何对象贴上正确的源SRID,再变换到目标SRID。若源数据本身没有SRID,直接ST_Transform会报“transform: Invalid coordinate”或返回错误结果。
查询阶段的性能陷阱最典型。假设表存的是EPSG:4547,用户传入的是4326的经纬度范围,写成ST_DWithin(geom, ST_Transform(ST_MakePoint(116.4,39.9),4547), 1000)是合理的,因为变换只发生在常量上,索引仍可用于geom列。反过来写成ST_DWithin(ST_Transform(geom,4326), point4326, 0.01),就会对每一行geom做变换,索引完全用不上,百万级数据下响应时间可能从毫秒级升到秒级甚至分钟级。判断依据很直接:用EXPLAIN ANALYZE看是否出现Seq Scan,以及ST_Transform是否出现在索引条件之外。
如果业务确实需要频繁按另一种SRID查询,可以建立表达式索引,把变换结果物化:
CREATE INDEX idx_poi_geom_4326
ON poi USING GIST (ST_Transform(geom, 4326));
这样按4326查询时,优化器可以直接走该索引。代价是写入时多一次变换计算,空间占用也会增加,适合读多写少的图层。
精度方面,EPSG:3857在纬度60度附近长度形变约2倍,用它算面积会严重偏大,因此量算必须回到投影坐标系或地理坐标系配合geography类型。ST_Transform本身是数值计算,4326与4490之间转换通常只有厘米级差异,但若源数据基准面未知,误差可能达到百米级。工程上应把SRID作为数据字典的一部分强制登记,入库前用ST_SRID检查,发现0或异常值先拦截。
综合来看,坐标系工程的核心不是记住多少EPSG编码,而是建立三条纪律:入库前确认并统一SRID,查询时让变换发生在常量侧或索引侧,展示时按目标底图SRID预转换。做到这三点,跨坐标系数据的精度与性能问题基本可控。