当前位置:   article > 正文

PostgreSQL+Postgis MapboxVectorTile应用_mapbox vector适量切片可以使用post请求吗

mapbox vector适量切片可以使用post请求吗

背景

该实验基于Postgres+Postgis搭建实时矢量瓦片服务。
矢量瓦片的优势

  1. 可以支持高分辨率屏幕显示
  2. 地图渲染在前端,可以支持前端随意更改配图方案,解决传统栅格瓦片动态样式上的问题
  3. 要素查询可以在前端进行
  4. 保持了矢量数据的优势,同时采用切片方式在传输效率上有一定提升

实时矢量瓦片

不在线下用工具预先切片,采用即时浏览传输矢量瓦片;
采用实时矢量切片可以做到数据编辑功能,同时解决数据时效性问题

使用到的相关函数

ST_AsMvtGeo 将Geom转化为MVT的geom
ST_AsMVT 将geom转换为MVT数据
ST_TileEnvelope 根据行列号获取Envelop (该函数需要Postgis3.0才有,使用postgis-vt-util代替

软件环境

  • PostgreSQL11
  • Postgis2.5
  • java
  • (前端渲染) leaflet1.7、mapbox

实现过程

模拟数据

使用sql语句创建表,并在给定坐标范围内模拟点数据

CREATE TABLE data_1000000 AS SELECT
( ST_Dump ( ST_GeneratePoints ( kl.geom, 200000 ) ) ).geom AS geom,
md5( ( random ( ) * random ( ) ) :: text ) AS id,
random ( ) * 1000 AS val 
FROM ( 
  SELECT ST_SetSRID ( ST_MakeBox2D ( ST_Point ( 113.213316,22.954094 ), ST_Point ( 113.62805,23.368228 ) ), 4326 
) geom ) kl;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

sql解读:
创建表名:data_1000000
坐标范围:左下角坐标(113.213316,22.954094)、右下角坐标(113.62805,23.368228),坐标系SRID为4326
模拟数据量:100万
表字段:geom(空间字段)、id(md5格式的唯一编码)、val(点的权重值0-1000)

部署扩展函数

由于需要用到ST_TileEnvelope函数,该函数需要postgis3.0才支持,所以使用扩展的函数库实现(postgis-vt-util下载)
执行postgis-vt-util.sql添加相关函数到数据库

这个库由mapbox提供,生成符合mapbox矢量切片格式的数据

后端接口

后端需要实现前端请求切片范围的矢量切片生成,传入的参数可能是行列号或者经纬度范围,最终返回byte的矢量切片数据

处理逻辑

graph LR
start("前端请求") --> isEnvelop("是否传入经纬度范围")
isEnvelop --no--> tileBBox("根据切片z,x,y转换经纬度范围")
isEnvelop --yes--> makeEnvelop("生成几何范围")
tileBBox --> makeEnvelop
makeEnvelop --> asMVTGeom("将几何图形转为mapbox矢量瓦片坐标")
asMVTGeom --> asMVT("返回mapbox矢量切片的byte数组")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

点线面在转换byte数组时需要分开处理

代码编写

//根据切片xyz获取矢量切片
public byte[] getMVT(String tableName, int x, int y, int z, int srid) {
    byte[] res;
    try {
        String sqlStr = "SELECT " +
                "ST_AsMVT( vt, 'points', 256, 'geo' ) FROM ( " +
                "SELECT ST_SetSRID ( ST_Point ( ST_X ( A.geo ), ST_Y ( A.geo )), " + srid + " ) geo FROM (" +
                "SELECT ST_AsMVTGeom ( w.geom, Box2D ( TileBBox ( " + z + "," + x + "," + y + ", " + srid + " )), 256, 0, TRUE ) AS geo  " +
                "FROM " + tableName + " w  WHERE TileBBox ( " + z + "," + x + "," + y + ", " + srid + " ) && geom  ) A  " +
                "GROUP BY ST_X ( A.geo ), ST_Y ( A.geo ) ) AS vt";

        res = jdbcTemplate.queryForObject(sqlStr,byte[].class);
    } catch (Exception e) {
        throw e;
    }
    return res;
}

//根据空间范围获取矢量切片
public byte[] getMVTByEnvelop(String tableName, double lon_min, double lat_min, double lon_max, double lat_max, int srid) {
    byte[] res;
    try {
        String sqlStr = "SELECT " +
                "ST_AsMVT( vt, 'points', 256, 'geo' ) FROM ( " +
                "SELECT ST_SetSRID ( ST_Point ( ST_X ( A.geo ), ST_Y ( A.geo )), " + srid + " ) geo FROM (" +
                "SELECT ST_AsMVTGeom ( w.geom,ST_MakeEnvelope ( " + lon_min + "," + lat_min + "," + lon_max + "," + lat_max + ", " + srid + " ), 256, 0, TRUE ) AS geo  " +
                "FROM " + tableName + " w  WHERE ST_MakeEnvelope ( " + lon_min + "," + lat_min + "," + lon_max + "," + lat_max + ", " + srid + " ) && geom  ) A  " +
                "GROUP BY ST_X ( A.geo ), ST_Y ( A.geo ) ) AS vt";

        res = jdbcTemplate.queryForObject(sqlStr, byte[].class);
    } catch (Exception e) {
        throw e;
    }
    return res;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

前端代码编写

mapboxgl加载方法

<!DOCTYPE html>
<html>
<head>
    <title>Mapbox加载MVT</title>
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <script src="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.js"></script>
    <link href="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.css" rel="stylesheet" />
    <style>
        body {
            margin: 0;
            padding: 0;
        }
        html,
        body,
        #map {
            height: 100%;
        }
        .layerPanel {
            position: absolute;
            top: 10px;
            right: 20px;
            background: #ffffff;
            padding: 5px 2px;
        }
    </style>
</head>

<body>
    <div id='map'></div>
    <script>
        let center = [113.33616, 23.0963];
		//获取地图样式
        let style = getStyle();
        //创建3857坐标系地图
        var map = window.map = new mapboxgl.Map({
            container: 'map',
            // epsg: 'EPSG:3857',
            center : center,
            zoom:12,
            hash: true,
            style: style
        });
        // map.showTileBoundaries = true;
        map.on('load', function () {
            //添加天地图底图
            addTDTLayers(map);
            map.addLayer({
                "type": "circle",
                "source": "test",
                "paint":{
					"circle-color": "blue",
					"circle-radius":4,
                },
                "source-layer": "points",
                "maxzoom": 17,
                "id": "1"
            })
        });

        function getStyle() {
            let style = {
                "version": 8,
                "name": "Basic-my",
                "metadata": {
                    "mapbox:autocomposite": true,
                    "mapbox:type": "template"
                },
                "center": center,
                "zoom": 3,
                "bearing": 0,
                "pitch": 0,
                "sources": {
                    "test": {
                        "type": "vector",
                        "tiles": ["{后端服务}/getMVT?tableName=data_1000000&x={x}&y={y}&z={z}"]
                    }
                },
                "layers": []
            };
            return style;
        }
        //添加3857坐标系天地图
        function addTDTLayers(map) {
            //添加3857天地图矢量source
            var source_vec = {
                "type": "raster",
                "tiles": [
                    "http://t0.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=7b06bd204544a8631beccd5fd56ad8c1",
                ],

                "tileSize": 256
            }
            if (!map.getSource("TDT_VEC")) {
                map.addSource("TDT_VEC", source_vec);
            }

            //添加3857天地图矢量图层
            var Layer_vec = {
                "id": "tdtvec",
                "type": "raster",
                "source": "TDT_VEC",
                "minzoom": 0,
                "maxzoom": 17
            }
            if (!map.getLayer("tdtvec")) {
                map.addLayer(Layer_vec);
            }
        }
    </script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

Leaflet加载方式

<!DOCTYPE html>
<html>
<head>
	<title>VectorGrid.Protobuf example</title>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<link rel="stylesheet" href="src/leaflet.css" />
</head>
<body style='margin:0'>
	<div id="map" style="width: 100vw; height: 100vh; background: white"></div>
	<script type="text/javascript"  src="src/leaflet-src.js"></script>
	<script type="text/javascript"  src="src/Leaflet.VectorGrid.js"></script>
	<script>
		var map = L.map('map',{
			renderer: L.canvas
		});			
		map.setView({ lat: 23.0963, lng: 113.33616 }, 14);
		//加载底图
		L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
			attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
		}).addTo(map);
	
		var vectorTileStyling = {
			points:{
				weight: 2,
				color: 'red',
				opacity: 1,
				fillColor: 'yellow',
				fill: true,
				radius: 6,
				fillOpacity: 0.7
			}
		}
		
		var mapboxUrl = "http://localhost:8081/xxx/getMVT?tableName=data_1000000&x={x}&y={y}&z={z}";

		var mapboxVectorTileOptions = {
			rendererFactory: L.canvas.tile,
			vectorTileLayerStyles: vectorTileStyling
		};

		var mapboxPbfLayer = L.vectorGrid.protobuf(mapboxUrl, mapboxVectorTileOptions);
		map.addLayer(mapboxPbfLayer)
	</script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

结果展示

详情浏览

相关链接

https://blog.csdn.net/qq_35241223/article/details/106439268
https://github.com/mapbox/postgis-vt-util

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号