GeoJSON 数据加载
GeoJSON 是 Web 地图最通用的矢量交换格式。Leaflet 用一个
L.geoJSON 就能把点、线、面转成图层,样式和交互通过三个回调控制。
结构回顾
一个合法的 GeoJSON 对象要么是单个 Feature,要么是
FeatureCollection。几何部分支持 Point、LineString、Polygon、
MultiPoint、MultiLineString、MultiPolygon 和 GeometryCollection。
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "name": "中关村", "level": 2 },
"geometry": {
"type": "Point",
"coordinates": [116.316, 39.983]
}
}
]
}
坐标顺序与 Leaflet 相反
GeoJSON 规范是 [经度, 纬度],Leaflet 的 LatLng 是
[纬度, 经度]。直接手写数据时把顺序写反,点会跑到几千公里外,
而且不会报错。L.geoJSON 在内部会自动翻转,
所以从标准文件读进来的数据不需要手动处理;只有自己拼 L.marker 时才需要留心。
三个关键回调
L.geoJSON(data, options) 的 options 里有三个回调决定了最终呈现:
| 选项 | 作用 | 返回值 |
|---|---|---|
pointToLayer | 把点要素换成自定义图层 | 一个 Layer,例如 L.circleMarker |
style | 为线、面设置路径样式 | Path 的 options 对象 |
onEachFeature | 逐个要素绑定弹窗、事件、提示 | 无 |
filter | 返回 false 的要素直接跳过 | 布尔值 |
coordsToLatLng | 自定义坐标转换,用于非 WGS84 数据 | LatLng |
L.geoJSON(data, {
style: function (feature) {
return {
color: feature.properties.color || '#1f5fd0',
weight: 2,
fillOpacity: 0.22
};
},
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, {
radius: 6,
color: '#ffffff',
weight: 2,
fillColor: '#1f5fd0',
fillOpacity: 1
});
},
onEachFeature: function (feature, layer) {
layer.bindPopup(
'<b>' + feature.properties.name + '</b><br>' +
'等级 ' + feature.properties.level
);
},
filter: function (feature) {
return feature.properties.level >= 2;
}
}).addTo(map);
点要素默认会被画成 Marker。默认 Marker 依赖一张 PNG 图标,数量一多既慢又容易缺图。
用 pointToLayer 返回 L.circleMarker,渲染更轻,也不需要任何图片资源。
获取远程数据
Leaflet 1.9 不再内置 AJAX 辅助方法,用浏览器原生的 fetch 即可。
重点是把 HTTP 错误和 JSON 解析错误分开处理,否则用户只会看到一张空地图:
fetch('/data/poi.geojson')
.then(function (res) {
if (!res.ok) throw new Error('HTTP ' + res.status);
return res.json();
})
.then(function (data) {
var layer = L.geoJSON(data, { pointToLayer: dot }).addTo(map);
map.fitBounds(layer.getBounds(), { padding: [24, 24] });
})
.catch(function (err) {
console.error('数据加载失败', err);
});
getBounds() 在没有要素时返回无效的范围,直接 fitBounds
会让地图跳到奇怪的位置。加一个 layer.getLayers().length 判断更稳妥。
样式随属性变化
专题图通常按属性分级设色。把分级逻辑写在 style 回调里,返回不同的
fillColor;数值变化后调用 layer.setStyle(fn) 重新计算,
不需要删除重建整个图层。
function ramp(v) {
return v > 80 ? '#7f1d1d' : v > 50 ? '#c2410c' : v > 20 ? '#a16207' : '#17694a';
}
layer.setStyle(function (feature) {
return { fillColor: ramp(feature.properties.value), fillOpacity: 0.75 };
});
数据量大时
- 地图初始化时加
preferCanvas: true,矢量改用 canvas 渲染,几千个面也不会卡。 - 给
L.geoJSON传interactive: false,省掉每个要素的事件绑定。 - 先用
filter剔除无关要素,比全部渲染后再隐藏便宜得多。 - 超过几万个要素时,Leaflet 的单线程渲染会成为瓶颈,应换用矢量瓦片或 WebGL 方案。
常见问题
面画出来了但空洞没显示
带洞的多边形要用嵌套数组:外环在前,内环依次在后。 如果只传一层坐标数组,Leaflet 会把它当成单个外环。
数据是 GCJ-02,叠在 OSM 上整体偏移
这不是 GeoJSON 的问题。coordsToLatLng 可以在解析阶段统一转换坐标,
但更推荐在数据生产环节转成 WGS84,避免每个前端各写一套转换。
属性里的数字变成了字符串
GeoJSON 不约束 properties 的值类型。分级设色前用
Number(feature.properties.value) 显式转换,否则字符串比较会得到意外结果。
经纬志(2026)。《GeoJSON 数据加载》。https://www.jingweizhi.com/leaflet/tutorial/geojson.html(访问日期 2026-11-03)。