4. 数据管理和查询

4.1. GIS 对象

由 KGIS 支持的 GIS 对象是 OpenGIS Consortium (OGC) 定义的 GIS “简单特性” 的超集。 KGIS 插件支持 OGC 标准中 “SQL 简单特性” 所涉及的所有对象和特性,同时扩展了这个标准以便支持: 3DZ(即 x,y,z 三个维度),3DM(即 x,y,m 三个维度,其中 m 为 “测量值”,具体含义由应用决定)和 4D(即 x,y.z.m 四个维度)坐标。 此外,KGIS 还支持 SQL-MM 规范第三部分中定义的几何类型。

GIS 对象有多种不同的表示形式,例如:在 SQL 命令行中,GIS 对象多采用 WKT/EWKT 格式表示,以便人工识读和输入;在存储和传输 GIS 对象时,WKB/EWKB(及其变体)格式更加紧凑。

4.1.1. OpenGIS 的 WKB 和 WKT 格式

OpenGIS 规范定义了两种表达空间对象的标准方法:Well-Known Text (WKT) 格式和 Well-Known Binary (WKB) 格式。 这两种格式都包含了关于构成该对象所需要的对象的类型和坐标信息。

WKT 表示空间对象特征的样例如下:

  • POINT(0 0)

  • LINESTRING(0 0,1 1,1 2)

  • POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))

  • MULTIPOINT((0 0),(1 2))

  • MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))

  • MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))

  • GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))

Well-Known Binary (WKB) 提供了一种通用的、不损失精度的二进制空间数据(以字节数组的形式)表示形式。 空间对象 WKB 形式的示例如下:

  • WKT: POINT(1 1)

    WKB: 0101000000000000000000F03F000000000000F03

  • WKT: LINESTRING (2 2, 9 9)

    WKB: 0102000000020000000000000000000040000000000000004000000000000022400000000000002240

WKB 格式的输入和输出分别通过 ST_GeomFromWKB 和 ST_AsBinary 函数实现:

bytea WKB = ST_AsBinary(geometry);
geometry = ST_GeomFromWKB(bytea WKB, SRID);

例如,下列语句在数据库中插入一个以 WKB 形式输入的空间对象:

INSERT INTO geotable ( geom, name )VALUES ( ST_GeomFromWKB('\x0101000000000000000000f03f000000000000f03f', 312), 'A Place');

OpenGIS 规范也定义了空间对象的内部存储格式,包括空间参照系统即 SRID 等。对于需要写入数据库的空间对象,SRID 是必需的属性。

以下接口可以用于上述两种格式几何对象的输入输出:

bytea WKB = ST_AsBinary(geometry);
text WKT = ST_AsText(geometry);
geometry = ST_GeomFromWKB(bytea WKB, SRID);
geometry = ST_GeometryFromText(text WKT, SRID);

例如:下面是一个正确的插入语句,用了创建和插入 OGC 规范的空间对象

INSERT INTO geotable ( the_geom, the_name )
VALUES ( ST_GeomFromText('POINT(-126.4 45.32)', 4326), 'A Place');

4.1.2. KGIS 几何对象的 EWKB, EWKT 和 Canonical 形式

OGC 规范只支持 2D 几何对象,并且在几何对象的输入和输出格式中也不带有 SRID 信息。 KGIS 插件扩展了 OGC 规范(每一个有效的 WKB/WKT 对象都是一个有效的 EWKB/EWKT 对象),但未来可能会有变化,特别是 OGC 规范出台了与扩展格式相冲突的格式。 KGIS 插件 EWKB/EWKT 格式添加了对 3dm,3dz,4d 坐标的支持,并且以 EWKT 表示的几何对象可以带有 SRID 信息,样例如下:

  • POINT(0 0 0) -- XYZ

  • SRID=32632;POINT(0 0) -- XY with SRID

  • POINTM(0 0 0) -- XYM

  • POINT(0 0 0 0) -- XYZM

  • SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- XYM with SRID

  • MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1))

  • POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0))

  • MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)),((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0)))

  • GEOMETRYCOLLECTIONM( POINTM(2 3 9), LINESTRINGM(2 3 4, 3 4 5) )

  • MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4) )

  • POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)), ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)), ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)), ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)), ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )

  • TRIANGLE ((0 0, 0 9, 9 0, 0 0))

  • TIN( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )

如下接口使用时已经支持如下格式的输入与输出:

bytea EWKB = ST_AsEWKB(geometry);
text EWKT = ST_AsEWKT(geometry);
geometry = ST_GeomFromEWKB(bytea EWKB);
geometry = ST_GeomFromEWKT(text EWKT);

例如一个有效的创建和插入 KGIS 插件空间对象的 SQL 语句如下:

INSERT INTO geotable ( the_geom, the_name )
VALUES ( ST_GeomFromEWKT('SRID=312;POINTM(-126.4 45.32 15)'), 'A Place');

用 Canonical 形式表示几何类型场景是:做一个简单查询(没有函数调用),比如:插入、更新或拷贝,此时返回的几何类型的数据将以 Canonical 形式显示。 对于 KGIS 插件的 “geometry“类型,Canonical 表述方式是:

- Output
- binary: EWKB
ascii: HEXEWKB (EWKB in hex form)
- Input
- binary: EWKB
ascii: HEXEWKB|EWKT

下列语句读取 EWKT 格式数据,然后处理 canonical 格式的输入 / 输出,最后返回 HEXEWKB 格式(EWKB 数据的 16 进制数字表示形式)数据:

=# SELECT 'SRID=4;POINT(0 0)'::geometry;geometry
---------------------------------------------------
01010000200400000000000000000000000000000000000000
(1 row)

4.1.3. SQL-MM 规范第三部分

SQL Multimedia Applications Spatial (SQL-MM)空间应用规范扩展了 “Simple features for SQL”(SQL 简单特性)规范, 包括定义了一些圆弧插值曲线(circularly interpolated curves)。 SQL-MM 定义了包括 3dm, 3dz and 4d 坐标,但不允许这些格式的坐标携带 SRID 信息。

目前该规范还不完全支持 WKT 的扩展格式。一些简单的曲线几何对象样例如下:

  • CIRCULARSTRING(0 0, 1 1, 1 0)

    CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0)

    CIRCULARSTRING 是基本的曲线类型,在线性范畴内,和 LINESTRING 类似。 一个简单段(可能是线段也可能是弧线段)包括 3 个点:起点、终点和这两点之间的其他点。例外是封闭的圆,起点和终点是相同的。 在这种情况下第二个点必须是弧线的中心。为了把弧线连接起来,前一个弧线的终点必须是下一个弧线的起点,就像在 LINESTRING 中一样。 这意味着一个有效的 CIRCULARSTIRNG 对象的点个数必须大于 1,且为奇数个。

  • COMPOUNDCURVE(CIRCULARSTRING(0 0, 1 1, 1 0),(1 0, 0 1))

    一个复合曲线类型(COMPOUNDCURVE)对象是一个简单的、连贯的曲线,它包含曲线段和直线段。 这意味着该对象除了要有良好格式(比如 WKT 格式)的对象组成外,每个对象的终点(除了最后一个点)必须与下一个几何对象的起点重合。

  • CURVEPOLYGON(CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0),(1 1, 3 3, 3 1, 1 1))

    在一个曲线多边形(CURVEPOLYGON)对象中包含 CURVE 对象的例子: CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (0 0,2 0, 2 1,2 3, 4 3),(4 3, 4 5, 1 4, 0 0)), CIRCULARSTRING (1.7 1, 1.4 0.4, 1.6 0.4, 1.6 0.5, 1.7 1) )

    CURVEPOLYGON 与 POLYGON 对象很类似,有一个外环和 0 个或更多内环。所不同的是一个圆环的形式可以为 CIRCULARSTRING, LINESTRING 或复合的线(COMPOUNDCURVE)。

    KGIS 插件支持 CURVEPOLYGON 对象中包含一个复合的曲线(COMPOUNDCURVE)。

  • MULTICURVE((0 0, 5 5),CIRCULARSTRING(4 0, 4 4, 8 4))

    MULTICURVE 是一个 CURVE GEOMETRYCOLLECTION 对象(集合对象),该对象可以包含:线性对象、圆形对象和复合线性对象。

  • MULTISURFACE(CURVEPOLYGON(CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0),(1 1, 3 3, 3 1, 1 1)),((10 10, 14 12, 11 10, 10 10),(11 11, 11.5 11, 11 11.5, 11 11)))

    这是一个 SURFACE GEOMETRYCOLLECTION 对象例子,可以是(线性的)POLYGON 或曲线的 CURVEPOLYGON 对象。

注意

当前 SQL-MM 规范里面的浮点型精度的数值之间比较的误差是 1E-8,即:当两个浮点数之间的差值小于 1E-8 (一亿分之一)时,这两个数值将被判定为相等。

4.1.4. KGIS 中各种几何对象类型之间的关系

图 4.1.31 KGIS 中各种几何对象类型之间的关系。白色为 OGC SFSQL 标准中规定的类型,黄色为 SQL-MM 标准中规定的类型。

4.1.5. KGIS 中几何对象类型与几何计算函数之间的关系

在 KGIS 中,所有的几何对象类型都在数据库层面都体现为 Geometry 类型。因此,几何对象类型与几何计算函数之间通常没有绑定关系。 在大多数情况下,几何计算函数都可以接受不同类型的几何对象作为其参数。 但有些函数可能出于算法本身的特性,对输入的几何对象类型有一定的限制。这种限制会在参考手册中该函数的部分予以说明。

4.2. 地理数据类型

地理数据类型提供了对地理坐标(有时候也称为大地坐标,或者经度 / 维度,或者维度 / 经度)上的空间特征的原生支持。地理坐标是一种用弧度单位表示的球面坐标。

KGIS 插件几何数据类型的基本类型是平面类型。平面上两点最短路径是一条直线。这意味着几何数据类型的计算(面积,距离,长度,交集等等)可以使用笛卡尔数学和矢量方式来计算。

KGIS 插件地理类型的基础是一个球体。球面上两点的最短距离是一个大圆弧。这意味着地理计算(areas、distances、lengths、intersections 等等)必须在球面上使用更多复杂函数计算。要想得到更精确的计算值,计算时候必须要把地球是一个椭球因素考虑进去,但计算实际上也变得非常复杂了。

因为基本的数学计算(地理数据类型对象之间的关系及运算等等)要复杂得多,所以支持地理类型的函数要比支持几何数据类型的函数要少得多。将来的话,随着新算法的增加,KGIS 插件对地理类型数据的支持能力会随之扩展。

该类型的一个限制是当前只支持 WGS84 参考系 (SRID:4326),数据格式是经纬度。该参考系使用的是新数据类型地理数据类型。GEOS 的函数都不支持这种新类型。作为一种解决方案,用户可以在几何数据类型和地理数据类型之间进行来回转换。

新的 geography 地理数据类型使用 KingbaseES 类型修饰符(typmod)的方式来定义字段,因此创建一个地理数据类型字段是一个简单的步骤。除了曲线类型,所有的 OGC 标准地理数据类型都支持。

4.2.1. 地理数据类型基础

地理数据类型目前只支持最简单的特征对象。如果 SRID 值为 4326,那么标准几何类型数据可以自动转换为 geography 类型。

也可以使用 EWKT 和 EWKB 规范描述的数据格式来插入数据。

  • POINT: 创建一个带有 2D 点几何数据类型的表:

CREATE TABLE testgeog(gid serial PRIMARY KEY, the_geog geography(POINT,4326) );创建一个带有Z坐标的Point类型列:
CREATE TABLE testgeog(gid serial PRIMARY KEY, the_geog geography(POINTZ,4326) );
  • LINESTRING

CREATE TABLE lgeog(gid serial PRIMARY KEY, geog geography(LINESTRING) );
  • POLYGON

CREATE TABLE lgeognad27(gid serial PRIMARY KEY, geog geography(POLYGON, 4267) );
  • MULTIPOINT

  • MULTILINESTRING

  • MULTIPOLYGON

  • GEOMETRYCOLLECTION

新的 geography 类型字段不会注册在 geometry_columns 视图中,而是注册在一个新的视图 geography_columns 中。 该视图依赖于更底层的系统表,因此没有必要在该视图中使用 AddGeom... 之类的函数来手动注册。

现在,可以查看视图 "geography_columns" 以确认相应的表列是否在该视图里面。

可以使用 CREATE TABLE 语法创建一个带有 GEOGRAPHY 类型的列,没有必要使用 AddGeometryColumns () 函数来创建列:

CREATE TABLE global_points (
id SERIAL PRIMARY KEY,
name VARCHAR(64),
location GEOGRAPHY(POINT,4326)
);

注意列 location 是一个 GEOGRAPHY 类型的列,该类型字段有两种可选的修饰符:第一种是类型修改器(typmod),它能限制列的形状和维度。还有一个 SRID 修饰符,是一个整型数字,用于限制字段类型的坐标系。

类型修饰符可能的值有:POINT、LINESTRING、POLYGON、MULTIPOINT、MULTILINESTRING、MULTIPOLYGON。 该类型修饰符也支持通过后缀坐标:Z、M 和 ZM 来限制维度。因此,一个修饰符‘LINESTRINGM’表示一个 3 维的 LINESTRING 对象,它把第三个维度当做一个测量维度。类似地,‘POINTZM’表示该字段需要一个四维数据。

SRID 修饰符目前只限于:4326(WGS84)这个值。如果没有指定 SRID 值,那么默认使用值是 0(未定义的球面),所有的计算基于 WGS84 坐标系来计算。

未来可选的 SRID 值将会允许在除了 WGS84 参考系外的坐标系上计算。

一旦创建了表,可以在元数据视图 GEOGRAPHY_COLUMNS 中看到该表的相应表列:

SELECT * FROM geography_columns;

可以像使用 GEOMETRY 类型的列那样,向含有 GEOGRAPHY 类型列的表插入数据:

INSERT INTO global_points (name, location) VALUES ('Town', ST_GeographyFromText('SRID=4326; POINT(-110 30)') );
INSERT INTO global_points (name, location) VALUES ('Forest', ST_GeographyFromText('SRID =4326;POINT(-109 29)') );
INSERT INTO global_points (name, location) VALUES ('London', ST_GeographyFromText('SRID =4326;POINT(0 49)') );

创建索引的方式也与 GEOMETRY 类型一样。KGIS 插件会意识到该列类型是一个 GEOGRAPHY 类型,并创建一个合适的基于球面的索引,而不是一个使用 GEOMETRY 类型的常规平面索引:

CREATE INDEX global_points_gix ON global_points USING GIST ( location );

对 GEOGRAPHY 类型的查询和测量函数使用的单位都是米,因此距离参数应该用米表示,返回值也应该是米(对于面积来说是平方米)。 下列语句查询距离指定的点 1000 公里以内的地点(location)的名称:

SELECT name FROM global_points WHERE ST_DWithin(location, ST_GeographyFromText('SRID =4326;POINT(-110 29)'), 1000000);

可以使用 GEOGRAPHY 类型来计算一架飞机从西雅图飞到伦敦这条航线 (LINESTRING (-122.33 47.606, 0.0 51.5)) 到雷克雅末克(冰岛首都)(POINT (-21.96 64.15)) 的距离,这是非常强大的功能。

使用 GEOGRAPHY 类型,得到的距离将是 122.2 公里:

SELECT ST_Distance('LINESTRING(-122.33 47.606, 0.0 51.5)'::geography, 'POINT(-21.96 64.15)':: geography);

使用 GEOMETRY 类型,得到的距离将是 13.3 度(以经纬度为单位):

SELECT ST_Distance('LINESTRING(-122.33 47.606, 0.0 51.5)'::geometry, ‘POINT(-21.96 64.15) ':: geometry);

当使用 GEOGRAPHY 类型时,计算的是雷克雅默克到西雅图与伦敦的航线之间的最短球面距离。

当使用 GEOMETRY 类型时,计算的是雷克雅默克到西雅图与伦敦的航线之间笛卡尔距离。 这种计算方式是无意义的,因为它计算的是两者之间的平面距离。 实际上,这两者之间的距离的单位或许叫做 “度”,但这个结果并不对应两个点之间的真实角度差,所以称为 “度” 甚至是不准确的。

4.2.2. 什么时候使用 geography 类型而不是 geometry 数据类型?

新的 GEOGRAPHY 类型允许以经度 / 维度坐标的方式存储数据,但这是有代价的:支持 GEOGRAPHY 类型的函数要比支持 GEOMETRY 类型的函数要少一些,而且前者会多花一些 CPU 时间来执行。 选择的数据类型应该支持构建的应用,即:您的数据是跨越了全球、一片大陆,还是当地的一个州、县或者直辖市?

如果的数据只是包含了一个小范围区域,考虑到性能和功能的可用性,您可能会发现:选择一个合适的投影系,并使用 GEOMETRY 类型是最佳选择。

如果的数据包含的是全球或者一片大陆的数据,您可能会发现:GEOGRAPHY 类型能够让您构建一个不需要考虑投影细节的应用系统。只需将数据以经度 / 维度格式存储,并使用支持 GEOGRAPHY 类型的函数。

如果不理解投影系,也不想学习这些知识,也接受支持 GEOGRAPHY 类型函数的局限性,那么对您来说,使用 GEOGRAPHY 类型要比使用 GEOMETRY 类型要更容易一些。 只需要把经纬度数据加载到表中,然后就可以使用了。

参考 :ref:`KGIS 支持 Geography 类型的函数 ` 获取支持 geography 类型数据的函数列表和对应的描述。

4.3. 使用 OpenGIS 规范

OpenGIS 的 “Simple Features Specification for SQL” 规范定义了标准的 GIS 对象类型,以及处理这些类型对象的函数以及一些元数据表。为了保证元数据的一致性,OpenGIS 规范定义并实现了一些具体的程序来实现一些操作比如创建和删除空间列。

有两个 OpenGIS 元数据表: SPATIAL_REF_SYS 和 GEOMETRY_COLUMNS。 SPATIAL_REF_SYS 表包含了空间数据库中使用的 SRID 和参考系的文本描述。 GEOMETRY_COLUMNS 视图则包含了数据库中所有 GEOMETRY 类型列的 SRID 设置。

4.3.1. SPATIAL_REF_SYS 表和空间参考系

spatial_ref_sys 是一个兼容 OGC 规范的 KGIS 插件的系统表,该表包含了八千多个已知的空间参考系(spatial reference systems),以及参考系之间转换 / 重投影时候的细节。

虽然 KGIS 插件的表 spatial_ref_sys 中包含了超过八千种较为常用的空间参考系定义(这些参考系是由 Proj 库提供处理支持的),但这个表并不能涵盖全部已知的参考系。 如果对 Proj 库熟悉的话,甚至可以自定义自己的投影系统。需要记住的是:大多数空间参考系是区域性的,超出它们的使用区域范围是无意义的。

如果要查找在核心空间参考系集中没有定义的空间参考系,可以访问这个资源:http://spatialreference.org/

一些较为常用的空间参考系是:

  • 4326 - WGS 84 Long Lat

  • 4269 - NAD 83 Long Lat

  • 3395 - WGS 84 World Mercator

  • 2163 – US National Atlas Equal Area

以及 NAD 83 的各个投影系,WGS 84 UTM 参考系(Universal Transverse Mercator Projection,通用横轴墨卡托投影)。 UTM 参考系是最理想的测量参考系之一,但是它的每个分区只能覆盖 6 度(经度)乘 8 度(纬度)的区域(南北极地区除外)。

美国各州的平面空间参考系(单位为米或英尺)—— 通常每个州有 1 到 2 个参考系。大多数参考系都是以米为单位的,但也有不少以英尺为单位或者是 ESRI 创建的参考系。 您可以在网站:spatialreference.org 上找到更多信息,以确定该使用哪个 UTM 参考系来处理您感兴趣的区域数据。

SPATIAL_REF_SYS 表定义如下:

CREATE TABLE spatial_ref_sys (
srid        INTEGER NOT NULL PRIMARY KEY,
auth_name   VARCHAR(256),
auth_srid   INTEGER,
srtext      VARCHAR(2048),
proj4text   VARCHAR(2048))

SPATIAL_REF_SYS 表包含的列如下:

SRID :一个能标识数据库里面空间参考系(SRS)唯一性的整型值。

AUTH_NAME:本参考系统所引用的标准或标准化组织的名称。例如,“EPSG” 将是一个有效的 AUTH_NAME。

AUTH_SRID:一个由 AUTH_NAME 引用的,由权威机构定义的空间参考系的 ID 值。在 EPSG 中,这由 EPSG 投影代码(projection code)来决定。

SRTEXT :空间参考系的 WKT 描述。一个 WKT 描述的 SRS 样例如下:

PROJCS["NAD83 / UTM Zone 10N",
GEOGCS["NAD83",
DATUM["North_American_Datum_1983",
SPHEROID["GRS 1980",6378137,298.257222101]
],
PRIMEM["Greenwich",0],
UNIT["degree",0.0174532925199433]],
PROJECTION["Transverse_Mercator"], PARAMETER["latitude_of_origin",0],
PARAMETER["central_meridian",-123],
PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],
PARAMETER["false_northing",0], UNIT["metre",1]
]

EPSG 投影代码列表及其与 WKT 描述的对应关系,请前往这里查找: http://www.opengeospatial.org/ 。

关于 WKT 的一般讨论,请参考 OpenGIS 的 "Coordinate Transformation Services Implementation Specification" http://www.opengeospatial.org/standards 。 关于 ESPG 机构(European Petroleum Survey Group)和他们的空间参考系的数据集,请查看这里: http://www.epsg.org 。

4.3.2. 关于几何类型列的视图

在 KGIS 插件中 GEOMETRY_COLUMNS 变成了一个视图,可以从数据库系统目录表中读取数据:

\d geometry_columns View "public.geometry_columns"

Column | Type | Modifiers ---------------------------+---------------------------------+----------- f_table_catalog | character varying(256) | f_table_schema | charactervarying(256) | f_table_name | character varying(256) | f_geometry_column | character varying(256) | coord_dimension | integer | srid | integer | type | character varying(30)

F_TABLE_CATALOG、F_TABLE_SCHEMA、F_TABLE_NAME 三个列包含几何数据列的表全名。

注意

“catalog” 和 “schema” 都是 Oracle 语法的术语。KingbaseES 里面没有和 "catalog" 对应的对象,因此这一列是空的。 至于 "schema" 概念,KingbaseES 里面就是 schema 名称(默认是 public)。

F_GEOMETRY_COLUMN :含有空间特征的表里面的几何数据类型列。

COORD_DIMENSION :几何数据列的空间维度(2、3 或 4 维)。

SRID:含有空间特征的表里面的几何列的空间参考系 ID 值,SPATIAL_REF_SYS 表的外键。

TYPE:空间对象的类型。

为了把空间列限制为单个类型,可使用下列类型其中之一: POINT、LINESTRING、POLYGON,MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION 或者对应 XYM 坐标的 POINTM、LINESTRINGM、POLYGONM、MULTIPOINTM、MULTILINESTRINGM、MULTIPOLYGONM、GEOMETRYCOLLECTIONM 。 对于异构类型(混合类型),可以使用 "GEOMETRY" 作为定义该字段时候的类型。

注意

TYPE 这个参数(可能)不是 OpenGIS 的规范,但它是保证类型一致性的必要条件。

4.3.3. 创建一个带有空间数据的表

创建一个含有空间数据的表是很简单的,一步即可。 正如下面创建一个带有 2D LINESTRING 几何类型列的道路表一样,该字段列用 WGS 84 经纬度格式表示:

CREATE TABLE roads ( ID int4, ROAD_NAME varchar(25), geom geometry(LINESTRING,4326) );

可以使用标准的 ALTER TABLE 语法来添加额外的列,和接下来添加一个 3D LINESTRING 类型列一样的方法:

ALTER TABLE roads ADD COLUMN geom2 geometry(LINESTRINGZ,4326);

为了后向兼容,依然可以用管理函数两步创建空间表:

  • 创建一个正常的不含空间数据的表,例如:

CREATE TABLE roads ( ID int4, ROAD_NAME varchar(25) );
  • 使用 OpenGIS"AddGeometryColumn" 函数来添加一个空间类型列到表中,可参考函数 AddGeometryColumn 获取更多信息。该函数用法是:

    AddGeometryColumn(
    <schema_name>,
    <table_name>,
    <column_name>,
    <srid>,
    <type>,
    <dimension>
    )

    或者使用当前连接下的 schema 值,即 current_schema () 函数返回的值:

    AddGeometryColumn(
    <table_name>,
    <column_name>,
    <srid>,<type>, <dimension>
    )

    样例 1: SELECT AddGeometryColumn ('public', 'roads', 'geom', 423, 'LINESTRING', 2);

    样例 2: SELECT AddGeometryColumn ('roads', 'geom', 423, 'LINESTRING', 2);

下面的例子是用 SQL 语句来创建表,添加一个空间类型列(假定存在 SRID 值为 128 的空间参考系):

CREATE TABLE parks (park_id INTEGER,park_name VARCHAR,park_date DATE,park_type VARCHAR
);
SELECT AddGeometryColumn('parks', 'park_geom', 128, 'MULTIPOLYGON', 2 );

这是另一个例子,使用几何数据类型,SRID 值没有定义,为 0:

CREATE TABLE roads (road_id INTEGER,road_name VARCHAR
);
SELECT AddGeometryColumn('roads', 'roads_geom', 0, 'GEOMETRY', 3 );

4.3.4. 手动在 geometry_columns 表中注册几何数据类型列

函数 AddGeometryColumn () 可以创建一个几何列,并把这个新列注册在视图 geometry_columns 中。 如果有的软件需要使用视图 geometry_columns,那么任何几何类型的列都必须在这个视图中注册。

然而如果在创建列的过程中没有定义成一个具体的几何类型,那么该列可能被注册为一个一般类型的 geometry 类型列。 下面这些样例中有两个可能会出现这种状况,但是不能在 SQL 视图或者批量插入中使用 AddGeometryColumn 方法来创建几何列。 对于这些情况,可以通过再添加列约束的方式来把列正确地注册到 geometry_columns 视图中。 如果有的几何列是通过类型修饰符方式创建的,那么列会自动进行注册,不需要做任何事情。

假设我们有一个数据表 public.mytable,基于它创建一个视图:

CREATE VIEW public.vwmytablemercator ASSELECT gid, ST_Transform(geom,3395) As geom, f_nameFROM public.mytable;

为了能够正确地注册其中的几何类型列,我们需要强制转换类型:

DROP VIEW public.vwmytablemercator;
CREATE VIEW public.vwmytablemercator ASSELECT gid, ST_Transform(geom,3395)::geometry(Geometry, 3395) As geom, f_nameFROM public.mytable;

如果你确定数据的几何类型就是 2D POLYGON,就可以这样做:

DROP VIEW public.vwmytablemercator;
CREATE VIEW public.vwmytablemercator ASSELECT gid, ST_Transform(geom,3395)::geometry(Polygon, 3395) As geom, f_nameFROM public.mytable;

让我们通过批量插入的方式创建一个衍生的表:

SELECT poi.gid, poi.geom, citybounds.city_name
INTO myschema.my_special_pois
FROM poi INNER JOIN citybounds ON ST_Intersects(citybounds.geom,poi.geom);

在新表上创建二维索引:

CREATE INDEX idx_myschema_myspecialpois_geom_gistON myschema.my_special_pois USING gist(geom);

如果你的数据中点是 3DZ 或者 3DM 格式的,你可能希望创建一个 n 维索引而非二维索引。就像下边这样:

CREATE INDEX my_special_pois_geom_gist_ndON my_special_pois USING gist(geom gist_geometry_ops_nd);

在 geometry_columns 视图中手工注册新表中的几何列。这会改变表的底层结构:

SELECT populate_geometry_columns('myschema.my_special_pois'::regclass);

如果你需要旧的、基于约束的定义(例如:子表继承父表的 type 和 SRID),可以设置一个新的选项 use_typmod 为 false:

SELECT populate_geometry_columns('myschema.my_special_pois'::regclass,false);

虽然旧的基于约束的方式依然被支持,但在视图中一个基于约束的几何类型列不会自动注册在 geometry_columns 视图中, 而类型修饰符的方式可以自动注册,不管是在表中还是在视图中。 在这个样例中定义了两个几何列,一个是使用类型修饰符的方式,另一个是使用约束的方式:

CREATE TABLE pois_ny(gid SERIAL PRIMARY KEY, poi_name text, cat varchar(20), geom geometry(POINT,4326) );
SELECT AddGeometryColumn('pois_ny', 'geom_2160', 2160, 'POINT', 2, false);

如果在 ksql 下执行

\\d pois_ny;

会发现这两种定义方式是不同的:一个是类型修饰符(typmod)方式,一个是约束的方式。

Table "public.pois_ny"Column          | Type                    | Modifiers
----------------+-------------------------+-------------------------------------------------------
gid             | integer                 | not null default nextval('pois_ny_gid_seq'::regclass)
poi_name        | text                    |
cat             | character varying(20)   |
geom            | geometry(Point,4326)    |
geom_2160       | geometry                |Indexes:"pois_ny_pkey" PRIMARY KEY, btree (gid)
Check constraints:"enforce_dims_geom_2160" CHECK (st_ndims(geom_2160) = 2)"enforce_geotype_geom_2160" CHECK (geometrytype(geom_2160) = 'POINT'::text OR geom_2160 IS NULL)"enforce_srid_geom_2160" CHECK (st_srid(geom_2160) = 2160)

在 geometry_columns 视图中,这两种方式都成功地注册了:

SELECT f_table_name, f_geometry_column, srid, typeFROM geometry_columnsWHERE f_table_name = 'pois_ny';f_table_name | f_geometry_column | srid | type
-------------+-------------------+------+-------
pois_ny      | geom              | 4326 | POINT
pois_ny      | geom_2160         | 2160 | POINT

然而,如果创建这样一个视图:

CREATE VIEW vw_pois_ny_parks AS
SELECT *
FROM pois_ny
WHERE cat='park';SELECT f_table_name, f_geometry_column, srid, typeFROM geometry_columns
WHERE f_table_name = 'vw_pois_ny_parks';

基于类型修饰符(typmod)方式创建的视图的列在 geometry_columns 视图中成功注册了,但基于约束的却没有注册成功:

f_table_name     | f_geometry_column      | srid    | type
-----------------+------------------------+---------+----------------
vw_pois_ny_parks | geom                   | 4326    | POINT
vw_pois_ny_parks | geom_2160              | 0       | GEOMETRY
DROP VIEW vw_pois_ny_parks;
CREATE VIEW vw_pois_ny_parks AS
SELECT gid, poi_name, cat, geom,geom_2160::geometry(POINT,2160) As geom_2160
FROM pois_ny
WHERE cat='park';SELECT f_table_name, f_geometry_column, srid, typeFROM geometry_columnsWHERE f_table_name = 'vw_pois_ny_parks';f_table_name     | f_geometry_column  | srid  | type
-----------------+--------------------+-------+-------------
vw_pois_ny_parks | geom               | 4326  | POINT
vw_pois_ny_parks | geom_2160          | 2160  | POINT

4.3.5. 几何对象的有效性验证

KGIS 插件兼容 Open Geospatial Consortium (OGC) 的 OpenGIS 规范。 同样,许多 KGIS 插件函数需要,或者更准确地说,会假定它们要处理的数据是简单的而且是有效的。 例如 KGIS 插件不支持计算一个外部带孔的多边形的面积,或者从一个非简单的边界创建一个多边形对象。

根据 OGC 规范,一个简单几何对象指没有异常几何点,没有自相交或者自相切,主要是 0 或 1 维几何对象(比如:[MULTI] POINT、[MULTI] LINESTRING)。 从另一个角度来说,几何数据类型的有效性,主要是指 2 维几何对象 (比如:[MULTI] POLYGON),并且定义了一个有效多边形的特征集合。 规范中每一种几何类的描述都包含了特定的条件,更详细地描述了对几何对象的简单性和有效性要求。

POINT 点类型是一种继承了一个 0 维几何类型的类型;

MULTIPOINT 的简单性是指没有任意两个点重复的;

LINESTRING 的简单性是指该对象不存在两个重复的点(除非是终点,并且是一个线性环的终点,该环是封闭的);

一个 MULTILINESTRING 对象符合简单性要求的条件是:其所有元素都是简单的,并且任何两个元素之间如果存在交集,都是只在两个元素的端点处存在交集。

按照定义,POLYGON 对象总是简单的。如果 POLYGON 对象边界(由外环和内环组成)里面没有任何两个环相交,则它是有效的。 POLYGON 的边界可以在某个点相切(不是相切在一个线上)。一个有效的多边形不能被线切开,不能有线状的毛刺,并且内环必须完全在外环里面,不存在内外环重叠的部分。

一个 MULTIPOLYGON 有效的条件是:当且仅当其包含的所有基本对象元素都是有效的,且其内部的任何两个基本元素都不相交。其内部任何两个元素的边界可以有接触,但只能在有限数量的点接触。

大多数 GEOS 库实现的函数都依赖于这样的假定:输入的几何对象是符合 OpenGIS Simple Feature Specification 要求的、有效的几何对象。 为了检验几何对象的简单性或者有效性,可以使用函数 ST_IsSimple () 和 ST_IsValid () 。

通常,没必要对线性特征(几何对象)做有效性检查,因为总是会得到肯定的结果。 但在这个例子中,KGIS 扩展了 OGC 关于有效性的定义,当一个 LINESTRING 对象包含的不相同的点少于 2 个时,会被判定为无效。

gisdb=# SELECT
ST_IsValid('LINESTRING(0 0, 1 1)'),
ST_IsValid('LINESTRING(0 0, 0 0, 0 0)');st_isvalid  | st_isvalid
------------+-----------
t           | f

默认情况下,KGIS 插件不对几何类型数据做有效性检查,因为对于复杂查询,检查这种有效性会耗费很多 CPU 时间,特别是多边形对象。 如果不相信自己的数据源是有效的,可以手动给表添加约束用于约束插入数据的有效性:

ALTER TABLE mytable
ADD CONSTRAINT geometry_valid_check
CHECK (ST_IsValid(the_geom));

注意

严格遵守 OGC 规范的几何对象不包括 Z 或 M 值,因此 ST_IsValid () 函数不会检查那些维度并把更高维度的几何对象判定为无效! 不过,调用函数 AddGeometryColumn () 会添加一个检查几何对象维度的约束,因此这样设置对二维几何对象而言足够了。

4.3.6. 九交模型 ——Dimensionally Extended 9 Intersection Model (DE-9IM)

九交模型是一个九元组加维数扩展的空间拓扑关系数学描述框架。

图 4.3.3 上图为两条线交叠的例子。

例如,考虑一个表示道路网络的线性数据集。GIS 分析师也许会有这样的任务:区分所有相交于一条线,而不是一个点的路段。这个条件可能表示一些业务上的规则。 这种情况下函数 ST_Crosses 不能完全提供必要的空间过滤,因为对线性对象,只有他们相交于一个点的时候,它才会返回 true。

有一个可行的两步解决方案是:首先使用函数 (ST_Intersection) 对一对有空间交互 (ST_Intersects 函数来判定) 路段进行实际的相交, 然后把交集的几何类型(使用函数 ST_GeometryType 来获得)与类型 “LINESTRING” 进行比较 (需要正确处理交集返回 [MULTI] POINT、[MULTI] LINESTRING 这样的 GEOMETRY COLLECTION 情况)。

显然,我们期望有一个更优雅、更快速的解决方案。

图 4.3.4 上图为湖边码头的例子。

第二个 (理论性的) 例子是一个 GIS 分析师试图找到所有与湖的边界相交于一条线的码头,并且码头有一端在岸上。 换句话说,一个码头在湖里面,但不完全在一个湖里面,和湖的边界相交于一条线,且码头的两端之一在湖的边界上。分析师可能需要联合使用空间谓词来分离查找结果:

  • ST_Contains(lake, wharf) = TRUE

  • ST_ContainsProperly(lake, wharf) = FALSE

  • ST_GeometryType(ST_Intersection(wharf, lake)) = 'LINESTRING'

  • ST_NumGeometries(ST_Multi(ST_Intersection(ST_Boundary(wharf), ST_Boundary(lake)))) = 1

请参考 `Dimensionally Extended 9 Intersection Model( DE-9IM )<https://en.wikipedia.org/wiki/DE-9IM>`_ 。

4.3.6.1. 理论知识

根据 OpenGIS Simple Features Implementation Specification for SQL 规范 “比较两个几何对象关系的基本方法是对这两个几何对象的内部与内部之间、边界与边界之间、外部与外部之间进行相交试验,而区分两个几何对象之间的关系要根据相交矩阵来区分。”

Boundary(边界)

几何对象的边界是比该几何对象低一个维度的几何对象的集合。 对于 Point 对象,维度为 0,其边界也是空集。 LINESTRING 的边界是两个端点。 对于 POLYGON 对象,其边界是线条类对象,这些线条组成了 POLYGON 的内环和外环。

Interior(内部)

一个几何对象的内部,是指几何对象上除边界之外的点的集合。 对于点来说,点的内部就是点自身。 对于 LINESTRING 来说,其内部就是起点和终点之间的真实点。 对于 POLYGON 多边形来说,其内部就是多边形内部的面。

Exterior(外部)

几何对象的外部,是指除了几何对象占有的空间之外,其他的空间。 换句话说,所有不在几何对象内部和边界上的点都是几何对象的外部。(在 OGC 规范中)几何对象的外部是一个二维的、不封闭的面。

对于给定输入几何对象 a,如果 I (a)、B (a)、和 E (a) 分别表示 a 的内部、边界、外部,那么 a 和 b 的相交矩阵如下:

Interior

Boundary

Exterior

Interior

dim( I(a) ∩ I(b) )

dim( I(a) ∩ B(b) )

dim( I(a) ∩ E(b) )

Boundary

dim( B(a) ∩ I(b) )

dim( B(a) ∩ B(b) )

dim( B(a) ∩ E(b) )

Exterior

dim( E(a) ∩ I(b) )

dim( E(a) ∩ B(b) )

dim( E(a) ∩ E(b) )

Dim (a) 是函数 ST_Dimension 计算得到的 a 的维度,相交结果为 {0,1,2,T,F,*}

  • 0 => 点

  • 1 => 线

  • 2 => 面

  • T => {0,1,2},表示上面 1,2,3 都有

  • F => 空集

  • * => 不考虑

直观上看,对于两个叠加的多边形对象,看起来是这样:

从左到右读,从上到下读,维度矩阵表示为:“212101212”。

第一个例子中,表示两条线相交于一条线这样的关系矩阵是: “1*1***1**”。

假设存在一个道路表,从中查询穿越了一条给定的线的所有路段:

SELECT a.id
FROM roads a, roads b
WHERE a.id != b.id
AND a.geom && b.geom
AND ST_Relate(a.geom, b.geom, '1*1***1**');

表示码头部分地处于湖的海岸线上这样的关系矩阵是:“102101FF2”。

假设存在一个湖泊表和一个码头表,从中查询部分位于湖岸线上的码头:

SELECT a.lake_id, b.wharf_id
FROM lakes a, wharfs b
WHERE a.geom && b.geom
AND ST_Relate(a.geom, b.geom, '102101FF2');

更多信息,请参考:

  • `OpenGIS Simple Features Implementation Specification for SQL<https://ogc.org/standards/sfs>`_

  • `Dimensionally Extended Nine-Intersection Model (DE-9IM)<https://en.wikipedia.org/wiki/DE-9IM>`_

  • `GeoTools: Point Set Theory and the DE-9IM Matrix<https://docs.geotools.org/latest/userguide/library/jts/dim9.html>`_

4.4. 加载 GIS (矢量) 数据

一旦创建了空间表,就做好了将 GIS 数据上传到数据库的准备。KGIS 有两种内置的工具来将空间数据加载到 KingbaseES 数据库中:使用 SQL 语句或 Shapefile 加载器。

如果能把的数据转换成 text 文本方式表示,那么把数据加载到数据库的最简单办法就是使用 SQL 的方式。 就像 Oracle 和其他支持 SQL 的数据库一样,可将以 “INSERT” 语句方式表示的语句,在数据库终端中执行,实现大批量的数据插入。 以一个上传好的数据文件为例(比如 road.sql 这个文件),文件内容可能是这样的:

BEGIN;
INSERT INTO roads (road_id, roads_geom, road_name)VALUES (1,ST_GeomFromText('LINESTRING(191232 243118,191108 243242)',-1),'Jeff Rd');
INSERT INTO roads (road_id, roads_geom, road_name)VALUES (2,ST_GeomFromText('LINESTRING(189141 244158,189265 244817)',-1),'Geordie Rd');
INSERT INTO roads (road_id, roads_geom, road_name)VALUES (3,ST_GeomFromText('LINESTRING(192783 228138,192612 229814)',-1),'Paul St');
INSERT INTO roads (road_id, roads_geom, road_name)VALUES (4,ST_GeomFromText('LINESTRING(189412 252431,189631 259122)',-1),'Graeme Ave');
INSERT INTO roads (road_id, roads_geom, road_name)VALUES (5,ST_GeomFromText('LINESTRING(190131 224148,190871 228134)',-1),'Phil Tce');
INSERT INTO roads (road_id, roads_geom, road_name)VALUES (6,ST_GeomFromText('LINESTRING(198231 263418,198213 268322)',-1),'Dave Cres');
COMMIT;

使用 ksql 命令,数据库可以很容易批量导入到 KingbaseES 中:

ksql -d [database] -f roads.sql

4.4.1. shp2pgsql: 使用 ESRI Shapefile 文件加载工具

这实际上是一个转换工具,它把 shp 文件转换成 sql 文件,然后需要 ksql 命令将数据插入进去。

shp2pgsql 工具把 ESRI Shapefile 转化成 SQL 语句,以便用于插入到 KingbaseES 数据库中。 Shapefile 文件可以包含 geometry 类型数据,也可以包含 geography 类型的数据。该工具有几种操作模式,以不同的命令行参数来区分。

  • (c|a|d|p) 这些参数是互相排斥的:

    • -c 创建一个新表,并将 shp 数据插入进去。这是默认的模式。

    • -a 将 shp 文件数据以增量方式插入到数据表中。注意:如果要使用这种模式加载许多个文件包含的数据,这些文件必须有相同的列和相同的数据类型。

    • -d 创建存放 shp 文件数据的表前,删除掉该表。

    • -p 只生成创建表的 SQL 语句,不用加载实际的数据。如果需要完全把创建表和数据加载两步分开,那可以使用这个模式。

  • -? 展示帮助内容

  • -D 使用 KingbaseES "dump" 格式输出数据。该参数可以和 - a、-c 或 - d 其中之一联合使用。它比通过 INSERT 方式插入数据要快得多。对于海量数据,可以使用这个参数。

  • -s [<from>:]<SRID> 根据指定的 SRID 值,创建并在表中填充数据。可以根据给定的 FROM_SRID 值,指定任意 shp 格式文件,这样几何数据会被重投影到目的 SRID 值。SRID 值不能用 - D 参数来指定。

  • -k 保持标识符名称的大小写格式 (比如列,schema 和字段。注意:shp 文件里面的字段名称都是大写的。

  • -i 强制将所有的整型值转换为 32 位的整型值,带上该参数,则不会创建 64 位的值,即便.dbf 文件的签名头里面注明了这一点。

  • -I 在一个几何数据列上创建 GiST 索引

  • -S 生成简单几何对象,而不是 MULTI 几何数据。如果所有的几何对象真的都是简单的(比如 MULTIPOLYGON 带有一个简单的环或者 MULTIPOINT 有个简单的点),那么才会成功生成简单几何对象。

  • -t <关于维度的参数> 强制将输出的几何对象加上指定的维度。使用如下字符串值来表明指定的维度类型:2D、3DZ、3DM、4D。如果输入的维度数没有指定的多,输出时候这些多加的维度坐标会用 0 填充。如果输入的数据维度比指定的多,那么不在指定范围的维度将会被丢弃。

  • -w 输出 WKT 格式描述的数据,而不是 WKB 的。注意:该参数可能由于精度损失导致坐标有些细微变化。

  • -e 不使用一个大事务提交数据,而是对于每执行一条插入语句就提交一次。对于一个 SQL 格式的数据文件来说,如果这个文件里面包含主要的都是 “好的数据” 并且有一些会导致的错误的 “坏的数据”,那么插入数据时候可以将好的数据成功插入进去。注意:该参数不能和 - D 参数一起使用,因为 - D 参数使用的 dump 格式数据总是使用一个大事务来插入数据。

  • -W <数据文件编码> 指定输入数据文件(就是 dbf 后缀的文件)的编码格式。使用时候,所有的 dbf 数据列都会从指定的文件编码转换成 UTF-8 格式的。输出的 SQL 数据文件脚本会包含一条命令:SET CLIENT_ENCODING to UTF8,这样后台就可以将数据从 UTF-8 转换成内部使用的任意数据库编码。

  • -N <NULL 处理策略> 插入几何数据列值为 NULL 记录时候的处理策略,有跳过(skip)或者终止(abort)两种。

  • -n -n 参数只导入 DBF 格式的文件。如果数据没有对应的 shp 格式文件,那么这个命令 hi 自动切换到这个模式,并加载 dbf 格式的文件数据。因此设置这个参数意味着,虽然有 shp 文件数据集,但只想要属性数据而不需要几何特征数据。

  • -G

    在 WGS84(SRID=4326)参考系中使用地理数据类型而不是几何数据类型。

  • -T <表空间参数> 指定新创建表的表空间。除非使用了 - X 参数,否则索引也会使用默认的表空间。

  • -X <表空间参数> 指定新创建表索引的表空间。这个参数适用于 primary key 索引,如果指定了 - I 参数,那么这个参数也适用于 GiST 空间索引。

使用 shp2pgsql 命令来创建一个输入数据文件并导入到数据库的步骤大致如下:

shp2pgsql -c -D -s 4269 -i -I shaperoads.shp myschema.roadstable > roads.sql
ksql -d roadsdb -f roads.sql

使用 UNIX 管道功能可以在一个命令中完成 shp 文件的转化和数据导入:

shp2pgsql shaperoads.shp myschema.roadstable | psql -d roadsdb

4.5. 获取 GIS 数据

无论使用 SQL 还是 shp 文件加载 / 导出工具,都可以把数据从数据库中抽取出来。在关于使用 SQL 方式的部分,会讨论一些可用的操作符来做 bounding box 和在空间表上的查询。

4.5.1. 使用 SQL 获取数据

从数据库中获取数据的最直接的方法是使用 SQL 语句的 select 查询方式,来减少查询的记录数和列的数量,然后把数据结果列存储在一个可解析的文本中。

SELECT road_id, ST_AsText(road_geom) AS geom, road_name FROM roads;road_id | geom                                     | road_name
--------+------------------------------------------+-----------------------
1       | LINESTRING(191232 243118,191108 243242)  | Jeff Rd
2       | LINESTRING(189141 244158,189265 244817)  | Geordie Rd
3       | LINESTRING(192783 228138,192612 229814)  | Paul St
4       | LINESTRING(189412 252431,189631 259122)  | Graeme Ave
5       | LINESTRING(190131 224148,190871 228134)  | Phil Tce
6       | LINESTRING(198231 263418,198213 268322)  | Dave Cres
7       | LINESTRING(218421 284121,224123 241231)  | Chris Way
(6 rows)

然而会有时候需要一些限制来减少返回的记录数。如果是属性数据列(就是非空间类型数据)的限制,像查询非空间数据表那样直接正常使用 SQL 语法即可。 如果要使用空间数据限制条件,可以使用下面的函数或运算符:

ST_Intersects 检测两个几何对象在空间上是否存在交集。

= 检测两个几何对象是否在几何意义上完全相同。例如检测 “POLYGON ((0 0,1 1,1 0,0 0))” 和 “POLYGON ((0 0,1 1,1 0,0 0))” 是否相等(的确相等)。

现在可以在查询中使用这些函数或运算符。 注意:当在 SQL 语句中指定了具体的几何对象或者 box 时候,必须显式地通过使用函数 ST_GeomFromText () 来把字符串描述的格式转换成实际的几何对象。 SRID 为 4326 是匹配数据的参考 SRID。例如:

SELECT road_id, road_name
FROM roads
WHERE ST_OrderingEquals(roads_geom , ST_GeomFromText('LINESTRING(191232 243118,191108 243242)',4326) ) ;

上面的查询会从 roads 表中返回匹配几何对象和给定的 LINESTRING (191232 243118,191108 243242) 一样的几何对象。

要检测是否有某些道路经过了由指定多边形定义的区域,可进行类似如下格式的查询:

SELECT road_id, road_name
FROM roads
WHERE roads_geom && ST_GeomFromText('POLYGON((...))',4326);

上面的查询会使用多边形的 bounding box 来进行比对以便加快查询。 最常见的空间查询可能会是一个 “框选” 查询,通过使用客户端软件的使用(例如数据浏览器和 Web 地图工具),查询框选范围内的数据。 此时需要用 “BOX3D” 对象表示该选择框,查询语句如下:

SELECT ST_AsText(roads_geom) AS geom
FROM roads
WHERE
roads_geom && ST_MakeEnvelope(191232, 243117,191232, 243119,312);

注意:SRID 值为 4326 是为了指定包络线(Envelope)的投影系。

4.5.2. 使用批量导出工具(Dump 数据工具)

pgsql2shp 是一个直接连接数据库然后导出表数据的工具,它会将表(也可以是一个查询)导出成 Shape 文件。基本用法是:

pgsql2shp [<options>] <database> [<schema>.]<table>
pgsql2shp [<options>] <database><query>

这些命令参数是:

-f <filename>:将导出结果输出到指定文件。

-h <host> :数据库 IP。

-p <port> :数据库监听端口。

-P <password> :连接数据库所需要的密码。

-u <user>:连接数据库所需的用户名。

-g <geometry column> :指定写入到 shape 文件中时候的列名。

-b:使用二进制游标,这会让导出更快,但是如果任何一个非几何类型的列没有转换成 text 格式,那么该参数无效。

-r :裸模式,不删除 gid 字段或者转义列名称。

-m filename,重新把标识符映射为一个 10 个字符的名称。由 filename 指定的文件中的每行,是由被单个空格分隔的两个符号组成,并且行首或者行尾没有空格,例如:

VERYLONGSYMBOL SHORTONE
ANOTHERVERYLONGSYMBOL SHORTER

4.6. 创建索引

索引让空间数据库的大量数据查询成为可能,没有索引,任何特性的查询都需要进行全表扫描。索引通过将数据丢到一个遍历树中以便快速找到一个指定的记录。KingbaseES 默认支持三种索引类型:B-Tree 索引,R-Tree 索引和 GiST 索引:

  • B-Tree 索引:这种索引常用于可以在一个方向上排序的数据,比如数值,字母,日期。GIS 数据不能在一个方向上进行排序(比如 POINT (0 0)、POINT (0 1)、POINT (1 0) 三个对象哪个更大呢?) ,因此 B-Tree 索引对 GIS 数据无用。

  • R-Trees 索引:这种索引把数据分隔成矩形,子矩形(sub-rectangles)和子子矩形(sub-sub rectangles)等等,R-Tree 索引适用于部分空间数类型。

  • GiST (Generalized Search Trees 缩写) 索引把数据分成这样几个部分:某一边的对象,重叠的对象,内部的对象。该索引适用范围很广,包括 GIS 数据。KGIS 插件使用的 R-Tree 索引是基于 GiST 实现的。

4.6.1. GiST 索引

GiST 表示 "Generalized Search Tree",是索引的一种常规形式。除了支持空间数据索引外,GiST 索引还可以加速所有不规则数据结构(整型数组,谱数据等等)的查询,而 B-Tree 索引在这样的情形不建议使用。

一旦一个 GIS 数据表有数千行记录,应当创建一个索引来加速空间数据查询(除非所有查询都是在属性列(非几何类型列)上,这样的话可以在属性列上面创建一个正常的索引)。

在一个 geometry 类型的列上面创建一个 GiST 索引方法如下:

CREATE INDEX [indexname] ON [tablename]
USING GIST ( [geometryfield] );

上面的语法是用于创建 2D 索引的,要想创建一个 KGIS 插件支持的 N 维几何对象索引,方法如下:

CREATE INDEX [indexname] ON [tablename]
USING GIST ([geometryfield] gist_geometry_ops_nd);

创建空间索引是一种密集计算的工作:一个有百万记录的表,CPU 为 300MHz 的 Solaris 机器上,创建 GIS 索引发现需要 1 个小时。 创建完索引后,必须强制让 KingbaseES 更新表的统计信息,这些统计信息可以用于查询计划的优化,更新方法如下:

VACUUM ANALYZE [table_name] [(column_name)];
SELECT UPDATE_GEOMETRY_STATS([table_name], [column_name]);

GiST 索引与 KingbaseES 中的 R-Tree 索引相比有两个优势。 第一,GiST 索引是 NULL 值安全的,这意味着用户可以在一个包含 NULL 值的列(包括几何类型列)上面创建索引。 第二,GiST 索引支持 “无丢失” 存储,这对于处理超过 8K 存储大小的大型 GIS 对象来说是很重要的。 这种 “无丢失” 特性能够让 KingbaseES 在索引中只存储一个对象的关键部分 —— 比如对于 GIS 对象,只存储它的 bounding box。 而在创建 R-Tree 索引的过程中,超过 8K 的 GIS 对象会引起创建失败。

4.6.2. 使用索引

一般来说,索引能够隐式地加速获取数据:一旦创建了索引,查询优化器会决定什么时候使用索引信息来加速查询。 不幸地是 KingbaseES 不能尽可能有效地像使用其他索引一样使用 GiST 索引,这会导致有时候应该使用空间索引,却使用了全表扫描。

如果发现空间索引 (就这里来说就是字段索引) 没有很好的使用,可以按照下面的方式来做:

首先,确保表的记录数和值分布这样的统计信息已经被收集,以便查询优化器有更丰富的信息来决定是否使用索引。 收集统计信息可以使用函数 update_geometry_stats ([table_name, column_name]) (compute distribution) 和命令 VACUUM ANALYZE [table_name] [column_name] (compute number of values) 来做。 执行命令 VACUUM ANALYZE 会将上面两个操作一步完成。应该定期 VACUUM 数据库 —— 许多 KingbaseES 的 DBA 会经常定期执行 VACUUM 任务。

如果 VACUUM 操作没有解决问题,可以强制通过 SET ENABLE_SEQSCAN=OFF 把全表扫描关了来使用索引。应该有节制地使用这个命令,并且只在空间索引查询上使用。一般来说,查询优化器会比在何时使用 B-Tree 索引上更懂。一旦完成了查询,应该把 SET ENABLE_SEQSCAN 值改回 ON,以便其他查询可以正常使用查询优化器。

如果发现查询优化器对全表扫描和索引扫描的成本消耗估算是错误的,可以减少配置文件中 KingbaseES.conf 的 random_page_cost 参数值,或者使用 SET random_page_cost=# 语句设置。改值的默认值为 4,可以将这个值设置为 1 或 2. 减少这个值可以让查询优化器更倾向于使用索引。

4.7. 复杂查询

用户需要使用空间数据库的理由,是因为需要在数据库内部进行空间查询 —— 这些功能是 GIS 客户端所需要的。 要想高效使用 KGIS 插件,需要知道哪些空间函数是可用的,并且确保在空间表中有合适的索引以便提供好的查询性能。 下面的这些样例中 SRID 值为 4326 是用来展示样例的,实际开发中应该使用在 spatial_ref_sys 中真实的和的数据匹配的 SRID 值。

如果没有 SRID 的原因是正在构建一个没有地理空间参考系的应用,比如分子内部建模或者在发生核灾难时候要把人类运送到火星的一个好位置上,那么省略掉 SRID 值吧, 自己造一个参考系,然后把它插入到 spatial_ref_sys 表中。

4.7.1. 充分利用索引

构建查询时候,记住只有基于 bounding box 的运算符比如 && 才能利用 GiST 索引。 一些函数像 ST_Distance () 不能使用索引来优化查询。例如下面的在大表上的查询是非常慢的:

SELECT the_geom
FROM geom_table
WHERE ST_Distance(the_geom, ST_GeomFromText('POINT(100000 200000)', 4326)) < 100;

这个查询会查出所有距离点 POINT (100000 200000) 100 个长度单位范围内的所有记录。 这个查询非常慢的原因是,它会计算出给定的点与表中的所有点的距离。可以通过 && 运算符来减少所需的距离计算量。

SELECT the_geom
FROM geom_table
WHERE ST_DWithin(the_geom, ST_MakeEnvelope(90900, 190900, 100100, 200100, 4326), 100);

这个查询同样结果的几何对象,但是这个查询会更高效。假定在字段 the_geom 上面有一个 GiST 索引,查询优化器会识别到这个索引,并用来减少使用函数 ST_Distance 进行计算的记录数。 注意运算符 && 隐含使用函数 ST_MakeEnvelope,返回的几何对象是一个以给定点为原点的长宽为 200 个长度单位的正方形盒子 —— 这个是的 “查询盒子”。 运算符 && 使用索引来把要查询的几何对象集减少到其 bounding box 和查询盒子有叠加的几何对象。假定的 “查询盒子” 要比整个表小很多,那么这个办法会大幅度减少距离的计算量。

4.7.2. 空间 SQL 样例

本节所用的样例会使用两张表,一个是线性道路表,一个是多边形的市政边界。道路表 bc_roads 的定义如下:

Column              | Type                | Description
--------------------+---------------------+----------------------------------
gid                 | integer             | Unique ID
name                | character varying   | Road Name
the_geom            | geometry            | Location Geometry (Linestring)

市政边界表定义如下 bc_municipality:

Column             | Type                | Description
-------------------+---------------------+----------------------------------
gid                | integer             | Unique ID
Code               | integer             | Unique ID
name               | character varying   | City / Town Name
the_geom           | geometry            | Location Geometry (Polygon)
  1. 所有的道路以千米为长度单位,总长度是多少?

    这个问题答案很简单,一个 SQL 就 OK:

    SELECT sum(ST_Length(the_geom))/1000 AS km_roads FROM bc_roads;km_roads
    ------------------
    70842.1243039643
    (1 row)
  2. Prince George 市有多少公顷?

    该查询需要把查询时候的条件(该条件在 bc_municipality 的字段 name 上)和一个空间计算结(面积计算)合起来:

    SELECTST_Area(the_geom)/10000 AS hectares
    FROM bc_municipality
    WHERE name = 'PRINCE GEORGE';hectares
    ------------------
    32657.9103824927
    (1 row)
  3. 该省最大面积的城市是哪个?

    该查询需要在查询条件中加入空间测量计算。有许多方法解决这个问题,但是最高效的是下面的方法:

    SELECTname,ST_Area(the_geom)/10000 AS hectares
    FROMbc_municipality
    ORDER BY hectares DESC
    LIMIT 1;Name                        | hectares
    ----------------------------+-----------------
    TUMBLER RIDGE               | 155020.02556131
    (1 row)

    注意

    为了解决这个查询,必须计算每一个多边形的面积。如果要计算的面积对象很多,很重要的一点是要在要计算面积的列上面单独加上索引以便提高性能。通过逆序排序结果,然后使用 KingbaseES 的 LIMIT 子句,可以很容易地找出最大的面积值,而不必使用像 max()这样的聚合函数

  4. 完全包含在每个直辖市里面的道路长度分别是多少?

    这是一个 “空间 join” 类型的查询,因为需要把两个表的数据放到一起(做 join),但使用的是空间相交的条件(空间包含),而不是通常的、在一个字段上进行的 join:

    SELECTm.name,sum(ST_Length(r.the_geom))/1000 as roads_km
    FROMbc_roads AS r,bc_municipality AS m
    WHEREST_Contains(m.the_geom,r.the_geom)
    GROUP BY m.name
    ORDER BY roads_km;Name              |roads_km
    ------------------+------------------
    SURREY            | 1539.47553551242
    VANCOUVER         | 1450.33093486576
    LANGLEY DISTRICT  | 833.793392535662
    BURNABY           | 773.769091404338
    PRINCE GEORGE     | 694.37554369147
    ...

这个查询会花费一点时间,因为表中的每一个道路的长度都要加起来(聚合)到最终的结果(对于的这个表大概有 25W 条记录)。对于较小的覆盖(几千条记录覆盖后只有几百条),查询响应会非常快。

  1. 创建城市 Prince George 内所有道路的一个新表

    这是一个叠加的样例,它使用两张表,输出一张新表,该新表是这两个表包含的几何对象空间切割的产物,和上面的空间 join 是不同的,这个查询实际上创造了新的几何对象。叠加可以看做是空间 join 的增强版,对于精确数据分析很有用。

    CREATE TABLE pg_roads as
    SELECTST_Intersection(r.the_geom, m.the_geom) AS intersection_geom,ST_Length(r.the_geom) AS rd_orig_length,
    r.* FROMbc_roads AS r,bc_municipality AS m
    WHERE m.name = 'PRINCE GEORGE' AND ST_Intersects(r.the_geom, m.the_geom);
  2. Victoria 市的 “Douglas St” 大街长度是多少?

    SELECTsum(ST_Length(r.the_geom))/1000 AS kilometers
    FROMbc_roads r,bc_municipality m
    WHERE r.name = 'Douglas St' AND m.name = 'VICTORIA'AND ST_Contains(m.the_geom, r.the_geom) ;kilometers
    ------------------
    4.89151904172838
    (1 row)
  3. bc_municipality 表中含有孔的面积最大的多边形是什么?

    SELECT gid, name, ST_Area(the_geom) AS area
    FROM bc_municipality
    WHERE ST_NRings(the_geom) > 1
    ORDER BY area DESC LIMIT 1;Gid    | name           |area
    -------+----------------+----------------------
    12     | SPALLUMCHEEN   | 257374619.430216
    (1 row)

金仓数据库 KingbaseGIS 使用手册(4. 数据管理和查询)相关推荐

  1. 金仓数据库 KingbaseGIS 使用手册(6.8. 几何对象输入函数)

    6.8. 几何对象输入函数 6.8.1. ST_BdPolyFromText ST_BdPolyFromText - 根据一个任意的封闭的WKT描述的MultiLineString几何类型对象创建一个 ...

  2. 金仓数据库 KingbaseGIS 使用手册(9.4. 拓扑结构构造函数、9.5. 拓扑结构编辑函数)

    9.4. 拓扑结构构造函数 9.4.1. CreateTopology CreateTopology - 创建一个拓扑结构的schema,并将这个schema注册到系统表the topology.to ...

  3. 金仓数据库 KingbaseGIS 使用手册(8.11. 栅格处理函数)

    8.11. 栅格处理函数 8.11.1. 地图代数函数 8.11.1.1. ST_Clip ST_Clip - 返回输入几何对象修改后的栅格.如果波段没有指定,那么会处理所有的波段.如果参数crop没 ...

  4. 金仓数据库 KingbaseGIS 使用手册(6.11. 空间关系函数)

    6.11. 空间关系函数 6.11.1. ST_3DIntersects ST_3DIntersects -如果几何对象在3维空间内相交,则返回TRUE. 用法 boolean ST_3DInters ...

  5. 金仓数据库KStudio使用手册(3. 数据库管理)

    目录 3.1. 数据库连接管理 ¶ 3.1.1. 连接的创建,编辑 ¶ 3.1.2. SSL链接配置 ¶ 3.1.3. 驱动属性 ¶ 3.1.4. 参数中的变量 ¶ 3.1.5. 连接.断开连接与重新 ...

  6. 【数据库】Kingbase金仓数据库工程维护简明手册

    1. 导出:## 方法一:通过sysmodel界面工具导出 确认SMARTSYS_HOME/conf目录下的配置文件db_mainten_mng.ini中,db_type=kingbase. 打开sy ...

  7. KINGBASE人大金仓数据库安装配置手册

    1.上传安装包 安装包和license文件上传到/home/setup/kingbase目录 2.操作系统配置 关闭防火墙 中标麒麟: systemctl stop firewalld systemc ...

  8. jpa 人大金仓数据库方言_兼容认证 | 人大金仓数据库管理系统完成与浪潮产品兼容认证...

    日前,人大金仓数据库管理系统KingbaseES V7.V8完成与浪潮GS Cloud数字化管理平台.iGIX数字化能力平台完成兼容认证. 测试结果显示:双方产品完全兼容,其功能.性能和兼容性等各方面 ...

  9. 炫“库”行动-人大金仓有奖征文—金仓数据库入门体验

    目录 前言 正文 一.人大金仓数据库介绍 二.安装金仓数据库 1. 下载安装包 2. 解压安装包 3. 安装数据库系统 4. 数据库设置 三.安装过程中的常见问题 结尾 文本正在参与[炫"库 ...

最新文章

  1. 深度学习中的随机种子
  2. Bing空间数据服务
  3. Hbase(4)——hive on hbase
  4. 《C++ Primer》7.3.4节练习
  5. 海报PSD分层促销模板|深层剖析设计套路
  6. 20155322 《Java程序设计》课堂实践项目 数据库-3-4
  7. delphi 获取数组长度_C++可变长的数组,老司机手把手教你实现!
  8. 63. Event button 事件属性
  9. CFA一级知识点总结
  10. 阿里云Centos7修改22默认端口
  11. Nodelist转化问题。
  12. StarGFS海量小文件的高性能存储和保护方案
  13. 从打的看商业共赢模式
  14. 你真的认为iPhone只是一部手机?苹果惊天秘密之 二
  15. 关于校园霸凌,刚刚发生(第四天的进展)
  16. 全志H616方案香橙派orangepi zero2的26pin接口 SPI测试
  17. 数组名 int a[5] = {1,2,3,4,5}; int *ptr = (int *)( a + 1);
  18. 元胞自动机:森林火灾模拟(Matlab:heatmap、colormap)
  19. 2022医院三基相关专业每日一练模拟题及答案
  20. 诺基亚手机软件设计大赛

热门文章

  1. 车载以太网线束测试接口的种类以及各自特点?---FAKRA
  2. 移动用户最爱视频播放、电商导购、新闻阅读
  3. 自媒体新手注册账号,在填写资料时有哪几个点是需要注意的?
  4. 【Java】《OnJava8》笔记——第20章泛型
  5. 浙江高校计算机大学外语,关于做好2020年下半年浙江省大学外语等级(CET3)考试报名工作的通知...
  6. 杂学第十篇:这几天,在毕业论文调整格式中踩过的坑,满满的干货助你快速解决格式调整的烦恼
  7. 数分学习笔记 vol.1 <游戏数分基本工作内容>
  8. TIA博途_通过GATHER指令实现HMI触摸屏离散量报警的具体方法示例
  9. Bootstrap常用布局样式
  10. ubuntu退出shell终端命令_ubuntu常用终端命令