样式与图层
MapLibre 里没有「往地图上画一个图形」这种操作,只有数据源和图层。 样式 JSON 描述的就是这两者的组合,运行时改样式本质上也是改这份 JSON。
样式 JSON 的四个部分
{
"version": 8,
"sources": {
"base": {
"type": "vector",
"url": "https://example.com/tiles.json"
}
},
"layers": [
{ "id": "bg", "type": "background", "paint": { "background-color": "#eef3f7" } },
{
"id": "land",
"type": "fill",
"source": "base",
"source-layer": "countries",
"paint": { "fill-color": "#f7f4ec" }
}
],
"glyphs": "https://example.com/font/{fontstack}/{range}.pbf"
}
| 字段 | 作用 |
|---|---|
version | 样式规范版本,固定为 8 |
sources | 数据源定义:vector、raster、geojson、raster-dem |
layers | 绘制顺序,数组越靠后越在上层 |
glyphs | 字体切片地址,文本图层必需 |
sprite | 图标雪碧图地址,符号图层用到图标时必需 |
source-layer 只对矢量瓦片有效,值必须等于瓦片里实际存在的图层名。
写错不会报错,图层就是空的。用
https://demotiles.maplibre.org/tiles/tiles.json
可以查看 demo 数据里只有 countries、geolines、
centroids 三个图层。
paint 与 layout 的分工
paint 控制外观:颜色、透明度、线宽、半径,可以随缩放插值。
layout 控制几何排布与显示:是否可见、线端样式、文字大小与位置。
map.addLayer({
id: 'route-line',
type: 'line',
source: 'route',
layout: {
'line-cap': 'round',
'line-join': 'round'
},
paint: {
'line-color': '#1f5fd0',
'line-width': ['interpolate', ['linear'], ['zoom'], 3, 1.5, 8, 5],
'line-dasharray': [2, 2]
}
});
line-width 用 interpolate 表达式按缩放变化,
这是矢量地图的常见写法:低缩放细线,放大后加粗。
直接写固定值也不会报错,只是放大后显得太细。
图层顺序与 beforeId
addLayer 默认把新图层放到最上层。需要压在某层下面时,
第二个参数传目标图层的 id:
map.addLayer(pointLayer, 'route-line'); // 放在路线之下
用样式里已有的图层 id 做锚点最可靠。锚点 id 拼错时, 新图层会直接加到最上层,不抛错,于是覆盖关系与预期相反。
运行时改样式
| 操作 | 方法 |
|---|---|
| 改单个绘制属性 | setPaintProperty(layerId, prop, value) |
| 改单个布局属性 | setLayoutProperty(layerId, prop, value) |
| 显示 / 隐藏 | setLayoutProperty(id, 'visibility', 'none') |
| 删除图层 | removeLayer(id) |
| 替换整个样式 | setStyle(urlOrObject) |
| 读取当前样式 | map.getStyle() |
map.setPaintProperty('route-line', 'line-color', '#c2410c');
map.setLayoutProperty('route-line', 'visibility', 'none');
map.setLayoutProperty('route-line', 'visibility', 'visible');
切换样式的代价
setStyle 会丢弃当前的源与图层,换成新样式的内容。
底图切换必须这么做,但自定义图层要重新添加。
需要保留要素状态时,在 style.load 里重建:
function addOverlays() {
map.addSource('zones', { type: 'geojson', data: zones });
map.addLayer({ id: 'zones', type: 'fill', source: 'zones',
paint: { 'fill-color': '#17694a', 'fill-opacity': 0.25 } });
}
map.on('style.load', addOverlays);
map.setStyle('https://demotiles.maplibre.org/style.json');
v4 的 setStyle 支持 diff 参数,新样式与原样式共享的部分会被复用。
对同一份样式只改少量图层时,diff 能避免整图重绘与闪烁。
过滤表达式
filter 写在图层上,按属性筛选要素。
矢量瓦片的属性名来自瓦片数据,GeoJSON 的属性名来自你自己的 properties。
map.addLayer({
id: 'capitals',
type: 'circle',
source: 'base',
'source-layer': 'centroids',
filter: ['==', ['get', 'NAME'], 'Beijing'],
paint: { 'circle-radius': 5, 'circle-color': '#c2410c' }
});
控件与图层是两套东西。导航按钮、比例尺属于控件,
不写在样式里,用 addControl 添加。
样式只描述数据怎么画。
常见错误
用 Mapbox 的图层 id 改 MapLibre 的样式
不同底图的图层 id 完全不同。把 water、
road-primary 这类 id 从 Mapbox 样式照搬到另一份样式上,
改属性时提示图层不存在。先用 map.getStyle().layers 查真实 id。
在 load 之前 addLayer
样式还没解析完时,源可能不存在,添加图层会失败。
统一放进 load 回调,能消掉这一整类问题。
把 visibility 写成 paint 属性
visibility 属于 layout。写进 paint 会被忽略,
图层依然可见,看起来像「隐藏不生效」。
经纬志(2026)。《样式与图层》。https://www.jingweizhi.com/maplibre/tutorial/style-layers.html(访问日期 2026-11-03)。