inaccurate distance measurements in Python

I've tried several ways of measuring the distance between two points in my Django application and comparing the results to a reliable measurement. My numbers are way off. For example, I'm assuming the real distance between Dublin and Liverpool is ~217 km as reported by Google Maps: Using geopy (should be extremely accurate):

>>> from geopy.distance import distance
>>> dublin = (-6.270447, 53.339791)
>>> liverpool = (-2.991028, 53.402061)
>>> distance(dublin, liverpool).km
362.70989455939394

Using Django's GEOS API (less accurate linear calculation):

>>> from django.contrib.gis.geos import Point
>>> dublin = Point(-6.270447, 53.339791, srid=3857)
>>> liverpool = Point(-2.991028, 53.402061, srid=3857)
>>> dublin.distance(liverpool)*100
328.00101418228877

EDIT: Using a better projection for this area (UTM 30N) yields almost the same result:

>>> dublin.transform(32630)
>>> liverpool.transform(32630)
>>> dublin.distance(liverpool)*100
328.32200116442266

In both cases I'm off by over 100km! Measuring small distances (<1km) is just as inaccurate. What am I doing wrong here!?

• For your django example, doesn't EPSG:3857 use meters instead of degrees? – Evil Genius Jun 15 '16 at 18:52
• Mercator should not ever be used for distance measurement, especially that far north. – Vince Jun 15 '16 at 18:54
• I tried transforming the points to 32630 (UTM zone 30N) and got 328.3 km. Almost no difference in this case. There's nothing about projection in the geopy docs or in other answers, and the Vincenty distance I get should be accurate on the mm level. – kontextify Jun 15 '16 at 19:06
• dublin.distance(liverpool)*100 <-- The fact that you had to multiply by 100 instead of 1000 should tell you something suspicious is going on here. – Mintx Jun 15 '16 at 20:45

If you inverse the coordinates, it does not work (geopy uses (latitude,longitude) in the WGS84 crs)

dublin = (53.33306,-6.24889)
liverpool  = ( 53.41058,-2.97794)
print distance(dublin, liverpool).km
217.863019038
print(vincenty(dublin, liverpool).kilometers)
217.863019038
print(great_circle(dublin, liverpool).kilometers)
217.211596704

GEOS (shapely, django) uses a Cartesian plane and the Euclidean distance. With pyproj (django uses (longitude,latitude))

from django.contrib.gis.geos import Point
dublin = Point(-6.270447, 53.339791, srid=4326) # in degrees
liverpool = Point(-2.991028, 53.402061, srid=4326) # in degrees
dublin.distance(liverpool)*100
328.00101418228877 # units ?
import pyproj
# conversion from WGS84 to epsg:3857
p1 = pyproj.Proj(proj='latlong',datum='WGS84')
p2 = pyproj.Proj(init='epsg:3857')
a = pyproj.transform(p1,p2,-6.270447, 53.339791)
b = pyproj.transform(p1,p2,-2.991028, 53.402061)
dublin = Point(a) # in meters
liverpool = Point(b) # in meters
dublin.distance(liverpool)/1000 # Euclidean
365.2480859440489 #in km

But, as Vince says, the Mercator projection should not ever be used for distance measurement.

With the EPSG:32630 (UTM zone 30N):

p3 = pyproj.Proj(init='epsg:32630')
a = pyproj.transform(p1,p3,-6.270447, 53.339791)
b = pyproj.transform(p1,p3,-2.991028, 53.402061)
dublin = Point(a)
liverpool = Point(b)
dublin.distance(liverpool)/1000
218.32514783088294 #in km

And all the results (geopy and django ) are comparable with the Google distance or the Distance from Liverpool to Dublin (218 km)

• The lon,lat / lat,lon confusion strikes again! I was sure I had it right there. – kontextify Jun 15 '16 at 20:42