1. 引言
MVT(Mapbox Vector Tile)是一种高效的地理矢量切片格式,广泛应用于 WebGIS 和地图可视化场景。它把地理要素按瓦片网格切分,并以 Protocol Buffers 编码压缩,从而在保证渲染质量的同时大幅降低传输体积。本文围绕「Java 服务端如何生成或读取 MVT」以及「前端如何加载并渲染 MVT」两条主线展开,帮助读者打通从数据切片到浏览器展示的完整链路。
2. MVT 基础概念
在动手之前,先厘清几个关键概念,便于后续理解代码。
- 瓦片坐标 :MVT 使用 XYZ 瓦片编号,即
z/x/y三层结构,z 表示缩放级别,x、y 表示该级别下的瓦片行列号。 - 矢量切片:与栅格瓦片(PNG/JPEG)不同,矢量切片保存的是几何坐标和属性字段,前端可自由缩放、旋转、设置样式。
- 编码格式:MVT 基于 Protocol Buffers 编码,几何坐标经过量化、压缩,体积远小于 GeoJSON。
- 渲染方式:前端拿到 MVT 后,通常借助 MapLibre GL、Mapbox GL JS 等库进行 GPU 加速渲染。
3. Java 服务端生成 MVT
Java 生态中生成 MVT 的常见方案是使用 no.ecc.vectortile 库,它可以把 GeoJSON 或 JTS Geometry 编码为 MVT 瓦片。下面给出一个基于 Spring Boot 的示例。
3.1 引入依赖
xml
<dependency>
<groupId>no.ecc.vectortile</groupId>
<artifactId>java-vector-tile</artifactId>
<version>1.3.12</version>
</dependency>
3.2 生成 MVT 瓦片
java
import no.ecc.vectortile.VectorTileEncoder;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class TileController {
@GetMapping(value = "/tiles/{z}/{x}/{y}.mvt", produces = "application/x-protobuf")
public byte[] getTile(@PathVariable int z, @PathVariable int x, @PathVariable int y) throws Exception {
VectorTileEncoder encoder = new VectorTileEncoder(4096);
// 构造一个点要素,实际项目中应从数据库或空间索引查询
GeometryFactory gf = new GeometryFactory();
Point point = gf.createPoint(new Coordinate(116.391, 39.907));
Map<String, Object> attributes = new HashMap<>();
attributes.put("name", "天安门");
attributes.put("type", "landmark");
encoder.addFeature("poi", attributes, point);
return encoder.encode();
}
}
上述代码把单个点要素编码进瓦片。实际业务中,需要根据 z/x/y 计算瓦片对应的地理范围,再从数据库查询该范围内的要素批量写入。
4. Java 服务端读取 MVT
除了生成 MVT,有时也需要解析已有的 MVT 数据,例如做服务端空间分析或格式转换。可以使用 no.ecc.vectortile.VectorTileDecoder 完成解码。
java
import no.ecc.vectortile.VectorTileDecoder;
import no.ecc.vectortile.VectorTileDecoder.Feature;
import org.locationtech.jts.geom.Geometry;
import java.io.ByteArrayInputStream;
public class MvtParser {
public void parse(byte[] mvtBytes) throws Exception {
VectorTileDecoder decoder = new VectorTileDecoder();
VectorTileDecoder.FeatureIterable features = decoder.decode(new ByteArrayInputStream(mvtBytes));
for (Feature feature : features) {
String layerName = feature.getLayerName();
Geometry geometry = feature.getGeometry();
Map<String, Object> attributes = feature.getAttributes();
System.out.println("图层: " + layerName + ", 几何: " + geometry + ", 属性: " + attributes);
}
}
}
解码后得到 JTS Geometry 对象,可直接用于空间计算,或转换为 GeoJSON 输出给其他系统。
5. 前端加载与渲染 MVT
前端推荐使用 MapLibre GL JS 加载 MVT,它开源免费且兼容 Mapbox 生态。下面给出一个完整的 HTML 示例。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>MVT 加载示例</title>
<script src="https://unpkg.com/maplibre-gl@3/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@3/dist/maplibre-gl.css" rel="stylesheet" />
<style>
body { margin: 0; }
#map { width: 100vw; height: 100vh; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new maplibregl.Map({
container: 'map',
style: {
version: 8,
sources: {
mvtSource: {
type: 'vector',
tiles: ['http://localhost:8080/tiles/{z}/{x}/{y}.mvt'],
maxzoom: 14
}
},
layers: [
{
id: 'poi-layer',
type: 'circle',
source: 'mvtSource',
'source-layer': 'poi',
paint: {
'circle-radius': 6,
'circle-color': '#ff5722'
}
}
]
},
center: [116.391, 39.907],
zoom: 12
});
</script>
</body>
</html>
关键点说明:
type: 'vector'声明数据源为矢量瓦片。tiles数组指向 Java 服务端暴露的瓦片接口,{z}/{x}/{y}会被自动替换。source-layer必须与 Java 端encoder.addFeature传入的图层名一致,这里是poi。- 样式层
type: 'circle'表示把点要素渲染为圆点,也可换成fill、line等类型渲染面或线要素。
6. 前后端联调注意事项
联调过程中最容易踩坑的是图层名不匹配和跨域问题,这里集中说明。
- 图层名一致性 :前端
source-layer必须与后端addFeature的图层名完全一致,否则要素无法显示。 - Content-Type :后端接口应返回
application/x-protobuf或application/vnd.mapbox-vector-tile,避免浏览器误判。 - 跨域配置:若前端页面与瓦片服务不同源,需要在 Spring Boot 中开启 CORS。
- 坐标范围:生成瓦片时要注意坐标转换,确保要素落在对应瓦片范围内,否则会出现要素丢失或错位。
java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/tiles/**")
.allowedOrigins("*")
.allowedMethods("GET");
}
}
7. 总结
本文从 MVT 基础概念出发,分别介绍了 Java 服务端生成与解析 MVT 的方法,以及前端基于 MapLibre GL 加载渲染矢量瓦片的完整流程。核心要点可归纳为:后端负责把空间数据编码为矢量瓦片并暴露 HTTP 接口,前端通过矢量数据源声明瓦片地址,并保证 source-layer 与后端图层名一致。掌握这条链路后,即可在自有业务中落地高性能的地图可视化方案。