Ganos低代码实现免切片遥感影像浏览(二):动态栅格瓦片

Ganos的动态栅格瓦片能力能够快速实现库内栅格数据或栅格分析结果的可视化,且无需依赖GeoServer等空间服务中间件。该技术栈简洁高效,使用灵活便捷。

关于Ganos栅格引擎

Ganos Raster是PolarDB PostgreSQL版的一个时空引擎扩展,使上述数据库能够有效快速存储管理栅格类型数据,同时支持多源栅格数据(如遥感、摄影测量和专题地图)之间的融合与分析,并提供了GeoServer插件将库内栅格对象发布为OGC标准的服务(如WMS或WMTS等)。Ganos Raster可用于包括气象、环境监测、地质勘探、自然资源管理、国防、应急响应、电信、传媒、交通、城市规划以及国土安全等领域。

关于栅格引擎的详细介绍,请参考栅格模型

由于栅格数据的规模较大,其可视化通常采用预制栅格瓦片并发布服务的方式。然而,切片所带来的数据现势性问题被越来越多的用户所诟病,同时,切出来的瓦片数据也带来额外的存储成本。长期以来,Ganos始终致力于构建卓越的时空数据查询与分析性能,以推动更多时空业务的“在线化”,其中也包括“切片”等离线作业方式。之前我们介绍了通过Ganos Raster的金字塔方法,在不进行预切片的情况下快速可视化库内的栅格数据,详情请参考低代码实现免切片遥感影像浏览(一):金字塔。本次我们提供一种更加直观的方式,类似于在矢量数据中使用asMVT方法,通过动态生成栅格瓦片以快速查看栅格数据。与前一种方案相比,本文方案采用标准瓦片形式进行动态处理和输出,无需依赖GeoServer等空间服务中间件,具有更为简洁高效的技术栈,从而使得使用过程更加灵活。

动态栅格瓦片能力介绍

业务背景

传统遥感影像服务发布过程中,需要事先对栅格对象按照切片规则进行分层切片,然后使用GeoServer等服务器进行瓦片服务发布。该流程存在如下缺点:

  • 对超大范围的遥感影像切片,处理耗时通常较长,服务发布流程较为复杂,导致无法实现快速发布服务。

  • 切片存储需要消耗额外的存储空间,造成数据大量冗余。

  • 数据更新后,需要重新进行切片,无法实现实时更新。

能力介绍

针对上述问题,Ganos在6.3版本及后续版本提供ST_AsTile函数。通过该函数,可以对指定范围内的Raster对象进行实时动态瓦片生成。在栅格数据入库后,只需指定空间范围及相应的瓦片生成参数,数据库将自动执行栅格对象的裁剪、重投影、重采样和渲染等操作,并按照256x256或512x512的尺寸输出为标准瓦片。通过测试,单个瓦片的处理时间控制在百毫秒以内,完全能够满足对栅格数据服务发布实时性的要求,同时有效避免数据冗余和更新相关的问题。动态栅格瓦片将Ganos的快显能力与栅格数据类型进行深度融合,使得动态可视化栅格数据的使用与动态矢量瓦片(ST_AsMVT)同样便捷高效,从而在很大程度上提升Ganos栅格数据类型视算一体的能力。

技术优势

相比传统的栅格切片,使用ST_AsTile的主要优势包括:

  • 支持实时动态生成规则的瓦片,避免传统切瓦片操作存在的数据冗余问题,节省存储成本。

  • 自动实现栅格裁剪、重投影、重采样操作,更加方便简洁。

  • 单个瓦片生成效率达到百毫秒内,满足实时性要求。

  • 数据更新后,无需重新切片,可实时展示更新内容。

说明

目前ST_AsTile功能仅支持对单个Raster对象的操作,如果涉及到多个Raster对象(如多景影像需要同时查看)则需要对Raster对象进行镶嵌操作。

最佳实践

为了得到良好的体验,PolarDB集群建议使用以下配置:

  • 数据库引擎:PostgreSQL 14

  • 内核版本:14.13.25.0

  • CPU:>4 核

  • 内存:>16GB

  • 磁盘:>50GB

  • Ganos版本:>=6.3

  1. 安装ganos_raster插件。

    CREATE EXTENSION ganos_raster CASCADE;
  2. 数据准备与入库。准备4景landsat8影像,共RGB+NIR四个波段,上传到OSS。

    image

    • 创建栅格数据表。

      CREATE TABLE landsat8
      (
        id integer,
        rast raster
      );
    • 使用ST_ImportFrom函数进行数据入库。

      INSERT INTO landsat8 VALUES (1, ST_ImportFrom('chunk_table','OSS://<access_id>:<secrect_key>@<Endpoint>/<bucket>/path_to/file1.TIF'));
      INSERT INTO landsat8 VALUES (2, ST_ImportFrom('chunk_table','OSS://<access_id>:<secrect_key>@<Endpoint>/<bucket>/path_to/file2.TIF'));
      INSERT INTO landsat8 VALUES (3, ST_ImportFrom('chunk_table','OSS://<access_id>:<secrect_key>@<Endpoint>/<bucket>/path_to/file3.TIF'));
      INSERT INTO landsat8 VALUES (4, ST_ImportFrom('chunk_table','OSS://<access_id>:<secrect_key>@<Endpoint>/<bucket>/path_to/file1.TIF'));
      说明

      PolarDB集群需要和OSS在同一个Region中,并通过使用内部地址方式进行访问。以上SQL语句的OSS文件地址需要替换为实际信息(不带尖括号)。数据入库成功后可以查看4景raster影像。

      SELECT ST_Name(rast), ST_Width(rast),ST_Height(rast),ST_SRID(rast),ST_NumBands(rast) FROM landsat8;
  3. 数据镶嵌/匀色。使用Raster提供的ST_MosaicFrom函数将多景遥感影像镶嵌为一个Raster对象,id为101。这里通过设置"color_balance"属性为true以支持匀色功能(按需)。然后,通过ST_BuildPyramid函数对镶嵌后Raster对象创建金字塔信息以提高显示效率。具体SQL语句如下。

    --镶嵌/匀色
    INSERT INTO landsat8 values(101, ST_MosaicFrom(Array(
      SELECT rast FROM landsat8 WHERE id < 5),
      'rbt_mosaic','',
      '{"srid":4326, "cell_size":[0.0002,0.0002], "nodata": true, "nodatavalue":0, "color_balance":true}'));
    
    --创建金字塔
    UPDATE landsat8 SET rast=st_buildpyramid(rast) WHERE id=101;

    匀色后的Raster对象导出为GTiff格式后显示如下:

    image

  4. 生成瓦片。使用ST_AsTile函数可以对指定范围内的Raster对象进行裁剪,重投影,并按照256x256尺寸输出为标准瓦片。在具体使用过程中,空间范围可以按照Google地图的切片方式,通过zoom的层级和行列号来换算出具体的空间范围,然后通过下面SQL就可以获取PNG格式的256x256的瓦片,从而可直接从地图上展示。

    1. RGB DOM影像

      对于三波段的DOM影像,不需要额外像素值拉伸处理,可以直接导出为PNG格式的Tile进行显示。每个瓦片的空间范围可以通过ST_TileEnvelope获得。具体SQL如下。

      SELECT ST_AsTile(
        rast,
        ST_TileEnvelope(tile_zoom, tile_column, tile_row),
        '{"strength":"none", "format":"PNG", "alpha":true}'
      )
      FROM landsat8 WHERE id=101;

      ST_TileEnvelope参数介绍:

      • format:导出瓦片格式,目前支持PNG、JPEG和Tiff三种格式。默认为PNG,为经过拉伸后可以直接显示的RGB瓦片。如果希望对原始像素信息进行处理,可以选择GTiff,可以拿到最原始的像素值。

      • bands:指定RGB对应的波段,如果不指定,默认选择前三个波段。

      • strength:显示增强的方式,取值范围包括:

        • none:不进行增强。

        • stats(默认):使用统计值进行拉伸。

        • ratio:按照百分比拉伸。

    2. 多波段

      对于像Landsat这种多波段遥感影像,一般需要选择3个波段,并对像素值进行拉伸,变成0-255范围内的RGB波段,才能正常显示。具体SQL语句需要修改strength和bands参数,如下:

      SELECT ST_AsTile(
        rast,
        ST_TileEnvelope(tile_zoom, tile_column, tile_row),,
        '{"strength":"ratio", "format":"PNG", "bands":"0,1,2", "alpha":true}'
      )
      FROM landsat8 WHERE id=101;

      这里选择0,1,2三个波段作为RGB波段,并采用ratio方式按照百分比拉伸。

  5. 前端设计。可以使用任何开发语言进行服务接口的开发,此处以Python环境为例。

    1. 首先安装需要的包。

      pip install asyncpg
      pip install quart
    2. 创建app.py文件。

      from quart import Quart, send_file, render_template
      import asyncpg
      import io
      import re
      
      ## 数据库连接参数
      CONNECTION = {"host": "数据库连接地址", "port": "端口",
                    "user": "用户名", "password": "密码", "database": "数据库名称"}
      
      ## 目标表名
      TABLE_NAME = "landsat8"
      
      ## 目标字段
      RASTER_COLUMN = "rast"
      
      ## 目标ID
      RASTER_ID = "101"
      
      app = Quart(__name__, template_folder='./')
      
      
      @app.before_serving
      async def create_db_pool():
          app.db_pool = await asyncpg.create_pool(**CONNECTION)
      
      
      @app.after_serving
      async def close_db_pool():
          await app.db_pool.close()
      
      
      @app.route("/")
      async def home():
          sql = f'''
          SELECT ST_Extent(
            ST_Transform(
              ST_Envelope({RASTER_COLUMN}), 4326))
          FROM {TABLE_NAME} 
          WHERE ID = {RASTER_ID};
          '''
          async with app.db_pool.acquire() as connection:
              box = await connection.fetchval(sql)
              box = re.findall('BOX\((.*?) (.*?),(.*?) (.*?)\)', box)[0]
              min_x, min_y, max_x, max_y = list(map(float, box))
              bounds = [[min_x, min_y], [max_x, max_y]]
              center = [(min_x + max_x) / 2, (min_y + max_y) / 2]
              return await render_template('./index.jinja2', center=str(center), bounds=str(bounds))
      
      
      @app.route("/raster/<int:z>/<int:x>/<int:y>")
      async def raster(z, x, y):
          # 指定波段及拉伸方式
          config = '{"strength":"ratio","bands":"0,1,2","alpha":true}'
          sql = f'''
          SELECT (
            ST_AsTile({RASTER_COLUMN},
              ST_Transform(
                ST_Tileenvelope($1,$2,$3),
              ST_Srid({RASTER_COLUMN})), \'{config}\')
            ).data tile
          FROM {TABLE_NAME}
          WHERE ID = {RASTER_ID};'''
          async with app.db_pool.acquire() as connection:
              tile = await connection.fetchval(sql, z, x, y)
              return await send_file(io.BytesIO(tile), mimetype='image/png')
      
      if __name__ == "__main__":
          # 指定端口为5500,可自行修改为合适的端口
          app.run(port=5500)
    3. 启动服务。

      python app.py
  6. 在浏览器中查看图层效果.

    image

总结

Ganos的栅格数据快显能力,可以快速可视化存储在PolarDB数据库中的各类栅格数据。相较于传统发布预制栅格瓦片服务的方式,全新的动态栅格瓦片可以更好地保持数据现势性,同时,与Ganos强大的栅格分析能力结合,保证分析结果实时可见。Ganos作为全球首个专业级空间数据库,已经将狭义的空间数据拓展至“空天地、室内外、地上下、动静态”等全空间范畴,从数据库系统最底层为物理世界数字化提供时空处理框架,未来Ganos还将提供更多高效的库内空间分析与可视化能力,推动各行业的空间信息应用真正走向“视算一体”。

试用体验

您可以访问PolarDB免费试用页面,选择试用“云原生数据库PolarDB PostgreSQL版”,体验Ganos快显能力。