暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB Ganos矢量快显功能上手

原创 Ganos全空间数据库 2023-06-14
611

本文介绍了由阿里云数据库产品事业部联合阿里巴巴达摩院数据库与存储实验室联合研发的多模态时空数据库组件Ganos的2D和3D矢量快显功能在云原生关系型数据库PolarDB上的应用。其中,Ganos 2D矢量快显功能可用于对亿级规模2D矢量数据的高效可视化,解决了传统切片方案切片时间长和切片存储开销大两大痛点,并且支持局部更新,相较于现有系统,在效率、存储开销以及功能丰富性上都有很大提升;Ganos 3D矢量可视化功能通过对2D矢量切片进行扩展,使其能够支持Geometry3D数据的可视化,可用于可视化大范围3D场景。本文以实例的形式介绍了如何创建和更新2D矢量金字塔以及如何返回切片等功能。通过参考本文,用户可以很快上手体验Ganos快显功能。


Ganos产品简介

Ganos是阿里云数据库产品事业部联合阿里巴巴达摩院数据库与存储实验室联合共同研发的新一代云原生位置智能引擎,它将时空数据处理能力融入了云原生关系型数据库PolarDB、云原生多模数据库Lindorm云原生数据仓库AnalyticDB和云数据库RDS PG等核心产品中。Ganos目前拥有几何、栅格、轨迹、表面网格、体网格、3D实景、点云、路径、地理网格、快显十大核心引擎,为数据库构建了面向新型时空多模多态数据的一体化表达、存储、查询与分析计算能力。

本文介绍的快显引擎能力,依托阿里云云原生关系型数据库PolarDB与云数据库RDS PG建设输出。

2D矢量快显功能介绍

先来一张动图感受一下。

2D矢量快显是由Ganos的快显引擎提供的对亿级二维矢量数据(点、线、面)的可视化功能。现有系统支持矢量数据可视化,主要是采用离线切片的方式,当可视化请求到来时,将预先建好的切片经过简单处理后返回给用户系统。这种处理方案有两大痛点:一个是“慢”,大规模数据集上运行离线切片往往需要十几个小时甚至几天的时间才能完成;另一个是“大”,在最高支持16层级缩放的地图服务中,需要事先存储几十亿的切片,存储开销很大。另外由于“慢”这个问题,还衍生出了对数据更新不友好这个新的问题。这个问题在数据管理能力较弱的GIS系统中尤为明显,由于缺乏对数据更新内容、及其对切片影响范围的感知,一旦发生数据更新,哪怕只是小范围的局部更新,也只能采用耗时费力的全盘重建切片的方式来解决。

Ganos的2D矢量快显功能能够很好地解决以上问题,Ganos创新性地提出了稀疏金字塔索引,跳过对数据稀疏区域的切片构建,结合数据库查询优化并通过视觉可见性剔除算法,过滤掉大量不影响显示效果的数据,一举解决了切片时间漫长和存储开销巨大的两大痛点。不仅如此,Ganos还支持稀疏金字塔的动态更新。当矢量数据发生了小范围内的局部更新时,Ganos可以自动识别出显示效果发生变化的切片,将切片的更新限制在尽量小的范围内,避免了需要推翻稀疏金字塔重建,具有更好的更新效率。实测在一台配置普通的8核PolarDB for PG公共云实例上,给包含七千万条房屋的数据集构建稀疏金字塔,仅需6分钟,当发生一百万条以上的数据更新时,稀疏金字塔的更新也仅需不到1分钟。响应可视化请求时,平均返回一个切片的时间不到1毫秒。拥有如此高效的效率,但是所需要的磁盘存储空间却只需3GB左右。本文将结合实例帮助读者快速上手体验Ganos的矢量快显功能。

2D矢量快显使用步骤

准备数据表

首先需要准备一个包含Geometry属性的数据表。Ganos提供了丰富的函数可用于将矢量数据写入到数据表中,包括常见的ST_GeomFromText、ST_GeomFromWKT、ST_GeomFromWKB等。要使用Ganos的矢量快显功能,只需要数据表包含主键id和Geometry属性即可,十分简单。在开始使用Ganos的快显功能之前,先需要使用如下命令创建Ganos的快显功能的扩展:

CREATE EXTENSIION ganos_geometry_pyramid CASCADE;
复制

执行如下命令创建一个只包含主键id和geom列的数据表:

CREATE TABLE try_ganos_viz(id SERIAL NOT NULL, geom Geometry);
复制

用户可以使用脚本或其它工具将一个矢量数据集导入该表,只需要该脚本或工具能够生成类似如下的SQL命令即可(假设数据集文件存储的是4326坐标系的WKT格式数据):

INSERT INTO try_ganos_viz(geom) VALUES (ST_GeomFromText('WKT FORMAT TEXT', 4326));
复制

上述命令中,是将数据以4326坐标系格式存储到数据库中,用户也可以指定存储3857坐标系格式。导入所有数据后,还需要对Geometry属性列构建一个空间索引,可执行如下命令:

CREATE INDEX ON try_ganos_viz USING gist(geom);
复制

到此,就完成了数据表的准备,可以开始体验矢量快显功能了。

构建稀疏金字塔

构建稀疏金字塔功能已经被封装成自定义函数,方法原型如下:

boolean ST_BuildPyramid(cstring table_name, cstring geom_col_name, cstring id_col_name, cstring config);
复制

上述第一个参数表示矢量数据所在的表格的名字,第二个参数表示Geometry的列名,第三个参数表示id的列名,第四个参数指定构建金字塔时的配置,配置信息是以JSON格式指定,可以为空,接下来会以示例的形式做更多介绍。

执行以下命令,创建一个使用默认参数配置的稀疏金字塔:

SELECT ST_BuildPyramid('try_ganos_viz', 'geom', 'id', '');
复制

用户可以在config参数中指定构建稀疏金字塔时的并行度,执行如下命令以32并行度构建矢量金字塔:

SELECT ST_BuildPyramid('try_ganos_viz', 'geom', 'id', '{"parallel":32}');
复制

和PostGIS一样,Ganos也允许用户定义切片的像素大小和裁切范围,这是通过在config参数中提供"tileSize"和“tileExtend”的值来实现的,示例如下:

SELECT ST_BuildPyramid('try_ganos_viz', 'geom', 'id', '{"parallel":32, "tileSize":4096, "tileExtend":128}');
复制

Ganos允许用户根据其对性能和存储开销的综合考量,用参数控制稀疏金字塔的结构。若用户相对于存储开销更加看重查询性能,可以通过设置较小的"splitSize"值(如果一个切片中的要素小于splitSize,则该切片是在查询时动态构建的),使得尽量多的切片都是事先已经构建好的;若用户更加看重存储开销,除了可以设置较大的“splitSize”值以外,还可以设置较小的"maxLevel"控制金字塔的高度,从而减少需要维护的切片的数量。下面的示例,构建了一个高度为10,"splitSize"值为1000的稀疏金字塔:

SELECT ST_BuildPyramid('try_ganos_viz', 'geom', '{"maxLevel":10, "splitSize":1000}');
复制

除了上述常用的config参数以外,Ganos还提供了“buildRules”支持用户非常灵活地控制稀疏金字塔的构造规则。比如用户希望在层级小于等于5的切片中只可视化面积大于100的要素,则可以使用如下命令:

SELECT ST_BuildPyramid('try_ganos_viz', 'geom', '{"maxLevel":10, "buildRules":[
                       {"level":[0,1,2,3,4,5], "value":{"filter": "ST_Area(geom)>100"}}
]}');
复制

在上个例子中,“filter”对应的过滤条件可以是任意SQL语句中位于WHERE后面的条件语句。

更多关于ST_BuildPyramid函数的参数信息,请参考官方文档

除了ST_BuildPyramid函数,Ganos新增ST_BuildPyramidUseGeomSideLen函数构建矢量金字塔。ST_BuildPyramidUseGeomSideLen相对于ST_BuildPyramid进行了性能优化,在数据表中含有很多面积很小的要素时,能够有效提升稀疏金字塔的构建效率。预要使用ST_BuildPyramidUseGeomSideLen函数,数据表中需要新增一列实数类型的属性,用于记录geom列的ST_XMax(geom)-ST_XMin(geom)和ST_YMax(geom)-ST_YMin(geom)的较大值,同时还需要针对该属性列构建一个索引。以上述的try_ganos_viz表为例,使用以下语句新增一个max_side_len属性列,并对该列构建一个B树索引:

ALTER TABLE try_ganos_viz
ADD COLUMN max_side_len DOUBLE PRECISION;

CREATE OR REPLACE FUNCTION add_max_len_values() RETURNS VOID AS $$
DECLARE
  t_curs CURSOR FOR
    SELECT * FROM try_ganos_viz;
  t_row usbf%ROWTYPE;
  gm GEOMETRY;
  x_min DOUBLE PRECISION;
  x_max DOUBLE PRECISION;
  y_min DOUBLE PRECISION;
  y_max DOUBLE PRECISION;
BEGIN
  FOR t_row IN t_curs LOOP
    SELECT t_row.geom INTO gm;
    SELECT ST_XMin(gm) INTO x_min;
    SELECT ST_XMax(gm) INTO x_max;
    SELECT ST_YMin(gm) INTO y_min;
    SELECT ST_YMax(gm) INTO y_max;
    UPDATE try_ganos_viz
      SET max_side_len = GREATEST(x_max - x_min, y_max - y_min)
    WHERE CURRENT OF t_curs;
  END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT add_max_len_values();

CREATE INDEX ON try_ganos_viz USING btree(max_side_len);
复制


在调用ST_BuildPyramidUseGeomSideLen时,需要提供新增列的列名,其余参数和ST_BuildPyramid相同,其函数原型如下所示:

boolean ST_BuildPyramidUseGeomSideLen(cstring table, cstring geom_field, cstring geom_side_len_field, cstring fid, cstring config);
复制

执行下列语句,对新增了max_side_len列的try_ganos_viz表构建稀疏金字塔:

SELECT ST_BuildPyramidUseGeomSideLen('try_ganos_viz', 'geom', 'max_side_len', 'id', '{"parallel":32}');
复制

ST_BuildPyramidUseGeomSideLen同样支持各类config参数,允许用户灵活地调整稀疏金字塔的构建规则。

更新金字塔

在数据表的数据发生数据更新时,Ganos提供了ST_UpdatePyramid函数对金字塔进行更新,用户只需提供数据更新的外包框范围即可,其函数原型如下:

boolean ST_UpdatePyramid(cstring table, cstring geom_field, cstring id_field, BOX2D update_extent, cstring rules) ;
复制

其中update_extent和rules分别表示数据更新所在的外包框范围和JSON格式的更新参数值。rules参数包括updateBoxScale和sourceSRS,其含义如下

  • 参数updateBoxScale会影响到更新自底向上最高传递到哪一层,如updateBoxScale=2,用户指定的更新区域的最大宽高值为100时,只会更新到层次为1的切片(假设全局范围是全球经纬度),因为层次为1的切片的最大宽高值除以100小于2,而层次为0的切片的最大宽高值除以100大于2。updateBoxScale默认值为10。

  • sourceSRS指定参数update_extent的EPSG格式,默认值为4326。

假设用户在[(lon1, lat1), (lon2, lat2)]的经纬度范围内插入了多条数据,如果用户想要所有被影响到的切片都被更新(假设maxLevel=16),可以执行下列语句:

SELECT ST_UpdatePyramid('try_ganos_viz', 'geom', 'id', ST_MakeEnvelope(0,-10,20,30, 4326), '{"updateBoxScale":100000}');
复制

在上述例子中假设lon1=0, lat1=-10, lon2=20, lat2=30。

如果用户并不想要全局大规模的更新,可以指定一个较小的updateBoxScale参数值,避免较小层级的切片也被更新,执行下列语句更新只比更新范围稍大的切片及其层级以下的切片:

SELECT ST_UpdatePyramid('try_ganos_viz', 'geom', 'id', ST_MakeEnvelope(0,-10,20,30, 4326), '{"updateBoxScale":2}');
复制


调用ST_UpdatePyramid时,不需要指定并行度。函数会自动采用调用ST_BuildPyramid或ST_BuildPyramidUseGeomSideLen时提供的并行值。由于更新金字塔需要涉及稀疏金字塔的更新、旧切片的删除,以及新切片的生成等步骤,在发生大范围的数据更新时,建议直接调用ST_BuildPyramid或ST_BuildPyramidUseGeomSideLen对金字塔进行重建。

获取矢量切片

矢量切片具有矢量保留要素信息的优点,在地图服务中,多级缩放相比于传统的栅格切片更加平滑,视觉效果更优。Ganos提供了ST_Tile函数,允许用户实时调用获取矢量切片,其函数原型如下:

bytea ST_Tile(cstring name, cstring key);
bytea ST_Tile(cstring name, int x, int y, int z);
复制

第一个函数原型中的key为'z_x_y'的格式,表示切片的编号,z表示切片所在的层级,x表示x轴方向的编号(从左到右递增),y表示表示y轴方向的编号(从上到下递增)。执行以下语句之一,返回中国所在的切片z=1,x=1,y=0:

SELECT ST_Tile('try_ganos_viz', '1_1_0');
SELECT ST_Tile('try_ganos_viz', 1, 0, 1);
复制

获取栅格切片

Ganos同样支持仍然广泛使用的栅格切片。栅格切片是图片形式的切片,相对于矢量切片,不支持客户端动态渲染,对客户端系统的性能要求较低。Ganos提供了ST_AsPng函数,允许在数据库端将矢量数据按需动态渲染为栅格切片,再返回给用户。该函数提供了最基础的栅格符号化能力,更多面向一些不需要复杂符号化的轻量级场景。ST_AsPng的函数原型如下:

bytea ST_AsPng(cstring name, cstring key, cstring style);
bytea ST_AsPng(cstring name, int x, int y, int z, cstring style);
复制

ST_AsPng函数相对于ST_Tile函数新增了style参数。该参数是JSON格式,指定了渲染样式,包含的渲染样式如下:

  • point_size: 点大小,单位为像素

  • line_width: 线宽,指定组成线和面要素的外包框的线的像素宽度

  • line_color: 线渲染颜色,对线要素和面要素的外包框起作用。前六位为16进制颜色,后两位为16进制透明度

  • fill_color: 填充颜色,对面要素起作用

  • background: 背景色,通常设置为FFFFF00,即纯透明


调用以下语句返回切片编号为'1_1_0'的栅格切片,该切片按照提供的渲染参数进行渲染并以PNG图片格式返回:

SELECT ST_AsPng('try_ganos_viz', '1_1_0', '{"point_size":5, "line_width":2, "line_color":"#003399FF", 
                "fill_color":"#6699CCCC", "background":"#FFFFFF00"}');
复制

基于2D矢量快显的Web地图服务

上述内容介绍了如何使用Ganos的2D矢量快显功能,接下来我们讲解如何基于Ganos的矢量快显功能,快速搭建一个Web地图服务。

全栈架构

该Web服务由数据库、Python服务端和用户端三部分组成,全栈架构图如下所示:

数据库

需要导入数据到数据库中,并构建好稀疏金字塔所需要的索引,具体内容参考前文。

服务器端代码

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

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

# -*- coding: utf-8 -*-
# @File : Vector.py

import json
from psycopg2 import pool
from threading import Semaphore
from flask import Flask, jsonify, Response, send_from_directory
import binascii

# 连接参数,用户可按照自己的情况进行修改
CONNECTION = "dbname=DB_NAME user=USER_NAME password=PASSWORD host=YOUR_HOST port=PORT_NO"

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_name
        self.column_name = column_name
        # 创建一个连接池
        self.connect = ReallyThreadedConnectionPool(5, 10, connect)
        # 约定金字塔表名
        self.pyramid_table = f"{self.table_name}_{self.column_name}"
        self.fid = fid
        self.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 result

class 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 result

app = Flask(__name__)

@app.route('/vector')
def vector_demo():
    return send_from_directory("./", "Vector.html")

# 定义表名,geom字段名、id字段名,用户可按照自己的情况进行修改
pngViewer = PngViewer(CONNECTION, 'buildings', 'geom', 'id')

@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"
    )

# 定义表名,geom字段名、id字段名,用户可按照自己的情况进行修改
mvtViewer = MvtViewer(CONNECTION, 'points', 'geom', 'id')

@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命令即可启动服务。

从以上代码可以看出,无论我们使用何种语言、何种框架,我们只需将访问2D矢量或栅格切片的SQL语句封装为接口即可实现完全相同的功能。相比发布传统的地图服务,借助Ganos的2D矢量快显功能实现在线可视化是更加轻量好用的选择:

  • 针对栅格瓦片,可以在通过改变代码进行样式控制,灵活性大大增强。

  • 无需引入第三方的其他组件,也不需要进行针对性优化,就有令人满意的响应性能。

  • 可以任意选择使用者熟悉的编程语言与框架,也无需复杂专业的参数配置,对非地理从业者更加的友好。

用户端代码

我们选用Mapbox作为前端地图框架,展示服务器端提供的矢量切片层和栅格切片层,并为矢量切片层配置了渲染参数。为了方便说明,前端代码同时添加了矢量、栅格两个图层,实际使用时可以按需选择。

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

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title></title>
    <link
      href="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>
复制

3D矢量可视化功能介绍

Ganos除了支持2D矢量快显,还与阿里云数据可视化DataV产品团队联合打造了对3D矢量数据可视化的支持。通过扩展现有的2D矢量切片标准,Ganos协同DataV实现了可用于3D矢量物体可视化的3D矢量切片,其效果图如下:


3D矢量可视化使用步骤

准备数据表

首先需要准备一个包含Geometry3D属性的数据表。用户可使用ST_GeomFromText、ST_GeomFromWKT、ST_GeomFromWKB等函数完成数据的导入。

在开始使用3D矢量可视化功能前,先需要使用如下命令创建Ganos的3D矢量可视化功能的扩展:

CREATE EXTENSIION ganos_geometry CASCADE;
复制

执行如下命令创建一个只包含主键id和geom列的数据表:

CREATE TABLE try_ganos_viz3d(id SERIAL NOT NULL, geom Geometry);
复制

获取3D矢量切片

预使用3D矢量数据可视化功能,需要调用Ganos提供的两个函数,其函数原型如下:

geometry ST_AsMVTGeom3D(geometry geom, box2d bounds, integer extent=4096, 
                        integer buffer=256, boolean clip_geom=true);
复制
bytea ST_AsMVT3D(anyelement set row);
bytea ST_AsMVT3D(anyelement row, text name);
bytea ST_AsMVT3D(anyelement row, text name, integer extent);
bytea ST_AsMVT3D(anyelement row, text name, integer extent, text geom_name);
bytea ST_AsMVT3D(anyelement row, text name, integer extent, text geom_name, 
                 text feature_id_name);
复制

函数ST_AsMVTGeom3D的功能类似于PostGIS的ST_AsMVTGeom之于2D矢量数据,但是对其进行了扩展,能够将Geometry3D数据的坐标空间转换到MVT的坐标空间,如果有需要的话,会根据切片的外包框对Geometry3D数据进行裁切。第一个参数表示要做坐标转换的Geometry3D数据;第二个参数表示矢量切片的矩形外包框,注意这里仍是使用2D矢量切片的外包框(即只考虑Geometry3D矢量数据的x轴和y轴形成的矩形外包框,注意区分这里的x和y不是表示切片id的(z, x, y));第三个参数表示在MVT坐标系中的切片范围的边长,默认值为4096;第四个参数表示在MVT坐标系中的切片范围以外的缓冲区外扩长度,用于geom的裁切,默认值为256;第四个参数表示是否对超出切片范围的geom在切片边缘做裁切,默认值为True。使用实例如下:

SELECT ST_AsText(ST_AsMVTGeom3D(ST_Transform('SRID=4326; LINESTRING(-10 -10 30, -10 -20 30)'::geometry, 3857), ST_TileEnvelope(1, 0, 0))) AS geom;
                                        geom                                        
------------------------------------------------------------------------------------
 MULTILINESTRING Z ((3868.44444444444 4324.7197219642 30,3868.44444444444 4352 30))
(1 row)
复制


函数ST_AsMVT3D的功能类似于PostGIS的ST_AsMVT之于2D矢量数据,是将若干行数据封装进一个3D矢量切片中的聚合函数。被封装的每条数据都对应经过将坐标空间转换到MVT的坐标空间的Geometry3D数据,这些被封装的数据一起形成一个切片的图层。参数row表示一条至少包含Geometry列的数据,参数name表示图层的名字,默认值是"default",参数geom_name表示行数据中几何列的列名,默认是使用第一个几何列,参数feature_id_name表示行数据中表示几何要素ID列的列名,如果其值为NULL或为负值,则不设置。使用实例如下:

WITH mvtgeom AS
(
  SELECT ST_AsMVTGeom3D(
    ST_Transform('SRID=4326; MULTIPOLYGON(((100 50 0, -100 50 1, -100 -50 2, 100 -50 3, 100 50 0)), ((0 0 0, 1 0 1, 2 2 2, 0 0 0)))'::geometry, 3857),
    ST_TileEnvelope(1, 0, 0)) AS geom,  'test' AS name
)
select ST_AsMVT3D(mvtgeom.*) from mvtgeom;
                                                                                                                     st_asmvt3d                                                                                                                     
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 \x1a760a0764656661756c74125812020000180322500d8044a842b83116ff23d80105802400080f0d810481041d162e000e2e590e0f0dd920dc0405168024d70106c727f3160d0f0dc827f4160e1600f31615c72700080f0d0000001600cc1808c80300000f1a046e616d6522060a04746573742880207802
(1 row)
复制

总结

本文介绍了由阿里巴巴独立研发的多模态时空数据库组件Ganos的2D和3D矢量快显功能。其中,2D矢量快显功能可用于对亿级规模2D矢量数据的高效可视化。Ganos的2D矢量快显快显功能解决了传统切片方案切片时间长和切片存储开销大两大痛点,并且支持局部更新,相较于现有系统,在效率、存储开销以及功能丰富性上都有很大提升。本文以实例的形式介绍了如何创建和更新2D矢量金字塔,以及如何返回切片等功能。通过参考本文,用户可以很快上手体验这一功能。Ganos的3D矢量可视化功能通过对2D矢量切片进行扩展,使其能够支持Geometry3D数据的可视化,可用于可视化大范围3D场景。

最后修改时间:2023-06-14 14:24:28
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论