Порівнюючи дві геометрії в ArcPy?


18

Я намагаюся порівняти два окремі класи функцій, щоб визначити відмінності між ними (на зразок функції diff). Мій основний робочий процес:

  1. Я витягую геометрії за допомогою SearchCursor
  2. Збережіть геометрії двох класів функцій як GeoJSON за допомогою модифікованого __geo_interface__(отримано його від valveLondon return {'type': 'Polygon', 'coordinates': [[((pt.X, pt.Y) if pt else None) for pt in part] for part in self]} ). Це дозволяє уникнути спільного геометричного об’єкта, який ESRI використовує з курсорами, і неможливості зробити глибокі копії (деякі дискусії тут на gis.stackexchange говорять про це).
  3. Перевірте геометрію двох класів функцій на основі унікального ідентифікатора. Наприклад, порівняйте геометрію 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])). Порівняння просто так, geom1.equals(geom2)як зазначено в класі Геометрії .

Я очікую знайти 140 змін у геометріях, але мій сценарій наполягає на тому, що їх існує 430. Я намагався перевірити ці представлення GeoJSON, і вони однакові, але клас геометрії дорівнює () відмовлятись від цього.

Приклад нижче:

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

Очікувана поведінка тут повинна бути правдою (не помилковою).

Хтось має якісь пропозиції, перш ніж перенести все на географічну географію? (Я вагаюся, як ogr.CreateGeometryFromGeoJSON () очікує рядок, а arcpy __geo_interface__повертає словник, і я відчуваю, що додаю додаткову складність).

Наступні ресурси виявилися корисними, хоча вони не відповідають на питання:

  1. arcpy.Geometry питання тут на gis.stackexchange.com, яке було пов'язано вище в моєму тексті.
  2. Помилки в класі Polygon arcpy з форумів arcgis.com (мабуть, в ArcGIS 10.0 є багато помилок точності, які теоретично виправлені в 10.1, але я не можу перевірити, що в 10.0 SP5 ви все одно отримуєте помилку).

Відповіді:


12

Питання, швидше за все, стосується точності з плаваючою точкою . У вашому випадку ви вже видобули геометрії, використовуючи arcpy, і ви їх зіставили зі своїм RUID.

На щастя, з моменту встановлення arcpy у вас є numpy, що робить порівняння наборів числових масивів простим. У цьому випадку я б запропонував функцію numpy.allclose , яка доступна в numpy 1.3.0 (встановлена ​​з ArcGIS 10).

Із наведених вище зразків

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. Колись. Як я зазначив у цьому питанні (/ безсоромний штекер), в ArcGIS відома помилка, яка обрізає геометрії, коли вони створюються без системи координат (навіть після встановлення env.XYToleranceзмінної середовища). У arcpy.AsShapeцьому немає ніякого способу уникнути цього. На щастя geometry.__geo_interface__, витягують правильні геометрії з існуючих геометрій (хоча вони не обробляють складні багатокутники без виправлення від @JasonScheirer).


Дякую. Я не думав використовувати numpy для цього. Іншим рішенням, здається, є використання десяткового модуля і робота над цим, але це вимагає набагато більше роботи.
Міхаліс Авраам

Я думаю , що було б важливо , щоб встановити numpy.allclose() rtolпараметр в 0. За замовчуванням це 1e-05 , і це може привести до великої терпимості , якщо масиви значень великі см: stackoverflow.com/a/57063678/1914034
Нижче радара

11

Точність координат тут буде важливою увагою. Числа з плаваючою комою не можуть бути точно збережені.

Якщо ви використовуєте інструмент " Порівняння характеристик" , чи доходить до очікуваного результату, використовуючи допуск XY за замовчуванням?


Я не перевіряв інструмент «Порівняння характеристик», оскільки інструмент, який будую, насправді порівнює окремі функції, що переміщуються між різними класами функцій. Тобто, функція може переходити з CityRoads до CountyRoads, тому мені потрібно з’ясувати, чи змінилося щось у геометрії та інших атрибутах, крім класу функцій, який містить її. Всього існує 24 класи класів, і функції можуть переміщуватися між ними. Порівняння функцій порівнюватиме лише два класи функцій, тож воно може підказати мені, якщо його більше не існує у ФК. Тоді мені ще потрібно порівняти функцію, щоб переконатися, що вона не змінилася
Міхаліс Авраам

Я перевірив інструмент "Порівняння характеристик" з допуском за замовчуванням (8.983e-009, який зовсім невеликий, але це файл GDB), і він повідомляє про деякі зміни, але не правильні. Зокрема, там сказано, що існує 69 змін геометрії (я думаю, що краще, ніж раніше), але, здається, передбачається, що OID - це спосіб визначення унікальних особливостей (шукає старий OID1 та новий OID1), що не обов'язково відповідає дійсності (я встановив це для використання мій RUID як сорт, але це не сподобалось). Тож назад до дошки для малювання.
Міхаліс Авраам

4

крім @ blah328 відповіді, ви можете порівняти дві таблиці для подання звітності про різниці та подібності з табличними значеннями та визначеннями полів із Порівнянням таблиці .

Приклад:

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, а потім порівняти String-еквівалент Геометрії. Зауважте, truncateCoordinates()відсікайте всі значення за межами 4-х знаків після коми.

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

geom1 == geom2

@ klewis - Це один із способів порівняння геометрії, але відчувається, що geometry.equals (геометрія) повинна повернути істину, коли ви порівнюєте ту саму геометрію. Обрізання координат є певним злом. Можливо, ESRI потрібно почати використовувати тип decimal () замість float, якщо вони не можуть правильно обробляти значення плаваючої точки, але можуть представляти їх як рівні рядки.
Міхаліс Авраам

1

Ви можете скористатися інструментом " Вибір шару за місцем розташування (управління даними) з параметром" ARE_IDENTICAL_TO "перекриття_типу, переключити вибір , перевірити кількість рядків, а потім провести цикл через рядки для збору об'єктів або будь-якої іншої відповідної інформації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.