在90%的情況下,"爲什麼我的查詢給了我一個'TopologyException'錯誤"的問題的答案是"一個或多個輸入的幾何圖形是無效的",這就引出了這樣一個問題:幾何圖形"無效"是什麼意思?我們爲什麼要關心它?
一、什麼是有效性
對於定義有界區域並需要大量結構的多邊形來說,它的幾何圖形有效性是最重要的。線串非常簡單,不會無效,點也不會無效。
多邊形有效性的一些規則很明顯,而另一些規則是任意的(事實上,是任意的)。
- 多邊形的環必須閉合
- 定義孔洞的環應該是在外部邊界的環內
- 環不能自相交(它們不能相互接觸,也不能交叉)
- 環不能與其他環接觸,除非在某個點接觸
最後兩條規則屬於任意類別。定義多邊形的其他規則也是自洽合理的,但是上面的規則是PostGIS所遵循的OGC SFSQL標準所使用的多邊形有效性的規則。
規則之所以重要,是因爲幾何圖形的計算依賴於輸入的幾何圖形的結構。可以構建沒有結構假設的算法,但這些程序往往非常慢,因爲任何無結構程序的第一步都是分析輸入並在其中構建結構。
這裏有一個解釋爲什麼幾何圖形的結構重要的例子。首先這個多邊形是無效的:
POLYGON((0 0, 0 1, 2 1, 2 2, 1 2, 1 0, 0 0));
在此圖中,你可以更清楚地看到無效的原因:
這個多邊形的外環實際上是一個數字8的形狀,中間有一個自交點。圖形程序成功地渲染了多邊形填充,使其在視覺上看起來是一個"區域":兩個一個單位的正方形,因此多邊形總面積爲兩個單位的面積。
讓我們看看PostGIS數據庫認爲多邊形的面積是多少:
-
SELECT ST_Area(ST_GeometryFromText(
-
'POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))'
-
));
這裏發生了什麼?計算面積的算法假設環不自相交。程序始終計算位於邊界線的一側的區域的面積。
然而,在我們的(表現不佳)的數字8中,對於一個葉部分,圖形區域位於邊界線的右側,而對於另一個葉部分,圖形區域在邊界線的左側。這將導致爲每個葉部分計算的面積互相抵消(一個爲1,另一個爲-1),因此結果爲"0面積"。
二、檢測有效性
在前面的示例中,我們可以輕易發現一個多邊形是無效的。然而我們如何在一個包含數百萬個幾何圖形的表中檢測無效?答案是使用ST_IsValid(geometry)函數:
-
SELECT ST_IsValid(ST_GeometryFromText(
-
'POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))'
-
));
現在我們知道這個圖形是無效的,但是我們不知道爲什麼無效。我們可以使用ST_IsValidReason(geometry)函數來查找無效的原因:
-
SELECT ST_IsValidReason(ST_GeometryFromText(
-
'POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))'
-
));
請注意,除了原因(自相交),圖形自相交的座標(coordinate(1 1))也被返回了。
我們也可以使用ST_IsValid(geometry)函數來測試數據表:
-
-- Find all the invalid polygons and what their problem is
-
SELECT name, boroname, ST_IsValidReason(geom)
-
FROM nyc_neighborhoods
-
WHERE NOT ST_IsValid(geom);
三、修復無效的圖形
首先,壞消息是:沒有100%確定的方法來修復無效的幾何圖形。最壞的情況是使用ST_IsValid(geometry)函數識別它們,然後將它們移動另一張表,導出該表,然後在外部修復它們。
下面是SQL的一個示例,它將無效的幾何圖形從原錶轉移到另一張表中(注意,同時要:
-
-- Side table of invalids
-
CREATE TABLE nyc_neighborhoods_invalid AS
-
SELECT * FROM nyc_neighborhoods
-
WHERE NOT ST_IsValid(geom);
-
-- Remove them from the main table
-
DELETE FROM nyc_neighborhoods
-
WHERE NOT ST_IsValid(geom);
在視覺上修復無效幾何圖形的一個好工具是OpenJump(http://openjump.org),它在Tools->QA->Validate Selected Layers.下包含一個驗證程序。
現在好消息是:可以使用以下任何一種方法在數據庫中修復很大一部分的缺陷:
- ST_MakeValid函數
- ST_Buffer函數
3.1、ST_MakeValid
ST_MakeValid嘗試在不對輸入幾何圖形進行更改的情況下修復缺陷。不會刪除或移動任何頂點,只需重新排列對象的結構即可。對於清晰但無效的數據來說,這個函數非常適用,對於雜亂無章且無效的數據來說,這個函數可能並不適用。
-
-- Fix the invalid figure-8 polygon
-
SELECT ST_AsText(ST_MakeValid(
-
'POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))'
-
));
ST_MakeValid成功地將幾何圖形"8"轉換爲表示相同面積的multi-polygon。
3.2、ST_Buffer
使用緩衝區技巧清理時,可以利用緩衝區的生成方式來達到修復幾何圖形的目的:緩衝區幾何圖形是全新的幾何圖形,由關於原始圖形的偏移線構建。如果不偏移原始線(零),則新幾何圖形在結構上將與原始幾何圖形相同,但由於它是使用OGC拓撲規則構建的,因此它將是有效的。
例如,這裏有一個典型的無效現象——"香蕉多邊形" —— 一個環,它包圍着一個區域,但彎曲着接觸自己,留下一個"孔洞(hole)",實際上並不是一個孔洞。
POLYGON((0 0, 2 0, 1 1, 2 2, 3 1, 2 0, 4 0, 4 4, 0 4, 0 0))
在多邊形上計算零偏移緩衝區將返回有效的OGC多邊形,該多邊形由在一點接觸的外環和內環組成。
-
SELECT ST_AsText(
-
ST_Buffer(
-
ST_GeometryFromText('POLYGON((0 0, 2 0, 1 1, 2 2, 3 1, 2 0, 4 0, 4 4, 0 4, 0 0))'),
-
0.0
-
)
-
);