动态矢量瓦片缓存库方案
目录
前言
二、实现步骤
1.将数据写入postgis数据库
2.将矢量瓦片数据写入缓存库
3.瓦片接口实现
4.瓦片局部更新接口实现
总结
前言
矢量瓦片作为webgis目前最优秀的数据格式,其主要特点就是解决了大批量数据在前端渲染时出现加载缓慢、卡顿的问题,能够环境前端设备的计算压力。动态矢量瓦片技术,解决了矢量存储在数据库中的实时动态更新,不再需要使用离线工具对矢量进行本地切片发布的问题。但是动态矢量瓦片技术的缺陷也很大,就是因为其运行逻辑是通过对数据库矢量实时切片,那么当用户访问并发数过多的时候,pg库就会超负荷运行,会出现访问超时的情况。为解决这一问题,搭建矢量瓦片缓存库就非常重要。
一、缓存库的意义
为解决多用户访问时pg库切片压力过大的问题,首先对数据库所有数据进行切片,然后将X,Y,Z和瓦片信息储存在缓存库中。当用户访问矢量瓦片接口时,优先判断缓存库内有无对应的瓦片数据,如果没有,则调用pg函数实时切片,切片完成后再缓存库插入数据,如果缓存库有数据,则直接返回缓存库储存的瓦片数据。如果需要对数据库的矢量进行增删改查操作,则计算更改矢量对应的瓦片范围,对缓存库做对应的局部更新即可。
二、实现步骤
1.将数据写入postgis数据库
对各大空间数据库读写最强工具,非FME莫属,直接写模块即可,需要注意一点就是如果。pg的表是MultiPolygon,那么我们需要加入aggregator把要素变为聚合体格式写入,还有就是坐标系要和表一致。
2.将矢量瓦片数据写入缓存库
将矢量数据写入一个临时的postgis数据表,然后用fme生成对应层级的瓦片范围,最后用python调用postgis函数对数据进行切片,最后过滤一下空白瓦片,最后将数据写入缓存库。
获得了各类层级的二进制矢量瓦片数据
3.瓦片接口实现
后端框架采用python的geodjango,首先造一个x,y,z转换为wg84坐标范围的函数
import math
def xyz2lonlat(x,y,z):n = math.pow(2, z)lon_deg = (x / n) * 360.0 - 180.0lat_rad = math.atan(math.sinh(math.pi * (1 - (2 * y) / n)));lat_deg = (180 * lat_rad) / math.pireturn [lon_deg, lat_deg]
同时造一个mvt生成器,并实现空间库和缓存库信息判定,有则直接调用缓存库数据,不调用pg函数切片,无则调用pg函数切片,切片完成后更新缓存库并同时返回瓦片数据。
def make_mvt(model,temp_model,x,y,z):"""temp_model为矢量瓦片缓存库模型类model为pg矢量库模型要素x,y,z为前端请求参数"""temp_mvt = temp_model.objects.filter(x=x,y=y,z=z)if len(temp_mvt) == 1:return HttpResponse(temp_mvt[0].byte, content_type="application/x-protobuf")if len(temp_mvt) > 1:for i in range(0, len(temp_mvt) - 1):temp_mvt[i].delete()return HttpResponse(temp_mvt[len(temp_mvt) - 1].byte, content_type="application/x-protobuf")tablename = model._meta.db_tableboundbox_min = xyz2lonlat(x, y, z)boundbox_max = xyz2lonlat(x + 1, y + 1, z)sql = """SELECTST_AsMVT ( P,'polygon', 4096, 'geom' ) AS "mvt" FROM (SELECT ST_AsMVTGeom (ST_Transform (st_simplify(geom,0.0), 3857 ), ST_Transform (ST_MakeEnvelope( %s,%s, %s,%s, 4326 ),3857),4096, 64,TRUE ) geom FROM "%s" ) AS P""" % (boundbox_min[0], boundbox_min[1], boundbox_max[0], boundbox_max[1], tablename)cursor = connection.cursor()cursor.execute(sql)tile = bytes(cursor.fetchone()[0])temp_model.objects.create(x=x,y=y,z=z,byte=tile,)if not len(tile):return Falseelse:return tile
视图类
#视图类
class ZJGG_mvt_ViewSet(APIView):def get(self,request,z, x, y):tile=make_mvt(ZJGG,mvt_temp,x,y,z)if tile:return HttpResponse(tile, content_type="application/x-protobuf")else:return Response(status=status.HTTP_404_NOT_FOUND)
然后用postman调试一下接口,瓦片请求成功
4.瓦片局部更新接口实现
这一步的主要内容是为了前端需要对矢量数据增删改查的时候,同时删除缓存库的对应瓦片,用户在下一次访问的时候,通过上一步的mvt生成器完成新瓦片数据的更新。
首先造一个4326到3857坐标系的转换工具
import mathdef lonlat2mercator(lon, lat):semimajor_axis = 6378137.0x = semimajor_axis * math.radians(lon)y = semimajor_axis * math.log(math.tan((math.pi / 4) + (math.radians(lat) / 2)))return x, ydef mercator2lonlat(x, y):semimajor_axis = 6378137.0lon = math.degrees(x / semimajor_axis)lat = math.degrees(2 * math.atan(math.exp(y / semimajor_axis)) - math.pi / 2)return lon, latdef epsg4326_to_epsg3857(lon, lat):x, y = lonlat2mercator(lon, lat)r_major = 6378137.0x = r_major * math.radians(lon)scale = x / lony = 180.0 / math.pi * math.log(math.tan(math.pi / 4.0 + lat * (math.pi / 180.0) / 2.0)) * scalereturn x, ydef epsg3857_to_epsg4326(x, y):r_major = 6378137.0lon = x / r_major * 180.0 / math.pilat = math.atan(math.exp(y / r_major)) * 360.0 / math.pi - 90.0return lon, lat
造一个瓦片计算器,传入geojson的extend返回瓦片的x,y,z信息。
import mathHEMI_MAP_WIDTH = math.pi * float(6378137)
PRECISION = 6def generate(zoomLevel, tileSize, rows, cbeg, cend):# Calculate x-direction tile originscols = [(c, round(c * tileSize - HEMI_MAP_WIDTH, PRECISION)) for c in range(cbeg, cend + 1)]cols = [(cols[i][0], cols[i][1], cols[i + 1][1]) for i in range(len(cols) - 1)]tile_json=[]# Generate and output tile features.for row, ymin, ymax in rows:for column, xmin, xmax in cols:tile_json.append({"Z": zoomLevel,"X": column,"Y": row,})return tile_jsondef TileGenerate(xmin,ymin,xmax,ymax):west = float(xmin)east = float(xmax)south = float(ymin)north = float(ymax)tile=[]for i in range(6, 17):zoomLevel = iif zoomLevel < 0:zoomLevel = 0numColumns = int(math.pow(2, zoomLevel))tileSize = 2.0 * HEMI_MAP_WIDTH / numColumnsrbeg = int(math.floor((HEMI_MAP_WIDTH - north) / tileSize))rend = int(math.ceil((HEMI_MAP_WIDTH - south) / tileSize))rows = [(r, round(HEMI_MAP_WIDTH - r * tileSize, PRECISION)) for r in range(rbeg, rend + 1)]rows = [(rows[i][0], rows[i + 1][1], rows[i][1]) for i in range(len(rows) - 1)]cbeg = int(math.floor((HEMI_MAP_WIDTH + west) / tileSize))cend = int(math.ceil((HEMI_MAP_WIDTH + east) / tileSize))if cbeg < cend:tile_json=generate(zoomLevel, tileSize, rows, cbeg, cend)else:tile_json=generate(zoomLevel, tileSize, rows, cbeg, numColumns)tile_json1=generate(zoomLevel, tileSize, rows, 0, cend)tile_json.extend(tile_json1)tile.extend(tile_json)return tile
mvt局部更新函数
def del_mvt(model,temp_model,sm):"""model为空间数据存储模型类temp_model为缓存库模型类sm为空间数据库查询结果实现缓存库和空间库数据的局部删除"""data = make_geojson(model, sm)data = json.loads(data)xmin, ymin, xmax, ymax = bound(data)xmin, ymin = epsg4326_to_epsg3857(xmin, ymin)xmax, ymax = epsg4326_to_epsg3857(xmax, ymax)tilelist = TileGenerate(xmin, ymin, xmax, ymax)for i in tilelist:DEL = temp_model.objects.filter(x=i["X"], y=i["Y"], z=i["Z"])DEL.delete()sm.delete()
视图函数
class ZJGG_mvt_del_ViewSet(APIView):def post(self,request):id = request.data.get('id')sm=ZJGG.objects.filter(pk=id)del_mvt(ZJGG,mvt_temp,sm)return Response(status=status.HTTP_200_OK)
最后用postman测试接口,传入一个id,测试空间信息表和缓存表的对应内容是否都删除。
提交前
提交后
可以看到对应的空间库id为338的数据已经删除,同时缓存库中的对应的4条瓦片信息也被删。
总结
这项技术的前景是非常可观的,现阶段各类webgis平台对大批量地理空间数据的展现方式,几乎都为静态矢量瓦片和geojson配合的方式实现。但是这种方式在面对较大体量需要全局展示且需要动态更新的数据的时候,就显得捉襟见肘。
相关文章:

动态矢量瓦片缓存库方案
目录 前言 二、实现步骤 1.将数据写入postgis数据库 2.将矢量瓦片数据写入缓存库 3.瓦片接口实现 4.瓦片局部更新接口实现 总结 前言 矢量瓦片作为webgis目前最优秀的数据格式,其主要特点就是解决了大批量数据在前端渲染时出现加载缓慢、卡顿的问题࿰…...
628.三个数的最大乘积
给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积。 示例 1: 输入:nums [1,2,3] 输出:6 示例 2: 输入:nums [1,2,3,4] 输出:24 示例 3: …...
【数据结构】堆和集合笔记
自己写一个堆首先,明确一下,为什么需要堆?>考虑插入,删除,查找的效率。数组,查找,最快是二分查找O(lgN)。但查找完如果要做什么操作,比如删除,就要挪动元素了。所以合…...

java LinkedList 源码分析(通俗易懂)
目录 一、前言 二、LinkedList类简介 三、LinkedList类的底层实现 四、LinkedList类的源码解读 1.add方法解读 : 〇准备工作 。 ①跳入无参构造。 ②跳入add方法。 ③跳入linkList方法。 ④增加第一个元素成功。 ⑤向链表中添加第二个元素。 2.remove方法解读 : 〇准备工…...

Vue中实现路由跳转的三种方式详细分解
vue中实现路由跳转的三种方式 目录 vue中实现路由跳转的三种方式 一、使用vue-router 1.下载vue-router模块到当前工程 2.在main.js中引入VueRouter函数 3.添加到Vue.use()身上 – 注册全局RouterLink和RouterView组件 4.创建路由规则数组 – 路径和组件名对应关系 5…...
全国自学考试03708《中国近现代史纲要》重点复习精要
1. 西方列强的殖民扩张和鸦片战争的影响。(两面性) :反面—破坏了了中国的小农经济,是中国由封建社会转变为两半社会。 --一系列不公平条约,破坏了中国主权领土完整。 --压迫中国人民,给中国人民带来了巨大…...

数据库面试题——锁
了解数据库的锁吗? 锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。 InnoDB下两种标准行级锁: 共享锁(S Lock),允许事务读一行数据。 排他锁(X Lock&…...

Python笔记 -- 文件和异常
文章目录1、文件1.1、with关键字1.2、逐行读取1.3、写入模式1.4、多行写入2、异常2.1、try-except-else2.2、pass1、文件 1.1、with关键字 with关键字用于自动管理资源 使用with可以让python在合适的时候释放资源 python会将文本解读为字符串 # -*- encoding:utf-8 -*- # 如…...

蓝桥杯刷题冲刺 | 倒计时24天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.修剪灌木2.统计子矩阵1.修剪灌木 题目 链接: 修剪灌木 - 蓝桥云课 (lanqiao.cn) 找…...

真正理解微软Windows程序运行机制——什么是消息
我是荔园微风,作为一名在IT界整整25年的老兵,今天说说Windows程序的运行机制。经常被问到MFC到底是一个什么技术,为了解释这个我之前还写过帖子,但是很多人还是不理解。其实这没什么,我在学生时代也被这个问题困绕过。…...

HTTP 缓存的工作原理
缓存是解决http1.1当中的性能问题主要手段。缓存可能存在于客户端浏览器上,也可以存在服务器上面,当使用过期缓存可能给用户展示的是错误的信息而导致一些bug。 HTTP 缓存:为当前请求复用前请求的响应 • 目标:减少时延࿱…...

RK3568在Android上进行驱动模块开发(源码外)
文章目录 前言一、ARCH架构二、编译器三、建立自己的Makefile文件总结前言 本文记录在驱动开发时,由于编译内核时间较长,经常会选择单独编译一个模块,这里主要讲解,makefile文件如何编写(主要是编译器和架构) 提示:以下是本篇文章正文内容,下面案例可供参考 一、ARCH…...

操作技巧 | 在Revit中借用CAD填充图案的方法
在建模过程中,有时需要达到多种填充效果,而CAD中大量的二维填充图案,便是最直接的资源之一。 使用 填充图案之前 使用 填充图案之后 其中要用到主要命令便是对表面填充图案的添加与编辑 简单效果 如下 模型填充与绘图填充 区别 模型填…...

Java的二叉树、红黑树、B+树
数组和链表是常用的数据结构,数组虽然查找快(有序数组可以通过二分法查找),但是插入和删除是比较慢的;而链表,插入和删除很快(只需要改变一些引用值),但是查找就很慢&…...

昨天某读者拿到华为OD岗位offer,今天来分享一下经验,包含华为OD机试
来自读者投稿,已经拿到华为 OD 开发岗位 offer,询问了一些问题,下面是他的一些经验。 文章目录华为 OD 投递简历华为 OD 机试分数OD 机试通过之后,收到综合测评OD 技术面(时长 1 小时左右)主管/HR 面试&…...

树的遍历方式(前中后,层序遍历,递归,迭代,Morris遍历)-----直接查询代码
目录 一.前序遍历 1.递归 2.栈迭代 3.Morris遍历 二.中序遍历 1.递归 2.栈迭代 3.Morris遍历 三.后序遍历 1.递归 2.栈迭代 3.Morris遍历 四.前中后序的统一迭代法 1.前序遍历 2.中序遍历 3.后序遍历 五.层序遍历 1.队列迭代 2.之字形层序遍历 3.锯齿形层序…...

Docker Registry部署镜像私有仓库及鉴权认证
文章目录一、Docker Registry是什么?二、Docker Registry部署私有仓库2.1、Docker Registry安装2.2、Docker Registry配置2.3、启动Docker Registry2.4、Docker客户端配置2.5、向Docker Registry上传和下载镜像三、Docker Registry鉴权和认证3.1、基本认证3.2、Bear…...

stm32外设-中断详解
0. 写在最前 本栏目笔记都是基于stm32F10x 1. 中断是啥? 什么是中断:CPU在处理某一事件A时,发生的另外某一事件B请求CPU去处理(产生了中断),随后CPU暂时中断当前正在执行的任务,去对事件B进行处…...
第十四届蓝桥杯三月真题刷题训练——第 13 天
目录 第 1 题:特殊日期 问题描述 答案提交 运行限制 代码: 思路: 第 2 题:重合次数 问题描述 答案提交 运行限制 代码: 第 3 题:左移右移 问题描述 输入格式 输出格式 样例输入 样例输出…...
webgl_gpgpu_birds 样例分析
webgl_gpgpu_birds 是一个 three.js 的官方样例,这个例子模拟了鸟群的运动,是一个群组动画,并且动画的帧率也很高;鸟群的运动很自然,非常值得研究。类似的群组动画还有鱼群,boid是‘类鸟群’的英文 大概两…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10pip3.10) 一:前言二:安装编译依赖二:安装Python3.10三:安装PIP3.10四:安装Paddlepaddle基础框架4.1…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...

GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...

五、jmeter脚本参数化
目录 1、脚本参数化 1.1 用户定义的变量 1.1.1 添加及引用方式 1.1.2 测试得出用户定义变量的特点 1.2 用户参数 1.2.1 概念 1.2.2 位置不同效果不同 1.2.3、用户参数的勾选框 - 每次迭代更新一次 总结用户定义的变量、用户参数 1.3 csv数据文件参数化 1、脚本参数化 …...
iOS 项目怎么构建稳定性保障机制?一次系统性防错经验分享(含 KeyMob 工具应用)
崩溃、内存飙升、后台任务未释放、页面卡顿、日志丢失——稳定性问题,不一定会立刻崩,但一旦积累,就是“上线后救不回来的代价”。 稳定性保障不是某个工具的功能,而是一套贯穿开发、测试、上线全流程的“观测分析防范”机制。 …...