动态

详情 返回 返回

leaflet結合PostGIS動態渲染矢量瓦片(附源碼下載) - 动态 详情

前言

leaflet 入門開發系列環境知識點了解:

  • leaflet api文檔介紹,詳細介紹 leaflet 每個類的函數以及屬性等等
  • leaflet 在線例子
  • leaflet 插件,leaflet 的插件庫,非常有用

內容概覽

leaflet結合PostGIS動態渲染矢量瓦片
源代碼demo下載

效果圖如下:

具體實現思路:
根據前端地圖請求的地圖當前級別以及行列號zxy(http://localhost:5000/tiles/quanguospot/spot/14/13345/7097),
後台接口python根據前端地圖傳值過來的zxy,動態計算地圖當前級別z行列號對應的地圖範圍extent(lonmin,latmin,lonmax,latmax),然後結合postgis動態生成矢量瓦片返回前端地圖渲染可視化。

postgis-stMvt

python 後台連接postgis 返回矢量切片

使用

  • 在tileOline.py中配置自己的postgis連接參數

     Dbpool = psycopg2.pool.SimpleConnectionPool(
         1,
         2000,
         dbname='postgis_31_sample',
         user='postgres',
         host='localhost',
         password='postgres',
         port='5432')
    
  • 根據ZXY計算對應地圖範圍Extent

import math
def tile2lat(ytile, zoom):
  n = 2.0 ** zoom
  lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
  lat_deg = math.degrees(lat_rad)
  return lat_deg
def tile2lon(xtile, zoom):
  n = 2.0 ** zoom
  lon_deg = xtile / n * 360.0 - 180.0
  return lon_deg
def getLon(xtile, zoom):
  a = []
  a.append(tile2lon(xtile, zoom))
  a.append(tile2lon(xtile+1, zoom))
  return a
def getLat(ytile, zoom):
  a = []
  a.append(tile2lat(ytile, zoom))
  a.append(tile2lat(ytile+1, zoom))
  return a
 
  lons = Util.getLon(x, z)
  lats = Util.getLat(y, z)
  lonmin = str(lons[0])
  lonmax = str(lons[1])
  latmin = str(lats[1])
  latmax = str(lats[0])
  • 重要的查詢語句
     // 傳 source-layer 和 tableName參數動態獲取
     query = "WITH mvtgeom AS(SELECT ST_AsMVTGeom(geom,ST_MakeEnvelope(%s,%s,%s,%s,4326),4096,64,true) AS geom FROM public." + tableName + \
         " t where t.geom IS NOT NULL AND ST_Intersects(geom, ST_MakeEnvelope(%s,%s,%s,%s,4326))) SELECT ST_AsMVT(mvtgeom.*,'" + \
         sourceLayer + "') FROM mvtgeom ;"
      
    
  • postgis重要函數説明
  (1) ST_AsMVTGeom:將一個圖層中位於參數box2d範圍內的一個幾何圖形的所有座標轉換為MapBox VectorTile座標空間裏的座標。
  ST_AsMVTGeom的官方文檔API:http://postgis.net/docs/manual-3.0/ST_AsMVTGeom.html
  函數各個參數的含義:
  geom —— 被轉換的幾何圖形信息。
  bounds —— 某個矢量切片的範圍對應的空間參考座標系中的幾何矩形框(沒有緩衝區)。
  extent —— 是按規範定義的矢量切片座標空間中的某個矢量切片的範圍。如果為NULL,則默認為4096(邊長為4096個單位的正方形)。
  buffer —— 矢量座標空間中緩衝區的距離,位於該緩衝區的幾何圖形部位根據clip_geom參數被裁剪或保留。如果為NULL,則默認為256。
  clip_geom —— 用於選擇位於緩衝區的幾何圖形部位是被裁剪還是原樣保留。如果為NULL,則默認為true。
  注意:從3.0版本開始,可以在配置時選擇Wagyu庫來裁剪和驗證MVT多邊形。Wagyu庫比默認的GEOS庫更快且能產生更正確的結果,但是它可能會丟棄小的多邊形。

  (2) ST_AsMVT:ST_AsMVT聚合函數用於將基於MapBox VectorTile座標空間的幾何圖形轉換為MapBox VectorTile二進制矢量切片。
  PostGIS生成MVT矢量切片的步驟是:
  使用ST_AsMVTGeom函數將幾何圖形的所有座標轉換為MapBox VectorTile座標空間裏的座標,這樣就將基於空間座標系的幾何圖形轉換成了基於MVT座標空間的幾何圖形。
  使用ST_AsMVT函數將基於MVT座標空間的幾何圖形轉換為MVT二進制矢量切片。
  ST_AsMVT的官方文檔API:http://postgis.net/docs/manual-3.0/ST_AsMVT.html
  函數各個參數的含義:
  row —— 至少具有一個geometry列的行數據。
  name —— 圖層名字,默認為"default"。
  extent —— 由MVT規範定義的屏幕空間(MVT座標空間)中的矢量切片範圍。
  geom_name —— row參數的行數據中geometry列的列名,默認是第一個geometry類型的列。
  feature_id_name —— 行數據中要素ID列的列名。如果未指定或為NULL,則第一個有效數據類型(smallint, integer, bigint)的列將作為要素ID列,其他的列作為要素屬性列。

  • leaflet矢量瓦片插件前端加載代碼
    
         var map = L.map('map',{renderer: L.canvas}).setView({ lat:23.56,lng:113.23 }, 14);
         var positron = L.tileLayer('https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
             opacity: 1
         }).addTo(map);
     //設置圖斑的樣式
         var vectorTileStyling = {
             spot:{
                 color: '#ffd700',
                 fillColor: '#e6d933',
                 fillOpacity: 0.1,
                 fill: true,
                 opacity: 1,
                 weight: 3,
                 dashArray: '5',
             }
         }
         var pbfUrl = "http://localhost:5000/tiles/quanguospot/spot/{z}/{x}/{y}";
         var mapboxVectorTileOptions = {
             rendererFactory: L.canvas.tile, //L.canvas.tile L.svg.tile
             maxZoom: 20,
             minZoom: 5,
             vectorTileLayerStyles: vectorTileStyling
         };
     var vectorGrid = L.vectorGrid.protobuf(pbfUrl, mapboxVectorTileOptions).addTo(map)
    
  • leaflet結合mapboxGL矢量瓦片前端加載代碼
 var map = L.map('map', {maxZoom: 17}).setView([23.3759016568317, 113.22544097900392], 13);
 map.createPane("tileLayerZIndex");
 map.getPane("tileLayerZIndex").style.zIndex = 0;
 var positron = L.tileLayer('https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
     opacity: 1,
     pane: "tileLayerZIndex",
 }).addTo(map);

 var blankStyle = {
 version: 8,
 name: "BlankMap",
 sources: {},
 layers: [
 ]
 };

 var gl = L.mapboxGL({
     accessToken: 'pk.eyJ1IjoiZ2lzeGlhb3dlaTEyMyIsImEiOiJja25zcTk4b3cweXZlMndwZjEyNzF1dXM2In0.V5daL_pyIbuSRN8K2PI80Q',
     style: blankStyle
 }).addTo(map);

 gl.getMapboxMap().on('load', function() {
         gl.getMapboxMap().addSource('myspot',{
             'type':'vector',
             'tiles':['http://localhost:5000/tiles/quanguospot/spot/{z}/{x}/{y}']

         });
         gl.getMapboxMap().addLayer({
             'id': 'spot',//隨意
             'source': 'myspot',//和上面那個source保持一致
             'source-layer':'spot',//圖層名稱。就是數據的名稱
             'type': 'fill',
             'paint': {
                     "fill-color": "#e6d933",  //讀取數據裏的properties下的value獲取顏色
                     'fill-opacity': 0.25,
                     //"fill-outline-color" :"#e6d933",
                     /*"line-dasharray":[2,4]*/
             },
             "maxzoom": 20,
             "minzoom": 8,
         });
         gl.getMapboxMap().addLayer({
             'id': 'spot_line',//隨意
             'source': 'myspot',//和上面那個source保持一致
             'source-layer':'spot',//圖層名稱。就是數據的名稱
             'type': 'line',
             'paint': {
             "line-color": "#e6d933",
             "line-width": 3,
             "line-dasharray": [3,3]
             },
             "maxzoom": 20,
             "minzoom": 8,
         });

 });

源碼放在下面的【GIS之家的學習交流圈】
GIS之家的學習交流圈

user avatar s0611163 头像
点赞 1 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.