空间数据库存在的理由是在数据库内执行查询,这通常需要桌面 GIS 功能。有效使用 PostGIS 需要了解哪些空间函数可用、如何在查询中使用它们,并确保适当的索引以提供良好的性能。
空间关系显示两个几何图形如何相互关联。用于查询几何的基本功能。
根据OpenGIS SQL简单特征实现规范,“比较两个几何图形的基本方法是对两个几何图形的内部、边界和外部之间的相交进行交集测试,并根据'交集矩阵'的元素对两个几何之间的关系进行分类。”
在点集拓扑理论中,嵌入二维空间的几何中的点被分为三个集合:
几何的边界是下一个较低维度的几何的集合。 对于维数为 0 的 POINT,边界是空集。LINESTRING的边界是两个端点。 对于POLYGON,边界是外环和内环的线条。
几何图形的内部是几何图形中不在边界内的那些点。对于POINT来说,内部就是点本身。LINESTRING的内部是端点之间的点集。 对于POLYGON,内部是多边形内的面。
几何体的外部是几何体嵌入的空间的其余部分;换句话说,所有不在几何内部或边界上的点。 它是一个二维非封闭曲面。
九交模型 (DE-9IM)通过指定每个几何图形的上述集合之间的 9 个相交的维度来描述两个几何图形之间的空间关系。交集维度可以正式表示为 3x3 交集矩阵。
对于几何图形g,内部、边界和外部使用符号I(g)、B(g)和E(g)表示。 此外,dim(s) 表示集合s (域为 {0,1,2,F}):
0 => 点集合
1 => 线集合
2 => 面集合
F =>空集合
使用此表示法,两个几何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) ) |
The following overlapping polygons provide a concrete example. ST_Relate computes the matrix 212101212; it matches the overlap pattern T*T***T** used by ST_Overlaps.
WITH example AS (
SELECT
'POLYGON ((140 140,140 122,135 105,126 100,118 99,110 94,100 86,97 73,102 59,98 49,87 38,70 30,55 29,40 30,28 38,20 50,14 66,10 84,6 100,4 119,4 143,6 166,10 180,18 191,28 195,40 190,55 189,67 186,86 179,112 165,124 158,133 148,140 140),(48 177,40 177,30 174,24 166,22 159,25 155,30 153,40 154,45 157,52 162,53 169,51 174,48 177))'::geometry AS a,
'POLYGON ((75 63,79 50,87 38,95 31,108 25,124 22,140 18,154 11,166 6,176 10,184 21,188 35,190 58,190 82,193 104,190 121,185 139,178 154,166 163,154 171,139 172,124 171,112 165,96 152,92 142,92 126,86 116,79 110,75 104,72 94,73 86,75 76,75 63))'::geometry AS b
), relation AS (
SELECT a, b, ST_Relate(a, b) AS matrix
FROM example
)
SELECT matrix,
ST_RelateMatch(matrix, 'T*T***T**') AS matches_overlap_pattern,
ST_Overlaps(a, b) AS overlaps
FROM relation;
matrix | matches_overlap_pattern | overlaps -----------+-------------------------+---------- 212101212 | t | t (1 row)
The executable query and generated overlay above are the visual source for the relation. The query below renders the nine cells of the same kind of matrix as a 3-by-3 set of examples. The final exterior/exterior cell is clipped to a finite viewport, since the true exterior/exterior intersection is unbounded.
WITH example AS (
SELECT
ST_MakeEnvelope(0, 0, 6, 4) AS a,
ST_MakeEnvelope(3, -1, 8, 3) AS b,
ST_MakeEnvelope(-1, -2, 9, 5) AS viewport
), parts AS (
SELECT
a,
b,
ST_Boundary(a) AS ba,
ST_Boundary(b) AS bb,
viewport
FROM example
)
SELECT
ST_Relate(a, b) AS matrix,
ST_AsText(ST_Normalize(a)) AS input_a,
ST_AsText(ST_Normalize(b)) AS input_b,
ST_AsText(ST_Normalize(ST_Intersection(a, b))) AS "2 I(a) ∩ I(b)",
ST_AsText(ST_Normalize(ST_Intersection(a, bb))) AS "1 I(a) ∩ B(b)",
ST_AsText(ST_Normalize(ST_Difference(a, b))) AS "2 I(a) ∩ E(b)",
ST_AsText(ST_Normalize(ST_Intersection(ba, b))) AS "1 B(a) ∩ I(b)",
ST_AsText(ST_Normalize(ST_Intersection(ba, bb))) AS "0 B(a) ∩ B(b)",
ST_AsText(ST_Normalize(ST_Collect(
'LINESTRING(0 0,0 4,6 4,6 3)'::geometry,
'LINESTRING(0 0,3 0)'::geometry
))) AS "1 B(a) ∩ E(b)",
ST_AsText(ST_Normalize(ST_Difference(b, a))) AS "2 E(a) ∩ I(b)",
ST_AsText(ST_Normalize(ST_Collect(
'LINESTRING(3 -1,8 -1,8 3,6 3)'::geometry,
'LINESTRING(3 -1,3 0)'::geometry
))) AS "1 E(a) ∩ B(b)",
ST_AsText(ST_Normalize(ST_Difference(viewport, ST_Union(a, b))))
AS "2 E(a) ∩ E(b) clipped"
FROM parts;
从左到右,从上到下阅读。交集矩阵的字符串表示形式为“212101212”。
欲了解更多信息,请访问:
适用于 SQL 的 OpenGIS 简单功能实现规范 (1.1版本, 第 2.1.13.2 节)
为了便于确定常见的空间关系,OGC SFS 定义了一组命名空间关系谓词。PostGIS 将这些功能提供为函数 ST_Contains, ST_Crosses, ST_Disjoint, ST_Equals, ST_Intersects, ST_Overlaps, ST_Touches, ST_Within。 它还定义了非标准关系谓词 ST_Covers, ST_CoveredBy, 和ST_ContainsProperly。
空间谓词通常用作 SQL WHERE或JOIN子句中的条件。如果空间索引可用,命名空间谓词会自动使用空间索引,因此无需使用边界框运算符&&。例如:
SELECT city.name, state.name, city.geom FROM city JOIN state ON ST_Intersects(city.geom, state.geom);
有关更多详细信息和插图,请参阅PostGIS Workshop.
In some cases the named spatial relationships are insufficient to provide a desired spatial filter condition. These requirements can be expressed by computing the full DE-9IM intersection matrix with ST_Relate.
为了测试特定的空间关系,使用相交矩阵模式。这是用附加符号{T,*}增强的矩阵表示:
T => 表示交集的维度不为空;即{0,1,2}
* => 任何东西
Using intersection matrix patterns, specific spatial relationships can be evaluated succinctly. The ST_Relate and ST_RelateMatch functions can both test these patterns.
These road segments share a line segment. ST_Crosses does not find this relationship for linear features, because it only returns true when their interiors intersect at a point. The pattern 1*1***1** tests for a one-dimensional intersection instead.
WITH roads AS (
SELECT
'LINESTRING(10 10,40 90,70 110,140 110,170 130,190 190)'::geometry AS "road A",
'LINESTRING(10 190,50 130,90 110,130 110,160 70,180 10)'::geometry AS "road B"
), relation AS (
SELECT "road A", "road B", ST_Relate("road A", "road B") AS matrix
FROM roads
)
SELECT
ST_Intersection("road A", "road B") AS overlap,
matrix,
ST_Crosses("road A", "road B") AS "ST_Crosses",
ST_RelateMatch(matrix, '1*1***1**') AS "matches 1*1***1**"
FROM relation;
overlap | matrix | ST_Crosses | matches 1*1***1** ----------------------------+-----------+------------+------------------- LINESTRING(90 110,130 110) | 1F1FF0102 | f | t (1 row)
The next example finds a wharf which runs from the lake interior onto its shoreline, with one endpoint on the shoreline. The other wharves provide nearby counterexamples in the figure. The pattern 102101FF2 expresses the complete required relationship in one test.
WITH data AS (
SELECT
'POLYGON((-20 10,30 30,80 70,110 80,145 85,190 110,300 220,230 220,-20 220,-20 10))'::geometry AS lake,
'MULTILINESTRING((120 180,145 85,110 80),(10 130,30 70),(90 140,110 80,94.81118881118876 75.83496503496498),(150 160,180 80))'::geometry AS wharves
), wharf AS (
SELECT lake, (item).path[1] AS id, (item).geom
FROM data CROSS JOIN LATERAL ST_Dump(wharves) AS item
), relation AS (
SELECT id, geom, ST_Relate(lake, geom) AS matrix
FROM wharf
)
SELECT
id AS wharf,
geom AS "matching wharf",
matrix
FROM relation
WHERE ST_RelateMatch(matrix, '102101FF2')
ORDER BY id;
wharf | matching wharf | matrix
-------+-----------------------------------+-----------
1 | LINESTRING(120 180,145 85,110 80) | 102101FF2
(1 row)
使用空间条件构建查询时,为了获得最佳性能,确保使用空间索引(如果存在)非常重要(请参阅第 4.9 节 “空间索引”)。为此,必须在查询的WHERE或ON子句中使用空间运算符或索引感知函数。
空间运算符包括边界框运算符(其中最常用的是&&; 有关完整列表,请参见第 7.10.1 节 “边界框运算符”)以及最近邻查询中使用的距离运算符(最常见的是<->; 有关完整列表,请参见 第 7.10.2 节 “距离运算符”。)
索引感知函数会自动将边界框运算符添加到空间条件中。 索引感知函数包括命名空间关系谓词 ST_Contains, ST_ContainsProperly, ST_CoveredBy, ST_Covers, ST_Crosses, ST_Intersects, ST_Overlaps, ST_Touches, ST_Within, ST_Within, 和ST_3DIntersects 以及距离谓词ST_DWithin, ST_DFullyWithin, ST_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 Workshop。
本节中的示例使用线性道路表和多边形城市边界表。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. |
道路的总长度是多少公里? |
|
这个问题可以用非常简单的SQL来回答,例如: Code
SELECT sum(ST_Length(geom))/1000 AS km_roads FROM bc_roads; 栅格输出
km_roads ------------------ 70842.1243039643 |
|
|
5.3.2. |
乔治王子城有多大(以公顷为单位)? |
|
此查询将空间计算(面面积)与属性条件(自治市名称)结合使用: Code
SELECT ST_Area(geom)/10000 AS hectares FROM bc_municipality WHERE name = 'PRINCE GEORGE'; 栅格输出
hectares ------------------ 32657.9103824927 |
|
|
5.3.3. |
按面积计算,该省最大的直辖市是哪个? |
|
此查询使用空间测量作为排序值。 有多种方法可以解决此问题,但最有效的方法如下: Code
SELECT name, ST_Area(geom)/10000 AS hectares FROM bc_municipality ORDER BY hectares DESC LIMIT 1; 栅格输出
name | hectares ---------------+----------------- TUMBLER RIDGE | 155020.02556131 请注意,为了回答这个查询,我们必须计算每个多边形的面积。 如果我们经常这样做,那么向表中添加一个区域列是有意义的,可以为性能建立索引。 通过按降序对结果进行排序,并使用 PostgreSQL“LIMIT”命令,我们可以轻松地仅选择最大值,而无需使用 MAX() 等聚合函数。 |
|
|
5.3.4. |
每个城市包含的道路总长度是多少? |
|
这是“空间联接”的示例,它使用空间交互(“包含”)作为联接条件(而不是在公共键上联接的通常关系方法)将来自两个表(带有联接)的数据汇集在一起 : Code
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.476 VANCOUVER | 1450.331 LANGLEY DISTRICT | 833.793 BURNABY | 773.769 PRINCE GEORGE | 694.376 ... 此查询需要一段时间,因为表中的每条道路都会汇总到最终结果中(示例表中大约有 250K 条道路)。 对于较小的数据集(数百条记录中的数千条),响应可能非常快。 |
|
|
5.3.5. |
创建一个包含乔治王子市内所有道路的新表。 |
|
这是“覆盖”的一个示例。 也就是说,我们取两个表并输出一个由空间切割结果组成的新表。 与上面显示的“空间连接”不同,此查询实际上会生成新的几何图形。 生成的叠加就像涡轮增压的空间耦合,对于更精确的分析工作非常有用: Code
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. |
维多利亚州的“道格拉斯街”有多长(公里)? |
|
Code
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. |
哪个市政面的孔洞最大? |
|
Code
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 |