ArcPyの2つのジオメトリを比較しますか?


18

2つの別々のフィーチャクラスを比較して、それらの違いを識別しようとしています(diff関数の一種)。私の基本的なワークフロー:

  1. SearchCursorを使用してジオメトリを抽出します
  2. 修正した__geo_interface__valveLondon から取得したreturn {'type': 'Polygon', 'coordinates': [[((pt.X, pt.Y) if pt else None) for pt in part] for part in self]})を使用して、2つのフィーチャクラスのジオメトリをGeoJSONとして保存します。これは、ESRIがカーソルで使用する共有ジオメトリオブジェクトと、ディープコピーを作成できないことを回避するためです(gis.stackexchangeでのいくつかの議論でそれについて説明しています)。
  3. 一意の識別子に基づいて、2つのフィーチャクラスのジオメトリを確認します。たとえば、FC1 ​​OID1ジオメトリとFC2 OID1ジオメトリを比較します。ジオメトリをESRIオブジェクトインスタンスとして取得するには、を呼び出してarcpy.AsShape()(穴のあるポリゴン(上記のポイント2を参照)を読み込むように変更しますreturn cls(Array([map(lambda p: Point(*p) if p is not None else Point(), part) for part in coordinates]))。比較はGeometry Classにgeom1.equals(geom2)示されているとおりです。

ジオメトリに約140の変更が見つかると予想されますが、私のスクリプトでは430があると主張しています。

以下に例を示します。

>>> geom1geoJSON 
{'type': 'Polygon', 'coordinates': [[(-122.8423481559999, 47.060497293000083), (-122.84239755599992, 47.059262423000064), (-122.84416913599989, 47.059309693000046), (-122.84416913599989, 47.060497293000083), (-122.8423481559999, 47.060497293000083)]]}
>>> geom2geoJSON 
{'type': 'Polygon', 'coordinates': [[(-122.8423481559999, 47.060497293000083), (-122.84239755599992, 47.059262423000064), (-122.84416913599989, 47.059309693000046), (-122.84416913599989, 47.060497293000083), (-122.8423481559999, 47.060497293000083)]]}
>>> geom1 = arcpy.AsShape(geom1geoJSON)
>>> geom2 = arcpy.AsShape(geom2geoJSON)
>>> geom1.equals(geom2)
False
>>> geom2.equals(geom1)
False

ここで予想される動作はTrue(Falseではない)でなければなりません。

すべてをogrジオメトリに移動する前に、何か提案はありますか?(ogr.CreateGeometryFromGeoJSON()は文字列を期待し、arcpy's __geo_interface__は辞書を返すのでheしているので、さらに複雑になっているように感じます)。

質問に答えていない場合でも、次のリソースが役立つことがわかりました。

  1. ここでテキストにリンクされたgis.stackexchange.comのarcpy.Geometryの質問
  2. arcgis.comフォーラムのarcpyのPolygonクラスのエラー(明らかに10.1で理論的に修正されたArcGIS 10.0には多くの精度エラーがありますが、10.0 SP5ではまだエラーが発生することを確認できません)。

回答:


12

この問題は、浮動小数点精度の問題である可能性が高いです。あなたの場合、あなたはすでにarcpyを使用してジオメトリを抽出しており、それらをRUIDと照合しました。

幸いなことに、arcpyがインストールされているので、numpyがあります。これにより、数値配列のセットを簡単に比較できます。この場合、numpy.3.0(ArcGIS 10と共にインストール)で使用可能なnumpy.allclose関数をお勧めします。

上記のサンプルから

geom1geoJSON = {'type': 'Polygon', 'coordinates': [[(-122.8423481559999, 47.060497293000083), (-122.84239755599992, 47.059262423000064), (-122.84416913599989, 47.059309693000046), (-122.84416913599989, 47.060497293000083), (-122.8423481559999, 47.060497293000083)]]}
geom2geoJSON = {'type': 'Polygon', 'coordinates': [[(-122.8423481559999, 47.060497293000083), (-122.84239755599992, 47.059262423000064), (-122.84416913599989, 47.059309693000046), (-122.84416913599989, 47.060497293000083), (-122.8423481559999, 47.060497293000083)]]}

import numpy as np

close = np.allclose(np.array(geom1geoJSON["coordinates"]), np.array(geom2geoJSON["coordinates"]), atol=1e-7)
#Returns True

atolキーワードは、許容値を指定します。

使用arcpy.AsShapeしないでください。今まで。この質問(/恥知らずのプラグ)で述べたように、座標系なしで作成された場合(env.XYTolerance環境変数を設定した後でも)ジオメトリが切り捨てられるという既知のバグがArcGISにあります。これarcpy.AsShapeを避ける方法はありません。幸いなことにgeometry.__geo_interface__、既存のジオメトリから正しいジオメトリを抽出します(@JasonScheirerによる修正なしでは複雑なポリゴンを処理しません)。


ありがとうございました。numpyを使用してこれを行うことは考えていませんでした。別の解決策は、decimalモジュールを使用して処理するようですが、さらに多くの作業が必要です。
ミカリスアヴラム

numpy.allclose() rtolパラメータを0 に設定することが重要だと思います。デフォルトでは1e-05であり、配列の値が大きい場合、許容値が大きくなる可能性があります。stackoverflow.com/ a / 57063678/1914034
レーダー

11

ここでは、座標の精度が重要な考慮事項になります。浮動小数点数を正確に格納することはできません。

機能比較ツールを使用する場合、デフォルトのXY許容値を使用して期待される結果が得られますか?


作成中のツールは実際には異なるフィーチャクラス間を移動する個々のフィーチャを比較するため、フィーチャ比較ツールはチェックしませんでした。つまり、フィーチャはCityRoadsからCountyRoadsに移動する可能性があるため、ジオメトリとそれを保持するフィーチャクラス以外の属性に何か変更があったかどうかを把握する必要があります。合計24のフィーチャクラスがあり、フィーチャはそれらの間で移動できます。機能比較は2つの機能クラスのみを比較するため、FCに存在しないかどうかを確認できます。その後、機能を比較して、変更されなかったことを確認する必要があります
Michalis Avraam

デフォルトの許容値(8.983e-009は非常に小さいが、これはファイルGDB)で機能比較ツールをチェックし、いくつかの変更を報告しますが、正しい変更は報告しません。具体的には、69個のジオメトリの変更があると言います(以前よりも良いと思います)が、OIDは一意の機能を識別する方法であると仮定しているようです(古いOID1と新しいOID1を検索します)私のRUIDはソートとしてですが、それは好きではありませんでした)。それでは、図面に戻りましょう。
ミカリス

4

@ blah328 answerのほかに、違いと類似性をレポートするために2つのテーブルを比較し、Table Compare表の値とフィールド定義を選択することもできます。

例:

import arcpy
from arcpy import env
arcpy.TableCompare_management(r'c:\Workspace\wells.dbf', r'c:\Workspace
\wells_new.dbf', 'WELL_ID', 'ALL', 'IGNORE_EXTENSION_PROPERTIES', 'WELL_DEPTH 0.001',
'#','CONTINUE_COMPARE', r'C:\Workspace\well_compare.txt' 

属性データを比較しようとするときに調べていただきありがとうございます。今のところ、ジオメトリを比較できないようですが、これはより重要です。
ミカリスアヴラーム

3
def truncateCoordinates(myGeometry)
    trucated_coords = []
    partnum = 0

    for part in (myGeometry):
        for pnt in myGeometry.getPart(partnum):
            if pnt:
                trucated_coords.append("{:10.4f}".format(pnt.X))
                trucated_coords.append("{:10.4f}".format(pnt.Y))
             else:
                continue
        partnum += 1     
    return truncated_coords

.equals()関数が期待どおりに機能しない場合や、ArcGISで座標がわずかに変更されている場合は、XY座標をマッサージしてから、ジオメトリに相当する文字列を比較できます。お知らせ、truncateCoordinates()小数第4位を超えたすべての値をオフチョップ。

geom1 = truncateCoordinates(feature1.Shape)
geom2 = truncateCoordinates(feature2.Shape)

geom1 == geom2

@ klewis-これはジオメトリを比較する方法の1つですが、まったく同じジオメトリを比較する場合、geometry.equals(geometry)がtrueを返すように感じます。座標の切り捨ては、ある意味では一種のハックです。おそらく、ESRIは、内部で浮動小数点値を正しく処理できず、等しい文字列として表現できる場合、floatの代わりにdecimal()タイプの使用を開始する必要があります。
ミカリスアヴラム

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