简介: 本文介绍如何使用RDS PG或PolarDB(兼容PG版或Oracle版)的Ganos时空引擎提供的数据库快显技术,仅用百行代码实现亿级海量几何空间数据的在线快速显示和流畅地图交互,且无需关注切片存储和效率问题。

01 引言

如何对时空数据库中的亿级矢量空间数据进行在线可视化一直是业界难题。因数据体量大,传统方法需要将数据库中数据进行基于缓存切片的服务发布才能可视化,操作流程冗长,且有一大堆需要考虑的问题:

  • 如果对矢量数据进行预切片,数据要切多久?切多少级合适?存储瓦片的硬盘空间够用吗?
  • 如果使用实时瓦片,实时渲染瓦片的响应时间能保证吗?
  • 如果使用矢量瓦片,小比例尺的瓦片可能会有多大体积?传输会不会成为瓶颈?前端渲染能承受多大的数据量?

如果是要快速浏览数据库中的大规模在线数据,传统用于“底图服务”的离线切片生产流程几乎无解,不但费时费力,又无法在线联机处理。黑科技来了,本文介绍如何使用RDS PG或PolarDB(兼容PG版或Oracle版)的Ganos时空引擎提供的数据库快显技术,仅用百行代码实现亿级海量几何空间数据的在线快速显示和流畅地图交互,且无需关注切片存储和效率问题。

02 技术特性解读

Ganos的在线快显处理的核心是将数据库和可视化进行了关联,提供了一种新的可视化索引技术——稀疏矢量金字塔(Sparse Vector Pyramid,SVP)索引。SVP具备两个关键特性:快与省

其中,快指两个阶段的快:

  • 金字塔创建快:Ganos利用空间索引对数据在空间上进行密集度划分,根据密集度建立一种稀疏矢量金字塔索引,相比传统切图流程减少了90%的数据计算量。同时,创建金字塔采用了完全并行处理模式,即使1亿条地类图斑数据生成金字塔也仅需耗费约10分钟时间。
  • 数据展现快:Ganos采用了视觉可见性剔除算法,根据Z-order排序,过滤掉大量不影响显示效果的数据,从而加快实时显示的效率。Ganos支持直接输出PNG格式的栅格瓦片和MVT格式的矢量瓦片,1亿地类图斑数据实时渲染显示的响应时间都达到秒级。

省也具有两个维度:

  • 节省磁盘空间:1亿条地类图斑数据生成金字塔索引仅仅占据原表5%大小的额外空间。
  • 节省开发时间:仅使用简单的SQL语句,通过调整语句参数即可灵活控制显示效果。

03 使用步骤

Ganos的快显引擎使用上非常简洁,已高度封装了SQL函数。需要注意的是,第一次使用快显引擎之前,需要显式创建对应的扩展模块,执行的语句如下:

CREATE EXTENSION ganos_geometry_pyramid CASCADE;

通过执行以上语句,快显引擎的计算组件将会被加载起来。

3.1 建立稀疏矢量金字塔

假设您已创建了某个矢量大表并导入了数据,接着就可以使用Ganos的st_buildpyramid方法创建矢量金字塔。

方法原型如下,更详细的参数描述可以参考官方文档。

boolean ST_BuildPyramid(cstring table, cstring geom, cstring fid, cstring config)

注:*左右滑动阅览

其中

  • table:矢量数据所在的表名。
  • geom:矢量字段名。
  • fid:矢量要素记录的唯一标识,支持Int4/Int8类型。
  • config:json格式的配置参数字符串。
    • 在本例中,我们指定矢量金字塔的名称和使用的逻辑瓦片大小(这个瓦片大小并非真实存在的瓦片,仅表示一种空间上的逻辑划分)

实际调用如下:

ST_BuildPyramid('points', 'geom', 'gid', '{"name":"points_geom","tileSize":512}')

注:*左右滑动阅览

我们为表points的geom字段创建了一个矢量金字塔,金字塔名我们指定为points_geom,同时设定金字塔的逻辑瓦片大小为512。

3. 2 获取栅格瓦片

栅格瓦片是图片形式的瓦片(Tile),是使用最广泛的一种地图瓦片形式。Ganos的ST_AsPng方法提供了在数据库端将矢量数据按需动态渲染为栅格瓦片的功能。该功能提供了最基础的栅格符号化能力,更多面向一些不需要复杂符号化的轻量级场景,如数管系统中。

方法原型如下,更详细的参数描述可以参考官方文档

bytes ST_AsPng( cstring name, cstring tile, cstring style)

其中

  • name:金字塔表名。
  • tile:瓦片索引行列号,Z_X_Y的形式。
  • style:渲染样式。我们可以通过如下参数调节渲染效果:
    • point_size:点大小,单位为像素。
    • line_width:线宽,对线要素和面要素的外边框起作用,单位为像素。
    • line_color:线渲染颜色,对线要素和面要素的外边框起作用。前6位为16进制颜色,后2位为16进制透明度。
    • fill_color:填充颜色,对面要素起作用。
    • background:背景色。一般设置为FFFFFF00,即纯透明。

实际调用如下:

ST_AsPng('points_geom', '1_2_1','{"point_size": 5,"line_width": 2,"line_color": "#003399FF","fill_color": "#6699CCCC","background": "#FFFFFF00"}')

注:*左右滑动阅览

我们从矢量金字塔中获取到索引行列号为x=2,y=1,z=1的矢量瓦片,并将该矢量瓦片按照我们配置的样式渲染为栅格瓦片,返回PNG格式的图片。

3. 3 获取矢量瓦片

矢量瓦片是新兴的地图瓦片技术,具有在前端配置样式的灵活特性,使用WebGL渲染,效果也更加美观,Mapbox等地图框架可以方便的支持这一格式。使用Ganos的ST_Tile方法可以将矢量金字塔中的数据以矢量瓦片的形式提供。

方法原型如下,更详细的参数描述可以参考官方文档。

bytea ST_Tile(cstring name, cstring key);

其中

  • name:金字塔名。在本例中为表名_矢量字段名
  • key:瓦片索引行列号,Z_X_Y的形式。

我们只需要提供标准的TMS行列号和金字塔表的名称即可调用。

实际调用如下:

ST_Tile('points_geom', '1_2_1');

我们从矢量金字塔中获取到索引行列号为x=2,y=1,z=1的矢量瓦片,返回数据为标准的MVT格式。

04 实战案例

4.1 测试数据

我们准备两份矢量数据作为测试样例。

buildings表为面数据,数据总计1.25亿条,展现使用栅格瓦片的在线可视化效果。

gid|geom                                                                                                                                                                                                                                                           |
---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|1|MULTIPOLYGON(((-88.066953 34.916114 0,-88.066704 34.916114 0,-88.066704 34.91602 0,-88.066953 34.91602 0,-88.066953 34.916114 0)))                 2|MULTIPOLYGON(((-87.924658 34.994797 0,-87.924791 34.99476 0,-87.924817 34.994824 0,-87.924685 34.994861 0,-87.924658 34.994797 0)))                

注:*左右滑动阅览

points表为点数据,数据总计10.7万条,展现使用矢量瓦片的在线可视化效果。

id|geom                          |
--|------------------------------|1|POINT (113.5350205 22.1851929)|2|POINT (113.5334245 22.1829781)|

4.2 全栈架构

数据库-快显全栈架构包含数据库服务器、python服务端和用户端三个部分,全栈架构如下图所示。

4.3 服务端代码

为了代码简洁,更侧重于逻辑的描述,我们选择了Python(兼容Python3.6及以上版本)作为后端语言,Web框架使用了基于Python的Flask(使用pip install flask进行安装)框架,数据库连接框架使用了基于Python的Psycopg2(使用pip install psycopg2进行安装)。

值得一提的是,我们实现了最基础的功能,当Web服务自身的性能出现瓶颈时,可按不同的平台与框架进行优化,获得更好的响应性能。

我们在后端首先建立了矢量金字塔,其后分别实现了两个接口,矢量瓦片接口使用points表中的数据,栅格瓦片接口使用buildings表中的数据,并定义好样式,供前端直接调用。为了方便说明,后端代码同时提供了矢量栅格两个接口,实际使用时可以按需选择。

# -*- coding: utf-8 -*-
# @File : Vector.pyimport json
from psycopg2 import pool
from threading import Semaphore
from flask import Flask, jsonify, Response, send_from_directory
import binascii# 连接参数
CONNECTION = "dbname=postgres user=postgres password=postgres host=YOUR_HOST port=5432"class ReallyThreadedConnectionPool(pool.ThreadedConnectionPool):"""面向多线程的连接池,提高地图瓦片类高并发场景的响应。"""def __init__(self, minconn, maxconn, *args, **kwargs):self._semaphore = Semaphore(maxconn)super().__init__(minconn, maxconn, *args, **kwargs)def getconn(self, *args, **kwargs):self._semaphore.acquire()return super().getconn(*args, **kwargs)def putconn(self, *args, **kwargs):super().putconn(*args, **kwargs)self._semaphore.release()class VectorViewer:def __init__(self, connect, table_name, column_name, fid):self.table_name = table_nameself.column_name = column_name# 创建一个连接池self.connect = ReallyThreadedConnectionPool(5, 10, connect)# 约定金字塔表名self.pyramid_table = f"{self.table_name}_{self.column_name}"self.fid = fidself.tileSize = 512# self._build_pyramid()def _build_pyramid(self):"""创建金字塔"""config = {"name": self.pyramid_table,"tileSize": self.tileSize}sql = f"select st_BuildPyramid('{self.table_name}','{self.column_name}','{self.fid}','{json.dumps(config)}')"self.poll_query(sql)def poll_query(self, query: str):pg_connection = self.connect.getconn()pg_cursor = pg_connection.cursor()pg_cursor.execute(query)record = pg_cursor.fetchone()pg_connection.commit()pg_cursor.close()self.connect.putconn(pg_connection)if  record is not None:return record[0]class PngViewer(VectorViewer):def get_png(self, x, y, z):# 默认参数config = {"point_size": 5,"line_width": 2,"line_color": "#003399FF","fill_color": "#6699CCCC","background": "#FFFFFF00"}# 在使用psycpg2时,将二进制数据以16进制字符串的形式传回效率更高sql = f"select encode(st_aspng('{self.pyramid_table}','{z}_{x}_{y}','{json.dumps(config)}'),'hex')"result = self.poll_query(sql)# 只有在使用16进制字符串的形式传回时才需要将其转换回来result = binascii.a2b_hex(result)return resultclass MvtViewer(VectorViewer):def get_mvt(self, x, y, z):# 在使用psycpg2时,将二进制数据以16进制字符串的形式传回效率更高sql = f"select encode(st_tile('{self.pyramid_table}','{z}_{x}_{y}'),'hex')"result = self.poll_query(sql)# 只有在使用16进制字符串的形式传回时才需要将其转换回来result = binascii.a2b_hex(result)return resultapp = Flask(__name__)@app.route('/vector')
def vector_demo():return send_from_directory("./", "Vector.html")# 定义表名,字段名称等
pngViewer = PngViewer(CONNECTION, 'usbf', 'geom', 'gid')@app.route('/vector/png/<int:z>/<int:x>/<int:y>')
def vector_png(z, x, y):png = pngViewer.get_png(x, y, z)return Response(response=png,mimetype="image/png")mvtViewer = MvtViewer(CONNECTION, 'points', 'geom', 'gid')@app.route('/vector/mvt/<int:z>/<int:x>/<int:y>')
def vector_mvt(z, x, y):mvt=mvtViewer.get_mvt(x, y, z)return Response(response=mvt,mimetype="application/vnd.mapbox-vector-tile")if __name__ == "__main__":app.run(port=5000, threaded=True)

注:*左右滑动阅览

将以上代码保存为Vector.py文件,执行python Vector.py命令即可启动服务。

从代码不难推断,无论我们使用何种语言、何种框架,我们只需将矢量或栅格瓦片的SQL语句封装为接口即可实现完全相同的功能。相比发布传统的地图服务,借助Ganos的矢量金字塔功能实现在线可视化是更加轻量好用的选择:

  • 针对栅格瓦片,可以在通过改变代码进行样式控制,灵活性大大增强。
  • 无需引入第三方的其他组件,也不需要进行针对性优化,就有令人满意的响应性能。
  • 可以任意选择使用者熟悉的编程语言与框架,也无需复杂专业的参数配置,对非地理从业者更加的友好。

4.4 用户端代码

我们选用Mapbox作为前端地图框架,展示后端提供的矢量瓦片层和栅格瓦片层,并为矢量瓦片层配置了渲染参数。

为了方便说明,前端代码同时添加了矢量、栅格两个图层,实际使用时可以按需选择。

我们在后端代码的同一文件目录下新建名为Vector.html的文件,写入下列代码,在后端服务启动后,就可以通过
http://localhost:5000/vector访问了。

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title></title><linkhref="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/1.13.0/mapbox-gl.min.css"rel="stylesheet"/></head><script src="https://cdn.bootcdn.net/ajax/libs/mapbox-gl/1.13.0/mapbox-gl.min.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script><body><div id="map" style="height: 100vh" /><script>const sources = {osm: {type: "raster",tiles: ["https://b.tile.openstreetmap.org/{z}/{x}/{y}.png"],tileSize: 256,},};const layers = [{id: "base_map",type: "raster",source: "osm",layout: { visibility: "visible" },},];const map = new mapboxgl.Map({container: "map",style: { version: 8, layers, sources },});map.on("load", async () => {map.resize();// 添加栅格瓦片数据源map.addSource("png_source", {type: "raster",minzoom: 1,tiles: [`${window.location.href}/png/{z}/{x}/{y}`],tileSize: 512,});// 添加栅格瓦片图层map.addLayer({id: "png_layer",type: "raster",layout: { visibility: "visible" },source: "png_source",});// 添加矢量瓦片数据源map.addSource("mvt_source", {type: "vector",minzoom: 1,tiles: [`${window.location.href}/mvt/{z}/{x}/{y}`],tileSize: 512,});// 添加矢量瓦片图层,并为矢量瓦片添加样式map.addLayer({id: "mvt_layer",paint: {"circle-radius": 4,"circle-color": "#6699CC","circle-stroke-width": 2,"circle-opacity": 0.8,"circle-stroke-color": "#ffffff","circle-stroke-opacity": 0.9,},type: "circle",source: "mvt_source","source-layer": "points_geom",});});</script></body>
</html>

注:*左右滑动阅览

4.5 矢量瓦片的动态效果

可以在前端调节不同效果。调整为新的图层参数后效果如下:

{"circle-radius": 4,"circle-color": "#000000","circle-stroke-width": 2,"circle-opacity": 0.3,"circle-stroke-color": "#003399","circle-stroke-opacity": 0.9,
}

4.6 栅格瓦片的动态效果

05 与PGADmin集成

PG数据库管理工具PGAdmin原生支持矢量数据的可视化,但因缺乏快显技术,仅能单对象显示或有限结果集显示,无法对大规模矢量数据进行畅快淋漓的全局浏览。我们将Ganos的矢量快显功能与PGAdmin集成,数据入库即可在线浏览全局,快速评估数据概况,大大增强了数据管理的使用体验。

06 总结

本文从稀疏矢量金字塔的原理与优势入手,介绍了如何利用Ganos实现在线可视化服务的各种功能,并最终通过百行代码实现了一个可以应对亿级数据的地图可视化服务。读者可以进一步在可视化基础上,利用PG/PolarDB Ganos的服务器端快速查询和分析能力进行对象属性查询、空间圈选、空间分析等更复杂功能。这就是Ganos所带来的大规模空间图形显示加速黑科技——稀疏矢量金字塔索引带来的变革。

作者:李鹤

原文链接

本文为阿里云原创内容,未经允许不得转载

基于Ganos百行代码实现亿级矢量空间数据在线可视化相关推荐

  1. js 监听 安卓事件_百行代码实现js事件监听实现跨页面数据传输

    百行代码实现js事件监听实现跨页面数据传输 使用场景 类似消息队列的使用场景,支持同页面和跨页面通信,发送消息和接收消息 技术原理 跨页面通信: 基于事件监听,通过监听 storage事件监听回调机制 ...

  2. Kaggle—So Easy!百行代码实现排名Top 5%的图像分类比赛

    北京 上海巡回站 | NVIDIA DLI深度学习培训 2018年1月26/1月12日 NVIDIA 深度学习学院 带你快速进入火热的DL领域 阅读全文                        ...

  3. python图像分类代码_Kaggle—So Easy!百行代码实现排名Top 5%的图像分类比赛

    Kaggle-So Easy!百行代码实现排名Top 5%的图像分类比赛 作者:七月在线彭老师 说明:本文最初由彭老师授权翟惠良发布在公众号"七月在线实验室"上,现再由July重新 ...

  4. python写百行代码可运行_56 岁潘石屹学俩月 Python ,写下百行代码

    原标题:56 岁潘石屹学俩月 Python ,写下百行代码 By 超神经 内容导读:跨界王潘石屹在近期迷上了编程,不仅高调宣布学习 Python,拜老师,还隔三差五晒出自己的「编程课作业」,和网友进行 ...

  5. 基于阿里云的超级性能测试 亿级企业压力测试神器JMeter4.X实战 抗压神器JMeter课程

    基于阿里云的超级性能测试 亿级企业压力测试神器JMeter4.X实战 抗压神器JMeter课程 ===============课程目录=============== ├─第1章 章节一JMeter压力 ...

  6. 鱼佬:百行代码入手数据挖掘赛!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:鱼佬,武汉大学,Datawhale成员 本实践以科大讯飞xData ...

  7. android底部滑出view,Android CoordinatorLayout与NestedScrollView基于Behavior几行代码实现底部View滑入滑出...

    Android CoordinatorLayout与NestedScrollView基于Behavior几行代码实现底部View滑入滑出 在CoordinatorLayout的Behavior出现之前 ...

  8. 百行代码打造一个DI容器(支持瞬时生命周期、单利生命周期、构造函数自动注入、属性自动注入、字段自动注入)...

    DI注入在.Net平台是非常流行的, 很多项目都用到了,很多开发人员或多或少也用到DI容器了,感觉DI容器很神奇很厉害.本文将通过百行代码展示DI容器的内部核心代码(包括组件的瞬时生命周期.单利生命周 ...

  9. 猜猜乐游戏php源码,C/C++百行代码实现热门游戏消消乐功能的示例代码

    游戏设计 首先我们需要使用第三方框架,这里我使用的是sfml,不会使用sfml在我的上几篇文章当中-扫雷(上)有详细的开发环境搭建介绍 首先准备图片资源 一张背景图片,一张宝石图片 窗口初始化加载图片 ...

最新文章

  1. golang 得到字符串在文件中的行号
  2. arch linux引导不启动_Linux 内核源代码的目录结构
  3. 【机器学习基础】一文读懂用于序列标注的条件随机场(CRF)模型
  4. BZOJ[1972] [Sdoi2010]猪国杀
  5. asp.net开发 网络硬盘[转贴+删节]
  6. python增加一列数据_python数据怎么添加列?
  7. 一道数学题引发的思考
  8. oracle vm virtualbox 虚拟光盘_虚拟机管理神器Vagrant完整操作手册来了
  9. Jmeter打开url时提示“请在微信客户端打开链接问题”
  10. It's a beautiful world!
  11. 11/27 记事本
  12. [工具]勒索病毒解密工具汇总
  13. linux触摸屏校准命令,FL2440下触摸屏校准
  14. nginx http强制跳转https 配置相关信息
  15. 摩伴windows桌面服务器,魔伴windows桌面
  16. 开源项目SMSS发开指南(四)——SSL/TLS加密通信详解
  17. 华为路由器实现ipsec
  18. 2021年东方市铁路中学高考成绩查询,2020东方市中考分数线预测
  19. 超全石膏材质贴图素材网站整理
  20. 赫夫曼树(WPL最小树)

热门文章

  1. 桶式排序 php,PHP实现桶排序算法
  2. linux下关闭网络服务,Linux 关闭网络管理服务
  3. php 输出可以设置格式文件,php格式输出文件var_export函数实例
  4. 查看socket缓冲区数据_什么是socket缓冲区?
  5. caffe不支持relu6_国产AI框架再进化!百度Paddle Lite发布:率先支持华为NPU在线编译,全新架构更多硬件支持...
  6. 矩阵键盘简易计算机设计报告,矩阵键盘显设计报告..doc
  7. mysql带参数的sql_MySql存储过程是带参数的存储过程(动态执行SQL语句)
  8. pythonsearch结果_python 查询Elasticsearch的小例子
  9. python notebook软件_Jupyter notebook快速入门教程(推荐)
  10. 怎么彻底重装清空电脑_电脑开不了机怎么重装系统?不用送去维修店啦!