ArcMap 10.7.1 (Oracle SDE.ST_GEOMETRY):
I'm hoping to use the Calculate Field GP tool to update the coordinates of a polyline FC's shape column.
I have a script that works in a standalone Python IDE (PyScripter):
Source: Set M-values to cumulative length of line (via ArcPy)
import arcpy connection = "Database Connections\my_conn.sde" feature_class = connection + "\my_owner.my_fc" spatial_reference = arcpy.Describe(feature_class).spatialReference with arcpy.da.Editor(connection) as edit_session: with arcpy.da.UpdateCursor(feature_class, "SHAPE@") as cursor: for row in cursor: geometry = row[0].densify("ANGLE", 10000, 0.174533) parts = arcpy.Array() for part in geometry: points = arcpy.Array() for point in part: point.M = geometry.measureOnLine(point) points.append(point) parts.append(points) row[0] = arcpy.Polyline(parts, spatial_reference) cursor.updateRow(row)
I want to use that same logic, but in the Calculate Field GP tool:
Expression: ----------- new_shape( !SHAPE! ) Code Block: ----------- def new_shape(geometry): geometry = geometry.densify("ANGLE", 10000, 0.174533) parts = arcpy.Array() for part in geometry: points = arcpy.Array() for point in part: point.M = geometry.measureOnLine(point) points.append(point) parts.append(points) return arcpy.Polyline(parts)
But when I run that Calculate Field code, I get an error:
"geoprocessing describe geometry object' object is not iterable"
ERROR 000539: Error running expression: new_shape( GPVARIANTOBJECT0 ) Traceback (most recent call last): File "<expression>", line 1, in <module> File "<string>", line 4, in new_shape TypeError: 'geoprocessing describe geometry object' object is not iterable Failed to execute (CalculateField).
Question:
What am I doing wrong?
The geometry was iterable when using the full blown script in PyScripter. I don't understand why it isn't iterable in the Calculate Field script too.
Edit:
I can get part of the script work. I can densify the shape:
Expression: ----------- new_shape( !SHAPE! ) Code Block: ----------- def new_shape(geometry):
geometry = geometry.densify("ANGLE", 10000, 0.174533)
return geometry
(And if I wanted to, I could also set the shape to None via the Calculate Field GP tool.)
But when I try to loop through the geometry's parts, I get the aforementioned error.
Any ideas? Thanks!
Solved! Go to Solution.
For whatever reason, for point in part: doesn't seem to work in the Field Calculator. It runs without errors, but it doesn't actually loop through the points. So the script returns an empty shape, which isn't what we want. I'm not sure what the root cause is.
But if we replace it with:
for j in range(part.count): point = part.getObject(j)
...then that works as expected. It loops through the points.
Expression: ----------- new_shape( !SHAPE! ) Code Block: ----------- def new_shape(geom): spatial_reference = geom.spatialReference geom = geom.densify("ANGLE", 10000, 0.174533) parts = arcpy.Array() for i in range(geom.partCount): part = geom.getPart(i) #I think "part" is an ArcPy array: https://desktop.arcgis.com/en/arcmap/10.7/analyze/arcpy-classes/array.htm points = arcpy.Array() for j in range(part.count): #Alternatively, this would work in the Calculate Field GP tool too: "for j in range(len(part)):" ...and this as well: "for j, point in enumerate(part):" #...but "for point in part:" doesn't work in the Calculate Field GP tool. It runs without errors, but it doesn't actually loop through the points. So the script returns an empty shape, which isn't what we want. #Note: this doesn't work in the Calculate Field GP tool: "point = part[j]" ...although that might work in newer versions of ArcPy. See a newer version of the ArcPy Array docs: "The getObject method is equivalent to indexing an object; that is, obj.getObject(0) is equivalent to obj[0]." point = part.getObject(j) #But this does work: ".getObject(j)" point.M = geom.measureOnLine(point) points.append(point) parts.append(points) return arcpy.Polyline(parts, spatial_reference)
Screenshot of using the field calculator on the shape column.
The following code almost works.
It runs without errors in the Calculate Field GP tool.
But unfortunately, the shape column gets set to null (not what I want).
I think the problem is: When the function is used by the Calculate Field gp tool, the points aren't being iterated (this bit is being skipped: for point in part:). So the shape that is returned is empty/invalid...that's why the shape is being set to null.
Expression: ----------- new_shape( !SHAPE! ) Code Block: ----------- def new_shape(geom): spatial_reference = geom.spatialReference geom = geom.densify("ANGLE", 10000, 0.174533) parts = arcpy.Array() for i in range(geom.partCount): part = geom.getPart(i) points = arcpy.Array() for point in part: point.M = geom.measureOnLine(point) points.append(point) parts.append(points) return arcpy.Polyline(parts, spatial_reference)
For what it's worth, the Python function above works fine, when run within a full-blown script in an IDE like PyScripter:
import arcpy connection = "Database Connections\my_conn.sde" feature_class = connection + "\my_owner.my_fc"
def new_shape(geom): spatial_reference = geom.spatialReference geom = geom.densify("ANGLE", 10000, 0.174533) parts = arcpy.Array() for i in range(geom.partCount): part = geom.getPart(i) points = arcpy.Array() for point in part: point.M = geom.measureOnLine(point) points.append(point) parts.append(points) return arcpy.Polyline(parts, spatial_reference) with arcpy.da.Editor(connection) as edit_session: with arcpy.da.UpdateCursor(feature_class, ["SHAPE@","OID@"]) as cursor: for row in cursor: if row[1] == 1928: geom = new_shape(row[0]) row[0] = geom cursor.updateRow(row)
But when I use the function in the Calculate Field tool (in ArcMap-->Calculate Field GP, or as a standalone Calculate Field GP script in PyScripter), the shape gets set to null.
I think the problem is: When the function is used by the Calculate Field gp tool, the points aren't being iterated properly (in other words, this bit is being skipped: for point in part:). So the shape that is returned is invalid and that's why the shape is being set to null.
For whatever reason, for point in part: doesn't seem to work in the Field Calculator. It runs without errors, but it doesn't actually loop through the points. So the script returns an empty shape, which isn't what we want. I'm not sure what the root cause is.
But if we replace it with:
for j in range(part.count): point = part.getObject(j)
...then that works as expected. It loops through the points.
Expression: ----------- new_shape( !SHAPE! ) Code Block: ----------- def new_shape(geom): spatial_reference = geom.spatialReference geom = geom.densify("ANGLE", 10000, 0.174533) parts = arcpy.Array() for i in range(geom.partCount): part = geom.getPart(i) #I think "part" is an ArcPy array: https://desktop.arcgis.com/en/arcmap/10.7/analyze/arcpy-classes/array.htm points = arcpy.Array() for j in range(part.count): #Alternatively, this would work in the Calculate Field GP tool too: "for j in range(len(part)):" ...and this as well: "for j, point in enumerate(part):" #...but "for point in part:" doesn't work in the Calculate Field GP tool. It runs without errors, but it doesn't actually loop through the points. So the script returns an empty shape, which isn't what we want. #Note: this doesn't work in the Calculate Field GP tool: "point = part[j]" ...although that might work in newer versions of ArcPy. See a newer version of the ArcPy Array docs: "The getObject method is equivalent to indexing an object; that is, obj.getObject(0) is equivalent to obj[0]." point = part.getObject(j) #But this does work: ".getObject(j)" point.M = geom.measureOnLine(point) points.append(point) parts.append(points) return arcpy.Polyline(parts, spatial_reference)
Screenshot of using the field calculator on the shape column.