How to copy lines with identical attribute values based on length?

1614
13
02-13-2017 11:08 AM
ChristopherBevilacqua
New Contributor III

I'm using ArcGIS 10.1, Advanced license.  I'm working with file geodatabase feature classes.  

I have a line feature class called sticksRF_2split.  Each record in this feature class has a unique value in a field called PROPNUM_GIS.  I run this feature class through the arcpy.SplitLineAtPoint_management tool.  The output, sticksRF_split, has two records with the same PROPNUM_GIS value.  From these pairs of matching PROPNUM_GIS values, I want to create a feature class that contains only the lines with the greater value in the SHAPE_Length field.  

I'm thinking the necessary approach would involve a dictionary with PROPNUM_GIS as the key and SHAPE_Length as the values, and then compare the values.  I'm having a challenge finding any documentation about how to accomplish this.  Is this the best way to approach this problem?  Any advice or suggestions would be greatly appreciated.

0 Kudos
13 Replies
ChristopherBevilacqua
New Contributor III

Thanks Richard.

When the script gets to line 30 above, i.e., row[1] = valueDict[PROPNUM][1], I get the following message in the Python interpreter:

Traceback (most recent call last):
 File "\\qep\dnshare\GIS\Haynesville\Scripts\ShortenSticksWupdateCursor_vRichard.py", line 42, in <module>
 row[1] = valueDict[PROPNUM][1]
IndexError: list index out of range

If I understand the meaning of this error message, the update cursor is looking for a field that is not provided in the field list when the cursor was defined.  However, SHAPE@ is included in the cursor definition as the second field, with an index number of 1 and referred to as row[1] on line 30.  I don't think the message is referring to SHAPE@ in the dictionary since a dictionary is not a list.  Thoughts?

0 Kudos
RichardFairhurst
MVP Honored Contributor

The dictionary's value for each key is supposed to be a list with two items: length, shape.  The second item is the shape.  So the failure may be happening on a specific record of the dictionary.  Set a counter and print it in the for loop to determine if the failure is happening for the first record or if it is happening on a specific record of the dictionary.  If it is happening on the first record then publish the print you get of the dictionary.  If it happens on a specific record, isolate which record fails and print its contents.  This is standard debugging and you should assume nothing.  The Split line at point is capable of producing features with Null geometry and this could cause a failure of the logic I used to populate the dictionary if that created an exceptional case I did not account for.

ChristopherBevilacqua
New Contributor III

Thanks again Richard.  Python and programming in general are new to me, so I always appreciate the basic debugging tips.  

I did some basic QC on my input feature classes and confirmed that there are no geometry errors.  

The dictionary appears to be correct.  It contains 335 items, which is the number I expect based on the input I used to create sticksRF_split.  Here is a sample of what the dictionary output looks like:

 u'J7MIJDJRDM': [(1368.551930715828, <Polyline object at 0x13beb890[0x13beb860]>)], 
 u'J7MIJLGT4M': [(1571.5487528800747, <Polyline object at 0x1346a510[0x1346a4e0]>)], 
 u'J7MIJDRLXQ': [(1347.2159353990594, <Polyline object at 0x13b53110[0x13b530e0]>)], 
 u'M5QMJ0CC2L': [(1313.688077024998, <Polyline object at 0x13bebf90[0x13bebf60]>)], 
 u'J7MIJ8HJZQ': [(1344.7776753085323, <Polyline object at 0x13beb650[0x13beb620]>)], 
 u'J7MIJ5QHMQ': [(1330.1471538449503, <Polyline object at 0x1346add0[0x1346ada0]>)], 
 u'K99ID3MOXV': [(1339.5960776588317, <Polyline object at 0x13471c70[0x13471c40]>)], 
 u'J7MIIT5TJQ': [(1344.1680296403613, <Polyline object at 0x2b989d0[0x2b98980]>)], 
 u'KA8ML48AOW': [(1385.925539247241, <Polyline object at 0x13471570[0x13471540]>)], 
 u'KA8MHNX69W': [(1461.8208431411147, <Polyline object at 0x1346a890[0x1346a860]>)], 
 u'J7MIJDCPVM': [(1151.2595366475489, <Polyline object at 0x134717b0[0x13471780]>)], 
 u'J7MIJKITUM': [(2126.2848170537795, <Polyline object at 0x1346ac10[0x1346abe0]>)], 
 u'L2OLSDE8NP': [(1409.090485039698, <Polyline object at 0x134714f0[0x134714c0]>)], 
 u'J7MIJB3R7Q': [(1368.551943220522, <Polyline object at 0x13beb850[0x13beb820]>)], 
 u'P69KSAA02B': [(1573.9873186602183, <Polyline object at 0x13466f30[0x13466f00]>)], 
 u'J7MIJASTCQ': [(1347.8256852251254, <Polyline object at 0x134664f0[0x134664c0]>)], 

I checked a few of the dictionary items in ArcMap and confirmed that the first value in the dictionary is the length of the longest line associated with each key.  

I took your advice @Anonymous User Fairhurst and tried some counts and prints.  Since the dictionary looks good, I've been focusing on the update cursor, as shown below.  I'm getting some odd results that I think are pointing me in the right direction.  When I run the code below to the print statement line 3, I get a list of 1,011 PROPNUM values.  If I run the entire block of code, I get 546 values from the print statement on line 3, and then I get the "IndexError: list index out of range" message.  

with arcpy.da.UpdateCursor(targetFC, ["PROPNUM_GIS","SHAPE@"]) as uCur:
    for count, row in enumerate(uCur, start=1):
        print "This is the count of rows in the update cursor: " + str(count) + " - PROPNUM = " + row[0] # This should match the number of sticks in targetFC (i.e., sticksRF)
        # The count of rows in the update cursor = the number of records in targetFC (i.e., sticks RF; N = 1011) when I run the update cursor only
        # to this print statement.  When I run this entire block of code the interpreter stops enumerating at 546.
        # print row
        PROPNUM = row[0]
        # print PROPNUM
        if PROPNUM in valueDict:
            print "This is the count of PROPNUM in valueDict: " + str(count)
            row[1] = valueDict[PROPNUM][1]
            # print row[1]
            uCur.updateRow(row)

I think the problem I am having may be similar to one described on stackoverflow.  In this case I am using a list of dictionary key values, and when I get to the end of the list, I get the error.  Instead, I need to use an index.  Am I on the right track here?  The code below runs but neither modifies the feature geometry, nor gives messages in the Python interpreter.

try:
    with arcpy.da.UpdateCursor(targetFC, ["PROPNUM_GIS","SHAPE@"]) as uCur:
        for row in uCur:
            # print "This is the count of rows in the update cursor: " + str(count) + " - PROPNUM = " + row[0] # This should match the number of sticks in targetFC (i.e., sticksRF)
            # The count of rows in the update cursor = the number of records in targetFC (i.e., sticks RF; N = 1011) when I run the update cursor only
            # to this print statement.  When I run this entire block of code the interpreter stops enumerating at 546.
            # print row
            PROPNUM = row[0]
            # print PROPNUM
            if PROPNUM in range(len(valueDict)):
                # print "This is the count of PROPNUM in valueDict: " + str(count)
                row[1] = [valueDict[PROPNUM] + valueDict[PROPNUM+1] for PROPNUM in range(len(valueDict)-1)]

                print row[1]
                uCur.updateRow(row)
except Exception as e:
    arcpy.AddMessage(str(e.message))
0 Kudos
RichardFairhurst
MVP Honored Contributor

The dictionary does look as expected, so it is most likely not the problem.  I would go back to the original code and do the count this way, now that you know the error hits on record 546 or 547.  I start checking from count 540 on to be sure to catch the erroneous record.  Watch to see if the error occurs after the print of row or the dictionary record.  Hopefully the print will show the record causing the problem, but it may throw the error on the record that fails to print just after the one that does print, so you would have to look in the table at that record you are updating.

count = 0
with arcpy.da.UpdateCursor(targetFC, ["PROPNUM_GIS","SHAPE@"]) as uCur:
    for row in uCur:
        count += 1
        if count >= 540:
           print('Count = {0}'format(count))
           print('Row is {0}'.format(row))
        PROPNUM = row[0]
        if PROPNUM in valueDict:
            if count >= 540:
               print('Count = {0} and dictionary value is {1}'.format(count, valueDict[PROPNUM]))
            row[1] = valueDict[PROPNUM][1]
            uCur.updateRow(row)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos