这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

地理空间分析

Vertica 提供的函数可用于操纵复杂的二维和三维空间对象。这些函数均遵循开放地理空间联盟 (OGC) 标准。Vertica 还提供了数据类型和 SQL 函数,用于根据 OGC 标准指定空间对象并存储在数据库中。

转换熟知文本 (WKT) 和熟知二进制 (WKB)

转换 WKT 和 WKB。

优化的空间联接

使用 ST_Intersects 和 STV_Intersects 执行快速空间联接。

通过 Shapefile 加载和导出空间数据

轻松地加载和导出 shapefile。

存储和检索对象

确定:

  • 对象是否包含自交点或自切点。

  • 一个对象是否完全在另一个对象范围内,例如多边形范围内的点。

测试对象之间的关系

例如,它们是否相交或接触:

  • 确定对象的边界。

  • 确定对象的顶点。

计算

  • 两个对象之间的最短距离。

  • 对象的大小(长度、面积)。

  • 一个或多个对象的质心。

  • 一个或多个对象周围的缓冲区。

1 - 地理空间分析的最佳实践

在 Vertica 中执行地理空间分析时,Vertica 建议采用以下最佳实践。

性能优化

使用点和多边形进行空间联接

Vertica 提供了两种方法来识别一组点是否与一组多边形相交。根据数据集的大小,选择可提供最佳性能的方法。

有关空间联接最佳实践的详细示例,请参阅空间联接的最佳实践

空间索引

STV_Create_Index 函数可能占用大量处理时间和内存。首次为新数据编制索引时,请监视内存使用情况,以确保其保持在安全限制以内。内存使用情况取决于:

  • 多边形数量

  • 顶点数量

  • 多边形之间的重叠量

检查多边形有效性

2 - 空间对象

Vertica 实施了多种数据类型,用于存储空间对象、熟知文本 (WKT) 字符串和熟知二进制 (WKB) 表示。这些数据类型包括:

2.1 - 支持的空间对象

Vertica 支持两种空间数据类型。这些数据类型在表列中存储二维和三维空间对象:

  • GEOMETRY:在笛卡尔平面中定义的且坐标表示为 (x,y) 对的空间对象。所有计算均使用笛卡尔坐标。
  • GEOGRAPHY:在理想球体的表面或在 WGS84 坐标系中定义的空间对象。坐标表示为经度/纬度角度值,单位为度。所有的计算都以米为单位。对于理想球体计算,球体半径为 6371 千米,与地球形状近似。

GEOMETRYGEOGRAPHY数据类型的最大大小为 10,000,000 字节 (10 MB)。不能将任何一种数据类型用作表的主键。

2.2 - 空间引用标识符 (SRID)

空间参照标识符 (SRID) 为整型值,表示在平面上投影坐标的方法。SRID 是指示在其中定义空间对象的坐标系的元数据。

使用几何实参的地理空间函数必须包含相同的 SRID。如果函数未包含相同的 SRID,查询将会返回错误。

例如,以下查询中的两个点具有不同的 SRID。因此,该查询返回错误:

=> SELECT ST_Distance(ST_GeomFromText('POINT(34 9)',2749), ST_GeomFromText('POINT(70 12)', 3359));
ERROR 5861:  Error calling processBlock() in User Function ST_Distance at [src/Distance.cpp:65],
error code: 0, message: Geometries with different SRIDs found: 2749, 3359

支持的 SRID

Vertica 支持派生自 EPSG 标准的 SRID。使用几何实参的地理空间函数在执行计算时必须使用支持的 SRID。SRID 值为 0 至 232-1 是有效的。使用超出此范围的 SRID 执行查询将会返回错误。

3.1 - 定义空间数据的表列

要定义包含 GEOMETRY 和 GEOGRAPHY 数据的列,请使用以下命令:

=> CREATE TABLE [[db-name.]schema.]table-name (
   column-name GEOMETRY[(length)],
   column-name GEOGRAPHY[(length)]);

如果忽略长度规范,则默认列大小为 1 MB。最大列大小为 10 MB。不强制执行上限,但地理空间函数只能接受或返回不超过 10 MB 的空间数据。

在创建后,无法修改 GEOMETRY 或 GEOGRAPHY 列的大小或数据类型。如果创建的列大小不足,请创建一个具有所需大小的新列。然后从旧列复制数据,并从表中删除旧列。

无法向包含另一个 Vertica 数据库的空间数据的表导入数据或从中导出数据。

3.2 - 从表中导出空间数据

可以将 Vertica 数据库表中的空间数据导出到 shapefile。

要将表中的空间数据导出到 shapefile:

  1. 超级用户的身份设置 shapefile 导出目录。

    => SELECT STV_SetExportShapefileDirectory(USING PARAMETERS path = '/home/geo/temp');
                   STV_SetExportShapefileDirectory
    ------------------------------------------------------------
     SUCCESS. Set shapefile export directory: [/home/geo/temp]
    (1 row)
    
  2. 将空间数据导出到 shapefile。

    => SELECT STV_Export2Shapefile(*
                  USING PARAMETERS shapefile = 'visualizations/city-data.shp',
                                shape = 'Polygon') OVER() FROM spatial_data;
     Rows Exported |                          File Path
    ---------------+----------------------------------------------------------------
           185873 | v_geo-db_node0001: /home/geo/temp/visualizations/city-data.shp
    (1 row)
    
    • 星号 (*) 值等同于列出 FROM 子句中的所有列。

    • 在导出 shapefile 时可以指定子目录。

    • Shapefile 必须以文件扩展名 .shp 结束。

  3. 确认现在有三个文件显示在 shapefile 导出目录中。

    $ ls
    city-data.dbf  city-data.shp   city-data.shx
    

3.3 - 识别 Null 空间对象

可使用 Vertica IS NULL 和 IS NOT NULL 结构来识别 null GEOMETRY 和 GEOGRAPHY 对象。

以下示例使用下面的表,其中 id=2 的行在 geog 字段中包含 null 值。


=> SELECT id, ST_AsText(geom), ST_AsText(geog) FROM locations
   ORDER BY 1 ASC;
 id |           ST_AsText              |                 ST_AsText
----+----------------------------------+--------------------------------------
  1 | POINT (2 3)                      | POINT (-85 15)
  2 | POINT (4 5)                      |
  3 | POLYGON ((-1 2, 0 3, 1 2, -1 2)) | POLYGON ((-24 12, -15 23, -20 27, -24 12))
  4 | LINESTRING (-1 2, 1 5)           | LINESTRING (-42.74 23.98, -62.19 23.78)
(4 rows)

识别具有 null geog 值的所有行:

=> SELECT id, ST_AsText(geom), (ST_AsText(geog) IS NULL) FROM locations
   ORDER BY 1 ASC;
 id |            ST_AsText             | ?column?
----+----------------------------------+----------
  1 | POINT (2 3)                      | f
  2 | POINT (4 5)                      | t
  3 | POLYGON ((-1 2, 0 3, 1 2, -1 2)) | f
  4 | LINESTRING (-1 2, 1 5)           | f
(4 rows)

识别 geog 值不为 null 的行:

=> SELECT id, ST_AsText(geom), (ST_AsText(geog) IS NOT NULL) FROM locations
   ORDER BY 1 ASC;
 id |            st_astext             | ?column?
----+----------------------------------+----------
  1 | POINT (2 3)                      | t
  2 | POINT (4 5)                      | f
  3 | LINESTRING (-1 2, 1 5)           | t
  4 | POLYGON ((-1 2, 0 3, 1 2, -1 2)) | t
(4 rows)

3.4 - 从 Shapefile 中加载空间数据

Vertica 提供了加载和解析 Shapefile 中存储的空间数据的功能。Shapefile 描述了点、线和多边形。Shapefile 由三个必要的文件构成:所有三个文件都必须存在,并且位于同一目录内以定义几何图形:

  • .shp—包含几何数据。

  • .shx—包含几何图形的位置索引。

  • .dbf—包含每个几何图形的属性。

要从 shapefile 加载空间数据:

  1. 使用 STV_ShpCreateTable 生成 CREATE TABLE 语句。

    => SELECT STV_ShpCreateTable ( USING PARAMETERS file = '/home/geo/temp/shp-files/spatial_data.shp')
                                   OVER() AS spatial_data;
               spatial_data
    ----------------------------------
     CREATE TABLE spatial_data(
       gid IDENTITY(64) PRIMARY KEY,
       uniq_id INT8,
       geom GEOMETRY(85)
    );
    (5 rows)
    
  2. 创建表。

    => CREATE TABLE spatial_data(
       gid IDENTITY(64) PRIMARY KEY,
       uniq_id INT8,
       geom GEOMETRY(85));
    
  3. 加载 Shapefile。

    => COPY spatial_data WITH SOURCE STV_ShpSource(file='/home/geo/temp/shp-files/spatial_data.shp')
        PARSER STV_ShpParser();
     Rows Loaded
    -------------
              10
    (1 row)
    

支持的 Shapefile 图形类型

下表列出了 Vertica 支持的 Shapefile 图形类型。

3.5 - 使用 COPY 将空间数据加载到表中

可以使用 COPY 语句将空间数据加载到 Vertica 中的表。

要使用 COPY 语句将数据加载到 Vertica:

  1. 创建表。

    => CREATE TABLE spatial_data (id INTEGER, geom GEOMETRY(200));
    CREATE TABLE
    
  2. 使用以下数据创建一个名为 spatial.dat 的文本文件。

    1|POINT(2 3)
    2|LINESTRING(-1 2, 1 5)
    3|POLYGON((-1 2, 0 3, 1 2, -1 2))
    
  3. 使用 COPY 将数据加载到表中。

    => COPY spatial_data (id, gx FILLER LONG VARCHAR(605), geom AS ST_GeomFromText(gx)) FROM LOCAL 'spatial.dat';
     Rows Loaded
    -------------
               3
    (1 row)
    

    该语句指定了一个 LONG VARCHAR(32000000) 填充符,它是 WKT 的最大大小。必须指定足够大的填充器值,以容纳想要插入表中的最大 WKT。

3.6 - 从表中检索作为熟知文本 (WKT) 的空间数据

GEOMETRY 和 GEOGRAPHY 数据在 Vertica 表中存储为用户不可读的 LONG VARBINARY。可使用 ST_AsText 返回熟知文本 (WKT) 格式的空间数据。

要返回 WKT 格式的空间数据:

=> SELECT id, ST_AsText(geom) AS WKT FROM spatial_data;
 id |               WKT
----+----------------------------------
  1 | POINT (2 3)
  2 | LINESTRING (-1 2, 1 5)
  3 | POLYGON ((-1 2, 0 3, 1 2, -1 2))
(3 rows)

3.7 - 使用 GeoHash 数据

Vertica 支持 GeoHash。GeoHash 是一种地理编码系统,用于对越来越细化的空间参照系进行分层编码。GeoHash 中的每个附加字符都会向下钻取到地图的较小部分。

您可以使用 Vertica 从 GeoHash 生成空间数据,以及从空间数据生成 GeoHash。Vertica 支持以下用于 GeoHash 的函数:

例如,从单点生成全精度和部分精度的 GeoHash。

=> SELECT ST_GeoHash(ST_GeographyFromText('POINT(3.14 -1.34)')), LENGTH(ST_GeoHash(ST_GeographyFromText('POINT(3.14 -1.34)'))),
                     ST_GeoHash(ST_GeographyFromText('POINT(3.14 -1.34)') USING PARAMETERS numchars=5) partial_hash;
      ST_GeoHash      | LENGTH | partial_hash
----------------------+--------+--------------
 kpf0rkn3zmcswks75010 |     20 | kpf0r
(1 row)

此示例说明如何从点集合点对象生成 GeoHash。返回的多边形是包含该 GeoHash 的最小图块的几何对象。

=> SELECT ST_AsText(ST_GeomFromGeoHash(ST_GeoHash(ST_GeomFromText('MULTIPOINT(0 0, 0.0002 0.0001)')))) AS region_1,
                    ST_AsText(ST_GeomFromGeoHash(ST_GeoHash(ST_GeomFromText('MULTIPOINT(0.0001 0.0001, 0.0003 0.0002)')))) AS region_2;
 -[ RECORD 1 ]---------------------------------------------------------------------------------------------
    region_1 | POLYGON ((0 0, 0.00137329101562 0, 0.00137329101562 0.00137329101562, 0 0.00137329101562, 0 0))
    region_2 | POLYGON ((0 0, 0.010986328125 0, 0.010986328125 0.0054931640625, 0 0.0054931640625, 0 0))

3.8 - 使用 ST_Intersects 和 STV_Intersect 执行空间联接

利用空间联接可以确定两组空间数据之间的空间关系。例如,可使用空间联接:

  • 计算不同区域内的移动呼叫密度,以确定新蜂窝站的位置。

  • 确定在飓风影响区域内的家庭。

  • 计算在某个邮政编码区域内生活的用户数量。

  • 计算零售店在任意指定时间的顾客数量。

3.8.1 - 空间联接的最佳实践

使用以下最佳实践来提高总体性能并优化空间查询。

在 Vertica 中使用空间联接的最佳实践包括:

  • 通过表分段提高创建索引的速度

  • 适当调整 Geometry 列的大小以存储点数据

  • 在 COPY 语句中使用 STV_GeometryPoint,将熟知文本 (WKT) 直接加载到 Geometry 列中。

  • 将 OVER (PARTITION BEST) 与 STV_Intersect 变换查询配合使用

最佳实践示例

在执行以下示例中的步骤之前,请从 Vertica Place GitHub 存储库 (https://github.com/vertica/Vertica-Geospatial) 下载 place_output.csv.zip。需要使用此存储库中的数据集。

  1. 创建用于多边形的表。使用适合数据但不会过大的 GEOMETRY 列宽。合适的列宽有助于提高性能。此外,通过哈希处理对表进行分段有利于并行计算。

    => CREATE TABLE artworks (gid int, g GEOMETRY(700)) SEGMENTED BY HASH(gid) ALL NODES;
    
  2. 将 Copy 语句与 ST_Buffer 配合使用,创建并加载对其运行相交函数的多边形。通过在 Copy 语句中使用 ST_Buffer,可使用该函数创建多边形。

    => COPY artworks(gid, gx FILLER LONG VARCHAR, g AS ST_Buffer(ST_GeomFromText(gx),8)) FROM STDIN DELIMITER ',';
    >> 1, POINT(10 45)
    >> 2, POINT(25 45)
    >> 3, POINT(35 45)
    >> 4, POINT(35 15)
    >> 5, POINT(30 5)
    >> 6, POINT(15 5)
    >> \.
    
  3. 创建用于位置数据(表示为点)的表。可以将点数据存储在大小为 100 字节的 GEOMETRY 列中。避免设置过大的 GEOMETRY 列。否则可能严重影响空间相交的性能。此外,通过哈希处理对表进行分段有利于并行计算。

    => CREATE TABLE usr_data (gid identity, usr_id int, date_time timestamp, g GEOMETRY(100))
         SEGMENTED BY HASH(gid) ALL NODES;
    
  4. 在执行 Copy 语句的过程中,将原始位置数据转换为 GEOMETRY 数据。由于位置数据需要使用 GEOMETRY 数据类型,因此必须执行此变换。使用函数 STV_GeometryPoint 变换源表的 xy 列。

    => COPY usr_data (usr_id, date_time, x FILLER LONG VARCHAR,
                      y FILLER LONG VARCHAR, g AS STV_GeometryPoint(x, y))
       FROM LOCAL 'place_output.csv' DELIMITER ',' ENCLOSED BY '';
    
  5. 创建用于多边形的空间索引。此索引有助于加快相交计算的速度。

    => SELECT STV_Create_Index(gid, g USING PARAMETERS index='art_index', overwrite=true) OVER() FROM artworks;
    
  6. 编写用于返回每个多边形的相交数量的分析查询。指定 Vertica 忽略与给定多边形相交次数少于 20 次的任何 usr_id

    => SELECT pol_gid,
           COUNT(DISTINCT(usr_id)) AS count_user_visit
       FROM
         (SELECT pol_gid,
           usr_id,
           COUNT(usr_id) AS user_points_in
        FROM
           (SELECT STV_Intersect(usr_id, g USING PARAMETERS INDEX='art_index') OVER(PARTITION BEST) AS (usr_id,
                                                            pol_gid)
        FROM usr_data
          WHERE date_time BETWEEN '2014-07-02 09:30:20' AND '2014-07-02 17:05:00') AS c
        GROUP BY pol_gid,
        usr_id HAVING COUNT(usr_id) > 20) AS real_visits
        GROUP BY pol_gid
        ORDER BY count_user_visit DESC;
    

示例查询中的优化

此查询采用了以下优化:

  • 断言的时间出现在子查询中。

  • 使用位置数据表避免需要巨大开销的联接操作。

  • 查询使用 OVER (PARTITION BEST),通过对数据进行分区提高了性能。

  • user_points_in 提供了所有游客与艺术品相交所花的综合时间估算值。

3.8.2 - 创建或刷新索引之前确保多边形的有效性

当 Vertica 创建或更新空间索引时,它不会检查多边形的有效性。为防止查询空间索引时获得无效结果,您应在创建或更新空间索引之前检查多边形的有效性。

以下示例向您展示如何检查多边形的有效性。

  1. 创建表并加载空间数据。

    => CREATE TABLE polygon_validity_test (gid INT, geom GEOMETRY);
    CREATE TABLE
    => COPY polygon_validity_test (gid, gx FILLER LONG VARCHAR, geom AS St_GeomFromText(gx)) FROM STDIN;
    Enter data to be copied followed by a newline.
    End with a backslash and a period on a line by itself.
    >> 2|POLYGON((-31 74,8 70,8 50,-36 53,-31 74))
    >> 3|POLYGON((-38 50,4 13,11 45,0 65,-38 50))
    >> 4|POLYGON((-12 42,-12 42,27 48,14 26,-12 42))
    >> 5|POLYGON((0 0,1 1,0 0,2 1,1 1,0 0))
    >> 6|POLYGON((3 3,2 2,2 1,2 3,3 3))
    >> \.
    
  2. 使用 ST_IsValid 和 STV_IsValidReason 查找任何无效的多边形。

    => SELECT gid, ST_IsValid(geom), STV_IsValidReason(geom) FROM polygon_validity_test;
     gid | ST_IsValid |            STV_IsValidReason
    -----+------------+------------------------------------------
       4 | t          |
       6 | f          | Self-intersection at or near POINT (2 1)
       2 | t          |
       3 | t          |
       5 | f          | Self-intersection at or near POINT (0 0)
    (5 rows)
    

既然我们已经在表中识别出无效的多边形,因此在创建或刷新空间索引时,我们可以通过几种不同的方式来处理无效的多边形。

使用 WHERE 子句筛选无效多边形

此方法比创建索引之前进行筛选要慢,因为它在执行时将检查每个多边形的有效性。

以下示例向您展示如何使用 WHERE 子句排除无效多边形。

```
=> SELECT STV_Create_Index(gid, geom USING PARAMETERS index = 'valid_polygons') OVER()
   FROM polygon_validity_test
   WHERE ST_IsValid(geom) = 't';
```

创建或刷新索引之前筛选无效多边形

此方法比使用 WHERE 子句进行筛选更快,因为在构建索引之前会降低性能。

以下示例向您展示如何创建一个排除无效多边形的新表,以此来排除无效多边形。

```
=> CREATE TABLE polygon_validity_clean AS
   SELECT *
   FROM polygon_validity_test
   WHERE ST_IsValid(geom) = 't';
CREATE TABLE
=> SELECT STV_Create_Index(gid, geom USING PARAMETERS index = 'valid_polygons') OVER()
   FROM polygon_validity_clean;
```

3.8.3 - STV_Intersect:标量函数与转换函数

这些 STV_Intersect 函数的用途相似,但用法不同。

在下面的示例中,STV_Intersect 标量函数会将 points 表中的点与名为 my_polygons 的空间索引中的多边形进行比较。 STV_Intersect 将会返回所有完全匹配的点和多边形:


=> SELECT gid AS pt_gid
   STV_Intersect(geom USING PARAMETERS index='my_polygons') AS pol_gid
   FROM points ORDER BY pt_gid;
 pt_gid | pol_gid
--------+---------
    100 |       2
    101 |
    102 |       2
    103 |
    104 |
    105 |       3
    106 |
    107 |
 (8 rows)

以下示例显示了如何使用 STV_Intersect 变换函数来返回关于匹配的三个点-多边形对以及它们所匹配的每个多边形的信息:


=> SELECT STV_Intersect(gid, geom
   USING PARAMETERS index='my_polygons')
   OVER (PARTITION BEST) AS (pt_gid, pol_id)
   FROM points;
 pt_gid | pol_id
--------+--------
    100 |      1
    100 |      2
    100 |      3
    102 |      2
    105 |      3
(3 rows)

另请参阅

3.8.4 - 使用 STV_Intersect 函数执行空间联接

假定您想要处理一个大中型空间数据集,并确定哪些点与哪些多边形相交。这种情况下,请首先使用 STV_Create_Index 创建一个空间索引。空间索引提高了访问多边形集的效率。

然后,使用 STV_Intersect 标量或变换函数确定匹配的点-多边形对。

3.8.4.1 - 空间索引和 STV_Intersect

在使用 STV_Intersect 函数之一执行空间联接之前,必须首先运行 STV_Create_Index,创建一个包含多边形相关信息的数据库对象。此对象被称为多边形集的空间索引。空间索引缩短了 STV_Intersect 函数访问多边形数据所用的时间。

Vertica 在全局空间中创建空间索引。因此,任何有权访问 STV_*_Index 函数的用户都可以描述、重命名或删除其他任何用户创建的索引。

Vertica 提供了多个与空间索引配合使用的函数:

3.8.5 - 何时使用 ST_Intersects 与 STV_Intersect

Vertica 提供了两种功能来识别一组点是否与一组多边形相交。根据数据集的大小,选择可提供最佳性能的方法:

  • 在将一组几何图形与单个几何图形进行比较以查看其是否相交时,请使用 ST_Intersects 函数。

  • 要确定一组点是否与一个大中型数据集中的一组多边形相交,请首先使用 STV_Create_Index 创建空间索引。然后,使用 STV_Intersect 函数之一返回相交的点-多边形对集合。

3.8.5.1 - 使用 ST_Intersects 执行空间联接

ST_Intersects 确定两个 GEOMETRY 对象是否在一个点相交或接触。

当需要确定一个列中的小型几何图形集与给定的几何图形是否相交时,可使用 ST_Intersects。

示例

以下示例使用 ST_Intersects 将一列点几何图形与单个多边形进行比较。包含点的表有 1 百万行。

ST_Intersects 仅返回与多边形相交的点。这些点约占表中点数的 0.01%:

=> CREATE TABLE points_1m(gid IDENTITY, g GEOMETRY(100)) ORDER BY g;
=> COPY points_1m(wkt FILLER LONG VARCHAR(100), g AS ST_GeomFromText(wkt))
   FROM LOCAL '/data/points.dat';
 Rows Loaded
-------------
     1000000
(1 row)
=> SELECT ST_AsText(g) FROM points_1m WHERE
   ST_Intersects
     (
      g,
      ST_GeomFromText('POLYGON((-71 42, -70.9 42, -70.9 42.1, -71 42.1, -71 42))')
      );
         st_astext
----------------------------
 POINT (-70.97532 42.03538)
 POINT (-70.97421 42.0376)
 POINT (-70.99004 42.07538)
 POINT (-70.99477 42.08454)
 POINT (-70.99088 42.08177)
 POINT (-70.98643 42.07593)
 POINT (-70.98032 42.07982)
 POINT (-70.95921 42.00982)
 POINT (-70.95115 42.02177)
...
(116 rows)

Vertica 建议通过创建空间索引来测试两列几何图形的相交情况。使用STV_Intersect:标量函数与转换函数中所述的 STV_Intersect 函数之一。

4 - 使用客户端应用程序中的空间对象

Vertica 客户端驱动程序库提供用于将客户端应用程序连接到 Vertica 数据库的接口。这些驱动程序简化了执行加载、报告生成及其他常见数据库任务时的数据交换操作。

以下是三种不同的客户端驱动程序:

  • 开放数据库连接 (ODBC)—对于以 C、Python、PHP、Perl 和其他大多数语言编写的第三方应用程序和客户端而言最常用的接口。

  • Java 数据库连接 (JDBC)—供采用 Java 编程语言编写的客户端使用。

  • 面向 .NET 的 ActiveX 数据对象 (ADO.NET)—供使用 Microsoft .NET Framework 开发的并且以 C#、Visual Basic .NET 和其他 .NET 语言编写的客户端使用。

Vertica Place 支持以下新数据类型:

  • LONG VARCHAR

  • LONG VARBINARY

  • GEOMETRY

  • GEOGRAPHY

客户端驱动程序库支持上述数据类型;以下几节将介绍该项支持并提供示例。

4.1 - 将 LONG VARCHAR 和 LONG VARBINARY 数据类型与 ODBC 配合使用

ODBC 驱动程序支持 LONG VARCHAR 和 LONG VARBINARY 数据类型(这两种数据类型分别类似于 VARCHAR 和 VARBINARY 数据类型)。将输入参数或输出参数绑定到查询中的 LONG VARCHAR 或 LONG VARBINARY 列时,请使用 SQL_LONGVARCHAR 和 SQL_LONGVARBINARY 常数设置列的数据类型。例如,若要将输入参数绑定到 LONG VARCHAR 列,应使用如下所示的语句:

rc = SQLBindParameter(hdlStmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR,
             80000, 0, (SQLPOINTER)myLongString, sizeof(myLongString), NULL);

4.2 - 将 LONG VARCHAR 和 LONG VARBINARY 数据类型与 JDBC 配合使用

在 JDBC 客户端应用程序中使用 LONG VARCHAR 和 LONG VARBINARY 数据类型与使用 VARCHAR 和 VARBINARY 数据类型相似。JDBC 驱动程序以透明方式处理转换(例如,Java String 对象和 LONG VARCHAR 之间的转换)。

以下示例代码演示了插入和检索 LONG VARCHAR 字符串。此示例使用 JDBC Types 类确定由 Vertica 返回的字符串的数据类型,但它实际上不需要知道数据库列是 LONG VARCHAR 还是 VARCHAR 即可检索值。

import java.sql.*;
import java.util.Properties;

public class LongVarcharExample {
    public static void main(String[] args) {
        try {
            Class.forName("com.vertica.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.err.println("Could not find the JDBC driver class.");
            e.printStackTrace();
            return;
        }
        Properties myProp = new Properties();
        myProp.put("user", "ExampleUser");
        myProp.put("password", "password123");
        Connection conn;
        try {
            conn = DriverManager.getConnection(
                            "jdbc:vertica://VerticaHost:5433/ExampleDB",
                            myProp);
            // establish connection and make a table for the data.
            Statement stmt = conn.createStatement();

            // How long we want the example string to be. This is
            // larger than can fit into a traditional VARCHAR (which is limited
            // to 65000.
            int length = 100000;

            // Create a table with a LONG VARCHAR column that can store
            // the string we want to insert.
            stmt.execute("DROP TABLE IF EXISTS longtable CASCADE");
            stmt.execute("CREATE TABLE longtable (text LONG VARCHAR(" + length
                            + "))");
            // Build a long string by appending an integer to a string builder
            // until we hit the size limit. Will result in a string
            // containing 01234567890123....
            StringBuilder sb = new StringBuilder(length);
            for (int i = 0; i < length; i++)
            {
                sb.append(i % 10);
            }
            String value = sb.toString();

            System.out.println("String value is " + value.length() +
                            " characters long.");

            // Create the prepared statement
            PreparedStatement pstmt = conn.prepareStatement(
                            "INSERT INTO longtable (text)" +
                            " VALUES(?)");
            try {
                // Insert LONG VARCHAR value
                System.out.println("Inserting LONG VARCHAR value");
                pstmt.setString(1, value);
                pstmt.addBatch();
                pstmt.executeBatch();

                // Query the table we created to get the value back.
                ResultSet rs = null;
                rs = stmt.executeQuery("SELECT * FROM longtable");

                // Get metadata about the result set.
                ResultSetMetaData rsmd = rs.getMetaData();
                // Print the type of the first column. Should be
                // LONG VARCHAR. Also check it against the Types class, to
                // recognize it programmatically.
                System.out.println("Column #1 data type is: " +
                                rsmd.getColumnTypeName(1));
                if (rsmd.getColumnType(1) == Types.LONGVARCHAR) {
                    System.out.println("It is a LONG VARCHAR");
                } else {
                    System.out.println("It is NOT a LONG VARCHAR");
                }

                // Print out the string length of the returned value.
                while (rs.next()) {
                    // Use the same getString method to get the value that you
                    // use to get the value of a VARCHAR.
                    System.out.println("Returned string length: " +
                                    rs.getString(1).length());
                }
            } catch (SQLException e) {
                System.out.println("Error message: " + e.getMessage());
                return; // Exit if there was an error
            }
            // Cleanup
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

4.3 - 将 GEOMETRY 和 GEOGRAPHY 数据类型用于 ODBC

Vertica GEOMETRY 和 GEOGRAPHY 数据类型受 LONG VARBINARY 原生类型支持,ODBC 客户端应用程序将其视为二进制数据。但是,这些数据类型的格式是 Vertica 所特有的。要在 C++ 应用程序中操纵此数据,必须使用 Vertica 中可将其转换为识别的格式的函数。

要将 WKT 或 WKB 转换为 GEOMETRY 或 GEOGRAPHY 格式,请使用以下 SQL 函数之一:

要将 GEOMETRY 或 GEOGRAPHY 对象转换为其对应的 WKT 或 WKB,请使用以下 SQL 函数之一:

  • ST_AsText—将 GEOMETRY 或 GEOGRAPHY 对象转换为 WKT,返回 LONGVARCHAR。

  • ST_AsBinary—将 GEOMETRY 或 GEOGRAPHY 对象转换为 WKB,返回 LONG VARBINARY。

以下代码示例使用 ST_GeomFromText 将 WKT 数据转换为 GEOMETRY 数据,然后将其存储在表中。之后,此示例从该表中检索 GEOMETRY 数据,并使用 ST_AsTextST_AsBinary 将它转换为 WKT 和 WKB 格式。

// Compile on Linux using:
//  g++ -g -I/opt/vertica/include -L/opt/vertica/lib64 -lodbc -o SpatialData SpatialData.cpp
// Some standard headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sstream>
// Only needed for Windows clients
// #include <windows.h>
// Standard ODBC headers
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
// Helper function to print SQL error messages.
template <typename HandleT>
void reportError(int handleTypeEnum, HandleT hdl)
{
    // Get the status records.
    SQLSMALLINT   i, MsgLen;
    SQLRETURN     ret2;
    SQLCHAR       SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
    SQLINTEGER    NativeError;
    i = 1;
    printf("\n");
    while ((ret2 = SQLGetDiagRec(handleTypeEnum, hdl, i, SqlState, &NativeError,
                                Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
        printf("error record %d\n", i);
        printf("sqlstate: %s\n", SqlState);
        printf("detailed msg: %s\n", Msg);
        printf("native error code: %d\n\n", NativeError);
        i++;
    }
    exit(EXIT_FAILURE); // bad form... but Ok for this demo
}
int main()
{
    // Set up the ODBC environment
    SQLRETURN ret;
    SQLHENV hdlEnv;
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hdlEnv);
    assert(SQL_SUCCEEDED(ret));
    // Tell ODBC that the application uses ODBC 3.
    ret = SQLSetEnvAttr(hdlEnv, SQL_ATTR_ODBC_VERSION,
        (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_UINTEGER);
    assert(SQL_SUCCEEDED(ret));
    // Allocate a database handle.
    SQLHDBC hdlDbc;
    ret = SQLAllocHandle(SQL_HANDLE_DBC, hdlEnv, &hdlDbc);
    assert(SQL_SUCCEEDED(ret));
    // Connect to the database
    printf("Connecting to database.\n");
    const char *dsnName = "ExampleDB";
    const char* userID = "dbadmin";
    const char* passwd = "password123";
    ret = SQLConnect(hdlDbc, (SQLCHAR*)dsnName,
        SQL_NTS,(SQLCHAR*)userID,SQL_NTS,
        (SQLCHAR*)passwd, SQL_NTS);
    if(!SQL_SUCCEEDED(ret)) {
        printf("Could not connect to database.\n");
        reportError<SQLHDBC>(SQL_HANDLE_DBC, hdlDbc);

    } else {
        printf("Connected to database.\n");
    }

    // Disable AUTOCOMMIT
    ret = SQLSetConnectAttr(hdlDbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF,
        SQL_NTS);

    // Set up a statement handle
    SQLHSTMT hdlStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);

    // Drop any previously defined table.
    ret = SQLExecDirect(hdlStmt, (SQLCHAR*)"DROP TABLE IF EXISTS polygons",
        SQL_NTS);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}

    // Run query to create a table to hold a geometry.
    ret = SQLExecDirect(hdlStmt,
        (SQLCHAR*)"CREATE TABLE polygons(id INTEGER PRIMARY KEY, poly GEOMETRY);",
        SQL_NTS);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}

    // Create the prepared statement. This will insert data into the
    // table we created above. It uses the ST_GeomFromText function to convert the
    // string-formatted polygon definition to a GEOMETRY datat type.
    printf("Creating prepared statement\n");
    ret = SQLPrepare (hdlStmt,
        (SQLTCHAR*)"INSERT INTO polygons(id, poly) VALUES(?, ST_GeomFromText(?))",
        SQL_NTS) ;
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}

    SQLINTEGER id = 0;
    int numBatches = 5;
    int rowsPerBatch = 10;

    // Polygon definition as a string.
    char polygon[] = "polygon((1 1, 1 2, 2 2, 2 1, 1 1))";
    // Bind variables to the parameters in the prepared SQL statement
    ret = SQLBindParameter(hdlStmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
        0, 0, &id, 0 , NULL);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT,hdlStmt);}
    // Bind polygon string to the geometry column
    SQLBindParameter(hdlStmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR,
        strlen(polygon), 0, (SQLPOINTER)polygon, strlen(polygon), NULL);
     if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT,hdlStmt);}
    // Execute the insert
    ret = SQLExecute(hdlStmt);
    if(!SQL_SUCCEEDED(ret)) {
       reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);
    }  else {
        printf("Executed batch.\n");
    }

    // Commit the transaction
    printf("Committing transaction\n");
    ret = SQLEndTran(SQL_HANDLE_DBC, hdlDbc, SQL_COMMIT);
    if(!SQL_SUCCEEDED(ret)) {
        reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);
    }  else {
        printf("Committed transaction\n");
    }
    // Now, create a query to retrieve the geometry.
    ret = SQLAllocHandle(SQL_HANDLE_STMT, hdlDbc, &hdlStmt);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}
    printf("Getting data from table.\n");
    // Execute a query to get the id, raw geometry data, and
    // the geometry data as a string. Uses the ST_AsText SQL function to
    // format raw data back into a string polygon definition
    ret = SQLExecDirect(hdlStmt,
        (SQLCHAR*)"select id,ST_AsBinary(poly),ST_AsText(poly) from polygons ORDER BY id;",
        SQL_NTS);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT,hdlStmt);}

    SQLINTEGER idval;
    // 10MB buffer to hold the raw data from the geometry (10Mb is the maximum
    // length of a GEOMETRY)
    SQLCHAR* polygonval = (SQLCHAR*)malloc(10485760);
    SQLLEN polygonlen, polygonstrlen;
    // Buffer to hold a LONGVARCHAR that can result from converting the
    // geometry to a string.
    SQLTCHAR* polygonstr = (SQLTCHAR*)malloc(33554432);

    // Get the results of the query and print each row.
    do {
        ret = SQLFetch(hdlStmt);
        if (SQL_SUCCEEDED(ret)) {
            // ID column
            ret = SQLGetData(hdlStmt, 1, SQL_C_LONG, &idval, 0, NULL);
            if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}
            printf("id: %d\n",idval);
            // The WKB format geometry data
            ret = SQLGetData(hdlStmt, 2, SQL_C_BINARY, polygonval, 10485760,
                &polygonlen);
            if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}
            printf("Polygon in WKB format: ");
            // Print each byte of polygonval buffer in hex format.
            for (int z = 0; z < polygonlen; z++)
                printf("%02x ",polygonval[z]);
            printf("\n");
            // Geometry data formatted as a string.
            ret = SQLGetData(hdlStmt, 3, SQL_C_TCHAR, polygonstr, 33554432, &polygonstrlen);
            if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}
            printf("Polygon in WKT format: %s\n", polygonstr);
        }
    } while(SQL_SUCCEEDED(ret));


    free(polygonval);
    free(polygonstr);
    // Clean up
    printf("Free handles.\n");
    ret = SQLFreeHandle(SQL_HANDLE_STMT, hdlStmt);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}
    ret = SQLFreeHandle(SQL_HANDLE_DBC, hdlDbc);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}
    ret = SQLFreeHandle(SQL_HANDLE_ENV, hdlEnv);
    if (!SQL_SUCCEEDED(ret)) {reportError<SQLHDBC>(SQL_HANDLE_STMT, hdlStmt);}
    exit(EXIT_SUCCESS);
}

运行上述示例后的输出如下所示:

Connecting to database.
Connected to database.
Creating prepared statement
Executed batch.
Committing transaction
Committed transaction
Getting data from table.
id: 0
Polygon in WKB format: 01 03 00 00 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 f0 3f
Polygon in WKT format: POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))
Free handles.

4.4 - 将 GEOMETRY 和 GEOGRAPHY 数据类型用于 JDBC

Vertica GEOMETRY 和 GEOGRAPHY 数据类型受 LONG VARBINARY 原生类型支持,JDBC 客户端应用程序将其视为二进制数据。但是,这些数据类型的格式是 Vertica 所特有的。要在 Java 应用程序中操纵此数据,必须使用 Vertica 中可将其转换为识别的格式的函数。

要将 WKT 或 WKB 转换为 GEOMETRY 或 GEOGRAPHY 格式,请使用以下 SQL 函数之一:

要将 GEOMETRY 或 GEOGRAPHY 对象转换为其对应的 WKT 或 WKB,请使用以下 SQL 函数之一:

  • ST_AsText—将 GEOMETRY 或 GEOGRAPHY 对象转换为 WKT,返回 LONGVARCHAR。

  • ST_AsBinary—将 GEOMETRY 或 GEOGRAPHY 对象转换为 WKB,返回 LONG VARBINARY。

以下代码示例使用 ST_GeomFromTextST_GeomFromWKB 将 WKT 和 WKB 数据转换为 GEOMETRY 数据,然后将其存储在表中。之后,此示例从该表中检索 GEOMETRY 数据,并使用 ST_AsTextST_AsBinary 将它转换为 WKT 和 WKB 格式。

import java.io.InputStream;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
public class GeospatialDemo
{
    public static void main(String [] args) throws Exception
    {
        Class.forName("com.vertica.jdbc.Driver");
        Connection conn =
              DriverManager.getConnection("jdbc:vertica://localhost:5433/db",
                                          "user", "password");
        conn.setAutoCommit(false);

        Statement stmt = conn.createStatement();
        stmt.execute("CREATE TABLE polygons(id INTEGER PRIMARY KEY, poly GEOMETRY)");

        int id = 0;
        int numBatches = 5;
        int rowsPerBatch = 10;

        //batch inserting WKT data
        PreparedStatement pstmt = conn.prepareStatement("INSERT INTO polygons
                                  (id, poly) VALUES(?, ST_GeomFromText(?))");
        for(int i = 0; i < numBatches; i++)
        {

            for(int j = 0; j < rowsPerBatch; j++)
            {
                //Insert your own WKT data here
                pstmt.setInt(1, id++);
                pstmt.setString(2, "polygon((1 1, 1 2, 2 2, 2 1, 1 1))");
                pstmt.addBatch();
            }
            pstmt.executeBatch();
        }

        conn.commit();
        pstmt.close();
        //batch insert WKB data
        pstmt = conn.prepareStatement("INSERT INTO polygons(id, poly)
                                      VALUES(?, ST_GeomFromWKB(?))");
        for(int i = 0; i < numBatches; i++)
        {
            for(int j = 0; j < rowsPerBatch; j++)
            {
                //Insert your own WKB data here
                byte [] wkb = getWKB();
                pstmt.setInt(1, id++);
                pstmt.setBytes(2, wkb);
                pstmt.addBatch();
            }
            pstmt.executeBatch();
        }

        conn.commit();
        pstmt.close();
        //selecting data as WKT
        ResultSet rs = stmt.executeQuery("select ST_AsText(poly) from polygons");
        while(rs.next())
        {
            String wkt = rs.getString(1);
            Reader wktReader = rs.getCharacterStream(1);
            //process the wkt as necessary
        }
        rs.close();

        //selecting data as WKB
        rs = stmt.executeQuery("select ST_AsBinary(poly) from polygons");
        while(rs.next())
        {
            byte [] wkb = rs.getBytes(1);
            InputStream wkbStream = rs.getBinaryStream(1);
            //process the wkb as necessary
        }
        rs.close();

        //binding parameters in predicates
        pstmt = conn.prepareStatement("SELECT id FROM polygons WHERE
                                      ST_Contains(ST_GeomFromText(?), poly)");
        pstmt.setString(1, "polygon((1 1, 1 2, 2 2, 2 1, 1 1))");
        rs = pstmt.executeQuery();
        while(rs.next())
        {
            int pk = rs.getInt(1);
            //process the results as necessary
        }
        rs.close();

        conn.close();
    }
}

4.5 - 将 GEOMETRY 和 GEOGRAPHY 数据类型用于 ADO.NET

Vertica GEOMETRY 和 GEOGRAPHY 数据类型受 LONG VARBINARY 原生类型支持,ADO.NET 客户端应用程序将其视为二进制数据。但是,这些数据类型的格式是 Vertica 所特有的。要在 C# 应用程序中操纵此数据,必须使用 Vertica 中可将其转换为识别的格式的函数。

要将 WKT 或 WKB 转换为 GEOMETRY 或 GEOGRAPHY 格式,请使用以下 SQL 函数之一:

要将 GEOMETRY 或 GEOGRAPHY 对象转换为其对应的 WKT 或 WKB,请使用以下 SQL 函数之一:

  • ST_AsText—将 GEOMETRY 或 GEOGRAPHY 对象转换为 WKT,返回 LONGVARCHAR。

  • ST_AsBinary—将 GEOMETRY 或 GEOGRAPHY 对象转换为 WKB,返回 LONG VARBINARY。

以下 C# 代码示例使用 ST_GeomFromText 将 WKT 数据转换为 GEOMETRY 数据,然后将其存储在表中。之后,此示例从该表中检索 GEOMETRY 数据,并使用 ST_AsTextST_AsBinary 将它转换为 WKT 和 WKB 格式。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Vertica.Data.VerticaClient;
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            VerticaConnectionStringBuilder builder =
                             new VerticaConnectionStringBuilder();
            builder.Host = "VerticaHost";
            builder.Database = "VMart";
            builder.User = "ExampleUser";
            builder.Password = "password123";
            VerticaConnection _conn = new
                              VerticaConnection(builder.ToString());
            _conn.Open();

            VerticaCommand command = _conn.CreateCommand();
            command.CommandText = "DROP TABLE IF EXISTS polygons";
            command.ExecuteNonQuery();
            command.CommandText =
              "CREATE TABLE polygons (id INTEGER PRIMARY KEY, poly GEOMETRY)";
            command.ExecuteNonQuery();
            // Prepare to insert a polygon using a prepared statement. Use the
            // ST_GeomFromtText SQl function to convert from WKT to GEOMETRY.
            VerticaTransaction txn = _conn.BeginTransaction();
            command.CommandText =
             "INSERT into polygons VALUES(@id, ST_GeomFromText(@polygon))";
            command.Parameters.Add(new
                           VerticaParameter("id", VerticaType.BigInt));
            command.Parameters.Add(new
                           VerticaParameter("polygon", VerticaType.VarChar));
            command.Prepare();
            // Set the values for the parameters
            command.Parameters["id"].Value = 0;
            //
            command.Parameters["polygon"].Value =
                               "polygon((1 1, 1 2, 2 2, 2 1, 1 1))";
            // Execute the query to insert the value
            command.ExecuteNonQuery();

            // Now query the table
            VerticaCommand query = _conn.CreateCommand();
            query.CommandText =
               "SELECT id, ST_AsText(poly), ST_AsBinary(poly) FROM polygons;";
            VerticaDataReader dr = query.ExecuteReader();
            while (dr.Read())
            {
                Console.WriteLine("ID: " + dr[0]);
                Console.WriteLine("Polygon WKT format data type: "
                    + dr.GetDataTypeName(1) +
                    " Value: " + dr[1]);
                // Get the WKB format of the polygon and print it out as hex.
                Console.Write("Polygon WKB format data type: "
                               + dr.GetDataTypeName(2));
                Console.WriteLine(" Value: "
                               + BitConverter.ToString((byte[])dr[2]));
            }
            _conn.Close();
        }
    }
}

示例代码在系统控制台上输出以下内容:

ID: 0
Polygon WKT format data type: LONG VARCHAR Value: POLYGON ((1 1, 1 2,
2 2, 2 1,1 1))
Polygon WKB format data type: LONG VARBINARY Value: 01-03-00-00-00-01
-00-00-00-05-00-00-00-00-00-00-00-00-00-F0-3F-00-00-00-00-00-00-F0-3F
-00-00-00-00-00-00-F0-3F-00-00-00-00-00-00-00-40-00-00-00-00-00-00-00
-40-00-00-00-00-00-00-00-40-00-00-00-00-00-00-00-40-00-00-00-00-00-00
-F0-3F-00-00-00-00-00-00-F0-3F-00-00-00-00-00-00-F0-3F

5 - OGC 空间定义

使用 Vertica 需要了解开放地理空间联盟 (OGC) 的概念和功能。有关详细信息,请参见 OGC 简易功能访问第 1 部分 - 通用架构规范。

5.1 - 空间类

Vertica 支持 OGC 标准中定义的多个对象类。

5.1.1 - Point

使用以下方法之一标识的二维空间中的位置:

  • X 和 Y 坐标

  • 经度和纬度值

点的维度为 0,并且没有边界。

示例

下面的示例使用 GEOMETRY 点:

=> CREATE TABLE point_geo (gid int, geom GEOMETRY(100));
CREATE TABLE
=> COPY point_geo(gid, gx filler LONG VARCHAR, geom AS ST_GeomFromText(gx)) FROM stdin delimiter ',';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1, POINT(3 5)
>>\.
=> SELECT gid, ST_AsText(geom) FROM point_geo;
 gid |  ST_AsText
-----+-------------
   1 | POINT (3 5)
(1 row)

下面的示例使用 GEOGRAPHY 点:

=> CREATE TABLE point_geog (gid int, geog geography(100));
CREATE TABLE
=> COPY point_geog(gid, gx filler LONG VARCHAR, geog AS ST_GeographyFromText(gx)) FROM stdin delimiter ',';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1, POINT(42 71)
>>\.
=> SELECT gid, ST_AsText(geog) FROM point_geog;
 gid |   ST_AsText
-----+---------------
   1 | POINT (42 71)
(1 row)

5.1.2 - Multipoint

一个或多个点的集合。点集合对象的维度为 0,并且没有边界。

示例

下面的示例使用 GEOMETRY 点集合:

=> CREATE TABLE mpoint_geo (gid int, geom GEOMETRY(1000));
CREATE TABLE
=> COPY mpoint_geo(gid, gx filler LONG VARCHAR, geom AS ST_GeomFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|MULTIPOINT(4 7, 8 10)
>>\.
=> SELECT gid, ST_AsText(geom) FROM mpoint_geo;
 gid |       st_astext
-----+-----------------------
   1 | MULTIPOINT (7 8, 6 9)
(1 row)

下面的示例使用 GEOGRAPHY 点集合:

=> CREATE TABLE mpoint_geog (gid int, geog GEOGRAPHY(1000));
CREATE TABLE
=> COPY mpoint_geog(gid, gx filler LONG VARCHAR, geog AS ST_GeographyFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|MULTIPOINT(42 71, 41.4 70)
>>\.
=> SELECT gid, ST_AsText(geom) FROM mpoint_geo;
 gid |       st_astext
-----+-----------------------
   1 | MULTIPOINT (42 71, 41.4 70)
(1 row)

5.1.3 - 线串

由连续点对标识的一个或多个连接的线。线串的维度为 1。线串边界是包含其起点和终点的点集合对象。

以下是线串的示例:

示例

下面的示例使用 GEOMETRY 类型创建表,使用复制将线串加载到表,然后查询表来查看线串:

=> CREATE TABLE linestring_geom (gid int, geom GEOMETRY(1000));
CREATE TABLE
=> COPY linestring_geom(gid, gx filler LONG VARCHAR, geom AS ST_GeomFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|LINESTRING(0 0, 1 1, 2 2, 3 4, 2 4, 1 5)
>>\.
=> SELECT gid, ST_AsText(geom) FROM linestring_geom;
 gid |                 ST_AsText
-----+-------------------------------------------
   1 | LINESTRING (0 0, 1 1, 2 2, 3 4, 2 4, 1 5)
(1 row)

下面的示例使用 GEOGRAPHY 类型创建表,使用 COPY 将线串加载到表,然后查询表以查看线串:

=> CREATE TABLE linestring_geog (gid int, geog GEOGRAPHY(1000));
CREATE TABLE
=> COPY linestring_geog(gid, gx filler LONG VARCHAR, geog AS ST_GeographyFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|LINESTRING(42.1 71, 41.4 70, 41.3 72.9, 42.99 71.46, 44.47 73.21)
>>\.
=> SELECT gid, ST_AsText(geog) FROM linestring_geog;
 gid |                             ST_AsText
-----+--------------------------------------------------------------------
   1 | LINESTRING (42.1 71, 41.4 70, 41.3 72.9, 42.99 71.46, 44.47 73.21)
(1 row)

5.1.4 - 线串集合

零个或多个线串的集合。线串集合没有维度。线串集合的边界是包含所有线串起点和终点的点集合对象。

以下是线串集合的示例:

示例

下面的示例使用 GEOMETRY 类型创建表,使用复制将线串集合加载到表,然后查询表来查看线串集合:

=> CREATE TABLE multilinestring_geom (gid int, geom GEOMETRY(1000));
CREATE TABLE
=> COPY multilinestring_geom(gid, gx filler LONG VARCHAR, geom AS ST_GeomFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|MULTILINESTRING((1 5, 2 4, 5 3, 6 6),(3 5, 3 7))
>>\.
=> SELECT gid, ST_AsText(geom) FROM multilinestring_geom;
 gid |                     ST_AsText
-----+----------------------------------------------------
   1 | MULTILINESTRING ((1 5, 2 4, 5 3, 6 6), (3 5, 3 7))
(1 row)

下面的示例使用 GEOGRAPHY 类型创建表,使用 COPY 将线串集合加载到表,然后查询表以查看线串集合:

=> CREATE TABLE multilinestring_geog (gid int, geog GEOGRAPHY(1000));
CREATE TABLE
=> COPY multilinestring_geog(gid, gx filler LONG VARCHAR, geog AS ST_GeographyFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|MULTILINESTRING((42.1 71, 41.4 70, 41.3 72.9), (42.99 71.46, 44.47 73.21))
>>\.
=> SELECT gid, ST_AsText(geog) FROM multilinestring_geog;
 gid |                                  ST_AsText
-----+----------------------------------------------------------------------------
   1 | MULTILINESTRING((42.1 71, 41.4 70, 41.3 72.9), (42.99 71.46, 44.47 73.21))
(1 row)

5.1.5 - 多边形

由一组封闭的线串标识的对象。多边形可能包含一个或多个由内部边界定义的孔,但所有点都必须保持连接状态。以下是两个多边形的示例:

包含性和独占性多边形

如果多边形以顺时针顺序将其点包括在内,则包含多边形边内的所有空间,排除边外的所有空间。如果多边形以逆时针顺序将其点包括在内,则排除多边形边内的所有空间,包含边外的所有空间。

示例

下面的示例使用 GEOMETRY 类型创建表,使用复制将多边形加载到表,然后查询表来查看多边形:

=> CREATE TABLE polygon_geom (gid int, geom GEOMETRY(1000));
CREATE TABLE
=> COPY polygon_geom(gid, gx filler LONG VARCHAR, geom AS ST_GeomFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|POLYGON(( 2 6, 2 9, 6 9, 7 7, 4 6, 2 6))
>>\.
=> SELECT gid, ST_AsText(geom) FROM polygon_geom;
 gid |                 ST_AsText
-----+------------------------------------------
   1 | POLYGON((2 6, 2 9, 6 9, 7 7, 4 6, 2 6))
(1 row)

下面的示例使用 GEOGRAPHY 类型创建表,使用 COPY 将多边形加载到表,然后查询表以查看多边形:

=> CREATE TABLE polygon_geog (gid int, geog GEOGRAPHY(1000));
CREATE TABLE
=> COPY polygon_geog(gid, gx filler LONG VARCHAR, geog AS ST_GeographyFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|POLYGON((42.1 71, 41.4 70, 41.3 72.9, 44.47 73.21, 42.99 71.46, 42.1 71))
>>\.
=> SELECT gid, ST_AsText(geog) FROM polygon_geog;
 gid |                                ST_AsText
-----+---------------------------------------------------------------------------
   1 | POLYGON((42.1 71, 41.4 70, 41.3 72.9, 44.47 73.21, 42.99 71.46, 42.1 71))
(1 row)

5.1.6 - 多边形集合

零个或多个未重叠的多边形的集合。

示例

下面的示例使用 GEOMETRY 类型创建表,使用 COPY 将多边形集合加载到表,然后查询表以查看多边形:

=> CREATE TABLE multipolygon_geom (gid int, geom GEOMETRY(1000));
CREATE TABLE
=> COPY multipolygon_geom(gid, gx filler LONG VARCHAR, geom AS ST_GeomFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>9|MULTIPOLYGON(((2 6, 2 9, 6 9, 7 7, 4 6, 2 6)),((0 0, 0 5, 1 0, 0 0)),((0 2, 2 5, 4 5, 0 2)))
>>\.
=> SELECT gid, ST_AsText(geom) FROM polygon_geom;
 gid |                                           ST_AsText
-----+----------------------------------------------------------------------------------------------
   9 | MULTIPOLYGON(((2 6, 2 9, 6 9, 7 7, 4 6, 2 6)),((0 0, 0 5, 1 0, 0 0)),((0 2, 2 5, 4 5, 0 2)))
(1 row)

下面的示例使用 GEOGRAPHY 类型创建表,使用 COPY 将多边形集合加载到表,然后查询表以查看多边形:

=> CREATE TABLE multipolygon_geog (gid int, geog GEOGRAPHY(1000));
CREATE TABLE
=> COPY polygon_geog(gid, gx filler LONG VARCHAR, geog AS ST_GeographyFromText(gx)) FROM stdin delimiter '|';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>1|POLYGON((42.1 71, 41.4 70, 41.3 72.9, 44.47 73.21, 42.99 71.46, 42.1 71))
>>\.
=> SELECT gid, ST_AsText(geog) FROM polygon_geog;
 gid |                                ST_AsText
-----+---------------------------------------------------------------------------
   1 | POLYGON(((42.1 71, 41.4 70, 41.3 72.9, 42.1 71)),((44.47 73.21, 42.99 71.46, 42.1 71, 44.47 73.21)))
(1 row)

5.2 - 空间对象表示方法

OGC 定义了两种表示空间对象的方法:

5.2.1 - 熟知文本 (WKT)

熟知文本 (WKT) 是空间对象的 ASCII 表示。

WKT 不区分大小写;Vertica 可识别小写字母和大写字母的任意组合。

以下是一些有效的 WKT 示例:

无效的 WKT:

  • POINT(1 NAN), POINT(1 INF)—坐标必须为数字。

  • POLYGON((1 2, 1 4, 3 4, 3 2))—多边形必须是封闭的。

  • POLYGON((1 4, 2 4))—线串不是有效的多边形。

5.2.2 - 熟知二进制 (WKB)

熟知二进制 (WKB) 是空间对象的二进制表示。此格式主要用于在应用程序之间移植空间数据。

5.3 - 空间定义

OGC 定义了描述以下内容的属性

  • 空间对象的特征

  • 对象之间可能存在的空间关系

Vertica 提供了用于测试和分析以下属性和关系的函数。

5.3.1 - 边界

定义空间对象限制的点集:

  • 点、点集合和 GeometryCollection 没有边界。

  • 线串的边界是点集合对象。此对象包含其起点和终点。

  • 线串集合的边界是点集合对象。此对象包含构成线串集合的所有线串的起点和终点。

  • 多边形的边界是在同一点开始和结束的线串。如果多边形包含一个或多个孔,则边界是包含外部多边形边界和任何内部多边形边界的线串集合。

  • 多边形集合的边界是包含构成多边形集合的所有多边形的边界的线串集合。

5.3.2 - 缓冲区

与空间对象边界的距离小于或等于指定距离的所有点的集合。该距离可能为正值或负值。

正缓冲区:

负缓冲区:

5.3.3 - 包含

如果一个空间对象的内部包括了另一个空间对象的所有点,则表示前者包含后者。如果某个对象(例如点或线串)仅沿多边形的边界存在,则多边形未包含该对象。如果某个点在线串上,则线串包含该点;线串的内部是指线串上除起点和终点以外的所有点。

Contains(a, b) 在空间上等同于 within(b, a)。

5.3.4 - 凸包

包含一个或多个空间对象的最小凸多边形。

在下图中,虚线表示一个线串和一个三角形的凸包。

5.3.5 - 交叉

如果以下两项均成立,则表示两个空间对象交叉:

  • 两个对象具有部分但非全部的公共内点。

  • 它们的相交结果的尺寸小于两个对象的最大尺寸。

5.3.6 - 非联接

没有任何公共点的两个空间对象;它们既不相交也不接触。

5.3.7 - 包络

包含空间对象的最小边界矩形。

以下多边形的包络表示为下图中的虚线。

5.3.8 - 相等

如果两个空间对象的坐标完全匹配,则二者相等。与在空间上等同同义。

在确定空间等价关系时点的顺序不重要:

  • LINESTRING(1 2, 4 3) 等于 LINESTRING(4 3, 1 2)。

  • POLYGON ((0 0, 1 1, 1 2, 2 2, 2 1, 3 0, 1.5 -1.5, 0 0)) 等于 POLYGON((1 1 , 1 2, 2 2, 2 1, 3 0, 1.5 -1.5, 0 0, 1 1))。

  • MULTILINESTRING((1 2, 4 3),(0 0, -1 -4)) 等于 MULTILINESTRING((0 0, -1 -4),(1 2, 4 3))。

5.3.9 - 外部

空间对象或其边界均未包含的点集。

5.3.10 - GeometryCollection

任何支持的空间对象类的零个或更多个对象的集合。

5.3.11 - 内部

空间对象中包含的点集,不包括其边界。

5.3.12 - 交集

两个或更多个空间对象的公共点集。

5.3.13 - 重叠

如果某个空间对象与另一个对象共享空间,但没有包含在该对象内,则表示这两个对象重叠。对象必须在其内部重叠;如果两个对象在单个点接触,或仅仅沿边界相交,则它们没有重叠。

5.3.14 - 相关

按照 DE-9IM 模式矩阵字符串的定义,某个空间对象与另一个对象在空间上相关。

DE-9IM 模式矩阵字符标识了两个空间对象彼此在空间上的相关性。有关 DE-9IM 标准的详细信息,请参见了解空间关系

5.3.15 - 简单

对于点、点集合、线串或线串集合,如果未与自身相交也没有自切点,则为简单空间对象。

多边形、多边形集合和 GeometryCollection 始终为简单空间对象。

5.3.16 - 余集

一对空间对象中没有彼此相交的所有点的集合。此差集在空间上等同于两个对象的并集减去其交集。余集包含交集的边界。

在下图中,阴影区域表示这两个矩形的余集。

下图显示了两个重叠的线串的余集。

5.3.17 - 并集

所有对象中的所有点的集合(针对两个或更多个空间对象)。

5.3.18 - 有效性

对于多边形或多边形集合,如果以下所有项均成立:

  • 它是封闭的;其起点与终点相同。

  • 其边界是一组线串。

  • 边界中没有任何两个交叉的线串。边界中的线串可能在某个点接触,但它们不能交叉。

  • 内部的任何多边形都必须完全包含在其中;这些多边形不能接触外部多边形边界除顶点以外的任何位置。

有效的多边形:

无效的多边形:

5.3.19 - 范围内

当一个空间对象的所有点都在另一个对象的内部时,前者被视为在后者范围内。因此,如果某个点或线串仅沿多边形的边界存在,则不被视为在该多边形范围内。多边形边界不属于其内部。

如果某个点在线串上,则被视为在该线串范围内。线串的内部是线串上除起点和终点以外的所有点。

Within(a, b) 在空间上等同于 Contains(b, a)。

6 - 空间数据类型支持限制

Vertica 不支持所有类型的 GEOMETRY 和 GEOGRAPHY 对象。有关函数支持的对象列表,请参见相应的函数页。球面几何通常比欧式几何更复杂。因此,支持 GEOGRAPHY 数据类型的空间函数较少。

空间数据类型支持的局限性:

  • 非 WGS84 GEOGRAPHY 对象是在半径为 6371 千米的理想球体的表面定义的空间对象。这个球体接近地球的形状。其他空间程序可能使用椭圆对地球进行建模,从而产生略微不同的数据。

  • 在创建后,无法修改 GEOMETRY 或 GEOGRAPHY 列的大小或数据类型。

  • 无法向包含另一个 Vertica 数据库的空间数据的表导入数据或从中导出数据。

  • 只能将 STV_Intersect 函数与点和多边形配合使用。

  • 不支持类型为 GEOMETRYCOLLECTION 的 GEOGRAPHY 对象。

  • 经度值必须介于 -180 和 +180 度之间。纬度值必须介于 –90 和 +90 度之间。Vertica 地理空间函数不验证这些值。

  • GEOMETRYCOLLECTION 对象不能包含空对象。例如,您不能指定 GEOMETRYCOLLECTION (LINESTRING(1 2, 3 4), POINT(5 6), POINT EMPTY)

  • 如果将 NULL 几何传递给空间函数,该函数将返回 NULL,除非另有说明。NULL 结果没有值。

  • Polymorphic 函数(例如 NVLGREATEST)不接受 GEOMETRY 和 GEOGRAPHY 实参。