Confrontando due geometrie in ArcPy?


18

Sto cercando di confrontare due classi di caratteristiche separate per identificare le differenze tra loro (una specie di funzione diff). Il mio flusso di lavoro di base:

  1. Estraggo le geometrie usando un SearchCursor
  2. Salvare le geometrie delle due classi __geo_interface__di entità geografiche come GeoJSON usando un modificato (ottenuto da valveLondonreturn {'type': 'Polygon', 'coordinates': [[((pt.X, pt.Y) if pt else None) for pt in part] for part in self]} ). Questo per evitare l'oggetto della geometria condivisa che ESRI usa con i cursori e l'impossibilità di fare copie profonde (alcune discussioni qui su gis.stackexchange ne parlano).
  3. Controlla le geometrie delle due classi di feature in base a un identificatore univoco. Ad esempio, confrontare la geometria OID1 FC1 con la geometria OID1 FC2. Per ottenere la geometria come istanza di un oggetto ESRI, chiamare arcpy.AsShape()(modificato per leggere poligoni con buchi (vedere il punto 2 sopra) con return cls(Array([map(lambda p: Point(*p) if p is not None else Point(), part) for part in coordinates])). Il confronto è semplicemente geom1.equals(geom2)come indicato nella Classe Geometria .

Mi aspetto di trovare ~ 140 cambiamenti nelle geometrie, ma il mio script insiste sul fatto che ci sono 430. Ho provato a controllare quelle rappresentazioni GeoJSON e sono identiche, eppure la Geometry Class equals () si rifiuta di dirlo.

Un esempio è di seguito:

>>> 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

Il comportamento previsto qui dovrebbe essere True (non False).

Qualcuno ha qualche suggerimento prima di spostare tutto in geometrie ogr? (Sono titubante perché ogr.CreateGeometryFromGeoJSON () si aspetta una stringa, e arcpy __geo_interface__restituisce un dizionario e mi sembra di aggiungere ulteriore complessità).

Trovate utili le seguenti risorse, anche se non rispondono alla domanda:

  1. domanda di arcpy.Geometry qui su gis.stackexchange.com che era collegata sopra nel mio testo.
  2. Errori nella classe Polygon di arcpy dai forum arcgis.com (apparentemente ci sono molti errori di precisione in ArcGIS 10.0 che teoricamente sono stati corretti in 10.1 ma non posso verificare che, in 10.0 SP5 si ottenga ancora l'errore).

Risposte:


12

Molto probabilmente il problema riguarda la precisione in virgola mobile . Nel tuo caso hai già estratto le geometrie usando arcpy e le hai abbinate al tuo RUID.

Fortunatamente da quando hai installato arcpy hai intorpidito, il che rende facile il confronto di set di matrici numeriche. In questo caso, suggerirei la funzione numpy.allclose , disponibile in numpy 1.3.0 (installato con ArcGIS 10).

Dai campioni che hai dato sopra

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

La atolparola chiave specifica il valore di tolleranza.

Nota che non dovresti usare arcpy.AsShapeaffatto. Mai. Come ho notato in questa domanda (/ plug shameless) c'è un bug noto in ArcGIS che tronca le geometrie quando vengono create senza un sistema di coordinate (anche dopo aver impostato la env.XYTolerancevariabile d'ambiente). Non arcpy.AsShapec'è modo di evitarlo. Fortunatamente geometry.__geo_interface__estrae le geometrie corrette da geometrie esistenti (sebbene non gestisca poligoni complessi senza la correzione di @JasonScheirer).


Grazie. Non ho pensato di usare numpy per farlo. Un'altra soluzione sembra utilizzare il modulo decimale e risolverlo, ma richiede molto più lavoro.
Michalis Avraam,

Penso che sarebbe importante impostare il numpy.allclose() rtolparametro su 0. Per impostazione predefinita è 1e-05 e può portare a una grande tolleranza se i valori delle matrici sono grandi, vedere: stackoverflow.com/a/57063678/1914034
Sotto il radar

11

La precisione delle coordinate sarà una considerazione importante qui. I numeri in virgola mobile non possono essere memorizzati esattamente.

Se si utilizza lo strumento Confronto funzioni , viene fornito il risultato previsto utilizzando la tolleranza XY predefinita?


Non ho controllato lo strumento Confronto funzionalità in quanto lo strumento che sto realizzando confronta effettivamente le singole funzionalità che si spostano tra le diverse classi di funzionalità. Vale a dire, una funzione potrebbe spostarsi da CityRoads a CountyRoads, quindi ho bisogno di capire se qualcosa è cambiato nella geometria e negli attributi diversi dalla classe di caratteristiche che la contiene. Esistono un totale di 24 classi di funzioni e le funzioni possono spostarsi tra loro. Confronto funzioni confronta solo 2 classi di caratteristiche, quindi può dirmi se non esiste più in un FC. Quindi devo ancora confrontare la funzionalità per assicurarmi che non sia cambiata
Michalis Avraam

Ho controllato lo strumento Confronto funzioni con la tolleranza predefinita (8.983e-009, che è piuttosto piccola, ma si tratta di un file GDB) e riporta alcune modifiche, ma non quelle giuste. In particolare, dice che ci sono 69 cambiamenti di geometria (immagino meglio di prima) ma sembra presumere che OID sia il modo per identificare caratteristiche uniche (cerca il vecchio OID1 e il nuovo OID1) che non è necessariamente vero (l'ho impostato per l'uso il mio RUID come una specie ma non gli è piaciuto). Quindi torniamo al tavolo da disegno.
Michalis Avraam,

4

oltre alla risposta di @ blah328, devi scegliere di confrontare due tabelle per riportare differenze e somiglianze con valori tabulari e definizioni di campo con Confronto tabelle .

Esempio:

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' 

Grazie, lo esaminerò quando proverò a confrontare i dati degli attributi. Per ora, sembra che non sia possibile confrontare le geometrie, il che è più importante.
Michalis Avraam,

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

Se la .equals()funzione non funziona come previsto e / o le coordinate sono leggermente modificate in ArcGIS, è possibile massaggiare le coordinate XY, quindi confrontare l'equivalente in stringa della geometria. Nota, truncateCoordinates()taglia tutti i valori oltre il quarto decimale.

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

geom1 == geom2

@ klewis- Questo è un modo per confrontare una geometria, ma sembra che geometry.equals (geometria) dovrebbe tornare vero quando si confronta la stessa geometria. Troncare le coordinate è una specie di hack in un certo senso. Forse ESRI deve iniziare a utilizzare il tipo decimale () anziché float se non sono in grado di gestire correttamente i valori in virgola mobile internamente ma possono rappresentarli come stringhe uguali.
Michalis Avraam,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.