ArcGIS Python Geometry from Shapely

The ArcGIS Python API Geometry object includes the capability to export the geometry to a Shapely Geometry object, but not the capability to create a new ArcGIS Geometry object from a Shapely object. Thankfully, it is not difficult to add this capability to every ArcGIS Geometry object in a namespace.

Periodically I am working in an environment without arcpy available. Typically this is when using Docker containers, but also can simply be due to working natively in a Mac environment as well. In these environments, if I need to perform geometry manipulation operations, I need to convert ArcGIS Geometry objects to Shapely Geometry objects, perform the spatial operations, and convert the Shapely Geometry objects back to ArcGIS Geometry objects.

Converting from ArcGIS Geometry to Shapely Geometry is easy enough using the as_shapely method. Going the other way is not as easy since there is not a native method. Simply add the following code at the top, and you will have the from_shapely method available on any type of Geometry object in the same namespace.

from arcgis.geometry import BaseGeometry

@classmethod
def from_shapely(cls, shapely_geometry):
    return cls(shapely_geometry.__geo_interface__)

BaseGeometry.from_shapely = from_shapely

Then, doing something like snapping a point to a line, and then trimming the line at the point can be performed using Shapely with the results being converted back to ArcGIS Python geometry objects.

from shapely import ops
from arcgis.geometry import Geometry

# convert from ArcGIS to Shapely
shapely_linestring = arcgis_polyline.as_shapely
shapely_point = arcgis_point.as_shapely

# snap using Shapely
snap_point = shapely_linestring.interpolate(
    shapely_linestring.project(shapely_point)
)

# trim using Shapely (split and only keep the first LineString)
trim_linestring = ops.split(shapely_linestring, snap_point)[0]

# convert back to ArcGIS and replace original variable values
arcgis_polyline = Geometry.from_shapely(trim_linestring)
arcgis_point = Geometry.from_shapely(snap_point)