st_distance returns different answer than haversine formula

I have two coordinates stored as geometry in a table. They are stored in column named latlng. Now the coordinates are:

1 31.54696470032501, 74.3782182429078

2 31.548894670525055, 74.37833017871426

If I calculate the distance using haversine formula it gives me 0.21Km If I calculate using st_distance it returns 0.00193321354170466 m

Which one should I suppose to be correct?

• Does it really say "0.0019... m", or is the "m" an addition of yours? Because the value suggests in my opinion, that you ran st_distance based on a geographic coordinate-system instead of a projected one. Meaning, your "0.0019..." is in decimal degree. – Erik Aug 8 '18 at 6:58
• yes I added m because this page says so postgis.net/docs/ST_Distance.html. If I am wrong please correct me. – mobileDeveloper Aug 8 '18 at 7:47
• The first example in your link shows, what went "wrong". Your projection must be geographic, e.g. WGS 84. – Erik Aug 8 '18 at 7:57
• With following query distance is still not what haversine formula gives "select st_distance( st_transform(st_setsrid(a.latlng,4326),24370), st_transform(st_setsrid(b.latlng,4326),24370) ) from location a, location b where a.location_id = 27348 and b.location_id = 27349;" st_distance ------------------ 84.6342793261745 – mobileDeveloper Aug 8 '18 at 8:09
• PostGIS has spheroidal algebra implicitly packed into the geography type; projections are the exact opposite approach to this. try SELECT ST_Distance(a.latlng::geography, b.latlng::geography) FROM .... unlike with the Haversine, in PostGIS caring for the correct CRS of your data is of highest importance! you will be rewarded with highest precision if you do, though. – ThingumaBob Aug 8 '18 at 9:23

The first issue is that you are using latitude-longitude coordinates, while they should be expressed as longitude-latitude. If you swap them, you will get the proper result.

The second issue is that st_distance applied on geometries returns a distance using the underlying coordinate system unit. The coordinates are in degrees... so the length is also in degrees, and it doesn't make sense (the length - in meters - of a degree of latitude is not the same as a degree of longitude). In the comments, you transform the geometries to CRS 24310, whose unit is in yards.

Swapping the lat-long to long-lat and converting yards to meters, you get a distance of 215m.

Alternatively, you can do as @ThingumaBob has suggested, i.e. to cast to geography first (but you still need to swap the lat-long to long-lat)

WITH a AS (select st_makepoint(74.3782182429078,31.54696470032501)  geom),
b AS (select st_makepoint(74.37833017871426,31.548894670525055)  geom)
select st_distance( st_transform(st_setsrid(a.geom,4326),24370),
st_transform(st_setsrid(b.geom,4326),24370)
) * 0.9144 as d_trans,
ST_Distance(a.geom::geography, b.geom::geography) d_geog
from a,b;

d_trans     | d_geog
-----------------+--------------
215.90051305541 | 214.25682418
(1 row)
• I am not using lat, long separately. rather I have a latlng column which stores the lat and lng as geometry. Do I need to swap their values ? – mobileDeveloper Aug 8 '18 at 13:59
• @mobileDeveloper by swapping the coordinates, the found distance is the one you have mentioned (215m). If it is the proper distance, then yes, you need to swap them. – JGH Aug 8 '18 at 14:16