MySQL distance calculation inaccurate result - mysql

I have created a table where I store Lat/Long. data along with ZIP codes.
Here is my procedure to find ZIPs within certain radius:
CREATE PROCEDURE geodistZCTA (IN userid char(5), IN dist double)
BEGIN
declare mylon double;
declare mylat double;
declare lon1 float;
declare lon2 float;
declare lat1 float;
declare lat2 float;
-- get the original lon and lat for the userid
select Y(location), X(location) into mylon, mylat from zcta where zip=userid limit 1;
-- calculate lon and lat for the rectangle:
set lon1 = mylon-dist/abs(cos(radians(mylat))*69);
set lon2 = mylon+dist/abs(cos(radians(mylat))*69);
set lat1 = mylat-(dist/69);
set lat2 = mylat+(dist/69);
-- run the query:
-- select astext(location), zip from zcta where st_within(location, envelope(linestring(point(lon1, lat1), point(lon2, lat2)))) order by st_distance(point(mylon, mylat), location) limit 10;
-- select zip,(3956 * 2 * ASIN(SQRT(POWER(SIN((lat1 - abs(lat2)) * pi()/180 / 2), 2) + COS(abs(lat1) * pi()/180 ) * COS(abs(lat2) * pi()/180) * POWER(SIN((lon1 - lon2) * pi()/180 / 2), 2) ))) AS distance FROM zcta HAVING distance<=dist ORDER BY distance ASC;
-- SELECT zip,X(location),Y(location),(((ACOS(SIN(mylat * PI() / 180) * SIN(X(location) * PI() / 180) + COS(mylat * PI() / 180) * COS(X(location) * PI() / 180) * COS((Y(location)-mylon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515)) AS distance FROM zcta HAVING distance<=dist ORDER BY distance ASC;
select zip,X(location),Y(location),
( 3959 * acos( cos( radians(mylat) )
* cos( radians( X(location) ) )
* cos( radians( Y(location) ) - radians(mylon) )
+ sin( radians(mylat) )
* sin( radians( X(location) ) ) ) ) AS distance
FROM zcta
where X(location) between lat1 and lat2
and Y(location) between lon1 and lon2
HAVING distance <= dist ORDER BY distance ASC;
END;
Now when I call the procedure like:
call geodistZCTA('03804',5);
I get the following result:
# zip, X(location), Y(location), distance
-------------------------------------------------------
'03042', '43.045076', '-71.07095', '3.9798046354127266'
But when I measure the distance using http://www.allplaces.us/dbz.cgi
I get 16.2 miles ( 26.1 km )
Any idea of what I am doing wrong here?

Related

The diffrence in mysql and psql syntax - finding the closest geocoordinates

I found a psql code which looks like this:
select * from (
SELECT *,( 3959 * acos( cos( radians(6.414478) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(12.466646) ) + sin( radians(6.414478) ) * sin( radians( lat ) ) ) ) AS distance
FROM station_location
) al
where distance < 5
ORDER BY distance
LIMIT 20;
find the nearest location by latitude and longitude in postgresql
The problem is I completely don't get it.
Before I used to use mysql and the syntax was completely different:
SELECT latitude, longitude, SQRT(
POW(69.1 * (latitude - [startlat]), 2) +
POW(69.1 * ([startlng] - longitude) * COS(latitude / 57.3), 2)) AS distance
FROM TableName HAVING distance < 25 ORDER BY distance;
Find nearest latitude/longitude with an SQL query
How to convert this mysql instruction into psql, so the user's longitude and latitude will be startlng and startlat?
Not commenting on the calculations themselves, this "port" to PostgreSQL should work:
select * from (select SQRT(
POW(69.1 * (10 - 8), 2) +
POW(69.1 * (100 - 10) * COS(10 / 57.3), 2)) AS distance
from table) d where distance < 25 ORDER BY distance;

Find distance between two points using latitude and longitude in 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;

MySQL Search An Item and all objects in distance

I have a query that searches for the all rows in some distance according to geo coordinates:
SELECT LOCATIONS.*,
( ( ACOS(
SIN(" . $location->Lat . " * PI() / 180)
* SIN(LAT * PI() / 180)
+
COS(" . $location->Lat . " * PI() / 180)
* COS(LAT * PI() / 180)
* COS( ( " . $location->Lng . " - LNG ) * PI() / 180)
)
* 180 / PI()
)
* 60 * 1.1515
) AS DISTANCE
FROM LOCATIONS
HAVING DISTANCE <= 100
ORDER BY DISTANCE ASC
I'm wondering if it's possible to search for the first record of the table and then treat it as a base - use its lat and lng coordinates?
I was trying achieving this with subquery - but with no luck.
Can anybody give me a suggestion?
Put the "base" record by itself into a temporary table using CREATE TEMPORARY TABLE AS SELECT ... LIMIT 1. Then join against the temporary table from the query above.
See http://dev.mysql.com/doc/refman/5.0/en/create-table-select.html

SQL query issue - Unknown column error

I'm trying to select db records based on wordpress custom fields that hold lat and long and have hit a brick wall on this query. Anyone see anything obvious that i'm overlooking?
Thanks!
WordPress database error: [Unknown column 'latitude.meta_value' in 'field list']
SELECT p.ID, p.post_title, (( ACOS( SIN( 39.1749 * PI() / 180 )
* SIN( `latitude.meta_value` * PI() / 180 )
+ COS( 39.1749 * PI() / 180 )
* COS( `latitude.meta_value` * PI() / 180 )
* COS(( -94.5804 - `longitude.meta_value` ) * PI() / 180 )) * 180 / PI() ) * 60 * 1.1515 ) AS distance
FROM wp_posts p
LEFT JOIN wp_postmeta latitude ON latitude.post_id = p.ID AND latitude.meta_key = 'neighborly_issue_lat'
LEFT JOIN wp_postmeta longitude ON longitude.post_id = p.ID AND longitude.meta_key = 'neighborly_issue_lng' HAVING distance < 10;
Yours commas are wrong, try with :
SELECT p.ID, p.post_title, (( ACOS( SIN( 39.1749 * PI() / 180 )
* SIN( `latitude`.`meta_value` * PI() / 180 )
+ COS( 39.1749 * PI() / 180 )
* COS( `latitude`.`meta_value` * PI() / 180 )
* COS(( -94.5804 - `longitude`.`meta_value` ) * PI() / 180 )) * 180 / PI() ) * 60 * 1.1515 ) AS distance

How to measure distance using Haversine formula with MySQL?

I get Latitude and Longitudes from Google Maps Reverse-Geocoding API and then I need something like this:
mysql_query("SELECT users.*, ".mysql_distance_column($lat,$lng)." FROM users ORDER BY DISTANCE";
function mysql_distance_column($lat=40 , $lng=-73) {
$defaultLatitudeColumn = 'user_lat';
$defaultLongitudeColumn='user_lng';
$defaultColumnName='user_distance';
return "((
(3956 * 2 * ASIN(SQRT( POWER(SIN(({$lat} - abs({$defaultLatitudeColumn}))
* pi()/180 / 2), 2) + COS({$lat} * pi()/180 )
* COS(abs({$defaultLatitudeColumn}) * pi()/180)
* POWER(SIN(({$lng} - {$defaultLongitudeColumn}) * pi()/180 / 2), 2) ))
)) ) as {$defaultColumnName} ";
}
UPDATE
I cant ge this to work
delimiter //
CREATE FUNCTION `GeoDistMiles`( lat1 FLOAT (10,6), lon1 FLOAT (10,6), lat2 FLOAT (10,6), lon2 FLOAT (10,6) )
RETURNS FLOAT
DETERMINISTIC
NO SQL
BEGIN
DECLARE pi, q1, q2, q3 FLOAT (10,6);
DECLARE rads FLOAT (10,6) DEFAULT 0;
SET pi = PI();
SET lat1 = lat1 * pi / 180;
SET lon1 = lon1 * pi / 180;
SET lat2 = lat2 * pi / 180;
SET lon2 = lon2 * pi / 180;
SET q1 = COS(lon1-lon2);
SET q2 = COS(lat1-lat2);
SET q3 = COS(lat1+lat2);
SET rads = ACOS( 0.5*((1.0+q1)*q2 - (1.0-q1)*q3) );
RETURN 3963.346 * rads;
END
Here is the formula I use. Remember that the Earth is not a perfect sphere, so the results will never be perfect.
CREATE DEFINER=`root`#`localhost` FUNCTION `GeoDistMiles`( lat1 FLOAT, lon1 FLOAT, lat2 FLOAT, lon2 FLOAT ) RETURNS float
BEGIN
DECLARE pi, q1, q2, q3 FLOAT;
DECLARE rads FLOAT DEFAULT 0;
SET pi = PI();
SET lat1 = lat1 * pi / 180;
SET lon1 = lon1 * pi / 180;
SET lat2 = lat2 * pi / 180;
SET lon2 = lon2 * pi / 180;
SET q1 = COS(lon1-lon2);
SET q2 = COS(lat1-lat2);
SET q3 = COS(lat1+lat2);
SET rads = ACOS( 0.5*((1.0+q1)*q2 - (1.0-q1)*q3) );
RETURN 3963.346 * rads;
END
I assume that you are trying to use http://en.wikipedia.org/wiki/Haversine_formula.
This is lightly tested, but I think that your formula should be:
(ROUND((3956 * 2 * ASIN(SQRT(POWER(SIN(({$lat} - {$defaultLatitudeColumn}) * pi() / 180 / 2), 2) + COS({$lat} * pi()/180 ) * COS({$defaultLatitudeColumn} * pi()/180) *POWER(SIN(({$lng} - {$defaultLongitudeColumn}) * pi()/180 / 2), 2) )) )*{$magicNumber}) )/{$magicNumber}
(I removed the abs calls.)
Hi I have a simple procedure you can use it for your work.
The procedure is very simple and calculate the distance between two cities.you can modify it in your way.
drop procedure if exists select_lattitude_longitude;
delimiter //
create procedure select_lattitude_longitude(In CityName1 varchar(20) , In CityName2 varchar(20))
begin
declare origin_lat float(10,2);
declare origin_long float(10,2);
declare dest_lat float(10,2);
declare dest_long float(10,2);
if CityName1 Not In (select Name from City_lat_lon) OR CityName2 Not In (select Name from City_lat_lon) then
select 'The Name Not Exist or Not Valid Please Check the Names given by you' as Message;
else
select lattitude into origin_lat from City_lat_lon where Name=CityName1;
select longitude into origin_long from City_lat_lon where Name=CityName1;
select lattitude into dest_lat from City_lat_lon where Name=CityName2;
select longitude into dest_long from City_lat_lon where Name=CityName2;
select origin_lat as CityName1_lattitude,
origin_long as CityName1_longitude,
dest_lat as CityName2_lattitude,
dest_long as CityName2_longitude;
SELECT 3956 * 2 * ASIN(SQRT( POWER(SIN((origin_lat - dest_lat) * pi()/180 / 2), 2) + COS(origin_lat * pi()/180) * COS(dest_lat * pi()/180) * POWER(SIN((origin_long-dest_long) * pi()/180 / 2), 2) )) * 1.609344 as Distance_In_Kms ;
end if;
end ;
//
delimiter ;
SELECT ACOS(COS(RADIANS(lat)) *
COS(RADIANS(lon)) * COS(RADIANS(34.7405350)) * COS(RADIANS(-92.3245120)) +
COS(RADIANS(lat)) * SIN(RADIANS(lon)) * COS(RADIANS(34.7405350)) *
SIN(RADIANS(-92.3245120)) + SIN(RADIANS(lat)) * SIN(RADIANS(34.7405350))) *
3963.1 AS Distance
FROM Stores
WHERE 1
HAVING Distance <= 50
Here's how I use it in PHP:
// Find rows in Stores within 50 miles of $lat,$lon
$lat = '34.7405350';
$lon = '-92.3245120';
$sql = "SELECT Stores.*, ACOS(COS(RADIANS(lat)) *
COS(RADIANS(lon)) * COS(RADIANS($lat)) * COS(RADIANS($lon)) +
COS(RADIANS(lat)) * SIN(RADIANS(lon)) * COS(RADIANS($lat)) *
SIN(RADIANS($lon)) + SIN(RADIANS(lat)) * SIN(RADIANS($lat))) *
3963.1 AS Distance
FROM Stores
WHERE 1
HAVING Distance <= 50";
3956 * 2 * ASIN(SQRT( POWER(SIN(($latitude -( cp.latitude)) * pi()/180 / 2),2) + COS($latitude * pi()/180 ) * COS( abs( cp.latitude) * pi()/180) * POWER(SIN(($longitude - cp.longitude) * pi()/180 / 2), 2) )) as distance
Returns miles or KM?