# Geometry Operations with ArcMap Field Calculator

Field Calculator is one of the most frequently used tools within ArcMap: taught as part of nearly every introductory GIS course, it offers spreadsheet-like features to the normally static attribute table. Starting in ArcGIS 10.0, the attribute table also exposes the raw `Geometry` object of each feature to Field Calculator. This underutilized feature allows for rapid access to data that normally requires running a separate geoprocessing tool and joining the result to the attribute table. Building on my previous post about geodesic areas, I’ve compiled some of the more useful one-line geometry field calculations.

These examples assume that the name of the shape field is `SHAPE`. Also, don’t forget that the Python parser must be used. If you immediately see an `ERROR 999999` message, then it is likely that the parser is inadvertently set to `VB Script`.

##### Centroids and Midpoints
• `!SHAPE!.trueCentroid.X` and `!SHAPE!.trueCentroid.Y` return the longitude and latitude coordinates of the true centroid of the feature as floating point numbers. For concave shapes (e.g. a horseshoe) this point can lie outside of the feature.
• `!SHAPE!.labelPoint.X` and `!SHAPE!.labelPoint.Y` return the longitude and latitude coordinates of the label point of the feature as floating point numbers. The label point always lies within the feature, even when concave.
• `!SHAPE!.centroid.X` and `!SHAPE!.centroid.Y` return the longitude and latitude coordinates of the centroid of the feature as floating point numbers. This is identical to the true centroid above, unless that point is outside of the feature in which case the label point is returned.
• `!SHAPE!.positionAlongLine(0.5,True).firstPoint.X` and `!SHAPE!.positionAlongLine(0.5,True).firstPoint.Y` return the longitude and latitude coordinates of the midpoint of the feature as floating point numbers. To get the coordinates for the point at another fraction along the line, replace `0.5` with a value between `0.0` and `1.0`. For example,  `!SHAPE!.positionAlongLine(0.2, True)` returns the point one-fifth of the length from the start of the feature to the end of the feature. Passing in `False` for the boolean parameter will return the point at a horizontal distance along the line rather than a fraction of the line.
##### Length, Perimieter, and Area
• `!SHAPE!.length `returns the planar length of a line feature or the planar perimeter of a polygon feature. The units are the feature’s coordinate system’s linear units. This is identical to the `SHAPE_Length` field automatically added in geodatabase feature classes.
• `!SHAPE!.getLength("PLANAR","KILOMETERS")` returns the planar length of a line feature or the planar perimieter of a polygon feature in kilometers. The area can be calculated in other units, identical to the Calculate Geometry tool in the attribute table.
• `!SHAPE!.getLength("GEODESIC","KILOMETERS")` returns the geodesic length of a line feature or the geodesic perimeter of a polygon feature in kilometers. This accurate length calculation can be performed on feature classes in geographic coordinate systems (i.e. “unprojected”). The length can be calculated in other units and with other measurement types, as discussed in my detailed post about geodesic areas.
• `!SHAPE!.area` returns the planar area of a polygon feature in the feature’s coordinate system as a floating point number. The units are the square of the feature’s coordinate system’s linear units. This is identical to the `SHAPE_Area` field automatically added in geodatabase feature classes.
• `!SHAPE!.getArea("PLANAR","SQUAREKILOMETERS")` returns the area of a polygon feature projected onto a two-dimensional plane in square kilometers. The area can be calculated in other units, identical to the Calculate Geometry tool in the attribute table.
• `!SHAPE!.getArea("GEODESIC","SQUAREKILOMETERS")` returns the geodesic area of a polygon feature in square kilometers. This accurate area calculation can be performed on feature classes in geographic coordinate systems (i.e. “unprojected”). The area can be calculated in other units and with other measurement types, as discussed in the in my detailed post about geodesic areas.
• `sorted([arcpy.Polygon(!SHAPE!.getPart(i), !SHAPE!.spatialReference).area for i in range( !SHAPE!.partCount)])[-1]` returns the area of the largest part within a polygon. If the polygon has only one part, it returns the area of that part. This complex expression gets the individual parts of a multipart polygon, retrieves their areas, stores them in a Python list via a list comprehension, sorts the list, then takes the last (i.e. largest) value.
##### Extents, Convex Hulls, and Compactness
• `!SHAPE!.extent.YMax`, `!SHAPE!.extent.XMax`, `!SHAPE!.extent.YMin`, and `!SHAPE!.extent.XMin` return the coordinate for the north, east, south, and western bounds of the feature, respectively.
• `!SHAPE!.extent.polygon` returns a polygonal representation of the extent that can then be the target of other operations, e.g. `!SHAPE!.extent.polygon.centroid` for the centroid of the extent, `!SHAPE!.extent.polygon.area` for the area of the extent, `!SHAPE!.extent.polygon.length`, for the perimeter of the extent.
• `!SHAPE!.convexHull()` returns a polygonal convex hull of an input point or line feature. This polygon can then be the target of other operations, e.g. `!SHAPE!.convexHull().centroid` for the centroid of the convex hull, `!SHAPE!.convexHull().extent` for the extent of the convex hull, `!SHAPE!.convexHull().area` for the area of the convex hull, `!SHAPE!.convexHull().length`, for the perimeter of the convex hull-.
• `!SHAPE!.area/!SHAPE!.convexHull().area` returns the area of the feature divided by the area of the convex hull. This is a useful quick measure of compactness of concave polygons, where values closer to 1 denote more compact polygons without large concavities.
##### Part and Vertex Counts
• `!SHAPE!.partCount` returns the number of parts in the feature as an integer number. Features with more than one part as considered multipart, and consist of distinct, non-contiguous polygons linked to a single row in the attribute table.
• `!Shape!.pointCount` returns the number of vertices in a line or polygon feature feature as an integer number. Remember that there is an additional last vertex in a polygon that is a duplicate of the first vertex to “close” the shape, so a square will return a value of 5, a hexagon will return a value of 7, etc.
• `!Shape!.pointCount-!Shape!.partCount` returns the number of unique vertices in a valid polygon feature as an integer number. The subtraction of `!Shape!.partCount` is necessary to avoid double counting the first vertex (of each part), which is repeated in order to “close” the part.
##### Densified and Simplified Geometry
• `!SHAPE!.densify("DISTANCE",1000,100)` returns a densified line or polygon with additional vertices every 1000 horizontal units and a maximum deviation from the original line of 100 horizontal units. Outputting this value to the `SHAPE` field itself is equivalent to the Densify tool, except that it can be performed with only an ArcGIS for Desktop Basic license rather than a Standard license.
• `!SHAPE!.generalize(150)` returns a simplified line with vertices removed within a tolerance of 150 horizontal units using the Douglas-Peucker algorithm. Outputting this value to the `SHAPE` field itself is equivalent to the Simplify Line tool/Simplify Polygon tool, except that it can be performed with only an ArcGIS for Desktop Basic license rather than a Standard license.
##### Projected Geometries
• It is possible to project geometries to a new coordinate system defined by a `SpatialReference` object. The constructor for this object uses the “well-known identifier” (WKID) for different coordinate systems, as listed in this PDF of geographic coordinate systems and this PDF of projected coordinate systems.
• `arcpy.PointGeometry(!SHAPE!.centroid, !SHAPE!.spatialReference ).projectAs(arcpy.SpatialReference(26986)).firstPoint.X` returns the longitude of the centroid projected from its native coordinate system to the NAD83 Massachusetts State Plane coordinate system (WKID 26986). To get the latitude, replace the final `X` with `Y`; to use different centroid types, replace `centroid` with `labelPoint` or `trueCentroid`; to output a different coordinate system, replace `26986` with the output coordinate system’s WKID.
• `!SHAPE!.projectAs(arcpy.SpatialReference(26986).exportToString()).area` returns the area of a polygon projected from its native coordinate system to the NAD83 Massachusetts State Plane coordinate system (WKID 26986). To output a different coordinate system, replace `26986` with the output coordinate system’s WKID.
• The previous example converts the `SpatialReference` object to a string using the `exportToString()` method, while the preceding example does not. This is necessary because `!SHAPE!` returns a `<type 'geoprocessing describe geometry object'>` rather than a true `<class 'arcpy.arcobjects.geometries.PointGeometry'>` or <`class 'arcpy.arcobjects.geometries.Polyline'>` or  `<class 'arcpy.arcobjects.geometries.Polygon'>` object. As such, all method parameters are converted to strings via `gp_fixargs()`. This works fine for the `getLength()`, `getArea()`, `conexHull()`, `densify()`, and `simplify()` methods whose parameters are all easily stringified `str`, `int`, and `float` primitives. However, this stringification does not handle `SpatialReference` objects. So if you encounter `RuntimeError: Object: CreateObject error creating spatial reference`, try using the `exportToString()` method on the `SpatialReference` object.

## 2 thoughts on “Geometry Operations with ArcMap Field Calculator”

1. Matt says:

Thanks Dan,

Very useful blog. Keep up the good work!

2. Andrew says:

Really helpful summary. I especially like the simple approach to measuring “compactness”

This site uses Akismet to reduce spam. Learn how your comment data is processed.