8

I am trying to make a polyline endpoint snapping tool in Python using ArcGIS 10. using a Search Cursor I read in the values to an array of line objects (which contain the feature id of the line, and two "EndPoint" objects - first and last - which contain the X and Y coord of each), then process some data interpolation of the points to give all points their correct values.

So far, that part works perfectly. However, when I try to go back and edit the shapefile, I can see the data that I am changing, but I can't seem to edit it.

Here is the code for updating the fields:

# --------------------------
#
# Update the file with the new EPs
#
# --------------------------

# Create update cursor
#
rows = arcpy.UpdateCursor(outputDirectory + "\\" + trails_fc + ".shp")

# Enter for loop for each feature/row
#
i = 0
for row in rows:
    # Create the geometry object
    #
    feat = row.getValue(shapefieldname)

    partnum = 0

    if feat.getPart(partnum)[0].X != allLines[i].startEP.x:
        arcpy.AddMessage("Change: " + str(feat.getPart(partnum)[0].X) + " to " + str(allLines[i].startEP.x) )
        feat.getPart(partnum)[0].X = allLines[i].startEP.x

    rows.updateRow(row)

    arcpy.AddMessage("Now is: " + str(feat.getPart(partnum)[0].X))

    i+=1

My readout consists of statements like this:

Change: -105.512166832 to -105.699533165 Now is: -105.512166832

For some reason, the rows are not updating. And for the life of me, I can't find a tutorial or instructions on how to edit a particular point in a polyline. I can only find how to edit a point as a field in a point shapefile.

Anyone have any ideas?

2
  • 1
    Do you only have an ArcView license? If you have ArcEditor/ArcInfo, what are you trying to accomplish by building your own snap tool that Arc10's SNAP geoprocessing tool won't accomplish (ArcToolbox > Editing Tools)? (help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//…) Nov 21, 2011 at 16:31
  • It is possible that there is another way, but I am trying to understand how to manipulate the coordinates whether or not the tool is completely original. It's as much more experience as it is for functionality.
    – Kivak Wolf
    Nov 21, 2011 at 22:02

1 Answer 1

6

Unfortunately you can't directly assign new values to the existing geometry of a feature - rather, you have to create a new geometry object, and update the shape field of the feature with that new object. Fortunately, the array objects have a replace method. So rather than trying to directly modify the X coord of the point inside the array, you need to:

  • Create a new arcpy.Point object with the correct coordinates (looks like you might have done this already)
  • Get a copy of the array object stored in the row's Shape field
  • Use the replace method to set the desired point in your array with your modified point
  • Make a new Polyline object with that array
  • Use the row object's setValue method to update the Shape field with your new, correct Polyline
  • Use the cursor object's updateRow method to insert the changed row into the dataset.

Concretely:

for r in cur:
    ary = r.getValue("SHAPE").getPart(0)
    ary.replace(0,correct_point_object) # first arg 0 replaces the first point in the line
    newLine = arcpy.Polyline(ary)
    r.setValue("SHAPE",newLine)
    cur.updateRow(r)

Note the replace method takes an index and a value. Unfortunately it doesn't accept e.g. -1 as an index to the last point in the array. However you can say my_array[my_array.count].

It looks like you're precomputing the X-coordinates somewhere else and retrieving them later. If this is the case, I'd probably go the whole hog and create new Polyline objects with the correct points for each line while you're computing the correct coordinates. This will likely be easier & cleaner. That way your code could be more like

row_num = 0
    for r in cur:
        r.setValue(shapeField,correct_geometry_list[row_num])
        cur.updateRow(r)
        row_num += 1

Which, at least for me, is a bit more clear... but that's stylistic!

Edit to add:

I couldn't fit this in a comment. Without seeing your code it's hard to tell where it might be falling over. Here's a complete tested script which works for me. Hopefully it will serve as a reference. Note that here I'm calculating the new geometry directly from the old one, rather than doing two passes; that may or may not be possible depending on how you're doing your snap position calculations. Also this time I'm constructing a brand new array based on the old one rather than using the replace method, in case that's necessary.

import arcpy

def offsetPoint(old_point,X_distance,Y_distance):
    """Trivial function to offset a point - replace with what you're
actually doing."""
    new_point = arcpy.Point(old_point.X+X_distance,
                            old_point.Y+Y_distance)
    return new_point

def offsetFirstPointInLine(line_geom,X_distance,Y_distance):
    """Takes a Polyline geometry object and returns a new Polyline with
the first point of the first part offset by the distance given."""
    array = line_geom.getPart(0)
    first_point = array[0]
    new_point = offsetPoint(first_point,X_distance,Y_distance)

    # Build a new array with your new point in the 0th position, and
    # the rest of the points from the old array.
    new_array = arcpy.Array([new_point]+
                            [array.getObject(x) for x in range(1,array.count)])

    # Then make a new Polyline object with that array.
    new_line = arcpy.Polyline(new_array)
    return new_line

fc = r"C:\Users\student\Documents\ArcGIS\Default.gdb\SomeStorms"

cur = arcpy.UpdateCursor(fc)

for r in cur:
    geom = r.getValue("SHAPE")
    r.setValue("SHAPE",offsetFirstPointInLine(geom,-45000,-5000))
    cur.updateRow(r)

del r,cur

Hopefully that helps clear it up.

4
  • I had this sinking feeling that might be the case. Thank you so much for explaining how to do it! I think I will just create the entire polylines like you said. That way I have more control over it should I want to go back and adjust something else.
    – Kivak Wolf
    Nov 22, 2011 at 14:13
  • Well, unfortunately I didn't get it to work. Something very strange is going on. If I use "ary = r.getValue("SHAPE").getPart(0)" and then immediately create a polyline from ary and set the value, the shapefile looks completely different. This shouldn't happen, should it?
    – Kivak Wolf
    Nov 22, 2011 at 16:15
  • It's hard to say what's wrong exactly; I updated the answer with a more complete solution in case it helps. Actually - it sounds like you're creating the array, then modifying the point; rather, you should make a new point, then modify the array to include that point (or make a new array with that point as above).
    – ThomM
    Nov 22, 2011 at 22:30
  • ThomM: Thanks for the expanded explanation! I really appreciate it! As I found out yesterday, I am creating the arcpy.Array(), arcpy.Point()s, and using the arcpy.Ployline() class correctly. But it appears there might be a bug in the actual programming of the arcpy.Ployline() class constructor. I've submitted a bug report, but it appears that the constructor for arcpy.Polyline() is 'tossing out' or ignoring arcpy.Point()s with similar yet still unique values. So while I thought my problem was the lack of understanding how it works, it appears that it might be a bug.
    – Kivak Wolf
    Nov 23, 2011 at 13:46

Your Answer

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

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