MariaDB : calculate distance between 2 points in km - mysql

I try to get some approximate distance between 2 points in mariaDB.
I use that SQL:
SELECT st_distance(POINT(50.6333,3.0667),p) from p
It outputs results such as:
0
1.9128040446888426
8.103248262271125
It seems mariaDB does not handle SRID.
Is there a way to convert these values to km ? (looks like multiplying by 110 is quite correct)
My goal is to avoid handling maths such as sin, cos, atan and approximate result is ok for me.

The result returned by st_distance are not kilometer but minutes.
For a circumference of the equator of d = 40.075km, the distance between two minutes is d / 360 = 111,319 km.
While the distance between the latitudes is constant, the distance between the longitudes from the equator to the pole caps decreases constantly. According to your example the point from one location must be somewhere in France, where the distance between longitudes is around 70km.
Since you don't want to use the Haversine formula, you can also use the Pythagorean theorem to get a more accurate result:
# Distance between Eiffel tower and Lille
SELECT ST_DISTANCE(GeomFromText("Point(50.6333 3.0669)"), GeomFromText("Point(48.853. 2.348)")) * 111.38;
-> 213.84627307672486
select sqrt(pow((50.63333 - 48.853) * 111.38,2) + pow((3.0669 - 2.348) * 70, 2));
->204.57903071304386

ST_DISTANCE is designed for flat-earth advocates.
ST_DISTANCE_SPHERE is available in InnoDB as of 5.7.
https://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html#function_st-distance-sphere

Related

Distance in meters between two Spacial Points in MySQL query

I am trying to query a MySQL database (version 5.7.15) to retrieve all locations that are within 300 meters from some coordinates (40.7542, -73.9961 in my case):
SELECT *
FROM location
WHERE st_distance_sphere(latlng, POINT(40.7542, -73.9961)) <= 300
From the MySQL documentation:
ST_Distance_Sphere(g1, g2 [, radius])
Returns the mimimum spherical distance between two points and/or
multipoints on a sphere, in meters, or NULL if any geometry argument
is NULL or empty.
Unfortunately, the query also returns points that are more than 300 meters away from POINT(40.7542, -73.9961) such as:
POINT(40.7501, -73.9949) (~ 470 meters in real life)
POINT(40.7498, -73.9937) (~ 530 meters in real life)
Note that in MySql the order of coordinates are:
1. POINT(lng, lat) - no SRID
2. ST_GeomFromText('POINT(lat lng)', 4326) - with SRID
select st_distance_sphere(POINT(-73.9949,40.7501), POINT( -73.9961,40.7542))
will return 466.9696023582369, as expected, and 466.9696023582369 > 300 of course
Just to make it clear for future people (like myself):
Mituha Sergey has answered the question in comments on the OP. The problem was that OP was using POINT(lat, lng) when in fact MySQL expects POINT(lng, lat).
Not sure about the time OP posted the question, but as of today the official documentation makes it a bit clearer:
The geometry arguments should consist of points that specify
(longitude, latitude) coordinate values:
Longitude and latitude are the first and second coordinates of the point, respectively.
Both coordinates are in degrees.
Longitude values must be in the range (-180, 180]. Positive values are east of the prime meridian.
Latitude values must be in the range [-90, 90]. Positive values are north of the equator.
From: https://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html#function_st-distance-sphere
And if you're getting "invalid arguments" errors it's probably because of that. Try adding WHERE lat between -90 and 90 AND lng between -180 and 180 just to be on the safe side haha :)

SQL needed to get a subset of locations based on lat/long with a min/max distance constraint

I have MySQL table with 500 location records that has a similar structure to:
id, name, lat, long
Lat & long are decimal (float) location values.
My need is to return a random 100 record set that are a minimum 200 meters and a maximum 500 meters away from each other. I'm familiar with using the great circle formula to get the distance between two points. However, I have no idea how to write a select statement to compare all locations against each other to ensure the distance requirements for the random 100 selected? Any thoughts or help would be greatly appreciated. My only tools are a MySQL database so the solution needs to be written in MySQL SQL. Thank you in advance.
SELECT *
FROM (
SELECT p.latitude1 AS latitude1, p.longitude1 AS longitude1, p.latitude2 AS latitude2, p.longitude2 AS longitude2,
(((ACOS(SIN((latitude2*PI()/180)) * SIN((latitude1*PI()/180))+COS((latitude2*PI()/180)) * COS((latitude1*PI()/180)) * COS(((longitude2- longitude1)* PI()/180))))*180/PI())*60*1.1515) AS distance
FROM places p
)
WHERE distance > 200 AND distance < 500
ORDER BY RAND()
LIMIT 100
If you are only looking at 500 metres then take some short cuts and pretend this is an xy space and use simple trigonometry. Can you even get away with a square instead of donut ?
You could use that as the first cut and then to proper maths on the remainder.
Your second cut will now be small enough. Assign a random no to each and take top X. Voila
edit For the first part, cheat and use Google maps. Find out what fraction of a degree equates to 500 metres at the equator. Use x +/- that value and the same for y. Quick and dirty first cut. **ok it won't work at the poles! **

ST_Distance_Sphere in mysql not giving accurate distance between two locations

My requirement is to calculate the distance between two locations on a given map using mysql. I found a function in mysql named ST_Distance_Sphere which returns the minimum spherical distance between two locations and/or multi locations on a sphere in meters.
When I computed the distance between two locations using ST_Distance_Sphere and the lat_lng_distance function , I found that the ST_Distance_Sphere is not giving the same distance as that of the lat_lng_distance function.
My lat_lng_distance function code is as follows
CREATE FUNCTION `lat_lng_distance` (lat1 FLOAT, lng1 FLOAT, lat2 FLOAT, lng2 FLOAT)
RETURNS FLOAT
DETERMINISTIC
BEGIN
RETURN 6371 * 2 * ASIN(SQRT(
POWER(SIN((lat1 - abs(lat2)) * pi()/180 / 2),
2) + COS(lat1 * pi()/180 ) * COS(abs(lat2) *
pi()/180) * POWER(SIN((lng1 - lng2) *
pi()/180 / 2), 2) ));
END
The two locations ((38.898556,-77.037852),(38.897147,-77.043934)) passed to the ST_Distance_Sphere and lat_lng_distance function is as follows
SET #pt1 = ST_GeomFromText('POINT (38.898556 -77.037852)');
SET #pt2 = ST_GeomFromText('POINT (38.897147 -77.043934 )');
SELECT ST_Distance_Sphere(#pt1, #pt2)/1000,lat_lng_distance(38.898556,-77.037852,38.897147,-77.043934 );
The Results Obtained is as follows
I checked the distance between the two locations on google maps and found that lat_lng_distance is close to the actual distance between the two locations. Can someone let me know why is the ST_Distance_Sphere not giving accurate distance between two locations?
ST_DISTANCE_SPHERE requires points to be expressed as POINT(longitude, latitude), you have them reversed in your code
set #lat1 = 38.898556;
set #lon1 = -77.037852;
set #lat2 = 38.897147;
set #lon2 = -77.043934;
SET #pt1 = point(#lon1, #lat1);
SET #pt2 = point(#lon2, #lat2);
SELECT ST_Distance_Sphere(#pt1, #pt2)/1000,
lat_lng_distance(#lat1,#lon1,#lat2,#lon2);
+-------------------------------------+-------------------------------------------+
| ST_Distance_Sphere(#pt1, #pt2)/1000 | lat_lng_distance(#lat1,#lon1,#lat2,#lon2) |
+-------------------------------------+-------------------------------------------+
| 0.549154584458455 | 0.5496311783790588 |
+-------------------------------------+-------------------------------------------+
This gives a result that is much closer to the value returned by your function.
For all who are working with MYSQL 8:
For all mysql geolocation functions there must be the right SRID used, otherwise you won't get the right results.
Most commenly used is SRID 4326 (GPS Coordinates, Google Earth) AND SRID 3857 (used on Google Maps, OpenStreetMap, and most other web maps).
Example of a correct distance calculation between two points:
SELECT ST_Distance(ST_GeomFromText('POINT(51.513 -0.08)', 4326), ST_GeomFromText('POINT(37.745 -122.4383)', 4326)) / 1000 AS km;
Here is a good explanation of this topic:
https://medium.com/maatwebsite/the-best-way-to-locate-in-mysql-8-e47a59892443
There are some very good explanations from the mysqlserverteam:
https://mysqlserverteam.com/spatial-reference-systems-in-mysql-8-0/
https://mysqlserverteam.com/geography-in-mysql-8-0/
First of all, you could not use default SRID of 0 to do any calculations. When you use geometry from text function you have to provide 4326 (SRID that is degrees) as this is what your input format is. MYSQL might not care about it, but it should done as every serious GIS database does care and demands that input SRID was specified.
Second longitude is X and latitude is Y (not another way around)
SET #pt1 = ST_GeomFromText('POINT (-77.037852 38.898556 )', 4326);
SET #pt2 = ST_GeomFromText('POINT (-77.043934 38.897147 )',4326);
Last but not least when you are calculating distance you must transform coordinates to a local most precise SRID available for the region you are.
For example SRID 2877 is used for USA (where according your coordinates you are).
MYSQL ST_Distance_Sphere function does not care about input SRID and always return results in meters.
However it is not generally right and all other database use designated SRID units of measures applicable to it.
Bellow I am trying to do things right and transforming SRID to 2877 even MYSQL would work the same way if we left everything as 4326 (google mercator).
For 2877 PostGRES would return results in feet for the same query but MYSQL is still giving back meters. So output is devided by 1609 and we are getting the correct result of around 0.34 miles. It is a correct value as was tested using different methods
SELECT ST_Distance_Sphere(ST_GeomFromText(ST_AsText(#pt1),2877), ST_GeomFromText(ST_AsText(#pt2),2877))/1609.344;
As an aside, MySQL internally implements this with an obscure and dated constant. So it really depends on your definition of accurate.
ST_Distance_Sphere
So in essence, the radius in MySQL was lifted from a lazy-copy-job from PostGIS that converted a radius in miles to meters from an obscure constant from a random 20-year old PostgreSQL module.

Correct way of finding distance between two coordinates using spatial function in MySql

I am trying to calculate distance between two locations using spatial functions in both Mysql and PostgresSQL. I have taken the latitude and longitude from Google. The details are below
Location one - Lat: 42.260223; Lon: -71.800010
Location two - Lat: 42.245647; Lon: -71.802521
SQL Query used:
SELECT DISTANCE(GEOMFROMTEXT('Point(42.260223 -71.800010)'),GEOMFROMTEXT('Point(42.245647 -71.802521)'))
The both databases are giving the same result 0.014790703059697. But when I calculate distance in other systems the results are different. Please refer the below links
http://www.zip-codes.com/distance_calculator.asp?zip1=01601&zip2=01610&Submit=Search = 1.44 miles
http://www.distancecheck.com/zipcode-distance.php?start=01601&end=01610 = 1.53 miles
So I want to know whether my calculation method/query is right or not. And if it is wrong, then what is the right way of querying the db for the distance.
The simple answer is to use the Haversine formula. This assumes the earth is a sphere, which it isn't, but it's not a bad approximation. This, with lots of other details are described in this presentation:
http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
In the case above, MySql is simply applying the pythagorean theorem: c2 = a^2 + b^2. In this specific case SQRT((42.245647 - 42.260223)^2 + (-71.802521^2 - -71.800010)^2) = 0.014790703.
There are actually two problems with using the MySql distance functon for distance with coordinates on a sphere. (1) MySql is caclulating distance on a plane, not a sphere. (2) The result is coming back in a form of degrees, not miles. To get a true, spherical distance in miles, km, ft etc, you need to convert your lat and long degrees into the units you want to measure by determining the radius from a line through the center of the earth for the latitude(s) you are measuring.
To get a true measure is quite complicated, many individuals and companies have made careers out of this.

Distance between two points. East-west + North-south

I need to calculate the distance between two points, but not in the regular way. I need to know 'the east to west distance' + 'the north to south distance'. I guess this is more simple then the regular 'as the crow flies' calculation but i still can't figure out how to do it.
I want to do this using a MySQL query and preferably have the result returned in km. One of the points will be a constant in the query and the other point is a point from the DB so something like SELECT abs(longitude-39.12345)...+abs(latitude... AS Distance FROM shops WHERE shopname='Bingo'.
Thanks in advance!
The north-to-south distance is proportional to the difference in the latitudes. It's about 1 nautical mile per minute of arc (the circumference of the earth is about 21600 nautical miles).
The east-to-west distance is proportional to the difference in the longitudes, but it also varies with the latitude (e.g. it's zero at the poles): I think it's proportional to the cosine of latitude.
Your answer depends on the accuracy required in your answer. If you don't need an answer more accurate than a spherical earth model, you can use a solution similar to the one given by Captain Tom. If you require more accuracy, you'll need to assume the earth is an oblate spheroid. See http://en.wikipedia.org/wiki/Vincenty%27s_formulae for a couple of solutions.
The east-west difference between two points at different latitudes is a distinct number in degrees of longitude, but converting this to miles is problematic because the miles per degree vary according to the latitude. For example, Los Angles and New York City are 44.3 degrees of longitude apart, but converting this to miles would result in a larger number at LA's latitude than at NYC's latitude, since latitude lines are longest at the equator and shrink to zero at the poles.
A reasonable convention would be to count the E-W distance as the average of the two distances calculated at the two latitudes.
You can determine the distance between any two geocode points using the Great-Circle Distance. Here is another decent article on the subject.
The following is an example in C# which shows how to do this:
var earth_radius = Constants.EarthRadius; // 6377.8 km
var dLat = ToRadians(fromLatitude - toLatitude);
var dLon = ToRadians(fromLongitude - toLongitude);
var a =
Math.Sin(dLat / 2) *
Math.Sin(dLat / 2) +
Math.Cos(ToRadians(fromLatitude)) *
Math.Cos(ToRadians(toLatitude)) *
Math.Sin(dLon / 2) *
Math.Sin(dLon / 2);
var c = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)));
var distanceInKilometers = earth_radius * c;
Use simple trig. The normal "as the crow flies" distance is the hypotenuse of the right triangle formed with the two points you have in mind at either ends of the hypotenuse.
Just look at this http://en.wikipedia.org/wiki/Hypotenuse and the solution should become clear.