Chapter 5. 空间查询

Table of Contents
5.1. 确定空间关系
5.1.1. 维度扩展的9交模型
5.1.2. 命名空间关系
5.1.3. 一般空间关系
5.2. 使用空间索引
5.3. 空间SQL示例

这个 《理由》 空间数据库的一个重要功能是在数据库内执行查询,这通常需要桌面地理信息系统功能。有效地使用PostGIS需要了解哪些空间函数可用,如何在查询中使用它们,并确保适当的索引到位以提供良好的性能。

5.1. 确定空间关系

空间关系指示两个几何图形如何相互作用。它们是查询几何图形的基本功能。

5.1.1. 维度扩展的9交模型

根据 OpenGIS针对SQL的简单功能实现规范 比较两个几何图形的基本方法是对两个几何图形的内部、边界和外部的交点进行成对测试,并根据得到的‘交集’矩阵中的条目对两个几何图形之间的关系进行分类。

在点集拓扑理论中,嵌入到2维空间中的几何中的点被分为三个集合:

边界

几何体的边界是下一个较低维度的几何体的集合。为 POINT S,其维度为0,则边界为空集。的边界 LINESTRING 是两个端点。为 POLYGON S,则边界是外环和内环的线条。

内饰

几何图形的内部是指几何图形中不在边界内的点。为 POINT S,内部就是点本身。它的内部是 LINESTRING 是终结点之间的点集。为 POLYGON S,则内部是多边形内部的面积曲面。

外部

几何图形的外部是嵌入几何图形的空间的其余部分;换句话说,不在几何图形内部或边界上的所有点。它是一个二维非闭合曲面。

这个 维度扩展的9交模型 (De-9IM)通过为每个几何图形指定上述集合之间的9个交叉点的尺寸来描述两个几何图形之间的空间关系。相交尺寸可以形式地表示为3x3 交集矩阵

For a geometry g the Interior, Boundary, and Exterior are denoted using the notation I(g), B(g), and E(g). Also, dim(s) denotes the dimension of a set s with the domain of {0,1,2,F}:

  • 0 = > 点

  • 1 = > 线

  • 2 = > 面积

  • F = > 空集

使用此表示法,两个几何图形的交矩阵 一个 B类 是:

 内饰 边界 外部
内饰 Dim(i(A)∩I(B))Dim(I(A)∩B(B))Dim(i(A)∩E(B))
边界 Dim(B(A)∩I(B))Dim(B(A)∩B(B))Dim(B(A)∩E(B))
外部 Dim(E(A)∩I(B))Dim(E(A)∩B(B))Dim(E(A)∩E(B))

从视觉上看,对于两个重叠的多边形几何体,如下所示:

 

 内饰 边界 外部
内饰

Dim(i(A)∩i(B))= 2

Dim(i(A)∩B(B)= 1

Dim(i(A)∩E(B))= 2

边界

Dim(B(A)∩I(B))= 1

Dim(B(A)∩B(B))= 0

Dim(B(A)∩E(B))= 1

外部

Dim(E(A)∩I(B))= 2

Dim(E(A)∩B(B))= 1

Dim(E(A)∩E(B)= 2

从左到右和从上到下阅读,交集矩阵表示为文本字符串‘ 212101212 ‘。

有关详细信息,请参阅:

5.1.2. 命名空间关系

为了便于确定常见的空间关系,OGC SFS定义了一组 命名空间关系谓词 。PostGIS将这些作为功能提供 ST_ContainsST_CrossesST_DisjointST_EqualsST_IntersectsST_OverlapsST_TouchesST_Within 。它还定义了非标准关系谓词 ST_CoversST_CoveredBy ,以及 ST_ContainsProperly

在SQL中,空间谓词通常用作条件 WHEREJOIN 条款。命名空间谓词自动使用空间索引(如果可用),因此不需要使用边界框运算符 && 也是。例如:

SELECT city.name, state.name, city.geom
FROM city JOIN state ON ST_Intersects(city.geom, state.geom);

有关更多详细信息和插图,请参阅 PostGIS专题讨论会。

5.1.3. 一般空间关系

在某些情况下,命名的空间关系不足以提供所需的空间过滤条件。

例如,考虑表示公路网的线性数据集。可能需要识别彼此交叉的所有路段,不是在一点,而是在一条线上(可能是为了验证某些业务规则)。在这种情况下 ST_Crosses 不提供必要的空间过滤器,因为对于线状要素,它会返回 true 只有他们在某一点上交叉的地方。

一个分两步进行的解决方案是首先计算实际交点( ST_Intersection )空间相交的成对道路线( ST_Intersects ),然后检查交叉口是否 ST_GeometryType IS‘ LINESTRING ‘(妥善处理退回的案件 GEOMETRYCOLLECTION S.的. [MULTI]POINT S, [MULTI]LINESTRING S等)。

显然,更简单、更快速的解决方案是可取的。

第二个例子是在一条线上定位与湖泊边界相交的码头,码头的一端在岸上。换句话说,如果码头位于湖泊内,但不完全被湖泊所控制,则码头与湖泊的边界在一条线上相交,并且码头的一个端点恰好在湖泊的边界之内或之上。可以使用空间谓词的组合来查找所需的功能:

这些要求可以通过计算完整的DE-9IM交矩阵来满足。PostGIS提供了 ST_Relate 函数来执行此操作:

SELECT ST_Relate( 'LINESTRING (1 1, 5 5)',
                  'POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))' );
st_relate
-----------
1010F0212

To test a particular spatial relationship, an intersection matrix pattern is used. This is the matrix representation augmented with the additional symbols {T,*}:

  • T => intersection dimension is non-empty; i.e. is in {0,1,2}

  • * = > 不管了

使用交集矩阵模式,可以以更简洁的方式评估特定的空间关系。这个 ST_Relate 以及 ST_RelateMatch 函数可用于测试交集矩阵模式。对于上面的第一个例子,指定在一条线中相交的两条线的交叉点矩阵模式是‘ 1*1*1** ‘:

-- Find road segments that intersect in a line
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 ‘:

-- Find wharves partly on a lake's shoreline
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');

5.2. 使用空间索引

使用空间条件构建查询时,为获得最佳性能,务必确保使用空间索引(如果存在)(请参阅 Section 4.9, “空间索引” )。为此,必须在中使用空间操作符或索引感知函数 WHEREON 查询的子句。

空间运算符包括边界框运算符(其中最常用的是 & & ;请参阅 Section 8.10.1, “边界框运算符” 获取完整列表)和最近邻查询中使用的距离运算符(最常见的是 <-> ;请参阅 Section 8.10.2, “距离运算符” 查看完整的名单。)

索引感知函数会自动向空间条件添加边界框运算符。索引感知函数包括命名的空间关系谓词 ST_ContainsST_ContainsProperlyST_CoveredByST_CoversST_CrossesST_IntersectsST_OverlapsST_TouchesST_WithinST_Within ,以及 ST_3DIntersects ,和距离谓词 ST_DWithinST_DFullyWithinST_3DDFullyWithin ,以及 ST_3DDWithin 。)

功能,如 ST_Distance 使用索引来优化它们的操作。例如,在一个大表上执行以下查询会非常慢:

SELECT geom
FROM geom_table
WHERE ST_Distance( geom, 'SRID=312;POINT(100000 200000)' ) < 100

此查询将选择中的所有几何 geom_table 它们位于点(100000,200000)的100个单位内。它会很慢,因为它正在计算表中每个点与指定点之间的距离,即。一 ST_Distance() 计算是为 每个 表中的行。

通过使用索引感知函数,可以大大减少处理的行数 ST_DWithin

SELECT geom
FROM geom_table
WHERE ST_DWithin( geom, 'SRID=312;POINT(100000 200000)', 100 )

该查询选择相同的几何图形,但它以更高效的方式执行。这可通过以下方式实现 ST_DWithin() 使用 && 查询几何图形的展开边界框上的内部运算符。如果上有空间索引 geom ,查询规划器将认识到它可以在计算距离之前使用索引来减少扫描的行数。空间索引仅允许检索其边界框与展开范围重叠的几何的记录 威力 在要求的距离内。然后计算实际距离以确认是否将该记录包括在结果集中。

有关更多信息和示例,请参阅 PostGIS研讨会

5.3. 空间SQL示例

本节中的示例使用线性道路表格和多边形市政当局边界表格。定义的定义 bc_roads 表如下:

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

定义的定义 bc_municipality 表如下:

Column   | Type              | Description
---------+-------------------+-------------------
gid      | integer           | Unique ID
code     | integer           | Unique ID
name     | character varying | City / Town Name
geom     | geometry          | Location Geometry (Polygon)
5.3.1. 所有道路的总长度是多少,以公里为单位?
5.3.2. 以公顷为单位,乔治王子的城市有多大?
5.3.3. 按地区划分,该省最大的直辖市是什么?
5.3.4. 每个直辖市内完全包含的道路长度是多少?
5.3.5. 创建一个包含乔治王子城内所有道路的新表格。
5.3.6. 维多利亚的道格拉斯街以公里为单位有多长?
5.3.7. 有洞的最大的市政多边形是什么?

5.3.1.

所有道路的总长度是多少,以公里为单位?

您可以用一段非常简单的SQL回答这个问题:

SELECT sum(ST_Length(geom))/1000 AS km_roads FROM bc_roads;

km_roads
------------------
70842.1243039643

5.3.2.

以公顷为单位,乔治王子的城市有多大?

此查询将属性条件(针对市政当局名称)与空间计算(针对面区域)相结合:

SELECT
  ST_Area(geom)/10000 AS hectares
FROM bc_municipality
WHERE name = 'PRINCE GEORGE';

hectares
------------------
32657.9103824927

5.3.3.

按地区划分,该省最大的直辖市是什么?

此查询使用空间度量值作为排序值。有几种方法可以解决这个问题,但最有效的方法如下:

SELECT
  name,
  ST_Area(geom)/10000 AS hectares
FROM bc_municipality
ORDER BY hectares DESC
LIMIT 1;

name           | hectares
---------------+-----------------
TUMBLER RIDGE  | 155020.02556131

请注意,为了回答这个问题,我们必须计算每个多边形的面积。如果我们经常这样做,那么向表中添加一个可为性能编制索引的Area列将是有意义的。通过将结果按降序排列,并使用PostgreSQL“Limit”命令对结果进行排序,我们可以轻松地选择最大的值,而无需使用Max()之类的聚合函数。

5.3.4.

每个直辖市内完全包含的道路长度是多少?

这是一个“空间连接”的例子,它使用空间交互(“包含的”)作为连接条件(而不是使用公共键连接的通常关系方法)从两个表(使用一个连接)汇集数据:

SELECT
  m.name,
  sum(ST_Length(r.geom))/1000 as roads_km
FROM bc_roads AS r
JOIN bc_municipality AS m
  ON ST_Contains(m.geom, r.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
...

这个查询需要一段时间,因为表中的每条道路都被汇总到最终结果中(示例表中大约有25万条道路)。对于较小的数据集(数百条记录中的数千条记录),响应速度可能非常快。

5.3.5.

创建一个包含乔治王子城内所有道路的新表格。

这是一个“叠加”的例子,它接受两个表,并输出一个由空间剪裁或剪切结果组成的新表。与上面演示的“空间连接”不同,该查询创建了新的几何图形。覆盖类似于增强型空间连接,对于更精确的分析工作非常有用:

CREATE TABLE pg_roads as
SELECT
  ST_Intersection(r.geom, m.geom) AS intersection_geom,
  ST_Length(r.geom) AS rd_orig_length,
  r.*
FROM bc_roads AS r
JOIN bc_municipality AS m
  ON ST_Intersects(r.geom, m.geom)
WHERE
  m.name = 'PRINCE GEORGE';

5.3.6.

维多利亚的道格拉斯街以公里为单位有多长?

SELECT
  sum(ST_Length(r.geom))/1000 AS kilometers
FROM bc_roads r
JOIN bc_municipality m
  ON ST_Intersects(m.geom, r.geom
WHERE
  r.name = 'Douglas St'
  AND m.name = 'VICTORIA';

kilometers
------------------
4.89151904172838

5.3.7.

有洞的最大的市政多边形是什么?

SELECT gid, name, ST_Area(geom) AS area
FROM bc_municipality
WHERE ST_NRings(geom) > 1
ORDER BY area DESC LIMIT 1;

gid  | name         | area
-----+--------------+------------------
12   | SPALLUMCHEEN | 257374619.430216