概述
可通过多种方式实现矢量切片的制作,前面讲到了基于postgis数据库、tippecanoe、Qgis等方式,本文讲述基于spring Boot框架下java的实现。
实现效果
实现代码
后端代码
- 引入依赖
<dependency><artifactId>giscat-vector-mvt</artifactId><groupId>org.wowtools</groupId><version>g1.6.1</version>
</dependency>
- MvtController
package com.lzugis.controller;import com.lzugis.utils.FileUtil;
import io.swagger.annotations.Api;
import org.locationtech.jts.geom.*;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.wowtools.giscat.vector.mvt.MvtBuilder;
import org.wowtools.giscat.vector.mvt.MvtLayer;
import org.wowtools.giscat.vector.pojo.Feature;
import org.wowtools.giscat.vector.pojo.FeatureCollection;
import org.wowtools.giscat.vector.pojo.converter.GeoJsonFeatureConverter;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;@Api(tags = "矢量切片")
@RestController
@RequestMapping("tile")
public class MvtController {private static final FeatureCollection areaFeatureCollection;//面数据private static final FeatureCollection lineFeatureCollection;//线数据private static final FeatureCollection pointFeatureCollection;//点数据private static final GeometryFactory geometryFactory = new GeometryFactory();private static final FileUtil fileUtil = new FileUtil();static {//构造示例数据GeometryFactory gf = new GeometryFactory();String strJson = fileUtil.getFileContent("data\\province.geojson");areaFeatureCollection = GeoJsonFeatureConverter.fromGeoJsonFeatureCollection(strJson, gf);ArrayList<Feature> pointFeatures = new ArrayList<>(areaFeatureCollection.getFeatures().size());ArrayList<Feature> lineFeatures = new ArrayList<>(areaFeatureCollection.getFeatures().size());for (Feature feature : areaFeatureCollection.getFeatures()) {Feature pointFeature = new Feature();ArrayList center = (ArrayList) feature.getProperties().get("center");if (center != null) {Point point = gf.createPoint(new Coordinate((Double) center.get(0), (Double) center.get(1)));Map map = new HashMap();map.put("name", feature.getProperties().get("name"));pointFeature.setProperties(map);pointFeature.setGeometry(point);pointFeatures.add(pointFeature);}Feature lineFeature = new Feature();if (feature.getGeometry() instanceof MultiPolygon) {MultiPolygon multiPolygon = (MultiPolygon) feature.getGeometry();LineString[] lines = new LineString[multiPolygon.getNumGeometries()];for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {Polygon polygon = (Polygon) multiPolygon.getGeometryN(i);lines[i] = gf.createLineString(polygon.getExteriorRing().getCoordinates());}MultiLineString ml = gf.createMultiLineString(lines);lineFeature.setGeometry(ml);} else {LineString line = gf.createLineString(feature.getGeometry().getCoordinates());lineFeature.setGeometry(line);}lineFeatures.add(lineFeature);}lineFeatureCollection = new FeatureCollection();lineFeatureCollection.setFeatures(lineFeatures);pointFeatureCollection = new FeatureCollection();pointFeatureCollection.setFeatures(pointFeatures);}@RequestMapping("/{z}/{x}/{y}")public void getTile(@PathVariable byte z, @PathVariable int x, @PathVariable int y, HttpServletResponse response) {//构造一个MvtBuilder对象MvtBuilder mvtBuilder = new MvtBuilder(z, x, y, geometryFactory);//向mvt中添加layerMvtLayer layer = mvtBuilder.getOrCreateLayer("province-polygon");//向layer中添加featurefor (Feature feature : areaFeatureCollection.getFeatures()) {//这里简单地从内存中取数据并判断其是否与瓦片有交集,实际运用中可从数据库查询,例如postgis的ST_intersects函数if (mvtBuilder.getBbox().envIntersects(feature.getGeometry())) {layer.addFeature(feature);}}//如法炮制添加layerlayer = mvtBuilder.getOrCreateLayer("province-line");for (Feature feature : lineFeatureCollection.getFeatures()) {if (mvtBuilder.getBbox().envIntersects(feature.getGeometry())) {layer.addFeature(feature);}}//如法炮制添加layerlayer = mvtBuilder.getOrCreateLayer("province-point");for (Feature feature : pointFeatureCollection.getFeatures()) {if (mvtBuilder.getBbox().envIntersects(feature.getGeometry())) {layer.addFeature(feature);}}//数据添加完毕,转为byte[] bytes = mvtBuilder.toBytes();String vtContentType = "application/octet-stream";exportByte(bytes, vtContentType, response);}//将bytes写进HttpServletResponseprivate void exportByte(byte[] bytes, String contentType, HttpServletResponse response) {response.setContentType(contentType);try (OutputStream os = response.getOutputStream()) {os.write(bytes);os.flush();} catch (org.apache.catalina.connector.ClientAbortException e) {//地图移动时客户端主动取消, 产生异常"你的主机中的软件中止了一个已建立的连接",无需处理} catch (IOException e) {throw new RuntimeException(e);}}
}
前端代码
<div id="map"></div><script>var mapStyle = {"version": 8,"name": "Dark","sources": {'province-data': {'type': 'vector','tiles': ['http://localhost:18888/lzugis/tile/{z}/{x}/{y}']}},"layers": [{'id': 'province-line','type': 'line','source': 'province-data','source-layer': 'province-line','paint': {'line-color': '#31aa00','line-width': 2}},{'id': 'province-point','type': 'circle','source': 'province-data','source-layer': 'province-point','paint': {'circle-color': '#f00','circle-radius': 5}}]};window.map = new mapboxgl.Map({container: 'map',maxZoom: 10,minZoom: 3,zoom: 3,center: [109.1699, 45.9719],style: mapStyle,attributionControl: false});map.on('click', function (e) {const coords = e.lngLat;const r = [[e.point.x - 5, e.point.y - 5],[e.point.x + 5, e.point.y + 5],];const features = map.queryRenderedFeatures(r, {});console.log(features);})</script>