5

I'm working on a little arcpy script to compare the geometry of features (linked by a matching ID field

I've got a couple of search cursors populating dictionaries for the two feature classes which I then am trying to compare to find where features (that should be identical shapes) don't match.

I have run into a snag in my concept though in that the output from SHAPE@ is returning False when I compare the features I know are identical (should return True).

import arcpy
fields = ['LineID', 'SHAPE@']
x = 'Line1' # Feature Class #1
y = 'Line2' # Feature Class #2
xDict = dict()
yDict = dict()
with arcpy.da.SearchCursor(x, fields) as cursor:
    for row in cursor:
        xDict[row[0]] = row[1]
with arcpy.da.SearchCursor(y, fields) as cursor:
    for row in cursor:
        yDict[row[0]] = row[1]

When I test I get False returned:

xDict[1] == yDict[1]
> False

This returns False even though I know that record #1 is identical in both feature classes.

Is there another way I can use my search cursors to compare the shape of the two features? I know there are .WKB, .WKT, .JSON options for Shape - are these valid options for a comparison? My brief testing suggests they are, but I'm unsure whether there are occasions where they would also return False.

xDict[1].WKB == yDict[1].WKB
> True

enter image description here

xDict[1].equals(yDict[1]) # Should be True
> True
xDict[2].equals(yDict[2]) # Should be False
> True
xDict[4].equals(yDict[4]) # Should be False
> False
12
  • A comparison for equivalence compares the objects, not their contents. You want the equals operator.
    – Vince
    Aug 14, 2016 at 23:17
  • @Vince by equals operator do you mean xDict[1] is yDict[1]? This also returns False
    – Midavalo
    Aug 14, 2016 at 23:28
  • No, shape1.equals(shape2)
    – Vince
    Aug 14, 2016 at 23:35
  • @Vince OK thanks - that returns True even for one of the features that should return False (although it does return False for another feature correctly)
    – Midavalo
    Aug 14, 2016 at 23:40
  • 1
    Does this have to be arcpy Midavalo? I have some working VB.net code that flags changed features (outside of given tolerance)... from a tool I wrote a few years ago to update 'has changed' attribution on bulk - to take the onus off operators updating these fields as they edited. I did write this before I found Feature Compare resources.arcgis.com/en/help/main/10.2/index.html#//… and Detect Feature Changes resources.arcgis.com/en/help/main/10.2/index.html#/… tools, which may do what you're after. Aug 15, 2016 at 4:16

1 Answer 1

3

I still think there should be a better way to do this, but this is what I've come up with, using SHAPE@WKB for the comparison of the shapes (matched on FeatID) in my two feature classes.

Here are my shapes - The arrows point at the start location:

enter image description here

And my script, including a description of what should be returned for each shape:

import arcpy

x = r'D:\temp\SE\CompareShapes\Data.gdb\TestPoly1'
y = r'D:\temp\SE\CompareShapes\Data.gdb\TestPoly2'

fields = ['FeatID', 'SHAPE@']
xDict = dict()
yDict = dict()
testDict = {
            1 : 'True - Identical',
            2 : 'False - Same shape, different start point',
            3 : 'True - Identical',
            4 : 'False - Same shape, different start point',
            5 : 'True - Identical',
            6 : 'False - Same shape, different start point',
            7 : 'False - Same shape, extra vertex',
            8 : 'False - Shape shape, different position (rotated)',
            9 : 'True - Identical'
           }

with arcpy.da.SearchCursor(x, fields) as cursor:
    for row in cursor:
        xDict[row[0]] = row[1]

with arcpy.da.SearchCursor(y, fields) as cursor:
    for row in cursor:
        yDict[row[0]] = row[1]

# Only 1, 3, 5, 9 are identical and should return True - rest should return False
for xx in xDict:
    print "Feature:        {}".format(xx) # FeatID of feature
    print "Expected:       {}".format(testDict[xx]) # Description of expected result
    print "x.equals(y):    {}".format(xDict[xx].equals(yDict[xx])) # Does the SHAPE@ equal SHAPE@?
    print "x.WKB == y.WKB: {}".format(xDict[xx].WKB == yDict[xx].WKB) # Does the WKB equal for each SHAPE@?

This returns the following output, which shows that x.equals(y) doesn't always return the required/expected value, and x.WKB == y.WKB seems to consistently return the expected value:

Feature:        1  
Expected:       True - Identical  
x.equals(y):    True  
x.WKB == y.WKB: True  
Feature:        2  
Expected:       False - Same shape, different start point  
x.equals(y):    True  
x.WKB == y.WKB: False  
Feature:        3
Expected:       True - Identical
x.equals(y):    True
x.WKB == y.WKB: True
Feature:        4
Expected:       False - Same shape, different start point
x.equals(y):    True
x.WKB == y.WKB: False
Feature:        5
Expected:       True - Identical
x.equals(y):    True
x.WKB == y.WKB: True
Feature:        6
Expected:       False - Same shape, different start point
x.equals(y):    True
x.WKB == y.WKB: False
Feature:        7
Expected:       False - Same shape, extra vertex
x.equals(y):    True
x.WKB == y.WKB: False
Feature:        8
Expected:       False - Shape shape, different position (rotated)
x.equals(y):    False
x.WKB == y.WKB: False
Feature:        9
Expected:       True - Identical
x.equals(y):    True
x.WKB == y.WKB: True
5
  • There is no point in having the Python equivalence operator listed, unless you want to test x == x
    – Vince
    Aug 15, 2016 at 23:06
  • @Vince - Just outlining what I tested and the result
    – Midavalo
    Aug 15, 2016 at 23:08
  • You didn't test 1 == 0? If you remove nine superfluous lines, a scrollbar wouldn't be necessary.
    – Vince
    Aug 15, 2016 at 23:13
  • @Vince fair comment - edited.
    – Midavalo
    Aug 15, 2016 at 23:18
  • Excellent answer, well researched and practically proven. I was surprised at the return values of x.equals(y), according to the docs many of those True values should have been False. +1 from me. Aug 16, 2016 at 0:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.