草庐IT

leaflet结合PostGIS动态渲染矢量瓦片(附源码下载)

GIS之家 2023-03-28 原文

前言

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,

});});

完整demo源码见小专栏文章尾部小专栏

文章尾部提供源代码下载,对本专栏感兴趣的话,可以关注一波

有关leaflet结合PostGIS动态渲染矢量瓦片(附源码下载)的更多相关文章

  1. ruby-on-rails - 结合 meta_search 与 acts_as_taggable_on - 2

    我在开发的Rails3网站的一些搜索功能上遇到了一个小问题。我有一个简单的Post模型,如下所示:classPost我正在使用acts_as_taggable_on来更轻松地向我的帖子添加标签。当我有一个标记为“rails”的帖子并执行以下操作时,一切正常:@posts=Post.tagged_with("rails")问题是,我还想搜索帖子的标题。当我有一篇标题为“Helloworld”并标记为“rails”的帖子时,我希望能够通过搜索“hello”或“rails”来找到这篇帖子。因此,我希望标题列的LIKE语句与acts_as_taggable_on提供的tagged_with方法

  2. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  3. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  4. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  5. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  8. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  9. ruby - 在 Ruby 中动态创建数组 - 2

    有没有办法在Ruby中动态创建数组?例如,假设我想遍历用户输入的书籍数组:books=gets.chomp用户输入:"TheGreatGatsby,CrimeandPunishment,Dracula,Fahrenheit451,PrideandPrejudice,SenseandSensibility,Slaughterhouse-Five,TheAdventuresofHuckleberryFinn"我把它变成一个数组:books_array=books.split(",")现在,对于用户输入的每一本书,我想用Ruby创建一个数组。伪代码来做到这一点:x=0books_array.

  10. ruby - 是否可以将 IRB 提示配置为动态更改? - 2

    我想在IRB中浏览文件系统并让提示更改以反射(reflect)当前工作目录,但我不知道如何在每个命令后进行提示更新。最终,我想在日常工作中更多地使用IRB,让bash溜走。我在我的.irbrc中试过这个:require'fileutils'includeFileUtilsIRB.conf[:PROMPT][:CUSTOM]={:PROMPT_N=>"\e[1m:\e[m",:PROMPT_I=>"\e[1m#{pwd}>\e[m",:PROMPT_S=>"FOO",:PROMPT_C=>"\e[1m#{pwd}>\e[m",:RETURN=>""}IRB.conf[:PROMPT_MO

随机推荐