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.
If your editing situation is conditional you may use a CASE expression within field calculator:
CASE expression A conditional expression that can be used to evaluate
multiple expressions and return a result.
Syntax:
CASE
WHEN condition THEN result
[ ...n ]
[ ELSE result ]
END
Example:
CASE
WHEN "mycolumn" = 'A' THEN 'Letter A'
WHEN "mycolumn" = 'B' THEN 'Letter B'
ELSE "mycolumn"
END
This should at least eliminate the need to perform selections and filters before running calculations.
Best Answer
This should do it and is a little simpler than the examples in the online help for UpdateCursor which is nevertheless worth a read.
I've assumed your shapefile is in a folder called C:\temp and that structuretype is an integer field. If it is a text field just use "3" and "4" instead of 3 and 4.
Warning: The way I am referencing field values here only works for old style cursors and not for the superior and faster cursors in the Data Access (arcpy.da) module, which was introduced with ArcGIS 10.1.