Ganos矢量栅格数据快速入库

矢量数据与栅格数据是最常见的空间数据类型,广泛应用于多个领域的GIS相关应用。Ganos具备全面的矢栅数据处理能力,依托PolarDB PostgreSQL版的弹性并行处理能力,其计算效率相比传统方案提升了数倍甚至数十倍。本文介绍Ganos提供的矢量、栅格数据高效入库方法,旨在帮助您迅速理解云原生数据库在空间数据写入方面的解决方案,从而更好地利用Ganos在矢量和栅格数据处理中的能力。

背景

社区版PostgreSQL的PostGIS组件提供了shp2pgsqlraster2pgsql两个命令行工具,用于快速导入矢栅数据。使用这些工具时,需自行指定导入数据的SRID、文件编码、表名及导入方式等相关信息,对于栅格数据还需额外指定栅格波段、切片大小等参数。这样的方式对于云原生数据库PolarDB并不适用,主要原因为:

  • PostGIS提供的导入工具需要对数据库所部署的服务器有完全的掌控,数据需要尽可能上传至服务器的本地磁盘。而云原生数据库PolarDB为共享存储架构,无本地磁盘的概念,同时云原生会考虑底层服务器资源的弹性能力,并不清楚数据库所在宿主机的相关信息。

  • PostGIS Raster与Ganos Raster在存储结构上存在显著的不同,相关对比信息请参考开展区域面雨量分析

因此,从云原生架构的角度出发,Ganos依托云原生对象存储OSS,提供更为便捷的函数级入库工具,支持矢量、栅格数据的快速写入。

矢栅数据入库方法

矢量数据入库

GIS桌面工具导入

承载GanosPolarDB PostgreSQL版100%兼容PostgreSQL,支持使用类似QGIS这样的GIS桌面工具进行矢量数据入库。通过创建PostgreSQL数据连接源的方式,与PolarDB数据库建立连接,随后采用桌面工具自带的数据导入功能完成数据入库。

基于FDW导入

Ganos推荐使用FDW的方式导入矢量数据。外部数据包装器FDW(FOREIGN DATA WRAPPER)是PostgreSQL提供用于访问外部数据的插件,外部数据源包括集群中其他数据库中的数据或其他实例中的数据。Ganos FDW提供对多种空间数据类型的统一访问,可以自动将几何空间数据类型映射为Geometry类型,从而实现与数据库内部表的统一访问与查询。

例如,可以将存储在OSS上的poly.shp文件导入到Ganos作为一张带有Geometry类型的数据表使用,具体操作步骤如下:

  1. 安装需要的插件。

    CREATE EXTENSION ganos_spatialref;
    CREATE EXTENSION ganos_geometry;
    CREATE EXTENSION ganos_fdw;
  2. 通过ST_ForeignTables函数将空间数据文件poly.shp注册为外表。

    SELECT ST_RegForeignTables('oss://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/poly.shp');
  3. 注册外表成功后,可通过information_schema.foreign_tables视图查询刚注册的FDW表(poly)。

    SELECT foreign_table_name FROM information_schema.foreign_tables 
      ORDER BY foreign_table_name ASC;
    说明

    将外部数据注册为外表,只是通过映射的方式查看数据,具体数据并未真正写入数据库,所以还需要使用如下命令创建数据库表并插入数据。

    CREATE TABLE poly_db AS SELECT * FROM poly;

栅格数据入库

Ganos提供ST_ImportFromST_CreateRast函数,用于将外部栅格数据源导入数据库。这两个函数均可从外部栅格数据创建Raster类型对象,以保存影像数据的元信息。二者的主要区别在于,ST_ImportFrom函数会将数据划分为若干规则大小(默认为256x256像素)的数据块(Chunk),并将其存储在数据库中(具体位置通过chunkTableName参数指定)。而ST_CreateRast函数则仅创建一个Raster对象,并不将具体的像素信息导入数据库。

Tiff数据入库

Tiff数据为目前最常见的栅格数据格式之一,以下通过具体示例展示如何从OSS导入Tiff数据到Ganos中。

  1. 在OSS准备Tiff数据(您可自行准备此类数据)。

  2. 创建包含有Raster字段类型的表用于保存Tiff数据。

    CREATE TABLE raster_table
    (
      id integer,
      format text,
      rast raster
    );
  3. Tiff数据入库。

    • 使用ST_ImportFrom函数导入。

      INSERT INTO raster_table
      SELECT 1, 'TIFF', ST_ImportFrom('chunk_table','oss://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/file');
    • 使用ST_CreateRast函数导入。

      INSERT INTO raster_table
      SELECT 2, 'TIFF', ST_CreateRast('oss://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/file');
  4. Tiff数据导入成功后,可以看到两种方式都会创建一个Raster对象,且生成的Raster对象都可以使用Ganos提供的栅格相关UDF进行操作。

    SELECT id,ST_Georeference(rast),st_extent(rast),ST_NumBands(rast),ST_SRID(rast),st_value(rast,0,100,100),st_value(rast,1,200,200) FROM raster_table;

    返回结果示例如下:

    image

    但是,ST_ImportFrom函数会创建一个新的表(chunk_table)用于保存具体像素值信息,此时即使原始影像删除也不会影响使用。而ST_CreateRast函数则只创建一个Raster对象,其中包含对外部影像的逻辑映射信息,但具体数据仍然以文件方式存在OSS上,如果原始影像删除将无法查看。

HDF5/NetCDF数据入库

HDF5/NetCDF是另外两种常见的栅格数据格式,广泛适用于地球对地观测、科学计算等领域,尤其在气象、海洋、地球科学相关领域。ST_ImportFrom和ST_CreateRast函数同样支持HDF5/NetCDF类型的数据导入,SQL语法与Tiff数据导入类似:

  • 使用ST_ImportFrom函数导入。

    INSERT INTO raster_table
    Select 3, 'NC', ST_ImportFrom('chunk_table','oss://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/file');
  • 使用ST_CreateRast函数导入。

    INSERT INTO raster_table
    Select 4, 'NC', ST_CreateRast('oss://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/file');

注意,与Tiff数据导入不同的是,对于含有subdataset的HDF5/NetCDF文件,在导入时候还需在文件路径后面注明subdataset的名称,具体方式如下:

  • NetCDF文件。

    INSERT INTO raster_table
    Select 5, 'NC', ST_ImportFrom('chunk_table','oss://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/file.nc:sub_name');
  • HDF5文件。

    INSERT INTO raster_table
    Select 5, 'HDF5', ST_ImportFrom('chunk_table','oss://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/file.hdf5://path/sub_name');

对于多维度(大于3)的NC/HDF5文件,还需要指定chunkdim参数。这是因为Raster数据在数据库中的chunk是按照(w,h,b)尺寸进行加载和存储,其中,w为chunk宽,h为chunk高,b为波段数,当NC文件波段数大于3个时候,需要将除去xy坐标后其他所有波段的乘积作为chunk的波段数进行存储。例如,示例中的这个NC文件:

image

这里的dimension除了lonlat外,还有两个维度timeisobaric。所以在使用ST_ImportFrom入库时,必须指定chunkdim的中的波段数为除去lat和lon之外的其他所有波段的乘积(此处示例为120),才可以查询到正确结果。查询SQL语句如下:

INSERT INTO nc_table VALUES(1, ST_ImportFrom('nc_rbt', '/Users/xiaofei/Data/raster/nc_demo.nc','{"chunkdim":"(256,256,120)"}'));

数据批量入库

对于OSS上的批量数据,需结合程序脚本以实现入库。以下以Python为例,展示如何将OSS上的栅格数据批量导入数据库中:

说明

以下示例仅为将OSS上的栅格数据批量导入数据库。在使用之前,请进行Python初始化,详情请参考Python初始化

##导入所需的库
import oss2
import psycopg2
import logging

##连接oss
auth = oss2.Auth('AK_ID', 'AK_Secret')
bucket = oss2.Bucket(auth, '***endpoint***', '***bucket_name***')

##连接数据库,获取游标
con = psycopg2.connect(database="***dataq***", user="***dde***",
                       options="-c search_path=aster,public",  # 模式
                       password="******", host="*******", port="******")

cur = con.cursor()

##入库的SQL语句
insert_sql = "INSERT into {raster_table_name}(filename, band_index, raster_obj) VALUES ..."

## 从oss中获取所有hdf5文件名,并保存
for obj in oss2.ObjectIterator(bucket, prefix="..."):
    ff = open("upload.txt", mode='a', encoding='utf-8')
    filename = obj.key.split('/')[-1]
    if filename.find(".hdf5") == -1:
        continue
    else:
        ff.write(filename+'\n')

## 获取文件列表
fileList = {}
with open('upload.txt', 'r') as f:
    fileList = f.read().split('\n')

## 遍历文件列表,生成sql并入库
for file in fileList:
    # 编辑insert_sql,添加参数...

    #入库:
    try:
        cur.execute(insert_sql)
        con.commit()
        logging.info(filename+" finished")
    except (Exception, psycopg2.Error) as e:
        logging.info(filename+" error!")
        print(filename +" 上传失败\n")

总结

云原生数据库PolarDB具备独特的数据写入方式。Ganos的函数级导入工具能够帮助构建SQL级别的入库、查询、分析和服务一体化能力。使您无需在不同业务阶段使用不同手段操作数据,从而实现标准化的工作流程,提升易用性。