Use Calculate Field GP tool to update feature coordinates

435
2
Jump to solution
02-14-2022 02:15 PM
Labels (1)
Bud
by
Notable Contributor

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:

Screenshot

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:

Screenshot

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!

0 Kudos
1 Solution

Accepted Solutions
Bud
by
Notable Contributor

 

Solved:

 

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.

View solution in original post

0 Kudos
2 Replies
Bud
by
Notable Contributor

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.

0 Kudos
Bud
by
Notable Contributor

 

Solved:

 

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.

0 Kudos