空間資料庫的 存在理由 是在資料庫內部執行查詢,而這些查詢通常需要桌面 GIS 功能。有效地使用 PostGIS 需要知道有哪些空間函數可用、如何在查詢中使用它們,並確保已建立適當的索引以提供良好的效能。
空間關係表示兩個幾何圖形如何相互作用。它們是查詢幾何圖形的基本功能。
根據 OpenGIS 適用於 SQL 的簡易功能實作規範,「比較兩個幾何圖形的基本方法是對兩個幾何圖形的內部、邊界和外部之間的交集進行成對測試,並根據結果的『交集』矩陣中的條目對兩個幾何圖形之間的關係進行分類。」
在點集拓樸理論中,嵌入在二維空間中的幾何圖形中的點分為三組
幾何圖形的邊界是下一較低維度的幾何圖形集合。對於維度為 0 的 POINT
,邊界是空集合。LINESTRING
的邊界是兩個端點。對於 POLYGON
,邊界是外部和內部環的線條。
幾何圖形的內部是幾何圖形中不在邊界上的點。對於 POINT
,內部是點本身。LINESTRING
的內部是端點之間的點集。對於 POLYGON
,內部是多邊形內部的面積表面。
幾何圖形的外部是嵌入該幾何圖形的空間中的其餘部分;換句話說,就是所有不在幾何圖形的內部或邊界上的點。它是一個二維非封閉曲面。
維度擴展的 9 交集模型 (DE-9IM) 透過指定每個幾何圖形的上述集合之間 9 個交集的維度,來描述兩個幾何圖形之間的空間關係。交集維度可以用 3x3 交集矩陣 來正式表示。
對於幾何圖形 g,內部、邊界 和 外部 使用符號 I(g)、B(g) 和 E(g) 表示。此外,dim(s) 表示集合 s 的維度,其域為 {0,1,2,F}
0
=> 點
1
=> 線
2
=> 區域
F
=> 空集合
使用此符號,兩個幾何圖形 a 和 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) ) |
在視覺上,對於兩個重疊的多邊形幾何圖形,看起來像這樣
|
||||||||||||||||||
|
|
從左到右、從上到下讀取,交集矩陣表示為文字字串 '212101212'。
如需更多資訊,請參閱
OpenGIS 適用於 SQL 的簡易功能實作規範(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 工作坊。
在某些情況下,具名空間關係不足以提供所需的空間篩選條件。
![]() 例如,考慮一個代表道路網路的線性資料集。可能需要識別所有彼此交叉的道路路段,交叉點不是在點,而是在線上(可能是為了驗證某些業務規則)。在這種情況下,ST_Crosses 並未提供必要的空間篩選條件,因為對於線性要素,它僅在它們在點交叉時才傳回 一個兩步驟的解決方案是首先計算空間相交的道路線對的實際交集 (ST_Intersection),然後檢查交集的 ST_GeometryType 是否為 ' 顯然,需要一個更簡單、更快速的解決方案。 |
![]() 第二個範例是找到在線上與湖泊邊界相交的碼頭,並且碼頭的一端位於岸上。換句話說,碼頭在湖泊內但未完全包含在湖泊內,在線上與湖泊的邊界相交,並且碼頭的其中一個端點在湖泊的邊界內或邊界上。可以使用空間述詞的組合來尋找所需的要素
|
這些要求可以透過計算完整的 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
若要測試特定的空間關係,則會使用交集矩陣模式。這是使用額外符號 {T,*}
擴增的矩陣表示法
T
=> 交集維度為非空;也就是在 {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');
建構使用空間條件的查詢時,為了獲得最佳效能,務必確保使用空間索引(如果有的話)(請參閱第 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 來回答這個問題 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 請注意,為了回答此查詢,我們必須計算每個多邊形的面積。如果我們經常這樣做,那麼在表格中新增一個可以針對效能建立索引的面積欄位會很有意義。藉由以降序方式對結果進行排序,然後使用 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. |
維多利亞的「Douglas St」長度是多少公里? |
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 |