第 4 章。数据管理

目录

4.1. 空间数据模型

4.1.1. OGC 几何

开放地理空间联盟(OGC)制定了简单要素访问标准(SFA),为地理空间数据提供模型。它定义了基本的空间类型几何,以及操作和转换几何值以执行空间分析任务的操作。PostGIS 将 OGC 几何模型实现为 PostgreSQL 数据类型 geometrygeography

几何是一种抽象类型。几何值属于其具体子类型之一,这些子类型表示各种形状和维度。这些包括原子类型 线串线性环多边形,以及集合类型 多点多线串多多边形几何集合简单要素访问 - 第 1 部分:通用架构 v1.2.1 添加了结构 多面体表面三角形TIN 的子类型。

几何在二维笛卡尔平面中建模形状。多面体表面、三角形和 TIN 类型也可以表示三维空间中的形状。形状的大小和位置由其坐标指定。每个坐标都有一个 X 和 Y 纵坐标值,用于确定其在平面中的位置。形状由点或线段构成,点由单个坐标指定,线段由两个坐标指定。

坐标可以包含可选的 Z 和 M 纵坐标值。Z 纵坐标通常用于表示高程。M 纵坐标包含一个度量值,可以表示时间或距离。如果几何值中存在 Z 或 M 值,则必须为几何中的每个点定义它们。如果几何具有 Z 或 M 纵坐标,则坐标维度为 3D;如果同时具有 Z 和 M,则坐标维度为 4D。

几何值与一个空间参考系统相关联,该系统指示它嵌入的坐标系。空间参考系统由几何 SRID 编号标识。X 轴和 Y 轴的单位由空间参考系统确定。在平面参考系统中,X 和 Y 坐标通常表示东向和北向,而在大地系统中,它们表示经度和纬度。SRID 0 表示一个无限笛卡尔平面,其轴未分配任何单位。请参阅 第 4.5 节“空间参考系统”

几何维度是几何类型的一个属性。点类型的维度为 0,线性类型的维度为 1,多边形类型的维度为 2。集合的维度是最大元素维度。

几何值可以是的。空值不包含顶点(对于原子几何类型)或不包含元素(对于集合)。

几何值的一个重要属性是其空间范围边界框,OGC 模型将其称为包络。这是一个 2 维或 3 维框,它包围了几何的坐标。它是一种在坐标空间中表示几何范围并检查两个几何是否交互的有效方法。

几何模型允许评估拓扑空间关系,如第 5.1.1 节“维度扩展的 9 交集模型”中所述。为了支持这一点,为每种几何类型定义了内部边界外部的概念。几何在拓扑上是封闭的,因此它们始终包含它们的边界。边界是维度比几何本身小 1 的几何。

OGC 几何模型为每种几何类型定义了有效性规则。这些规则确保几何值表示真实的情况(例如,可以指定一个孔位于外壳外部的多边形,但这在几何上没有意义,因此是无效的)。PostGIS 还允许存储和操作无效的几何值。这样可以在需要时检测和修复它们。请参阅第 4.4 节“几何验证”

4.1.1.1. 点

点是 0 维几何,表示坐标空间中的单个位置。

POINT (1 2)
POINT Z (1 2 3)
POINT ZM (1 2 3 4)

4.1.1.2. 线串

线串是由连续的线段序列形成的 1 维线。每个线段由两个点定义,一个线段的端点形成下一个线段的起点。OGC 有效的线串具有零个或两个或多个点,但 PostGIS 也允许单点线串。线串可能会自相交。如果线串的起点和终点相同,则该线串是闭合的。如果线串不自相交,则该线串是简单的。

LINESTRING (1 2, 3 4, 5 6)

4.1.1.3. 线性环

线性环是既闭合又简单的线串。第一个点和最后一个点必须相等,并且线不能自相交。

LINEARRING (0 0 0, 4 0 0, 4 4 0, 0 4 0, 0 0 0)

4.1.1.4. 多边形

多边形是由外部边界(外壳)和零个或多个内部边界(孔)分隔的二维平面区域。每个边界都是一个线性环

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

4.1.1.5. 多点

多点是点的集合。

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

4.1.1.6. 多线串

多线串是线串的集合。如果其每个元素都闭合,则多线串是闭合的。

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

4.1.1.7. 多多边形

多多边形是不重叠、不相邻的多边形的集合。集合中的多边形只能在有限数量的点上接触。

MULTIPOLYGON (((1 5, 5 5, 5 1, 1 1, 1 5)), ((6 5, 9 1, 6 1, 6 5)))

4.1.1.8. 几何集合

几何集合是几何的异构(混合)集合。

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

4.1.1.9. 多面体表面

多面体表面是共享一些边的连续的面片或刻面的集合。每个面片都是一个平面多边形。如果多边形坐标具有 Z 纵坐标,则该表面是三维的。

POLYHEDRALSURFACE Z (
  ((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)) )

4.1.1.10. 三角形

三角形是由三个不同的非共线顶点定义的多边形。由于三角形是多边形,因此由四个坐标指定,第一个坐标和第四个坐标相等。

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

4.1.1.11. TIN

TIN 是表示 不规则三角网 的不重叠的 三角形 的集合。

TIN Z ( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )

4.1.2. SQL/MM 第 3 部分 - 曲线

ISO/IEC 13249-3 SQL 多媒体 - 空间标准(SQL/MM)扩展了 OGC SFA,以定义包含具有圆弧的曲线的几何子类型。SQL/MM 类型支持 3DM、3DZ 和 4D 坐标。

[Note]

SQL-MM 实现中的所有浮点比较都是在指定的容差内执行的,目前为 1E-8。

4.1.2.1. 圆弧串

圆弧串是基本曲线类型,类似于线性世界中的线串。单个弧段由三个点指定:起点和终点(第一个和第三个)以及弧上的一些其他点。要指定一个闭合的圆,起点和终点是相同的,中间点是圆直径上的相反点(即弧的中心)。在弧的序列中,前一个弧的端点是下一个弧的起点,就像线串的段一样。这意味着圆弧串必须具有大于 1 的奇数个点。

CIRCULARSTRING(0 0, 1 1, 1 0)

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

4.1.2.2. 复合曲线

复合曲线是可能包含圆弧段和线段的单条连续曲线。这意味着除了具有格式正确的组件外,每个组件(最后一个除外)的端点必须与后续组件的起点重合。

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

4.1.2.3. 曲线多边形

曲线多边形类似于多边形,具有外环和零个或多个内环。不同之处在于环可以是圆弧串或复合曲线以及线串。

从 PostGIS 1.4 开始,PostGIS 支持曲线多边形中的复合曲线。

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

示例:曲线多边形,其外壳由包含圆弧串和线串的复合曲线定义,孔由圆弧串定义

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

4.1.2.4. 多曲线

多曲线是曲线的集合,可以包括线串、圆弧串或复合曲线。

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

4.1.2.5. 多表面

多表面是表面的集合,可以是(线性)多边形或曲线多边形。

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

4.1.3. WKT 和 WKB

OGC SFA 规范定义了两种用于外部表示几何值的格式:众所周知的文本 (WKT) 和众所周知的二进制 (WKB)。WKT 和 WKB 都包含有关对象类型和定义它的坐标的信息。

众所周知的文本 (WKT) 提供了空间数据的标准文本表示形式。空间对象的 WKT 表示形式示例包括:

  • POINT(0 0)

  • POINT Z (0 0 0)

  • POINT ZM (0 0 0 0)

  • POINT EMPTY

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

  • LINESTRING EMPTY

  • 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))

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

  • MULTIPOINT EMPTY

  • 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))

  • GEOMETRYCOLLECTION EMPTY

WKT 的输入和输出由 ST_AsTextST_GeomFromText 函数提供。

text WKT = ST_AsText(geometry);
geometry = ST_GeomFromText(text WKT, SRID);

例如,以下语句用于从 WKT 和 SRID 创建并插入空间对象:

INSERT INTO geotable ( geom, name )
  VALUES ( ST_GeomFromText('POINT(-126.4 45.32)', 312), 'A Place');

众所周知的二进制 (WKB) 将空间数据以二进制数据(字节数组)的形式提供了一种可移植的、全精度的表示形式。空间对象的 WKB 表示形式示例包括:

  • WKT: POINT(1 1)

    WKB: 0101000000000000000000F03F000000000000F03

  • WKT: LINESTRING (2 2, 9 9)

    WKB: 0102000000020000000000000000000040000000000000004000000000000022400000000000002240

WKB 的输入和输出由 ST_AsBinaryST_GeomFromWKB 函数提供。

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

例如,以下语句用于从 WKB 创建并插入空间对象:

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

4.2. 几何数据类型

PostGIS 通过定义一个名为 geometry 的 PostgreSQL 数据类型来实现 OGC 简单要素模型。它使用内部类型代码(请参阅 GeometryTypeST_GeometryType)表示所有几何子类型。这允许将空间要素建模为使用 geometry 类型列定义的表的行。

geometry 数据类型是不透明的,这意味着所有访问都是通过对几何值调用函数来完成的。函数允许创建几何对象、访问或更新所有内部字段以及计算新的几何值。PostGIS 支持 OGC 简单要素访问 - 第 2 部分:SQL 选项 (SFS) 规范中指定的所有函数,以及许多其他函数。有关完整函数列表,请参阅第 7 章,PostGIS 参考

[Note]

PostGIS 遵循 SFA 标准,在空间函数前面加上“ST_”。这原本代表“空间和时间”,但该标准的时序部分从未开发出来。相反,它可以解释为“空间类型”。

SFA 标准规定空间对象包含空间参考系统标识符 (SRID)。在创建空间对象以插入到数据库中时需要 SRID(它可能默认为 0)。请参阅 ST_SRID第 4.5 节,“空间参考系统”

为了使查询几何形状高效,PostGIS 定义了各种空间索引和使用它们的空间运算符。有关详细信息,请参阅第 4.9 节,“空间索引”第 5.2 节,“使用空间索引”

4.2.1. PostGIS EWKB 和 EWKT

OGC SFA 规范最初仅支持 2D 几何图形,并且几何 SRID 不包含在输入/输出表示形式中。OGC SFA 规范 1.2.1(与 ISO 19125 标准一致)增加了对 3D (ZYZ) 和测量 (XYM 和 XYZM) 坐标的支持,但仍然不包括 SRID 值。

由于这些限制,PostGIS 定义了扩展的 EWKB 和 EWKT 格式。它们提供 3D (XYZ 和 XYM) 和 4D (XYZM) 坐标支持,并包含 SRID 信息。包含所有几何信息允许 PostGIS 将 EWKB 用作记录的格式(例如,在 DUMP 文件中)。

EWKB 和 EWKT 用于 PostGIS 数据对象的“规范形式”。对于输入,二进制数据的规范形式为 EWKB,而对于文本数据,则接受 EWKB 或 EWKT。这允许通过使用 ::geometry 将 HEXEWKB 或 EWKT 中的文本值强制转换为几何值来创建几何值。对于输出,二进制的规范形式是 EWKB,文本的规范形式是 HEXEWKB(十六进制编码的 EWKB)。

例如,以下语句通过从 EWKT 文本值强制转换来创建几何图形,并使用 HEXEWKB 的规范形式输出它

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

PostGIS EWKT 输出与 OGC WKT 有一些差异

  • 对于 3DZ 几何图形,省略了 Z 限定符

    OGC: POINT Z (1 2 3)

    EWKT: POINT (1 2 3)

  • 对于 3DM 几何图形,包括 M 限定符

    OGC: POINT M (1 2 3)

    EWKT: POINTM (1 2 3)

  • 对于 4D 几何图形,省略了 ZM 限定符

    OGC: POINT ZM (1 2 3 4)

    EWKT: POINT (1 2 3 4)

EWKT 避免过度指定维度以及 OGC/ISO 格式中可能发生的不一致,例如

  • POINT ZM (1 1)

  • POINT ZM (1 1 1)

  • POINT (1 1 1 1)

[Caution]

PostGIS 扩展格式目前是 OGC 格式的超集,因此每个有效的 OGC WKB/WKT 也是有效的 EWKB/EWKT。但是,如果 OGC 以与 PosGIS 定义冲突的方式扩展格式,则将来可能会有所不同。因此,您不应该依赖这种兼容性!

空间对象的 EWKT 文本表示形式示例如下:

  • POINT(0 0 0) -- XYZ

  • SRID=32632;POINT(0 0) -- 带有 SRID 的 XY

  • POINTM(0 0 0) -- XYM

  • POINT(0 0 0 0) -- XYZM

  • SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- 带有 SRID 的 XYM

  • 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 10, 10 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);

例如,以下语句用于使用 EWKT 创建和插入 PostGIS 空间对象:

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

4.3. 地理数据类型

PostGIS geography 数据类型为以“地理”坐标(有时称为“大地”坐标或“纬度/经度”或“经度/纬度”)表示的空间要素提供原生支持。地理坐标是以角度单位(度)表示的球面坐标。

PostGIS 几何数据类型的基础是一个平面。平面上两点之间的最短路径是直线。这意味着使用直线向量和笛卡尔数学计算几何图形上的函数(面积、距离、长度、交点等)。这使得它们更易于实现和更快地执行,但也使它们对于地球椭球表面的数据不准确。

PostGIS 地理数据类型基于球面模型。球体上两点之间的最短路径是大圆弧。使用球体上的弧线计算地理函数(面积、距离、长度、交点等)。通过考虑世界的椭球形状,这些函数提供了更准确的结果。

由于底层数学更复杂,因此为地理类型定义的函数比为几何类型定义的函数少。随着时间的推移,随着新算法的添加,地理类型的功能将会扩展。作为一种解决方法,可以在几何类型和地理类型之间来回转换。

与几何数据类型一样,地理数据通过空间参考系统标识符 (SRID) 与空间参考系统相关联。spatial_ref_sys 表中定义的任何大地(基于经度/纬度)空间参考系统都可以使用。(在 PostGIS 2.2 之前,地理类型仅支持 WGS 84 大地 (SRID:4326))。您可以按照 第 4.5.2 节,“用户定义的空间参考系统”中的说明添加自己的自定义大地空间参考系统。

对于所有空间参考系统,测量函数(例如,ST_DistanceST_LengthST_PerimeterST_Area)返回的单位以及 ST_DWithin 的距离参数的单位均为米。

4.3.1. 创建地理表

您可以使用 CREATE TABLE SQL 语句和 geography 类型的列来创建一个表以存储地理数据。以下示例创建一个带有地理列的表,该列在 WGS84 大地坐标系 (SRID 4326) 中存储 2D LineString

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

地理类型支持两个可选的类型修饰符

  • 空间类型修饰符限制列中允许的形状和维度类型。空间类型允许的值为:POINT、LINESTRING、POLYGON、MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION。地理类型不支持曲线、TIN 或 POLYHEDRALSURFACE。该修饰符通过添加后缀来支持坐标维度限制:Z、M 和 ZM。例如,修饰符“LINESTRINGM”仅允许具有三个维度的线串,并将第三个维度视为度量。同样,“POINTZM”需要四维 (XYZM) 数据。

  • SRID 修饰符将空间参考系统 SRID 限制为特定数字。如果省略,则 SRID 默认为 4326 (WGS84 大地),并且所有计算都使用 WGS84 执行。

使用地理列创建表的示例

  • 创建带有默认 SRID 4326(WGS84 经度/纬度)的 2D POINT 地理表

    CREATE TABLE ptgeogwgs(gid serial PRIMARY KEY, geog geography(POINT) );
  • 创建在 NAD83 经纬度坐标系中具有 2D POINT 地理的表

    CREATE TABLE ptgeognad83(gid serial PRIMARY KEY, geog geography(POINT,4269) );
  • 创建具有 3D (XYZ) POINT 和显式 SRID 4326 的表

    CREATE TABLE ptzgeogwgs84(gid serial PRIMARY KEY, geog geography(POINTZ,4326) );
  • 创建带有默认 SRID 4326 的 2D LINESTRING 地理表

    CREATE TABLE lgeog(gid serial PRIMARY KEY, geog geography(LINESTRING) );
  • 使用 SRID 4267 (NAD 1927 经度纬度) 创建 2D POLYGON 地理表

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

地理字段在 geography_columns 系统视图中注册。您可以查询 geography_columns 视图,并查看该表是否已列出

SELECT * FROM geography_columns;

创建空间索引的方式与几何列相同。PostGIS 会注意到列的类型是 GEOGRAPHY,并创建一个合适的基于球体的索引,而不是用于 GEOMETRY 的常用平面索引。

-- Index the test table with a spherical index
CREATE INDEX global_points_gix ON global_points USING GIST ( location );

4.3.2. 使用地理表

您可以像插入几何数据一样插入地理表中的数据。如果几何数据具有 SRID 4326,它将自动转换为地理类型。 EWKT 和 EWKB 格式也可用于指定地理值。

-- Add some data into the test table
INSERT INTO global_points (name, location) VALUES ('Town', 'SRID=4326;POINT(-110 30)');
INSERT INTO global_points (name, location) VALUES ('Forest', 'SRID=4326;POINT(-109 29)');
INSERT INTO global_points (name, location) VALUES ('London', 'SRID=4326;POINT(0 49)');

spatial_ref_sys 表中列出的任何测地(经度/纬度)空间参考系统都可以指定为地理 SRID。如果使用非测地坐标系,则会引发错误。

-- NAD 83 lon/lat
SELECT 'SRID=4269;POINT(-123 34)'::geography;
                    geography
----------------------------------------------------
 0101000020AD1000000000000000C05EC00000000000004140
-- NAD27 lon/lat
SELECT 'SRID=4267;POINT(-123 34)'::geography;
                    geography
----------------------------------------------------
 0101000020AB1000000000000000C05EC00000000000004140
-- NAD83 UTM zone meters - gives an error since it is a meter-based planar projection
SELECT 'SRID=26910;POINT(-123 34)'::geography;

ERROR:  Only lon/lat coordinate systems are supported in geography.

查询和测量函数使用米作为单位。因此,距离参数应以米为单位表示,返回值应以米(或面积为平方米)为单位。

-- A distance query using a 1000km tolerance
SELECT name FROM global_points WHERE ST_DWithin(location, 'SRID=4326;POINT(-110 29)'::geography, 1000000);

您可以计算从西雅图到伦敦的大圆航线上的飞机(LINESTRING(-122.33 47.606, 0.0 51.5))与雷克雅未克(POINT(-21.96 64.15))之间的距离,从而了解地理类型的强大功能(绘制航线)。

地理类型计算出雷克雅未克与西雅图和伦敦之间的大圆航线在球体上的真实最短距离,为 122.235 公里。

-- Distance calculation using GEOGRAPHY
SELECT ST_Distance('LINESTRING(-122.33 47.606, 0.0 51.5)'::geography, 'POINT(-21.96 64.15)'::geography);
   st_distance
-----------------
 122235.23815667

几何类型计算出雷克雅未克与从西雅图到伦敦的直线路径在平面地图上绘制的无意义的笛卡尔距离。结果的标称单位是“度”,但结果不对应于点之间的任何真实角度差,因此即使称其为“度”也是不准确的。

-- Distance calculation using GEOMETRY
SELECT ST_Distance('LINESTRING(-122.33 47.606, 0.0 51.5)'::geometry, 'POINT(-21.96 64.15)'::geometry);
      st_distance
--------------------
 13.342271221453624

4.3.3. 何时使用地理数据类型

地理数据类型允许您以经度/纬度坐标存储数据,但代价是:GEOGRAPHY 上定义的功能比 GEOMETRY 上的少;那些已定义的功能需要更多的 CPU 时间来执行。

您选择的数据类型应由您正在构建的应用程序的预期工作区域决定。您的数据是跨越全球或大型大陆区域,还是限于州、县或市?

  • 如果您的数据包含在一个小区域内,您可能会发现选择合适的投影并使用 GEOMETRY 是最佳解决方案,在性能和可用功能方面都是如此。

  • 如果您的数据是全球性的或覆盖大陆区域,您可能会发现 GEOGRAPHY 允许您构建一个系统,而无需担心投影细节。您可以以经度/纬度存储数据,并使用 GEOGRAPHY 上定义的功能。

  • 如果您不了解投影,并且不想了解它们,并且您准备接受 GEOGRAPHY 中可用功能的限制,那么使用 GEOGRAPHY 可能比使用 GEOMETRY 更容易。只需将您的数据加载为经度/纬度即可。

请参阅 第 13.11 节,“PostGIS 函数支持矩阵”,了解 Geography 和 Geometry 支持的功能之间的比较。有关地理函数的简要列表和说明,请参阅 第 13.4 节,“PostGIS 地理支持函数”

4.3.4. 地理高级常见问题

4.3.4.1.

您是在球体上还是在椭球体上进行计算?

默认情况下,所有距离和面积计算都在椭球体上完成。您应该发现局部区域中的计算结果与良好局部投影中的局部平面结果一致。在更大的区域上,椭球计算将比在投影平面上进行的任何计算更准确。

通过将最终布尔参数设置为“FALSE”,所有地理函数都可以选择使用球体计算。这将加快计算速度,特别是对于几何图形非常简单的情况。

4.3.4.2.

日界线和极点呢?

所有计算都没有日界线或极点的概念,坐标是球形的(经度/纬度),因此从计算的角度来看,跨越日界线的形状与任何其他形状没有区别。

4.3.4.3.

您可以处理的最长弧线是多少?

我们使用大圆弧作为两点之间的“插值线”。这意味着任何两个点实际上都以两种方式连接,具体取决于您沿大圆移动的方向。我们所有的代码都假定这些点通过沿大圆的两个路径中*较短的*路径连接。因此,弧线超过 180 度的形状将无法正确建模。

4.3.4.4.

为什么计算欧洲/俄罗斯/在此处插入大地理区域的面积如此缓慢?

因为多边形太大了!大面积是坏的,原因有两个:它们的边界很大,因此无论您运行什么查询,索引都倾向于提取该要素;顶点的数量巨大,并且测试(距离、包含)必须至少遍历顶点列表一次,有时 N 次(N 是另一个候选要素中的顶点数)。

与 GEOMETRY 一样,我们建议当您拥有非常大的多边形,但在小区域中进行查询时,将您的几何数据“反规范化”为较小的块,以便索引可以有效地子查询对象的部分,这样查询就不必每次都提取整个对象。请参阅 ST_Subdivide 函数文档。仅仅因为您*可以*将整个欧洲存储在一个多边形中并不意味着您*应该*这样做。

4.4. 几何验证

PostGIS 符合开放地理空间联盟 (OGC) 的简单要素规范。该标准定义了几何图形简单有效的概念。这些定义允许简单要素几何模型以一致且明确的方式表示空间对象,从而支持高效计算。(注意:OGC SF 和 SQL/MM 对简单和有效的定义相同。)

4.4.1. 简单几何

简单几何是不具有异常几何点的几何图形,例如自相交或自相切。

作为 0 维几何对象,POINT 本质上是简单的。

如果不存在两个坐标(POINT)相等(具有相同的坐标值),则 MULTIPOINT简单的。

如果 LINESTRING 不两次通过同一点(端点除外),则它是简单的。如果简单 LineString 的端点相同,则称其为闭合,并称为线性环。

(a)(c) 是简单的 LINESTRING(b)(d) 不是简单的。 (c) 是一个闭合的线性环。

(a)

(b)

(c)

(d)

仅当 MULTILINESTRING 的所有元素都是简单的,并且任意两个元素之间的唯一交点发生在两个元素的边界上的点时,该 MULTILINESTRING 才是简单的。

(e)(f) 是简单的 MULTILINESTRING(g) 不是简单的。

(e)

(f)

(g)

POLYGON 由线性环组成,因此有效的多边形几何始终是简单的。

要测试几何是否简单,请使用 ST_IsSimple 函数

SELECT
   ST_IsSimple('LINESTRING(0 0, 100 100)') AS straight,
   ST_IsSimple('LINESTRING(0 0, 100 100, 100 0, 0 100)') AS crossing;

 straight | crossing
----------+----------
 t        | f

通常,PostGIS 函数不要求几何参数是简单的。简单性主要用作定义几何有效性的基础。它也是某些类型的空间数据模型的要求(例如,线性网络通常不允许相交的线)。可以使用 ST_UnaryUnion 使多点和线性几何简单化。

4.4.2. 有效几何

几何有效性主要适用于二维几何(POLYGONMULTIPOLYGON)。有效性由允许多边形几何明确地建模平面区域的规则定义。

如果满足以下条件,则 POLYGON有效

  1. 多边形边界环(外部外壳环和内部孔环)是简单的(不交叉或自接触)。因此,多边形不能有切割线、尖刺或环路。这意味着多边形孔必须表示为内部环,而不是通过外部环自接触(所谓的“反向孔”)。

  2. 边界环不交叉

  3. 边界环可能会在点处接触,但只能作为切线(即,而不是在一条线上)

  4. 内部环包含在外部环中

  5. 多边形内部是简单连接的(即,环的接触方式不能将多边形分割成多个部分)

(h)(i) 是有效的 POLYGON(j-m) 是无效的。 (j) 可以表示为有效的 MULTIPOLYGON

(h)

(i)

(j)

(k)

(l)

(m)

如果满足以下条件,则 MULTIPOLYGON有效

  1. 其元素 POLYGON 是有效的

  2. 元素不重叠(即,它们的内部不得相交)

  3. 元素仅在点处接触(即,不沿着一条线)

(n) 是有效的 MULTIPOLYGON(o)(p) 是无效的。

(n)

(o)

(p)

这些规则意味着有效的多边形几何也是简单的。

对于线性几何,唯一的有效性规则是 LINESTRING 必须至少有两个点并且具有非零长度(或等效地,至少有两个不同的点)。请注意,非简单(自相交)线是有效的。

SELECT
   ST_IsValid('LINESTRING(0 0, 1 1)') AS len_nonzero,
   ST_IsValid('LINESTRING(0 0, 0 0, 0 0)') AS len_zero,
   ST_IsValid('LINESTRING(10 10, 150 150, 180 50, 20 130)') AS self_int;

 len_nonzero | len_zero | self_int
-------------+----------+----------
 t           | f        | t

POINTMULTIPOINT 几何没有有效性规则。

4.4.3. 管理有效性

PostGIS 允许创建和存储有效和无效的几何图形。这允许检测、标记或修复无效几何图形。在某些情况下,OGC 有效性规则比预期的更严格(例如,零长度线串和具有反向孔的多边形)。

PostGIS 提供的许多函数都依赖于几何参数有效的假设。例如,计算在多边形外部定义了孔的多边形的面积或从非简单边界线构造多边形是没有意义的。假设有效的几何输入允许函数更高效地运行,因为它们不需要检查拓扑正确性。(值得注意的例外是,通常可以正确处理零长度线和具有反转的多边形。)此外,如果输入有效,则大多数 PostGIS 函数都会生成有效的几何输出。这允许将 PostGIS 函数安全地链接在一起。

如果在调用 PostGIS 函数时遇到意外的错误消息(例如“GEOS Intersection() 抛出错误!”),您应该首先确认函数参数是否有效。如果无效,请考虑使用以下技术之一来确保您正在处理的数据有效。

[Note]

如果函数报告输入有效时出现错误,那么您可能在 PostGIS 或其使用的库之一中发现了错误,您应该向 PostGIS 项目报告此错误。如果 PostGIS 函数为有效输入返回无效几何,则也是如此。

要测试几何图形是否有效,请使用 ST_IsValid 函数。

SELECT ST_IsValid('POLYGON ((20 180, 180 180, 180 20, 20 20, 20 180))');
-----------------
 t

有关几何图形无效的性质和位置的信息,请使用 ST_IsValidDetail 函数获取。

SELECT valid, reason, ST_AsText(location) AS location
    FROM ST_IsValidDetail('POLYGON ((20 20, 120 190, 50 190, 170 50, 20 20))') AS t;

 valid |      reason       |                  location
-------+-------------------+---------------------------------------------
 f     | Self-intersection | POINT(91.51162790697674 141.56976744186045)

在某些情况下,需要自动更正无效的几何图形。请使用 ST_MakeValid 函数来实现。(ST_MakeValid 是一个允许无效输入的空间函数!)

默认情况下,PostGIS 在加载几何图形时不会检查其有效性,因为对于复杂的几何图形,有效性测试会占用大量的 CPU 时间。如果您不信任您的数据源,可以通过添加检查约束来强制对表进行有效性检查。

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

4.5. 空间参考系统

空间参考系统 (SRS)(也称为坐标参考系统 (CRS))定义了如何将几何图形引用到地球表面的位置。SRS 有三种类型:

  • 一个大地 SRS 使用角度坐标(经度和纬度),直接映射到地球表面。

  • 一个投影 SRS 使用数学投影变换将球形地球的表面“展平”到一个平面上。它以允许直接测量诸如距离、面积和角度等量的方式分配位置坐标。坐标系是笛卡尔坐标系,这意味着它有一个定义的原点和两个垂直轴(通常朝向北方和东方)。每个投影 SRS 使用声明的长度单位(通常是米或英尺)。为了避免失真并保持在定义的坐标范围内,投影 SRS 的适用区域可能会受到限制。

  • 一个局部 SRS 是一个未参考到地球表面的笛卡尔坐标系。在 PostGIS 中,这由 SRID 值 0 指定。

有许多不同的空间参考系统在使用。常见的 SRS 在欧洲石油勘测集团的 EPSG 数据库中进行了标准化。为了方便起见,PostGIS(和许多其他空间系统)使用一个名为 SRID 的整数标识符来引用 SRS 定义。

几何图形通过其 SRID 值与空间参考系统相关联,该值可以通过 ST_SRID 访问。可以使用 ST_SetSRID 为几何图形分配 SRID。一些几何图形构造函数允许提供 SRID(例如 ST_PointST_MakeEnvelope)。EWKT 格式支持带有 SRID=n; 前缀的 SRID。

处理成对几何图形的空间函数(例如叠加关系 函数)要求输入几何图形位于同一空间参考系统中(具有相同的 SRID)。可以使用 ST_TransformST_TransformPipeline 将几何图形数据转换为不同的空间参考系统。从函数返回的几何图形具有与输入几何图形相同的 SRS。

4.5.1. SPATIAL_REF_SYS 表

PostGIS 使用的 SPATIAL_REF_SYS 表是一个符合 OGC 标准的数据库表,用于定义可用的空间参考系统。它保存了坐标系统的数字 SRID 和文本描述。

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

各列如下:

srid

一个整数代码,用于唯一标识数据库中的空间参考系统 (SRS)。

auth_name

为该参考系统引用的标准或标准机构的名称。例如,“EPSG”是一个有效的 auth_name

auth_srid

auth_name 中引用的机构定义的空间参考系统的 ID。对于 EPSG,这是 EPSG 代码。

srtext

空间参考系统的 Well-Known Text 表示形式。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]
]

有关 SRS WKT 的讨论,请参阅 OGC 标准 坐标参考系统的 Well-known text 表示形式

proj4text

PostGIS 使用 PROJ 库来提供坐标转换功能。proj4text 列包含特定 SRID 的 PROJ 坐标定义字符串。例如:

+proj=utm +zone=10 +ellps=clrk66 +datum=NAD27 +units=m

有关更多信息,请参阅 PROJ 网站spatial_ref_sys.sql 文件包含所有 EPSG 投影的 srtextproj4text 定义。

当检索用于转换的空间参考系统定义时,PostGIS 使用以下策略:

  • 如果存在(非 NULL)auth_nameauth_srid,则使用基于这些条目的 PROJ SRS(如果存在)。

  • 如果存在 srtext,则尽可能使用它创建 SRS。

  • 如果存在 proj4text,则尽可能使用它创建 SRS。

4.5.2. 用户定义的空间参考系统

PostGIS spatial_ref_sys 表包含由 PROJ 投影库处理的 3000 多个最常见的空间参考系统定义。但是,它不包含许多坐标系统。如果您拥有关于空间参考系统的必要信息,则可以将 SRS 定义添加到表中。或者,如果您熟悉 PROJ 结构,则可以定义自己的自定义空间参考系统。请记住,大多数空间参考系统是区域性的,并且在它们预期的边界之外使用时没有意义。

查找核心集中未定义的空间参考系统的资源是 http://spatialreference.org/

一些常用的空间参考系统包括:4326 - WGS 84 经纬度4269 - NAD 83 经纬度3395 - WGS 84 世界墨卡托2163 - 美国国家地图集等面积,以及 60 个 WGS84 UTM 区域。UTM 区域是最适合测量的区域之一,但仅覆盖 6 度区域。(要确定您感兴趣的区域使用哪个 UTM 区域,请参阅 utmzone PostGIS plpgsql 助手函数。)

美国各州使用州平面空间参考系统(基于米或英尺)- 每个州通常存在一个或两个。大多数基于米的参考系统都位于核心集中,但许多基于英尺的或 ESRI 创建的参考系统需要从 spatialreference.org 复制。

您甚至可以定义非基于地球的坐标系,例如 Mars 2000。这个火星坐标系是非平面的(它是球形的,以度为单位),但您可以将其与 geography 类型一起使用,以获得以米为单位而不是以度为单位的长度和邻近度测量值。

以下示例演示如何使用未分配的 SRID 和美国中心兰伯特等角投影的 PROJ 定义加载自定义坐标系统:

INSERT INTO spatial_ref_sys (srid, proj4text)
VALUES ( 990000,
  '+proj=lcc  +lon_0=-95 +lat_0=25 +lat_1=25 +lat_2=25 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'
);

4.6. 空间表

4.6.1. 创建空间表

您可以使用带有类型为 geometry 的列的 CREATE TABLE SQL 语句来创建用于存储几何图形数据的表。以下示例创建了一个表,该表带有一个几何图形列,用于存储 BC-Albers 坐标系(SRID 3005)中的 2D (XY) LineString:

CREATE TABLE roads (
    id SERIAL PRIMARY KEY,
    name VARCHAR(64),
    geom geometry(LINESTRING,3005)
  );

geometry 类型支持两个可选的类型修饰符

  • 空间类型修饰符限制列中允许的形状和维度类型。该值可以是任何受支持的几何子类型(例如,POINT、LINESTRING、POLYGON、MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION 等)。该修饰符通过添加后缀来支持坐标维度限制:Z、M 和 ZM。例如,修饰符 'LINESTRINGM' 仅允许具有三个维度的线串,并将第三个维度视为度量。类似地,'POINTZM' 要求四维 (XYZM) 数据。

  • SRID 修饰符空间参考系统 SRID 限制为特定数字。如果省略,SRID 默认为 0。

创建带有几何图形列的表的示例:

  • 创建一个表,其中包含具有默认 SRID 的任何类型的几何图形:

    CREATE TABLE geoms(gid serial PRIMARY KEY, geom geometry );
  • 创建一个表,其中包含具有默认 SRID 的 2D POINT 几何图形:

    CREATE TABLE pts(gid serial PRIMARY KEY, geom geometry(POINT) );
  • 创建一个表,其中包含具有显式 SRID 3005 的 3D (XYZ) POINT:

    CREATE TABLE pts(gid serial PRIMARY KEY, geom geometry(POINTZ,3005) );
  • 创建一个表,其中包含具有默认 SRID 的 4D (XYZM) LINESTRING 几何图形:

    CREATE TABLE lines(gid serial PRIMARY KEY, geom geometry(LINESTRINGZM) );
  • 创建一个表,其中包含具有 SRID 4267(NAD 1927 经纬度)的 2D POLYGON 几何图形:

    CREATE TABLE polys(gid serial PRIMARY KEY, geom geometry(POLYGON,4267) );

一个表中可以有多个几何图形列。可以在创建表时指定,也可以使用 ALTER TABLE SQL 语句添加列。此示例添加一个可以容纳 3D LineString 的列:

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

4.6.2. GEOMETRY_COLUMNS 视图

OGC SQL 简单要素规范定义了 GEOMETRY_COLUMNS 元数据表来描述几何图形表的结构。在 PostGIS 中,geometry_columns 是一个从数据库系统目录表中读取的视图。这确保空间元数据信息始终与当前定义的表和视图保持一致。视图结构如下:

\d geometry_columns
             View "public.geometry_columns"
      Column       |          Type          | Modifiers
-------------------+------------------------+-----------
 f_table_catalog   | character varying(256) |
 f_table_schema    | character varying(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

包含几何图形列的要素表的完整限定名称。PostgreSQL 没有“catalog”的类似物,因此该列为空白。对于“schema”,使用 PostgreSQL 架构名称(public 是默认值)。

f_geometry_column

要素表中几何图形列的名称。

coord_dimension

列的坐标维度(2、3 或 4)。

srid

此表中坐标几何所使用的空间参考系统的 ID。它是对 spatial_ref_sys 表的外键引用(请参阅第 4.5.1 节“SPATIAL_REF_SYS 表”)。

类型

空间对象的类型。要将空间列限制为单一类型,请使用以下类型之一:POINT、LINESTRING、POLYGON、MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION 或对应的 XYM 版本 POINTM、LINESTRINGM、POLYGONM、MULTIPOINTM、MULTILINESTRINGM、MULTIPOLYGONM、GEOMETRYCOLLECTIONM。对于异构(混合类型)集合,可以使用“GEOMETRY”作为类型。

4.6.3. 手动注册几何列

您可能需要手动注册几何列的两种情况是 SQL 视图和批量插入的情况。对于批量插入的情况,您可以通过约束列或执行 alter table 来更正 geometry_columns 表中的注册。对于视图,您可以使用 CAST 操作来公开。请注意,如果您的列是基于 typmod 的,则创建过程会正确注册它,因此无需执行任何操作。此外,未对几何应用任何空间函数的视图将与基础表几何列的注册相同。

-- Lets say you have a view created like this
CREATE VIEW public.vwmytablemercator AS
	SELECT gid, ST_Transform(geom, 3395) As geom, f_name
	FROM public.mytable;

-- For it to register correctly
-- You need to cast the geometry
--
DROP VIEW public.vwmytablemercator;
CREATE VIEW  public.vwmytablemercator AS
	SELECT gid, ST_Transform(geom, 3395)::geometry(Geometry, 3395) As geom, f_name
	FROM public.mytable;

-- If you know the geometry type for sure is a 2D POLYGON then you could do
DROP VIEW public.vwmytablemercator;
CREATE VIEW  public.vwmytablemercator AS
	SELECT gid, ST_Transform(geom,3395)::geometry(Polygon, 3395) As geom, f_name
	FROM public.mytable;
--Lets say you created a derivative table by doing a bulk insert
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 2D index on new table
CREATE INDEX idx_myschema_myspecialpois_geom_gist
  ON myschema.my_special_pois USING gist(geom);

-- If your points are 3D points or 3M points,
-- then you might want to create an nd index instead of a 2D index
CREATE INDEX my_special_pois_geom_gist_nd
	ON my_special_pois USING gist(geom gist_geometry_ops_nd);

-- To manually register this new table's geometry column in geometry_columns.
-- Note it will also change the underlying structure of the table to
-- to make the column typmod based.
SELECT populate_geometry_columns('myschema.my_special_pois'::regclass);

-- If you are using PostGIS 2.0 and for whatever reason, you
-- you need the constraint based definition behavior
-- (such as case of inherited tables where all children do not have the same type and srid)
-- set optional use_typmod argument to false
SELECT populate_geometry_columns('myschema.my_special_pois'::regclass, false); 

尽管仍然支持旧的基于约束的方法,但直接在视图中使用的基于约束的几何列不会在 geometry_columns 中正确注册,typmod 的列也不会。在此示例中,我们使用 typmod 定义一个列,并使用约束定义另一个列。

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

如果我们运行 psql

\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, type
	FROM geometry_columns
	WHERE 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, type
	FROM geometry_columns
	WHERE f_table_name = 'vw_pois_ny_parks';

基于 typmod 的 geom 视图列正确注册,但基于约束的列没有。

   f_table_name   | f_geometry_column | srid |   type
------------------+-------------------+------+----------
 vw_pois_ny_parks | geom              | 4326 | POINT
 vw_pois_ny_parks | geom_2160         |    0 | GEOMETRY

这在 PostGIS 的未来版本中可能会发生变化,但现在要强制基于约束的视图列正确注册,您需要执行以下操作

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, type
	FROM geometry_columns
	WHERE 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.7. 加载空间数据

创建空间表后,就可以将空间数据上传到数据库了。有两种内置方法可以将空间数据导入到 PostGIS/PostgreSQL 数据库中:使用格式化的 SQL 语句或使用 Shapefile 加载器。

4.7.1. 使用 SQL 加载数据

如果可以将空间数据转换为文本表示形式(WKT 或 WKB),那么使用 SQL 可能是将数据导入 PostGIS 的最简单方法。可以使用 psql SQL 实用程序加载包含 SQL INSERT 语句的文本文件,从而将数据批量加载到 PostGIS/PostgreSQL 中。

SQL 加载文件(例如 roads.sql)可能如下所示

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

可以使用 psql 将 SQL 文件加载到 PostgreSQL 中

psql -d [database] -f roads.sql

4.7.2. 使用 Shapefile 加载器

shp2pgsql 数据加载器将 Shapefile 转换为 SQL,适合以几何或地理格式插入到 PostGIS/PostgreSQL 数据库中。加载器具有通过命令行标志选择的多种操作模式。

还有一个 shp2pgsql-gui 图形界面,它具有与命令行加载器相同的多数选项。对于一次性非脚本加载或如果您是 PostGIS 新手,这可能更容易使用。它还可以配置为 PgAdminIII 的插件。

(c|a|d|p) 这些是互斥的选项

-c

创建一个新表并从 Shapefile 中填充它。 这是默认模式。

-a

将 Shapefile 中的数据追加到数据库表中。请注意,要使用此选项加载多个文件,这些文件必须具有相同的属性和相同的数据类型。

-d

在创建具有 Shapefile 中数据的新表之前,删除数据库表。

-p

仅生成表创建 SQL 代码,而不添加任何实际数据。如果需要完全分离表创建和数据加载步骤,可以使用此选项。

-?

显示帮助屏幕。

-D

对输出数据使用 PostgreSQL“dump”格式。它可以与 -a、-c 和 -d 结合使用。它的加载速度比默认的“insert”SQL 格式快得多。对于非常大的数据集,请使用此选项。

-s [<FROM_SRID>:]<SRID>

使用指定的 SRID 创建并填充几何表。可以选择指定输入 shapefile 使用给定的 FROM_SRID,在这种情况下,几何将重新投影到目标 SRID。

-k

保留标识符的大小写(列、架构和属性)。请注意,Shapefile 中的属性全部为大写。

-i

强制所有整数为标准的 32 位整数,即使 DBF 标头签名似乎保证,也不创建 64 位 bigint。

-I

在几何列上创建 GiST 索引。

-m

-m a_file_name 指定一个文件,其中包含一组将(长)列名映射到 10 个字符的 DBF 列名的映射。该文件的内容是一行或多行,其中包含两个由空格分隔且没有尾随或前导空格的名称。例如

COLUMNNAME DBFFIELD1
AVERYLONGCOLUMNNAME DBFFIELD2

-S

生成简单几何而不是 MULTI 几何。仅当所有几何实际上都是单个几何(例如,具有单个外壳的 MULTIPOLYGON,或者具有单个顶点的 MULTIPOINT)时,才会成功。

-t <维度>

强制输出几何具有指定的维度。使用以下字符串来指示维度:2D、3DZ、3DM、4D。

如果输入的维度小于指定的维度,则输出将使用零填充这些维度。如果输入的维度大于指定的维度,则将剥离不需要的维度。

-w

输出 WKT 格式,而不是 WKB 格式。请注意,这可能会由于精度损失而导致坐标漂移。

-e

单独执行每个语句,而不使用事务。这样可以在存在一些生成错误的错误几何的情况下加载大多数好数据。请注意,这不能与 -D 标志一起使用,因为“dump”格式始终使用事务。

-W <编码>

指定输入数据(dbf 文件)的编码。使用时,dbf 的所有属性都会从指定的编码转换为 UTF8。生成的 SQL 输出将包含一个 SET CLIENT_ENCODING to UTF8 命令,以便后端能够从 UTF8 重新转换为数据库配置为在内部使用的任何编码。

-N <策略>

NULL 几何处理策略(insert*、skip、abort)

-n

-n 仅导入 DBF 文件。如果您的数据没有相应的 shapefile,它将自动切换到此模式并仅加载 dbf。因此,仅当您有完整的 shapefile 集,并且只想要属性数据而不需要几何时,才需要设置此标志。

-G

使用地理类型而不是几何类型(需要在 WGS84 经纬度中输入经度/纬度数据)(SRID=4326)

-T <表空间>

指定新表的表空间。除非还使用了 -X 参数,否则索引仍将使用默认表空间。PostgreSQL 文档很好地描述了何时使用自定义表空间。

-X <表空间>

指定新表的索引的表空间。这适用于主键索引和 GIST 空间索引(如果还使用了 -I)。

-Z

使用时,此标志将阻止生成 ANALYZE 语句。如果没有 -Z 标志(默认行为),则将生成 ANALYZE 语句。

使用加载程序创建输入文件并加载的会话示例可能如下所示

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

可以使用 UNIX 管道在一个步骤中完成转换和加载

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

4.8. 提取空间数据

可以使用 SQL 或 Shapefile 转储程序从数据库中提取空间数据。SQL 部分介绍了一些可用于对空间表进行比较和查询的函数。

4.8.1. 使用 SQL 提取数据

从数据库中提取空间数据最直接的方法是使用 SQL SELECT 查询来定义要提取的数据集,并将生成的列转储到可解析的文本文件中

db=# 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 命令行上指定几何和框时,必须显式地将字符串表示形式转换为几何函数。312 是一个虚构的空间参考系统,与我们的数据匹配。所以,例如

SELECT road_id, road_name
  FROM roads
  WHERE roads_geom='SRID=312;LINESTRING(191232 243118,191108 243242)'::geometry;

上面的查询将返回“ROADS_GEOM”表中几何等于该值的单个记录。

要检查某些道路是否在多边形定义的区域内通过

SELECT road_id, road_name
FROM roads
WHERE ST_Intersects(roads_geom, 'SRID=312;POLYGON((...))');

最常见的空间查询可能是“基于框架”的查询,由客户端软件(如数据浏览器和 Web 地图)使用,以获取用于显示的“地图框架”数据。

使用“&&”运算符时,可以指定 BOX3D 作为比较特征或 GEOMETRY。但是,当指定 GEOMETRY 时,将使用其边界框进行比较。

使用“BOX3D”对象作为框架,此类查询如下所示

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

请注意,使用了 SRID 312 来指定信封的投影。

4.8.2. 使用 Shapefile 转储程序

pgsql2shp 表转储程序连接到数据库,并将表(可能由查询定义)转换为 shapefile。基本语法为

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

命令行选项为

-f <文件名>

将输出写入特定文件名。

-h <主机>

要连接的数据库主机。

-p <端口>

要连接的数据库主机上的端口。

-P <密码>

连接到数据库时使用的密码。

-u <用户>

连接数据库时使用的用户名。

-g <几何列>

在具有多个几何列的表中,写入 shape 文件时使用的几何列。

-b

使用二进制游标。这将使操作更快,但如果表中任何非几何属性缺少强制转换为文本,则将不起作用。

-r

原始模式。不要删除 gid 字段,或转义列名。

-m 文件名

将标识符重映射为十个字符的名称。文件的内容是两行符号,用单个空格分隔,并且没有前导或尾随空格:VERYLONGSYMBOL SHORTONE ANOTHERVERYLONGSYMBOL SHORTER 等。

4.9. 空间索引

空间索引使大型数据集的空间数据库成为可能。如果没有索引,对要素的搜索需要对数据库中的每个记录进行顺序扫描。索引通过将数据组织成可以快速遍历以查找匹配记录的结构来加速搜索。

通常用于属性数据的 B 树索引方法对于空间数据不是很有效,因为它仅支持存储和查询单维数据。诸如几何(具有 2 个或更多维度)之类的数据需要支持跨所有数据维度进行范围查询的索引方法。PostgreSQL 用于空间数据处理的关键优势之一是,它提供了多种非常适合多维数据的索引方法:GiST、BRIN 和 SP-GiST 索引。

  • GiST(广义搜索树) 索引将数据分解为“一边的事物”、“重叠的事物”、“内部的事物”,并且可以用于各种数据类型,包括 GIS 数据。PostGIS 使用在 GiST 之上实现的 R 树索引来索引空间数据。GiST 是最常用且用途最广泛的空间索引方法,可提供非常好的查询性能。

  • BRIN(块范围索引)索引通过总结表记录范围的空间范围来操作。搜索是通过扫描范围来完成的。BRIN 仅适用于某些类型的数据(空间排序、不频繁更新或不更新)。但是,它提供更快的索引创建时间和更小的索引大小。

  • SP-GiST(空间分区广义搜索树) 是一种通用索引方法,支持分区搜索树,如四叉树、k-d 树和基数树(尝试)。

空间索引仅存储几何体的边界框。空间查询使用索引作为主过滤器,以快速确定可能匹配查询条件的一组几何体。大多数空间查询都需要使用空间谓词函数来测试更具体的空间条件的辅助过滤器。有关使用空间谓词进行查询的更多信息,请参见 第 5.2 节,“使用空间索引”

另请参见PostGIS 工作坊关于空间索引的部分,以及PostgreSQL 手册

4.9.1. GiST 索引

GiST 代表“广义搜索树”,它是多维数据的一种通用索引形式。PostGIS 使用在 GiST 之上实现的 R 树索引来索引空间数据。GiST 是最常用且用途最广泛的空间索引方法,可提供非常好的查询性能。GiST 的其他实现用于加速搜索各种不规则数据结构(整数数组、光谱数据等),这些数据结构不适合正常的 B 树索引。有关更多信息,请参见PostgreSQL 手册

一旦空间数据表超过几千行,您将需要构建索引以加速对数据的空间搜索(除非您的所有搜索都基于属性,在这种情况下,您将需要在属性字段上构建一个普通索引)。

在“geometry”列上构建 GiST 索引的语法如下

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

上述语法将始终构建 2D 索引。要获取几何类型的 n 维索引,您可以使用以下语法创建一个

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

构建空间索引是一项计算密集型操作。它还会阻止在创建时对表的写入访问,因此在生产系统中,您可能希望以较慢的 CONCURRENTLY 感知方式进行此操作

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

构建索引后,有时有助于强制 PostgreSQL 收集表统计信息,这些信息用于优化查询计划

VACUUM ANALYZE [table_name] [(column_name)];

4.9.2. BRIN 索引

BRIN 代表“块范围索引”。它是 PostgreSQL 9.5 中引入的通用索引方法。BRIN 是一种有损索引方法,这意味着需要进行二次检查以确认记录是否与给定的搜索条件匹配(所有提供的空间索引都是这种情况)。它提供更快的索引创建和更小的索引大小,以及合理的读取性能。其主要目的是支持对与表中物理位置相关的列的非常大的表进行索引。除了空间索引外,BRIN 还可以加速对各种属性数据结构(整数、数组等)的搜索。有关更多信息,请参见PostgreSQL 手册

一旦空间表超过几千行,您将需要构建索引以加速对数据的空间搜索。只要 GiST 索引的大小不超过数据库可用的 RAM 大小,并且只要您可以承担索引存储大小以及写入时索引更新的成本,GiST 索引就具有非常高的性能。否则,对于非常大的表,可以将 BRIN 索引视为替代方案。

BRIN 索引存储包含在一组连续的表块(称为块范围)中的所有几何体的边界框。使用索引执行查询时,会扫描块范围以找到与查询范围相交的块范围。仅当数据在物理上排序,使得块范围的边界框具有最小的重叠(并且理想情况下是互斥的)时,这才是有效的。生成的索引尺寸非常小,但对于相同数据的读取性能通常不如 GiST 索引。

构建 BRIN 索引比构建 GiST 索引的 CPU 密集程度低得多。通常发现,构建 BRIN 索引比对相同数据构建 GiST 索引快十倍。而且由于 BRIN 索引仅为每个表块范围存储一个边界框,因此通常使用比 GiST 索引少一千倍的磁盘空间。

您可以选择在范围中汇总的块数。如果减少此数字,索引将更大,但可能会提供更好的性能。

为了使 BRIN 有效,应该以物理顺序存储表数据,以最大程度地减少块范围重叠的数量。数据可能已经以适当的方式排序(例如,如果它是从另一个已按空间顺序排序的数据集加载的)。否则,可以通过按一维空间键对数据进行排序来完成此操作。一种方法是创建一个按几何值排序的新表(在最近的 PostGIS 版本中使用高效的希尔伯特曲线排序)

CREATE TABLE table_sorted AS
   SELECT * FROM table  ORDER BY geom;

或者,可以使用 GeoHash 作为(临时)索引对数据进行原地排序,并在该索引上进行聚类

CREATE INDEX idx_temp_geohash ON table
    USING btree (ST_GeoHash( ST_Transform( geom, 4326 ), 20));
CLUSTER table USING idx_temp_geohash;

geometry 列上构建 BRIN 索引的语法为

CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geome_col] ); 

上面的语法构建一个 2D 索引。要构建 3D 维索引,请使用以下语法

CREATE INDEX [indexname] ON [tablename]
    USING BRIN ([geome_col] brin_geometry_inclusion_ops_3d);

您还可以使用 4D 运算符类获得 4D 维索引

CREATE INDEX [indexname] ON [tablename]
    USING BRIN ([geome_col] brin_geometry_inclusion_ops_4d);

以上命令使用范围中的默认块数,即 128。要指定在范围中汇总的块数,请使用以下语法

CREATE INDEX [indexname] ON [tablename]
    USING BRIN ( [geome_col] ) WITH (pages_per_range = [number]); 

请记住,BRIN 索引仅为大量行存储一个索引条目。如果您的表存储具有混合维数的几何体,则生成的索引很可能性能不佳。您可以通过选择具有存储几何体最少维数的运算符类来避免这种性能损失

geography 数据类型支持 BRIN 索引。在 geography 列上构建 BRIN 索引的语法为

CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geog_col] ); 

上面的语法在椭球体上为地理空间对象构建 2D 索引。

当前,仅提供“包含支持”,这意味着对于 2D 情况(对于 geometrygeography),只能使用 &&~@ 运算符,对于 3D 几何体,只能使用 &&& 运算符。目前不支持 kNN 搜索。

BRIN 和其他索引类型之间的一个重要区别是,数据库不会动态维护索引。对表中空间数据的更改只是附加到索引的末尾。这将导致索引搜索性能随时间推移而降低。可以通过执行 VACUUM 或使用特殊函数 brin_summarize_new_values(regclass) 来更新索引。因此,BRIN 可能最适合用于只读或很少更改的数据。有关更多信息,请参阅手册

使用 BRIN 总结空间数据

  • 索引构建时间非常快,索引大小非常小。

  • 索引查询时间比 GiST 慢,但仍然可以接受。

  • 要求表数据按空间顺序排序。

  • 需要手动维护索引。

  • 最适用于非常大的表,具有低重叠或无重叠(例如,点),这些表是静态的或很少更改。

  • 对于返回相对大量数据记录的查询更有效。

4.9.3. SP-GiST 索引

SP-GiST 代表“空间分割广义搜索树”,它是一种用于多维数据类型的通用索引形式,支持分区搜索树,例如四叉树、k-d 树和基数树(trie)。这些数据结构的共同特点是它们重复地将搜索空间划分为大小不必相等的多个分区。除了空间索引之外,SP-GiST 还用于加速许多类型数据的搜索,例如电话路由、IP 路由、子字符串搜索等。有关更多信息,请参阅PostgreSQL 手册

与 GiST 索引一样,SP-GiST 索引是有损的,因为它存储了包围空间对象的边界框。SP-GiST 索引可以被认为是 GiST 索引的替代方案。

一旦 GIS 数据表超过几千行,就可以使用 SP-GiST 索引来加速数据的空间搜索。在“geometry”列上构建 SP-GiST 索引的语法如下

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

以上语法将构建一个二维索引。可以使用 3D 运算符类为 geometry 类型创建三维索引

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

构建空间索引是一项计算密集型操作。它还会阻止对您的表进行写入访问,直到索引创建完成,因此在生产系统中,您可能希望以较慢的 CONCURRENTLY 方式执行此操作

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

构建索引后,有时有助于强制 PostgreSQL 收集表统计信息,这些信息用于优化查询计划

VACUUM ANALYZE [table_name] [(column_name)];

SP-GiST 索引可以加速涉及以下运算符的查询

  • 对于二维索引,包括 <<、&<、&>、>>、<<|、&<|、|&>、|>>、&&、@>、<@ 和 ~=。

  • 对于三维索引,包括 &/&、~==、@>> 和 <<@。

目前不支持 kNN 搜索。

4.9.4. 调整索引使用

通常,索引会无形地加速数据访问:一旦构建了索引,PostgreSQL 查询规划器会自动决定何时使用它来提高查询性能。但有些情况下,规划器不会选择使用现有索引,因此查询最终会使用缓慢的顺序扫描而不是空间索引。

如果您发现您的空间索引没有被使用,您可以尝试以下几种方法

  • 检查查询计划并确认您的查询实际上计算的是您需要的内容。错误的 JOIN,无论是忘记了还是连接到错误的表,都可能意外地多次检索表记录。要获取查询计划,请在查询前面执行 EXPLAIN

  • 确保收集表中值的数量和分布的统计信息,以便为查询规划器提供更好的信息以做出有关索引使用的决策。VACUUM ANALYZE 将计算这两者。

    无论如何,您都应该定期清理数据库。许多 PostgreSQL DBA 会定期在非高峰时段运行 VACUUM 作为 cron 作业。

  • 如果清理没有帮助,您可以暂时强制规划器使用索引信息,方法是使用命令 SET ENABLE_SEQSCAN TO OFF;。这样您就可以检查规划器是否能够为您的查询生成索引加速的查询计划。您应该仅将此命令用于调试;一般来说,规划器比您更了解何时使用索引。运行查询后,不要忘记运行 SET ENABLE_SEQSCAN TO ON;,以便规划器为其他查询正常运行。

  • 如果 SET ENABLE_SEQSCAN TO OFF; 可以帮助您的查询更快地运行,则您的 Postgres 很可能没有针对您的硬件进行调整。如果您发现规划器在顺序扫描与索引扫描的成本方面存在错误,请尝试减小 postgresql.conf 中的 RANDOM_PAGE_COST 值,或者使用 SET RANDOM_PAGE_COST TO 1.1;RANDOM_PAGE_COST 的默认值为 4.0。尝试将其设置为 1.1(对于 SSD)或 2.0(对于快速磁盘)。降低该值会使规划器更有可能使用索引扫描。

  • 如果 SET ENABLE_SEQSCAN TO OFF; 对您的查询没有帮助,则查询可能正在使用 Postgres 规划器尚未能够优化的 SQL 构造。有可能以规划器能够处理的方式重写查询。例如,带有内联 SELECT 的子查询可能不会产生有效的计划,但可以使用 LATERAL JOIN 重写。

有关更多信息,请参阅 Postgres 手册中关于查询规划的部分。