GeoJSON 数据源
业务数据大多是 GeoJSON。加一个 source、配一个图层, 数据就上了图;后续更新只改 source 的 data,不需要重建图层。
数据源的两种写法
data 可以是 GeoJSON 对象,也可以是一个 URL。
对象适合由前端计算或接口返回后直接上图的场景,URL 适合静态文件。
/* 内联对象 */
map.addSource('stations', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{ type: 'Feature', properties: { name: 'A' },
geometry: { type: 'Point', coordinates: [116.39, 39.90] } }
]
}
});
/* 远程文件 */
map.addSource('zones', {
type: 'geojson',
data: '/data/zones.geojson'
});
两种写法都要求坐标系是 WGS84 经纬度,坐标顺序
[经度, 纬度],可选第三位高程。
如果数据来自投影后的米制坐标,必须先转换,否则点会落在赤道附近。
按几何类型选图层
| 几何 | 图层 type | 关键属性 |
|---|---|---|
| Point / MultiPoint | circle | circle-radius、circle-color |
| Point | symbol | icon-image、text-field |
| LineString | line | line-color、line-width、line-dasharray |
| Polygon | fill | fill-color、fill-opacity |
| Polygon 边界 | line | 与 fill 共用 source,单独描边 |
| 点密度 | heatmap | heatmap-radius、heatmap-weight |
map.addLayer({
id: 'station-dots',
type: 'circle',
source: 'stations',
paint: {
'circle-radius': ['interpolate', ['linear'], ['zoom'], 3, 3, 7, 8],
'circle-color': '#1f5fd0',
'circle-stroke-color': '#fff',
'circle-stroke-width': 1.5
}
});
一个 source 可以被多个图层复用。常见的组合是面用
fill 填色、同一个 source 再用 line 描边,
分开之后填充透明度不会影响边界清晰度。
更新数据只改 setData
var src = map.getSource('stations');
src.setData(nextFeatureCollection);
图层不用删除重建,视野、交互状态都保持不变。
每次 setData 会全量替换数据并重新解析,
高频更新(比如每秒多次)时应先在外部合并变更,再一次性写入。
数据量大时先聚合
GeoJSON source 支持在客户端聚合点,把密集点合并成带计数的簇:
map.addSource('points', {
type: 'geojson',
data: points,
cluster: true,
clusterRadius: 45,
clusterMaxZoom: 8
});
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#1f5fd0',
'circle-radius': ['step', ['get', 'point_count'], 12, 20, 18, 100, 26]
}
});
聚合后要素的 properties 里会多出 point_count
与 cluster_id。展示单个点时,需要另加一个
filter: ['!', ['has', 'point_count']] 的图层,
否则原始点与簇会同时出现。
聚合适合几万个点。到几十万时,浏览器端解析和三角化会成为瓶颈, 应该改用矢量瓦片,把聚合与简化放到服务端。
feature-state 与选中态
高亮单个要素时不必改数据。给 source 指定
promoteId(或 generateId),
再用 setFeatureState 标记状态,图层用
feature-state 读取。
map.addSource('parcels', {
type: 'geojson',
data: parcels,
promoteId: 'code'
});
map.setFeatureState({ source: 'parcels', id: 'BJ-01' }, { hover: true });
map.addLayer({
id: 'parcels-fill',
type: 'fill',
source: 'parcels',
paint: {
'fill-color': ['case', ['boolean', ['feature-state', 'hover'], false],
'#c2410c', '#1f5fd0']
}
});
没有 promoteId 时,要素 id 来自数据的 id 字段;
两者都没有就取不到状态,高亮会静默失效。
查询点击到了哪个要素
map.on('click', 'parcels-fill', function (e) {
var f = e.features[0];
console.log(f.properties.code, f.properties.name);
});
/* 鼠标移入时改指针,是交互细节里最容易被忽略的一条 */
map.on('mouseenter', 'parcels-fill', function () {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'parcels-fill', function () {
map.getCanvas().style.cursor = '';
});
事件绑在图层上时,图层必须已存在。写在 load
回调之前会绑不上,且没有报错。样式切换后图层被清掉,事件仍然留着,
但再也触发不了,重建图层时需要重新绑定。
常见错误
data 传成 JSON 字符串
data: JSON.stringify(fc) 不会报错,但 source 解析为空,
地图上没有要素。要么传对象,要么传 URL 字符串。
只改对象不调用 setData
直接修改原 FeatureCollection 的 features 数组,地图不会自动刷新。
MapLibre 不监听对象内部变化,必须显式调用 setData。
属性值类型混乱
同一个字段一会儿是数字、一会儿是字符串,表达式里做数值比较时 部分要素会被过滤掉。入库前统一类型,比在表达式里做兼容更省事。
经纬志(2026)。《GeoJSON 数据源》。https://www.jingweizhi.com/maplibre/tutorial/geojson-source.html(访问日期 2026-11-03)。