对于矢量数据的渲染显示,一般是以geojson的方式加载,或者直接以shp的形式解析加载,不管那种方法,对于少量的数据显示还可以满足要求,但是面对百万级别的数据量,直接加载显示的话,会造成严重的卡顿,甚至卡死。这里介绍一种动态切片的方法,轻松加载百万级别的矢量数据。
用到的工具有:postgis + openlayers + springboot
用到的数据有:100万条点数据
数据准备
首先将数据导入到数据库,我这里使用的是测试数据,java连接数据库生成了包括经纬度和时间、类型、UUID等字段的矢量点数据,总共100万条。在后续渲染加载中,通过ptype字段设置不同的点颜色。
使用postgis根据参数对数据进行切片。使用到的方法为 st_asmvt这个方法返回的是mvt格式的切片数据,以二进制数据流的方式返回。
其中 st_makeenvelope(118, 30, 120, 32)是切片的范围。在前端是通过XYZ将要切片的行列和级别转换为经纬度,在这里使用。
这样根据返回的二进制数据流,通过一个后端服务传递到前端。
后端服务
后端服务我才用了springboot,主要就是接收二进制数据流,返回给前端。这里具体是通过前端的XYZ参数,将其转换为经纬度,以此为筛选条件,调用切片函数进行切片。
GET方法接收前端请求
@GetMapping("reqstream")
public byte[] getTileReqStream(@RequestParam("x") String x, @RequestParam("y") String y, @RequestParam("z") String z, HttpServletResponse response) throws IOException {
x = x.replace(".pbf", "");
y = y.replace(".pbf", "");
z = z.replace(".pbf", "");
//原始流数据
byte[] b1 = tileReqService.getTileReqStream(x, y, z);
return b1;
}
将XYZ转换为经纬度,切片
@Override
public byte[] getTileReqStream(String x, String y, String z) {
double [] lonlat = xyz2lonlat(Double.parseDouble(x), Double.parseDouble(y), Double.parseDouble(z));
double [] lonlat1 = xyz2lonlat(Double.parseDouble(x) + 0.5, Double.parseDouble(y) + 0.5, Double.parseDouble(z));
List<TileReq> tileReqs = tileReqMapper.getTileReqStream(lonlat[0], lonlat[1], lonlat1[0], lonlat1[1]);
TileReq tileReq = tileReqs.get(0);
byte[] s = tileReq.getMvt();
return s;
}
public double[] xyz2lonlat(double x, double y, double z){
double [] lonlat = {0, 0};
double n = Math.pow(2, z);
double lon_deg = (x / n) * 360.0 - 180.0;
double lat_rad = Math.atan(Math.sinh(Math.PI * (1 - (2 * y) / n)));
double lat_deg = (180 * lat_rad) / Math.PI;
lonlat[0] = lon_deg;
lonlat[1] = lat_deg;
return lonlat;
}
前端openlayers加载
前端使用openlayers进行加载渲染。根据字段ptype设置点的颜色。
let vtLayer = new VectorTileLayer({
source: new VectorTileSource({
format: new MVT(),
url: "http://localhost:10001/tile/reqstream?z={z}&x={x}&y={y}.pbf",
}),
style: (evt) => {
let type = evt.properties_.ptype;
let style = null;
switch (type) {
case 1:
style = styleList[0];
break;
case 2:
style = styleList[1];
break;
case 3:
style = styleList[2];
break;
case 4:
style = styleList[3];
break;
case 5:
style = styleList[4];
break;
case 6:
style = styleList[5];
break;
case 7:
style = styleList[6];
break;
case 8:
style = styleList[7];
break;
case 9:
style = styleList[8];
break;
case 10:
style = styleList[9];
break;
}
return style;
},
});