经纬志
首页 / 实战手册 / 数据加载

GeoServer 把 4326 的数据声明成 4490,图层偏了半米

先看结论

同一份数据直接读文件正常,从 GeoServer 出图就整体平移,差半米,肉眼几乎看不出。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

同一块行政边界,用 QGIS 打开原始 .shp 时与卫星底图吻合; 通过 GeoServer 的 WMS 出图后,整条边界向西平移约 0.45 米, 在 1:1000 的规划图上正好压在红线外侧,验收被判定为压线。

用 GetFeature 取一个顶点,返回的经纬度与原始值差 0.0000045 度左右。

原因

数据实际是 WGS84(EPSG:4326),但发布图层时 nativeCRS 选了 EPSG:4490。GeoServer 认为两者是不同基准, 在输出时会做一次基准转换,即便 towgs84 全为 0 也可能引入经度偏移。

更糟的是反过来的情况:nativeCRS 写 4326、数据其实是 4490, 每经过一次 WMS 请求就做一次转换,误差累积。

排查过程

  1. 在 GeoServer 的「编辑图层」页看 Declared SRS 与 Native SRS。
  2. 用 GetFeature 取一个已知顶点,与原始文件的坐标直接相减。
  3. 差额若在 1e-6~1e-5 度量级,说明是基准转换而非投影变换。
  4. 把 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, '坐标被改动');

适用版本

组件版本说明
GeoServer2.20 ~ 2.24发布页可选 SRS handling 三种策略
PostGIS3.x几何的 SRID 与图层声明应一致
QGIS3.28 及以上可直接读取 .prj 验证真实坐标系
引用本文

经纬志(2026)。《GeoServer 把 4326 的数据声明成 4490,图层偏了半米》。https://www.jingweizhi.com/cookbook/geoserver-srs-mismatch.html(访问日期 2026-11-03)。