GeoServer 把 4326 的数据声明成 4490,图层偏了半米
同一份数据直接读文件正常,从 GeoServer 出图就整体平移,差半米,肉眼几乎看不出。
现象
同一块行政边界,用 QGIS 打开原始 .shp 时与卫星底图吻合;
通过 GeoServer 的 WMS 出图后,整条边界向西平移约 0.45 米,
在 1:1000 的规划图上正好压在红线外侧,验收被判定为压线。
用 GetFeature 取一个顶点,返回的经纬度与原始值差
0.0000045 度左右。
原因
数据实际是 WGS84(EPSG:4326),但发布图层时 nativeCRS 选了
EPSG:4490。GeoServer 认为两者是不同基准,
在输出时会做一次基准转换,即便 towgs84 全为 0 也可能引入经度偏移。
更糟的是反过来的情况:nativeCRS 写 4326、数据其实是 4490,
每经过一次 WMS 请求就做一次转换,误差累积。
排查过程
- 在 GeoServer 的「编辑图层」页看
Declared SRS与Native SRS。 - 用
GetFeature取一个已知顶点,与原始文件的坐标直接相减。 - 差额若在
1e-6~1e-5度量级,说明是基准转换而非投影变换。 - 把
SRS handling改成Force declared或Reproject native to declared, 再取一次,看差额是否消失。
# 取一个顶点对比
curl "http://localhost:8080/geoserver/gis/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=gis:districts&maxFeatures=1&outputFormat=application/json"
# 原始值 116.3974280, 39.9092300
# 返回值 116.3974235, 39.9092302
# 差 -4.5e-6 度,约 0.45 米
解决方案
nativeCRS 必须与实际数据一致。CGCS2000 的数据就写 4490,
WGS84 的就写 4326,不要因为「差不了多少」而混用。
// 通过 REST 修改图层的 nativeCRS 与 projectionPolicy
PUT /rest/workspaces/gis/datastores/pg/featuretypes/districts
Content-Type: application/json
{
"featureType": {
"nativeCRS": "EPSG:4326",
"srs": "EPSG:4326",
"projectionPolicy": "FORCE_DECLARED"
}
}
FORCE_DECLARED 用于「数据就是声明的坐标系,不需要转换」;
REPROJECT_TO_DECLARED 用于需要真正转换的场景。
两者不可互换。
| projectionPolicy | 行为 | 适用 |
|---|---|---|
| FORCE_DECLARED | 不改坐标,只改元数据 | 声明有误,实际坐标系正确 |
| REPROJECT_TO_DECLARED | 做真实转换 | 输出坐标系与数据不同 |
| KEEP_NATIVE | 按原样输出 | 不关心输出坐标系 |
验证
重新发布后用 GetFeature 取值,应与原始文件完全一致(差值为 0);
WMS 出图与 QGIS 直接打开的边界重合,压线判定恢复正常。
console.assert(returned[0] === 116.3974280, '仍在做基准转换');
console.assert(Math.abs(returned[0] - original[0]) < 1e-9, '坐标被改动');
适用版本
| 组件 | 版本 | 说明 |
|---|---|---|
| GeoServer | 2.20 ~ 2.24 | 发布页可选 SRS handling 三种策略 |
| PostGIS | 3.x | 几何的 SRID 与图层声明应一致 |
| QGIS | 3.28 及以上 | 可直接读取 .prj 验证真实坐标系 |
经纬志(2026)。《GeoServer 把 4326 的数据声明成 4490,图层偏了半米》。https://www.jingweizhi.com/cookbook/geoserver-srs-mismatch.html(访问日期 2026-11-03)。