PostGIS:ST_Intersection = 100%のジオメトリの場合、ST_Equals false?


9

地籍の小包データで構成される2つのデータセットがあり、それぞれ約125,000行です。ジオメトリ列は、区画の境界を表すWKBポリゴンです。すべてのデータは幾何学的に有効です(ポリゴンが閉じているなど)。

いくつかの最近のデータは、比較ジョブに使用されているベースデータとは異なる投影法で到着しました。そのため、新しいデータを再投影しました(ベースは4326でした。もう1つは、900914としてPostGISに取り込まれたWGA94でした... 4326に再投影しました)。 。

分析の最初の段階は、一致しない区画を見つけて保存することでした。その一部は、同一のジオメトリを持つ区画を識別して保存することです。

だから私は非常に標準的なクエリを実行しました(以下のコードブロックはスキーマの詳細などを抽象化しています):

create table matchdata as
  select  a.*
  from gg2014 a, gg2013 b
  where ST_Equals(a.g1,b.g1)

結果はゼロです。

「おかしい…」と思った。「おそらく、再投影によって引き起こされた小さな頂点シフトがありました。それは煩わしいことであり、実際には起こらないはずです。」

幸いにも、空間的に同一である必要がある区画を確立できるようにする豊富な空間データ(5つの識別子列)があります。2014年のテーブルの変更日が2013年のデータの最大変更日より前である同じ識別子を持つものです。これは、120,086の個別の行になります。

識別子とジオメトリを別のテーブル(match_id)に保存し、次のクエリを実行しました。

select apid, 
       bpid, 
       ST_Area(ag::geometry) as aa, 
       ST_Area(bg::geometry) as ab,
       ST_Area(ST_Intersection(ag,bg)::geometry)/ST_Area(ag::geometry) as inta,
       ST_Area(ST_Intersection(ag,bg)::geometry)/ST_Area(ag::geometry) as intb
from match_id
order by inta

最初の16の値についてintaintb同様にゼロであった、次の456であった0.99999999っぽい(最小0.99999999999994、最大0.999999999999999)、以降1た473行-交差の面積があった行120050まで、より大きいいずれかの幾何学的形状(最大値intaとはintb)まだ1.00000000000029だったが、。

だからここに私の難問があります:2つのジオメトリがそれぞれの領域の99.999999999994%から100.000000000029%の間で空間的に交差する場合、「ST_Equals」に「そうです...それを与えます。十分に近い」と言ってもらいたいのです。

結局のところ、これは16兆分の1の割合で減少していることに相当します。

地球の周囲(約40,000 km)のコンテキストでは、0.0000000025 kmの頂上から離れているようなものです(面積の差が小さくなるため、頂点のシフトはさらに小さくする必要があります)。

TFD(私がR'dを持っています)によると、許容値ST_Intersects()は概念的に0.00001m(1mm)であるため、頂点の暗黙の変更(私が確認していないことを告白します:ST_Dump()それらを変更するので)は小さくなるようです。公差より。(私はそれを理解していますがST_Intersects !== ST_Intersection()、言及されている唯一の許容範囲です)。

ST_Equals()... によって行われた頂点比較に対応する許容誤差を見つけることができませんでしたが、少なくとも120,000行の行が空間的同一性の賢明な評価に合格するはずであるのに実際にはそうではないようです。

(注:私も同じ演習を使用しました::geography-結果はよりばらつきがありましたが、きれいな「1」で110,000エントリ以上ありました)。

コードの隙間を掘り下げる必要がない、ST_Equalsの許容範囲を緩和する方法はありますか?私はそうすることに熱心ではありません。

そうでない場合、誰もが知っているクラッジはありますか?

注:「kludge」が次のような二者間比較を行わなかった場合、それは良いことです。

where ST_within(g1, ST_Buffer(g2, 0.0000001))
  and ST_within(g2, ST_Buffer(g1, 0.0000001))


   - I've done that: sure, it works... but it's a gigantic documentation PITA).

私はこれを回避できますが、回避策を文書化するために20ページを書くことは-危険なデータを取得した場合にのみ再び表示されます-一度限りの可能性があることを考えると、私が行う必要はないPITAです。

(バージョン:Postgresql 9.3.5; PostGIS 2.1.3)


ここでの考えですが、新しい区画を、st_snaptogridを使用して興奮するデータに適合するグリッドに正規化しようとしましたか?
ニックス2015

ソースコードを見たくないのは理解できますが、あなたの質問が私にそうさせたのです(たとえ私のC ++はひどいのですが)、それでありがとう。興味があれば、関連するセクションを投稿できます。これらはすべてgithub.com/libgeosにあります。
John Powell

ST_Equalstrueジオメトリが等しい場合にのみ返されます-ジオメトリタイプ、頂点の数、SRID、および頂点の値(すべての次元で、同じ順序)。差異がある場合、比較は停止し、false返されます。
ビンス

@Vince:(ドキュメントから)私が理解しているように、ST_Equals()方向性を無視します。閉じた2次元ポリゴンの場合、ポイントが時計回りと反時計回りのどちらで列挙されても違いはないことを意味します。ST_OrderingEquals()より厳しいテストです。とはST_Dump()いえ、頂点を検査した(すべての頂点のデルタを使用および計算した)ので、@ JohnBarçaのすばらしい答えはお金にかかっていることは明らかです。ST_SnapToGrid()で比較が行われない限り、1つのジオメトリが再投影される場合、事前に同一である既知のデータST_equals()であっても禁忌です。
GT。

これに遅れて:空間の[ほぼ]等質性の許容できるテストを取得するための素早い方法は、各ジオメトリのどの部分が交差の一部であるかをチェックすることです。これは少し計算が面倒です。計算(100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(a.g1)))::int as int_pcaして(100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(b.g1)))::int as int_pcbJOINインクルードを確認してくださいST_Intersects(a.g1,b.g1))。(int_pca, int_pcb)=(100,100)(またはその他のカットオフのセット)かどうかをテストします。Kludgyですが、30分以内に260万個の区画を処理します(g1にGISTインデックスが作成されている限り)。
GT。

回答:


20

私の推測では、座標変換では小さな丸め誤差が発生します(以下の例を参照)。ST_Equalsに許容値を設定する方法がないため、ジオメトリがすべての点で同一である必要があるため、ST_Equalsが小数点以下n桁でのみ異なる一部のジオメトリに対してfalseを返します。libgeosの交差行列の定義を参照してください。。これは非常に極端な例で確認できます。

SELECT ST_Equals(
      ST_MakePoint(0,0),
      ST_MakePoint(0,0.000000000000000000000000000000000000000000000000000000000001));

これはfalseを返します

ST_SnapToGridを使用する場合、たとえば、小数点以下10桁に特定の精度を課すことができます。

SELECT ST_Equals(
      ST_MakePoint(0,0),
      ST_SnapToGrid(
             ST_MakePoint(0,0.00000000000000000000000000000000000000000000001),
      10));

現在はtrueを返します

走るとしたら

CREATE table matchdata AS
SELECT  a.*
FROM gg2014 a, gg2013 b
WHERE ST_Equals(ST_SnapToGrid(a.g1, 5), ST_SnapToGrid(b.g1, 5));

適切な許容値を設定すれば、問題が解消されると思います。

以下は、許容範囲についてのPostgis開発者の議論へのリンクです。これは、実装するのが簡単ではないことを示唆しています。

丸め精度に関するポイントを説明するために、British National Grid(EPSG:27700)とlat / lonの間でいくつかの変換を行い、ロンドンのどこかでポイントを取得しました。

SELECT ST_AsText(ST_Transform(ST_SetSrid(ST_MakePoint(525000, 190000),27700),4326));

戻り値 POINT(-0.19680497282746 51.5949871603888)

これを逆にします

SELECT ST_AsText(ST_Transform(ST_SetSrid(ST_MakePoint(-0.19680497282746, 51.5949871603888),4326),27700));

戻り値 POINT(525000.000880007 189999.999516211)

これは、1ミリメートル未満のずれですが、ST_Equalsがfalseを返すのに十分です。


ジョンバルカの答えは正解です。小さな丸めエラーによってST_Equalsがスローされる可能性があります。私の(不愉快な)経験は、2つのデータセット(どちらもEPSG 4326からEPSG 3857に投影されました-1つはArcCatalog (ArcToolbox-> Data Management Tools-> Projections and Transformations)を使用し、もう1つはGDAL ogr2ogrを使用しました)を操作したときの経験です。
Ralph Tee

この答えは私を大いに助けてくれました。しかし、地理インデックスが使用されなくなり、クエリに時間がかかりすぎることに気付きました。私の回避策は、スナップされたジオメトリで一時テーブルを作成し、クエリを実行する前にインデックスを追加することでした。物事をスピードアップするより良い方法はありますか?
2018年

1
@hfs。ST_SnapToGridを使用して機能インデックスを作成できると思います。equals / intersects / contains etc空間操作内で関数呼び出しを使用すると、インデックスが使用されなくなり、関数インデックスを作成するとこれが解決することは間違いありません。または、精度が疑わしく、クエリでST_SnapToGridを使用する必要がない場合は、データを永続的に更新できます。もちろん、それはデータとユースケースに依存します。
ジョンパウエル

2

ジオメトリでST_IsValidチェックを実行しましたか?それらが無効な場合、すべてのベットはオフになります。ST_Intersectsと他のGEOS空間関係関数ファミリーは、交差行列の観点からは領域が明確に定義されていないため、多くの場合falseを返します。ST_Bufferを実行する理由はおそらく、無効なジオメトリを有効なジオメトリに変換するためです。ST_Buffer(...、tinybit)は、「貧乏人がジオメトリを有効にしようとする」ツールとして知られています。


新しいデータセットの最初のステップは、有効なジオメトリのみを選択することです。ST_isValid(g1)これは(斜めに)言及されています。「ジオメトリ列は区画の境界を表すWKBポリゴンです。すべてのデータは幾何学的に有効です(ポリゴンは閉じているなど)。」
GT。

0

私の答えは少し遅れますが、同じ問題を抱えている誰かを助けるかもしれません。私の経験から、実際には等しいがST_EqualsがFalseを返す2つのジオメトリが2つの場合に役立ちます。

  1. ジオメトリの比較が単一のジオメトリであることを確認してください(MultiLinesting、MultiPoinなどはありません)。
  2. ST_Equals(st_astext(a.geom), st_astext(b.geom)) 代わりに試してくださいST_Equals(a.geom , b.geom)

最初のものはすでにドキュメントで言及されています。2つ目は不合理に見えますが機能します。わかりませんが、デフォルトのpostGISジオメトリのバイナリ形式に関係していると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.