Find distance between two points using latitude and longitude in mysql - mysql

Hi I have the following table
--------------------------------------------
| id | city | Latitude | Longitude |
--------------------------------------------
| 1 | 3 | 34.44444 | 84.3434 |
--------------------------------------------
| 2 | 4 | 42.4666667 | 1.4666667 |
--------------------------------------------
| 3 | 5 | 32.534167 | 66.078056 |
--------------------------------------------
| 4 | 6 | 36.948889 | 66.328611 |
--------------------------------------------
| 5 | 7 | 35.088056 | 69.046389 |
--------------------------------------------
| 6 | 8 | 36.083056 | 69.0525 |
--------------------------------------------
| 7 | 9 | 31.015833 | 61.860278 |
--------------------------------------------
Now I want to get distance between two points. Say a user is having a city 3 and a user is having a city 7. My scenario is one user having a city and latitue and longtitude is searching other users distance from his city. For example user having city 3 is searching. He wants to get distance of user of any other city say it is 7. I have searched and found following query
SELECT `locations`.`city`, ( 3959 * acos ( cos ( radians(31.589167) ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians(64.363333) ) + sin ( radians(31.589167) ) * sin( radians( Latitude ) ) ) ) AS `distance` FROM `locations` HAVING (distance < 50)
As for as I know this query finds distance from one point to all other points. Now I want to get distance from one point to other point.
Any guide line will be much appreciated.

I think your question says you have the city values for the two cities between which you wish to compute the distance.
This query will do the job for you, yielding the distance in km. It uses the spherical cosine law formula.
Notice that you join the table to itself so you can retrieve two coordinate pairs for the computation.
SELECT a.city AS from_city, b.city AS to_city,
111.111 *
DEGREES(ACOS(LEAST(1.0, COS(RADIANS(a.Latitude))
* COS(RADIANS(b.Latitude))
* COS(RADIANS(a.Longitude - b.Longitude))
+ SIN(RADIANS(a.Latitude))
* SIN(RADIANS(b.Latitude))))) AS distance_in_km
FROM city AS a
JOIN city AS b ON a.id <> b.id
WHERE a.city = 3 AND b.city = 7
Notice that the constant 111.1111 is the number of kilometres per degree of latitude, based on the old Napoleonic definition of the metre as one ten-thousandth of the distance from the equator to the pole. That definition is close enough for location-finder work.
If you want statute miles instead of kilometres, use 69.0 instead.
http://sqlfiddle.com/#!9/21e06/412/0
If you're looking for nearby points you may be tempted to use a clause something like this:
HAVING distance_in_km < 10.0 /* slow ! */
ORDER BY distance_in_km DESC
That is (as we say near Boston MA USA) wicked slow.
In that case you need to use a bounding box computation. See this writeup about how to do that. http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
The formula contains a LEAST() function. Why? Because the ACOS() function throws an error if its argument is even slightly greater than 1. When the two points in question are very close together, the expression with the COS() and SIN() computations can sometimes yield a value slightly greater than 1 due to floating-point epsilon (inaccuracy). The LEAST(1.0, dirty-great-expression) call copes with that problem.
There's a better way, a formula by Thaddeus Vincenty. It uses ATAN2() rather than ACOS() so it's less susceptible to epsilon problems.
Edit 2022 (by Alexio Vay):
As of today the modern solution should be the following short code:
select ST_Distance_Sphere(
point(-87.6770458, 41.9631174),
point(-73.9898293, 40.7628267))
Please check out the answer of Naresh Kumar.

You can use the ST_Distance_Sphere() MySQL built-in function, supported since MySQL 5.7 version and above.
It computes the distance in meters more efficiently.
select ST_Distance_Sphere(point(lng, lat), point(lng,lat))
i.e.
select ST_Distance_Sphere(
point(-87.6770458, 41.9631174),
point(-73.9898293, 40.7628267)
)
Referred from Calculating distance using MySQL

Heres is MySQL query and function which use to get distance between two latitude and longitude and distance will return in KM.
Mysql Query :-
SELECT (6371 * acos(
cos( radians(lat2) )
* cos( radians( lat1 ) )
* cos( radians( lng1 ) - radians(lng2) )
+ sin( radians(lat2) )
* sin( radians( lat1 ) )
) ) as distance
FROM your_table;
Mysql Function :-
DELIMITER $$
CREATE FUNCTION `getDistance`(`lat1` VARCHAR(200), `lng1` VARCHAR(200), `lat2` VARCHAR(200), `lng2` VARCHAR(200)) RETURNS varchar(10) CHARSET utf8
begin
declare distance varchar(10);
set distance = (select (6371 * acos(
cos( radians(lat2) )
* cos( radians( lat1 ) )
* cos( radians( lng1 ) - radians(lng2) )
+ sin( radians(lat2) )
* sin( radians( lat1 ) )
) ) as distance);
if(distance is null)
then
return '';
else
return distance;
end if;
end$$
DELIMITER ;
How to use in your PHP Code
SELECT getDistance($lat1,$lng1,$lat2,$lng2) as distance
FROM your_table.

Here's a MySQL function that will take two latitude/longitude pairs, and give you the distance in degrees between the two points. It uses the Haversine formula to calculate the distance. Since the Earth is not a perfect sphere, there is some error near the poles and the equator.
To convert to miles, multiply by 3961.
To convert to kilometers, multiply by 6373.
To convert to meters, multiply by 6373000.
To convert to feet, multiply by (3961 * 5280) 20914080.
DELIMITER $$
CREATE FUNCTION \`haversine\`(
lat1 FLOAT, lon1 FLOAT,
lat2 FLOAT, lon2 FLOAT
) RETURNS float
NO SQL
DETERMINISTIC
COMMENT 'Returns the distance in degrees on the Earth between two known points of latitude and longitude. To get miles, multiply by 3961, and km by 6373'
BEGIN
RETURN DEGREES(ACOS(
COS(RADIANS(lat1)) *
COS(RADIANS(lat2)) *
COS(RADIANS(lon2) - RADIANS(lon1)) +
SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))
));
END;
DELIMITER;

Not sure how your distance calculation is going on but you need to do a self join your table and perform the calculation accordingly. Something like this probably
select t1.id as userfrom,
t2.id as userto,
( 3959 * acos ( cos ( radians(31.589167) ) * cos( radians( t1.Latitude ) ) *
cos( radians( t1.Longitude ) - radians(64.363333) ) + sin ( radians(31.589167) ) *
sin( radians( t2.Latitude ) ) ) ) AS `distance`
from table1 t1
inner join table1 t2 on t2.city > t1.city

IMPORTANT! Anyone using or copying these calculations MAKE SURE to use least(1.0, (...)) when passing the calculation to the acos() function. The acos() function will NOT take a value above 1 and I have found when comparing lat/lng values that are identical there are times when the calculations come out to something like 1.000002. This will produce a distance of NULL instead of 0 and may not return results you're looking for depending on how your query is structured!
This is CORRECT:
select round(
( 3959 * acos( least(1.0,
cos( radians(28.4597) )
* cos( radians(lat) )
* cos( radians(lng) - radians(77.0282) )
+ sin( radians(28.4597) )
* sin( radians(lat)
) ) )
), 1) as distance
from locations having distance <= 60 order by distance
This is WRONG:
select round(
( 3959 * acos(
cos( radians(28.4597) )
* cos( radians(lat) )
* cos( radians(lng) - radians(77.0282) )
+ sin( radians(28.4597) )
* sin( radians(lat)
) )
), 1) as distance
from locations having distance <= 60 order by distance
The highest rated answer also talks about this, but I wanted to make sure this was very clear since I just found a long standing bug in my query.

Here's a formula I converted from https://www.geodatasource.com/developers/javascript
It's a nice clean function that calculates the distance in KM
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `FN_GET_DISTANCE`(
lat1 DOUBLE, lng1 DOUBLE, lat2 DOUBLE, lng2 DOUBLE
) RETURNS double
BEGIN
DECLARE radlat1 DOUBLE;
DECLARE radlat2 DOUBLE;
DECLARE theta DOUBLE;
DECLARE radtheta DOUBLE;
DECLARE dist DOUBLE;
SET radlat1 = PI() * lat1 / 180;
SET radlat2 = PI() * lat2 / 180;
SET theta = lng1 - lng2;
SET radtheta = PI() * theta / 180;
SET dist = sin(radlat1) * sin(radlat2) + cos(radlat1) * cos(radlat2) * cos(radtheta);
SET dist = acos(dist);
SET dist = dist * 180 / PI();
SET dist = dist * 60 * 1.1515;
SET dist = dist * 1.609344;
RETURN dist;
END$$
DELIMITER ;
You'll also find the same function in different languages on the site;

Maybe someone will come in handy, I managed to implement my task through the FN_GET_DISTANCE function:
SELECT SUM (t.distance) as Distance FROM
(SELECT (CASE WHEN (FN_GET_DISTANCE (Latitude, Longitude, #OLDLatitude, #OLDLongitude)) BETWEEN 0.01 AND 2 THEN
FN_GET_DISTANCE (Latitude, Longitude, #OLDLatitude, #OLDLongitude) ELSE 0 END) AS distance,
IF (#OLDLatitude IS NOT NULL, #OLDLatitude: = Latitude, 0),
IF (#OLDLongitude IS NOT NULL, #OLDLongitude: = Longitude, 0)
FROM `data`, (SELECT #OLDLatitude: = 0) var0, (SELECT #OLDLongitude: = 0) var1
WHERE ID_Dev = 1
AND DateTime BETWEEN '2021-05-23 08:00:00' AND '2021-05-23 20:00:00'
ORDER BY ID DESC) t;

Related

Measuring distance between two lat/long points in mysql

I have the following query in mysql
SELECT id,
(SELECT ( 6371 * Acos(Cos(Radians('31.502537032937294')) *
Cos(Radians(lat)) *
Cos(
Radians(lng) - Radians(
'74.34226725250483'))
+
Sin(
Radians('31.502537032937294')) *
Sin(Radians(lat))) ) AS distance
FROM `addresses`
WHERE orders.id = addresses.order_id
AND `type` = 'from'
AND `address_for` = 'order'
AND `is_archive` = 0
HAVING `distance` <= 50) AS distance
FROM orders
WHERE `is_archive` = 0
AND `is_complete` = 0
AND `status_id` = 1
AND Date(`expected_pick_up_at`) = '2018-09-10'
In which, I am calculating distance of orders from my current position. The result of this query is as following
id distance
1027 3.392817926161619
1028 2.957290773676651
While the actual distance is
id distance
1027 0.09565605656865
1028 0.43565656101560
original lat/long saved in the database from which I am calculating distance are as
id lat long
1027 31.53189017798653 74.35203921049833
1028 31.52842855342999 74.34939790517092
Can anyone guide me which part I am doing wrong in this,
Note: I have also looked at the following question

Searching within a distance in SQL

I have database with a list of contacts with puesdo coordinates of where they live. Here's a sample:
name e_point n_point
David 102 345
James 174 746
Ali 460 584
Kevin 364 479
Mark 385 274
I was wondering is it possible to create a query that can search within a distance of the two coordinates? E.g., I want a list of people who live within a 20 sqr mile radius of James.
What functions can help me do this?
Mysql syntax:
SELECT name FROM `table` WHERE SQRT(
POW(e_point - (SELECT e_point FROM `table` WHERE name='james'), 2) +
POW(n_point - (SELECT n_point FROM `table` WHERE name='james'), 2)) < 20
AND name <> 'james'
Notice:
you need to change 'name' in 3 places.
subqueries added for let you run one query with just one variable (name). If you remove subqueries, you need to run 2 queries (first query retrieving coord, and second query searching near people)
equation is:
Where p1=(p1x, p1y) and p2=(p2x, p2y)
To calculate the distance between 2 coordinates you need to get the Great-circle distance, as the earth is rounded and measurement of distance is affected by this fact.
Doing this with SQL would be something like this according to the Google Maps API docs:
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) )
AS distance
FROM markers
HAVING distance < 25
ORDER BY distance
LIMIT 0 , 20;
if your coordinates represent like x and y coordinates you can use something like this (just replaces occurrences of contacts with your actual table name) distance returned is the distance from 'James':
SELECT T2.name,
SQRT(POWER(T2.e_point-T1.e_point,2)+
POWER(T2.n_point-T1.n_point,2)) as distance
FROM contacts T1
INNER JOIN contacts T2 ON
SQRT(POWER(T2.e_point-T1.e_point,2)+
POWER(T2.n_point-T1.n_point,2)) <= 20
WHERE T1.name = 'James'
AND T1.name != T2.name
sqlfiddle

MySQL location select query returns 0 in stored procedure

Can anybody spot the problem here?
I have a stored procedure I wanna use to find closest locations to a specific location.
When I try the select outside of a stored proc (just in a query window) all is fine.
SELECT id,
( 3959*acos(
cos(radians(37)) *
cos(radians(lat)) *
cos(radians(lng) - radians(-122)) +
sin(radians(37)) *
sin(radians(lat))
)
) AS distance
FROM markers
HAVING distance < 25
ORDER BY distance
LIMIT 0 , 20;
But when I place it in a stored proc it always return 0.
DELIMITER $$
CREATE PROCEDURE `GetLocationsByRadius`(IN latitude double, IN longitude double,IN radius double)
begin
SELECT ( 6371 * acos( cos( radians(latitude) ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians(longitude ) ) + sin( radians(latitude) ) * sin( radians( Latitude ) ) ) ) AS distance FROM LocationTrades HAVING distance < radius ORDER BY distance LIMIT 0 , 20;
end $$
I tried changing the table data types from float4 to float8 to decimals, I tried using inner variables inside the stored, but nothing helps, it always returns distance 0.
Seems like it thinks something is INT inside there...
Any help would be appreciated.
Thanks
Ok, I found the problem.
The table columns need table identifiers before them. (Table.Column)
This works:
DELIMITER $$
CREATE PROCEDURE GetLocationsByRadius(IN latitude double, IN
longitude double,IN radius double) begin
SELECT ( 6371 * acos( cos( radians(latitude) ) * cos( radians(
Locations.Latitude ) ) * cos( radians( Locations.Longitude ) -
radians(longitude) ) + sin( radians(latitude) ) * sin( radians(
Locations.Latitude ) ) ) ) AS distance FROM Locations HAVING distance
< radius ORDER BY distance LIMIT 0 , 20;
end $$

Declaring and setting variables to test difference between SP and query

I am trying to solve another problem here. I was told by a member that in order to find an answer to my problem I needed to change the query to match the SP. In order to do that I was told to declare variables so I am using variables like I am in the SP. my problem is I have read so much my eyes are hurting and I cannot figure out how to declare friggin variables.
Please help!!
This is what I have read so far:
resource 1,
resource 2,
resource 3, resource 4, resource 5
and I could go on an on. I am literally stuck. I am new to SQL and this is bugging me out. I do not have to use a SP but I would really like to so I can learn the dos and donts and hopefully learn enough to help others.
DECLARE zipcode VARCHAR(30)
DECLARE radius VARCHAR(30)
SET zipcode = 08360
SET radius = 50
SELECT C.CustomerName, C.MenuId
FROM Customers C
INNER JOIN (
SELECT ZIPCODE, ( 3959 * ACOS( COS( RADIANS( (
SELECT Z.LAT
FROM ZipCodes Z
WHERE Z.ZIPCODE =zipcode
LIMIT 0 , 1
) ) ) * COS( RADIANS( LAT ) ) * COS( RADIANS( LNG ) - RADIANS( (
SELECT Z.LNG
FROM ZipCodes Z
WHERE Z.ZIPCODE =zipcode
LIMIT 0 , 1
) ) ) + SIN( RADIANS( (
SELECT Z.LAT
FROM ZipCodes Z
WHERE Z.ZIPCODE =zipcode
LIMIT 0 , 1
) ) ) * SIN( RADIANS( LAT ) ) ) ) AS distance
FROM ZipCodes
HAVING distance <radius
ORDER BY distance
) AS RelevantCodes ON ( C.ZIPCODE = RelevantCodes.ZIPCODE )
I have tried so many combinations from what I have seen on the internet that I am starting to think its just too late in the night to do anything more. If you take a look at some of the resources I looked at you will see where I saw the difference between with '#' and without.
If you want to create a stored proc, you'll need to use the proper syntax.
See: http://dev.mysql.com/doc/refman/5.5/en/create-procedure.html
Note that inside the proc body you'll need to terminate each and every statement with a ;.
Because you'll need to let MySQL know when the body proc ends and you'll need to redeclare the delimiter into something that not a ;.
This will prevent MySQL from interpreting your stored proc after the first ';', but instead listen all the way up to the $$.
Don't forget to revert the delimiter back to the default after you're done inputting the function.
SET DELIMITER $$
CREATE PROCEDURE QueryCustomerMenu(IN zipcode VARCHAR(30), IN radius VARCHAR(30))
BEGIN
DECLARE somevar VARCHAR(20);
SET somevar = '456';
SELECT C.CustomerName, C.MenuId
FROM Customers C
INNER JOIN (
SELECT ZIPCODE, ( 3959 * ACOS( COS( RADIANS( (
SELECT Z.LAT
FROM ZipCodes Z
WHERE Z.ZIPCODE =zipcode
LIMIT 0 , 1
) ) ) * COS( RADIANS( LAT ) ) * COS( RADIANS( LNG ) - RADIANS( (
SELECT Z.LNG
FROM ZipCodes Z
WHERE Z.ZIPCODE =zipcode
LIMIT 0 , 1
) ) ) + SIN( RADIANS( (
SELECT Z.LAT
FROM ZipCodes Z
WHERE Z.ZIPCODE =zipcode
LIMIT 0 , 1
) ) ) * SIN( RADIANS( LAT ) ) ) ) AS distance
FROM ZipCodes
HAVING distance <radius
ORDER BY distance
) AS RelevantCodes ON ( C.ZIPCODE = RelevantCodes.ZIPCODE );
END $$
SET DELIMITER ;
Now you can call the stored proc with:
CALL QueryCustomerMenu('09210','20');
And it will return a resultset with the CustomerName and MenuID's.

MySQL calculate distance between all geo locations in result

I want to calculate the distance between all geo locations(represented as latitude and longitude) in the result
the MySQL table looks like this:
Location 1, Latitude: x, Longitude: y
Location 2, Latitude: a, Longitude: b
Location 3, Latitude: c, Longitude: d
...
The output should be the distance from Location 1 to Location 2 summed up with the distance between Location 2 and Location 3 and so on.
I know how to calculate the distance between two location but i don't know how to sum up the distances and how to always calculate the distance between two successive locations/rows
Thanks for your help!
Edit: I am planning to use the following function to calculate the distance between two locations
CREATE FUNCTION `GeoDistance`(
latitude1 numeric (9,6),
longitude1 numeric (9,6),
latitude2 numeric (9,6),
longitude2 numeric (9,6)
) RETURNS decimal(10,5)
READS SQL DATA
BEGIN
DECLARE x decimal (20,20);
DECLARE pi decimal (21,20);
SET pi = 3.14159265358979323846;
SET x = sin( lat1 * pi/180 ) * sin( lat2 * pi/180 )
+ cos( lat1 * pi/180 ) * cos( lat2 * pi/180 )
* cos( abs( (lon2 * pi/180) - (lon1 *pi/180)));
SET x = acos( x );
RETURN ( 1.852 * 60.0 * ((x/pi)*180) );
END
Assuming the table is LOCATIONS (ID, LAT, LON), for simplification:
SELECT SUM(SQRT(POW(A.LAT - B.LAT, 2)+POW(A.LON - B.LON, 2)))
FROM LOCATIONS A
JOIN LOCATIONS B ON (A.ID = B.ID - 1)
Essentially, we self-join locations on itself, with an offset of 1 between locations (I'm freely assuming the IDs are assigned without gaps), then for each pair of points we calculate the distance (formula is quite simple, explained in detailed by google), then sum up all of these distances.