Calculating Distance with ArcPy PointGeometry
I posted a separate answer on how to tidy your existing syntax. But geometry on the Earth's surface is hard, and ArcMap is there to help us. It provides objects to store geometry, with methods to compare them. To use them to solve your problem, you need to:
- Construct a SpatialReference object. This must have the datum your coordinates are relative to.
- Construct Point objects. This just stores XY (and possibly Z) values and has no notion of coordinate reference systems.
- Construct PointGeometry objects using your Point and SpatialReference objects.
- Call the angleAndDistanceTo method to get the distance.
This will handle matters like the Earth being a bit squashed at the poles, instead of treating it as a fixed 6,371km radius. What difference does it make? From (0°N,0°E) to (0°N,90°E), angleAndDistanceTo returns a distance 11 kilometers longer. From (0°N,0°E) to (90°N,0°E), angleAndDistanceTo returns a distance 5 kilometers shorter.
Easy Traps
- If you don't construct a
SpatialReference
object appropriate to your coordinates, your distances will be meaningless.
- The constructor for
arcpy.Point
takes the X value (latitude), then the Y (longitude). To make the arguments clear, the sample code below uses keyword arguments.
- The
angleAndDistanceTo
method has an argument that can be "GEODESIC", "GREAT_ELLIPTIC", "LOXODROME", "PLANAR" or "PRESERVE_SHAPE". Make sure you know what these mean and choose the one appropriate to your task. If using "PLANAR" you will need to reproject your PointGeometry
.
- There is a method called
distanceTo
, except it always returns distances in the coordinate system of your PointGeometry
. If you are in WGS84, your "distances" will be in degrees. You cannot specify a calculation method the way that angleAndDistanceTo
does.
Place some points on your map and use the ArcMap Measure Tool to measure the distance between them. You should find that, as long as you specify the same method, the distances it gives you are the same as from your Field Calculator function.
The Code
def Distance(Latitude1, Latitude2, Longitude1, Longitude2):
# Need to define a coordinate reference system appropriate to our inputs.
# Here I assume they are coming from GPS, so WGS 1984.
# These can also be constructed using their EPSG numbers (4326 for WGS 1984)
spatialReferenceWGS84 = arcpy.SpatialReference("WGS 1984")
# Make sure to specify which argument to the Point constructor is X (Longitude) and Y (Latitude).
point1 = arcpy.Point(X = Longitude1, Y = Latitude1)
pointGeometry1 = arcpy.PointGeometry(point1, spatialReferenceWGS84)
point2 = arcpy.Point(X = Longitude2, Y = Latitude2)
pointGeometry2 = arcpy.PointGeometry(point2, spatialReferenceWGS84)
# Do not use the pointGeometry.distanceTo function. It will return answers in degrees.
# For the different method values, see:
# https://desktop.arcgis.com/en/arcmap/latest/analyze/arcpy-classes/pointgeometry.htm#M2_GUID-5853F68B-6164-4FDA-81C9-C0DEF50F0830
# They are "GEODESIC", "GREAT_ELLIPTIC", "LOXODROME", "PLANAR" and "PRESERVE_SHAPE"
angleBetweenPoints,distanceBetweenPoints = pointGeometry1.angleAndDistanceTo(pointGeometry2, "GEODESIC")
# Distance between the two points is known, return it.
return distanceBetweensPoints
Thank you to this answer that got me on the right track here.