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

CSV 的经纬度列写反,点全跑到索马里外海

先看结论

一批北京的点,加载后坐标显示 39.9, 116.4,落点在印度洋。

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

现象

上传的 CSV 表头是 name,lat,lng, 第一行数据为 天安门,116.397428,39.909230。 代码按 [row.lat, row.lng] 构造点位,地图上所有点都不见了; 若纬度写成 116,超出 ±90,Leaflet 会直接丢弃该点,不报错。

另一种情况是表头写成 lng,lat,但代码仍按 [row.lng, row.lat] 读取, 结果纬度 116 被钳制到 90,点全部堆在北极点附近。

原因

不同来源的 CSV 列序不统一:国内数据常见「经度在前」,国际数据常见「纬度在前」, 表头命名也有 lon/lng/longitude、lat/latitude 多种写法。 一旦按列下标(而不是按列名)读取,就会静默错位。

经纬度写反后,原本的经度 116 变成纬度,超出 ±90 被丢弃; 原本的纬度 39.9 变成经度,落在东经 39.9°,即索马里外海。所以「消失」和「跑到非洲」是同一个错误。

排查过程

  1. 打印 CSV 第一行原始文本,确认表头顺序与数据列顺序一致。
  2. 检查代码是按列名还是按列下标取值,按列下标的必须改为按列名。
  3. 加载前统计纬度列的最大绝对值,超过 90 就是列反了或数据错。
  4. 把第一个点单独标到地图上,看落点是在北京还是印度洋。
// 典型错误:按位置取
const pts = rows.map(r => [r[1], r[2]]);   // 实际拿到的是 [116.397428, 39.909230]
// -> Leaflet 认为纬度 116.397428,超限,点被丢弃

解决方案

按列名取值,并在入口加一次范围校验,把错误挡在渲染之前。

// PapaParse 表头已经解析成对象
const { data } = Papa.parse(text, { header: true, skipEmptyLines: true });

const points = data.map((r, i) => {
  const lat = Number(r.lat ?? r.latitude);
  const lng = Number(r.lng ?? r.lon ?? r.longitude);
  if (!Number.isFinite(lat) || Math.abs(lat) > 90)
    throw new Error(`第 ${i + 2} 行纬度非法:${r.lat}`);
  if (!Number.isFinite(lng) || Math.abs(lng) > 180)
    throw new Error(`第 ${i + 2} 行经度非法:${r.lng}`);
  return { name: r.name, lat, lng };
});

L.geoJSON(points.map(p => ({
  type: 'Feature',
  properties: { name: p.name },
  geometry: { type: 'Point', coordinates: [p.lng, p.lat] }   // GeoJSON 永远是经度在前
})), {
  pointToLayer: (f, ll) => L.circleMarker(ll, { radius: 4 })
}).addTo(map);

如果列反了但已经拿到坐标,交换一次即可。注意 GeoJSON 与 Leaflet 的构造顺序不同: GeoJSON 是 [lng, lat],L.latLng 是 (lat, lng)。

// 已确认列反:交换
const fixed = points.map(p => ({ ...p, lat: p.lng, lng: p.lat }));

// 校验交换后是否落在中国范围
const inChina = fixed.every(p => p.lng > 73 && p.lng < 135 && p.lat > 3 && p.lat < 54);

验证

第一个点应落在地图中心 116.397, 39.909, 全部点的经纬度 bbox 在中国范围内,点数与 CSV 有效行数一致(不再有被丢弃的行)。

console.assert(points.length === data.length, '有点被丢弃');
console.assert(points[0].lat === 39.909230 && points[0].lng === 116.397428, '列仍反着');

适用版本

库版本说明
PapaParse5.4.xheader: true 后按列名取值
Leaflet1.x纬度超限静默丢点,不抛异常
GeoJSON(RFC 7946)2016 起坐标顺序固定为 [经度, 纬度]
引用本文

经纬志(2026)。《CSV 的经纬度列写反,点全跑到索马里外海》。https://www.jingweizhi.com/cookbook/csv-lonlat-swapped.html(访问日期 2026-11-03)。