把 3857 的米值当经纬度传入,标记点直接消失
接口返回 13522390, 3669872,塞进 L.marker 后地图一 zoom 就飞到北极,标记也不见了。
现象
上海点位在 EPSG:3857 下是 x ≈ 13522390, y ≈ 3669872(米)。
直接写成 L.marker([3669872, 13522390]) 后:
地图要么瞬间缩放到最北边,要么整张图空白,控制台没有任何报错。
OpenLayers 里表现不同:new ol.Feature(new ol.geom.Point([13522390, 3669872]))
如果图层的 projection 声明为 EPSG:4326,点会被渲染到经纬度 13522390°,超出视野,同样看不见。
原因
Leaflet 的 L.latLng(lat, lng) 期望入参是度,内部再乘 1/111319.49
投影到像素。传入 3669872 这个数值,等于声称纬度 3669872°,远超 ±90°,
墨卡托投影把它夹到 85.0511°,于是地图定位到北极圈附近。
判断依据是数量级:经纬度经度范围 -180~180,纬度 -90~90;
EPSG:3857 的 x 范围 ±20037508.34,y 同范围,中国区域的 x 在
7.3e6~1.5e7,y 在 2.4e6~6.1e6。只要看到六七位整数,就是米,不是度。
| 坐标系 | 上海示例 | 数值量级 |
|---|---|---|
| EPSG:4326 | 121.473701, 31.230416 | 个位到三位 |
| EPSG:3857 | 13522390, 3669872 | 百万到千万 |
| EPSG:4547(高斯 3 度带) | 500000, 3458000 左右 | 十万到百万 |
排查过程
- 打开网络面板,看接口响应里坐标的原始数值,不要看地图上渲染后的结果。
- 把第一个坐标和第二个坐标分别与 ±180 / ±90 比较。任一超出,就不是经纬度。
- 在接口地址或响应字段里找
srs、crs、epsg、spatialReference之类的元数据。 - 仍不确定时,用反投影算一遍,结果落在上海市范围内即可确认是 3857。
// Leaflet 内置反投影,直接把米转回经纬度
const p = L.CRS.EPSG3857.unproject(L.point(13522390, 3669872));
// => LatLng(31.2304, 121.4737)
L.marker(p).addTo(map);
解决方案
在数据进入渲染层之前统一反投影,而不是在渲染层逐个打补丁。 用 proj4js 可以一次性处理整份 GeoJSON。
// proj4js:3857 -> 4326 批量转换
const src = 'EPSG:3857', dst = 'EPSG:4326';
function reprojectGeometry(geom, fn) {
if (geom.type === 'Point') return { ...geom, coordinates: fn(geom.coordinates) };
if (geom.type === 'LineString' || geom.type === 'MultiPoint')
return { ...geom, coordinates: geom.coordinates.map(fn) };
if (geom.type === 'Polygon' || geom.type === 'MultiLineString')
return { ...geom, coordinates: geom.coordinates.map(r => r.map(fn)) };
return { ...geom, coordinates: geom.coordinates.map(p =>
p.map(r => r.map(fn))) };
}
const fn = c => proj4(src, dst, c);
fc.features.forEach(f => { f.geometry = reprojectGeometry(f.geometry, fn); });
如果服务端可控,优先让服务端直接输出 4326(GeoJSON 的 RFC 7946 也只允许 4326), 前端就不需要承担投影转换的职责。
验证
转换后上海点应为 121.4737, 31.2304,纬度落在 31 附近,经度落在 121 附近;
用 map.fitBounds 能正常框住数据,不再跳到北极。
const ll = L.CRS.EPSG3857.unproject(L.point(13522390, 3669872));
console.assert(Math.abs(ll.lat - 31.2304) < 0.001, '纬度不对');
console.assert(Math.abs(ll.lng - 121.4737) < 0.001, '经度不对');
适用版本
| 库 | 版本 | 说明 |
|---|---|---|
| Leaflet | 1.7 ~ 1.9 | L.CRS.EPSG3857.unproject 可直接用 |
| OpenLayers | 6.x ~ 9.x | 数据需声明 projection,否则不参与重投影 |
| proj4js | 2.9 及以上 | 3857 与 4326 已内置,无需 defs |
| Cesium | 1.100 及以上 | WebMercatorTilingScheme 同样以米计 |
经纬志(2026)。《把 3857 的米值当经纬度传入,标记点直接消失》。https://www.jingweizhi.com/cookbook/epsg3857-as-4326.html(访问日期 2026-11-03)。