当前位置: 首页 > news >正文

动态矢量瓦片缓存库方案

目录

前言

二、实现步骤

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目前最优秀的数据格式&#xff0c;其主要特点就是解决了大批量数据在前端渲染时出现加载缓慢、卡顿的问题&#xff0…...

628.三个数的最大乘积

给你一个整型数组 nums &#xff0c;在数组中找出由三个数组成的最大乘积&#xff0c;并输出这个乘积。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;6 示例 2&#xff1a; 输入&#xff1a;nums [1,2,3,4] 输出&#xff1a;24 示例 3&#xff1a; …...

【数据结构】堆和集合笔记

自己写一个堆首先&#xff0c;明确一下&#xff0c;为什么需要堆&#xff1f;>考虑插入&#xff0c;删除&#xff0c;查找的效率。数组&#xff0c;查找&#xff0c;最快是二分查找O(lgN)。但查找完如果要做什么操作&#xff0c;比如删除&#xff0c;就要挪动元素了。所以合…...

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. 西方列强的殖民扩张和鸦片战争的影响。&#xff08;两面性&#xff09; &#xff1a;反面—破坏了了中国的小农经济&#xff0c;是中国由封建社会转变为两半社会。 --一系列不公平条约&#xff0c;破坏了中国主权领土完整。 --压迫中国人民&#xff0c;给中国人民带来了巨大…...

数据库面试题——锁

了解数据库的锁吗&#xff1f; 锁是数据库系统区别于文件系统的一个关键特性&#xff0c;锁机制用于管理对共享资源的并发访问。 InnoDB下两种标准行级锁&#xff1a; 共享锁&#xff08;S Lock&#xff09;&#xff0c;允许事务读一行数据。 排他锁&#xff08;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天

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

真正理解微软Windows程序运行机制——什么是消息

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

HTTP 缓存的工作原理

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

RK3568在Android上进行驱动模块开发(源码外)

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

操作技巧 | 在Revit中借用CAD填充图案的方法

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

Java的二叉树、红黑树、B+树

数组和链表是常用的数据结构&#xff0c;数组虽然查找快&#xff08;有序数组可以通过二分法查找&#xff09;&#xff0c;但是插入和删除是比较慢的&#xff1b;而链表&#xff0c;插入和删除很快&#xff08;只需要改变一些引用值&#xff09;&#xff0c;但是查找就很慢&…...

昨天某读者拿到华为OD岗位offer,今天来分享一下经验,包含华为OD机试

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

树的遍历方式(前中后,层序遍历,递归,迭代,Morris遍历)-----直接查询代码

目录 一.前序遍历 1.递归 2.栈迭代 3.Morris遍历 二.中序遍历 1.递归 2.栈迭代 3.Morris遍历 三.后序遍历 1.递归 2.栈迭代 3.Morris遍历 四.前中后序的统一迭代法 1.前序遍历 2.中序遍历 3.后序遍历 五.层序遍历 1.队列迭代 2.之字形层序遍历 3.锯齿形层序…...

Docker Registry部署镜像私有仓库及鉴权认证

文章目录一、Docker Registry是什么&#xff1f;二、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. 中断是啥&#xff1f; 什么是中断&#xff1a;CPU在处理某一事件A时&#xff0c;发生的另外某一事件B请求CPU去处理&#xff08;产生了中断&#xff09;&#xff0c;随后CPU暂时中断当前正在执行的任务&#xff0c;去对事件B进行处…...

第十四届蓝桥杯三月真题刷题训练——第 13 天

目录 第 1 题&#xff1a;特殊日期 问题描述 答案提交 运行限制 代码&#xff1a; 思路&#xff1a; 第 2 题&#xff1a;重合次数 问题描述 答案提交 运行限制 代码&#xff1a; 第 3 题&#xff1a;左移右移 问题描述 输入格式 输出格式 样例输入 样例输出…...

webgl_gpgpu_birds 样例分析

webgl_gpgpu_birds 是一个 three.js 的官方样例&#xff0c;这个例子模拟了鸟群的运动&#xff0c;是一个群组动画&#xff0c;并且动画的帧率也很高&#xff1b;鸟群的运动很自然&#xff0c;非常值得研究。类似的群组动画还有鱼群&#xff0c;boid是‘类鸟群’的英文 大概两…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...