矢量瓦片 MVT 接入
矢量瓦片把几何按瓦片切开,样式仍由前端决定。数据量再大也不会一次性下载完整图层, 代价是要面对瓦片坐标、源图层名与要素被裁剪的问题。
和普通矢量图层差在哪
| 维度 | 普通矢量图层 | 矢量瓦片图层 |
|---|---|---|
| 传输单位 | 整份 GeoJSON | 256 或 512 像素的瓦片 |
| 首次加载 | 数据量大时很慢 | 只取当前视野 |
| 样式 | 前端完整控制 | 前端控制,但要素被瓦片裁剪 |
| 要素完整性 | 完整几何 | 跨瓦片的要素被切成多段 |
| 适用规模 | 万级 | 百万级 |
加载一个 MVT 图层
ol.format.MVT 解析切片,ol.source.VectorTile 负责调度。
一个瓦片包里通常有多个源图层(source layer),用 layers
选项只取需要的那个,避免把整套数据都解析进来。
var parcels = new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: new ol.format.MVT({
layers: ['parcels'], // 只解析 parcels 这个源图层
featureClass: ol.Feature
}),
url: 'https://tiles.example.com/parcels/{z}/{x}/{y}.pbf',
maxZoom: 16,
projection: 'EPSG:3857'
}),
declutter: false,
style: function (feature, resolution) {
var usage = feature.get('usage');
var color = usage === 'residential' ? '#1f5fd0' : '#d0453a';
return new ol.style.Style({
stroke: new ol.style.Stroke({ color: color, width: resolution < 4 ? 1.5 : 1 }),
fill: new ol.style.Fill({
color: usage === 'residential' ? 'rgba(31, 95, 208, 0.18)' : 'rgba(208, 69, 58, 0.18)'
})
});
}
});
map.addLayer(parcels);
样式函数的第二个参数是当前分辨率,用它控制线宽可以避免放大后线变得太粗。 分辨率越小表示越放大,判断条件的方向不要写反。
源图层名不是数据里看到的那个。
MVT 的 layers 匹配的是切片内部的图层名,
通常由切片工具配置决定,不一定等于业务表名。抓一个 .pbf
用工具展开确认,比反复改代码快。
不同源图层用不同样式
样式函数作用于一整个图层,无法针对源图层分支。需要区分时,
按源图层拆成多个 VectorTile 图层,共享同一个瓦片地址,
每个图层各自的格式只解析自己的源图层。瓦片请求会被浏览器缓存,不会成倍增加流量。
function makeTileLayer(sourceLayer, styleFn, zIndex) {
return new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: new ol.format.MVT({ layers: [sourceLayer] }),
url: 'https://tiles.example.com/base/{z}/{x}/{y}.pbf',
maxZoom: 16
}),
style: styleFn,
zIndex: zIndex
});
}
var roads = makeTileLayer('roads', function (feature) {
return new ol.style.Style({
stroke: new ol.style.Stroke({ color: '#f5f7fa', width: 3 })
});
}, 10);
var labels = makeTileLayer('labels', function (feature) {
return new ol.style.Style({
text: new ol.style.Text({
text: feature.get('name'),
font: '500 12px sans-serif',
fill: new ol.style.Fill({ color: '#16181d' }),
stroke: new ol.style.Stroke({ color: '#fff', width: 3 })
})
});
}, 20);
map.addLayer(roads);
map.addLayer(labels);
自定义瓦片地址
需要签名、按 token 拼接路径时,用 tileUrlFunction 代替
url。注意 OpenLayers 内部瓦片坐标的 y 与常见 XYZ 方案相反,
拼接前要翻转。
var signed = new ol.source.VectorTile({
format: new ol.format.MVT(),
tileUrlFunction: function (tileCoord) {
if (!tileCoord) return undefined;
var z = tileCoord[0];
var x = tileCoord[1];
var y = -tileCoord[2] - 1; // 内部 y 轴向上,XYZ 向下
return '/tiles/' + z + '/' + x + '/' + y + '.pbf?token=' + apiToken;
},
tileGrid: ol.tilegrid.createXYZ({ maxZoom: 15, tileSize: 512 })
});
注意事项
- 矢量瓦片默认按 512 像素切片,与服务端切片大小不一致时会出现缝隙或重复,需同步
tileSize。 - 要素跨瓦片被裁成多段,做面积统计或邻接分析不能直接在这个图层上算。
- 源图层为空时图层完全不显示,也不报错,先确认
layers名写对。 - MVT 里的属性都按字符串或数值存储,布尔值可能变成 0 与 1。
- 中文标注依赖客户端字体,切片里不带字体信息;样式中的
font要指定系统可用字体。 - 跨域请求同样需要服务端允许,内网瓦片服务常因证书问题在 HTTPS 页面下加载失败。
经纬志(2026)。《矢量瓦片 MVT 接入》。https://www.jingweizhi.com/openlayers/tutorial/vector-tiles.html(访问日期 2026-11-03)。