在基于 Cesium 开发 GIS 应用时,我们经常会遇到矢量图层(WFS)渲染变形的问题 ------ 明明是矩形的区域在地图上却显示为平行四边形,或线条出现不规则扭曲。本文将分享一次完整的问题排查过程,从现象分析到根源定位,最终发现竟是 GeoServer 的一个精度设置在 "作祟"。
问题现象:WFS 图层变形与 WMS 的诡异对比
项目中需要加载某区域的矢量边界数据,分别尝试了 WMS 和 WFS 两种服务:
- WMS 服务 :通过
WebMapServiceImageryProvider
加载,图形显示正常,矩形边界保持规则形状 - WFS 服务 :通过
GeoJsonDataSource
加载相同图层,图形出现明显变形,矩形变成平行四边形,且偏差随区域范围扩大而加剧
排查过程:从坐标到投影的层层拆解
第一步:验证坐标转换逻辑
首先怀疑是 Cesium 的坐标转换过程存在精度损失。通过手动转换坐标进行验证:
ini
// 原始经纬度坐标
const originalLon = 116.345678;
const originalLat = 39.987654;
// 转换为Cesium笛卡尔坐标
const cartesian = Cesium.Cartesian3.fromDegrees(originalLon, originalLat);
// 反推回经纬度
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const convertedLon = Cesium.Math.toDegrees(cartographic.longitude);
const convertedLat = Cesium.Math.toDegrees(cartographic.latitude);
console.log("转换偏差:", (convertedLat - originalLat).toFixed(8));
第二步:检查投影与坐标系匹配
Cesium 在二维模式下默认使用 WGS84(EPSG:4326),查看 WFS 数据是否为 WGS84(EPSG:4326)。通过打印投影信息发现:
arduino
console.log("当前投影:", viewer.scene.mapProjection.constructor.name);
// 输出"GeographicProjection",符合预期
// 输出`WebMercatorProjection`, 则为 Web Mercator(EPSG:3857)平面坐标
GeographicProjection
(地理投影) ,其椭球体参数(长半轴 6378137m,短半轴 6356752.314m)完全符合 WGS84 坐标系(EPSG:4326) 的标准参数。
第三步:检查坐标数据传递正确
在geoserver中预览wfs geojson的数据,在控制台console.log获取的eojson的坐标数据,再检查最后绘制实体的顶点数据
ini
// 在获取geojson后,提取原始坐标(以第一个多边形为例)
if (geojson.features.length > 0) {
const feature = geojson.features[0];
let coords;
// 处理Polygon和MultiPolygon
if (feature.geometry.type === 'Polygon') {
coords = feature.geometry.coordinates[0]; // Polygon坐标:[[[x1,y1], [x2,y2], ...]]
} else if (feature.geometry.type === 'MultiPolygon') {
coords = feature.geometry.coordinates[0][0]; // MultiPolygon第一个多边形
}
// 输出原始坐标(取前4个顶点)
console.log('原始顶点坐标:', coords.slice(0, 4).map(c => `[${c[0].toFixed(6)}, ${c[1].toFixed(6)}]`));
// 存储原始坐标,供后续与渲染坐标对比
this.originalCoords = coords.slice(0, 4);
}
ini
// 在加载后添加(ds为dataSource实例)
ds.entities.values.forEach(entity => {
if (entity.polygon) {
// 取多边形的第一个顶点
const positions = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
const firstPos = positions[0];
// 转换为经纬度
const cartographic = Cesium.Cartographic.fromCartesian(firstPos);
const lon = Cesium.Math.toDegrees(cartographic.longitude);
const lat = Cesium.Math.toDegrees(cartographic.latitude);
console.log('Cesium渲染坐标:', [lon, lat]); // 与原始GeoJSON坐标对比
}
});
输出结果坐标数据一致,这时需要注意坐标的小数位数,主包发现数据只有4位小数,通过查询4位小数精度范围在11m左右,而原始数据在arcgis中精度不只4位小数,因此定位问题是geoserver的数据精度不够,修改geoserver的数据精度即可。
点开geoserver发现数据精度是在创建工作空间时就规定了,在一开始时可能默认忘记了,再次修改即可,这里修改为6位0.11m误差。
