经纬志
首页 / OpenLayers / 教程

矢量瓦片 MVT 接入

先看结论

矢量瓦片把几何按瓦片切开,样式仍由前端决定。数据量再大也不会一次性下载完整图层, 代价是要面对瓦片坐标、源图层名与要素被裁剪的问题。

最后验证 2026-11-03 · OpenLayers 10.3.1 · 难度:进阶 · 核验 2026-10-29 · 引用本文

和普通矢量图层差在哪

维度普通矢量图层矢量瓦片图层
传输单位整份 GeoJSON256 或 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)。