I am to create a stored function that calculates the distance between two points. However I cannot get it to work. Here is the code I am using at the moment.
DROP FUNCTION `haversine`//
CREATE DEFINER=`-----`#`%` FUNCTION `haversine`(givenLat DOUBLE, givenLong DOUBLE) RETURNS double
DETERMINISTIC
BEGIN
DECLARE dist double;
DECLARE longitude varchar(255);
DECLARE latitude varchar(255);
SET longitude = 'Longitude';
SET latitude = 'Latitude';
SET dist = (6371 * acos( cos( radians
(givenLat) ) * cos( radians
( latitude ) ) * cos( radians
( givenLong ) - radians
(longitude) ) + sin( radians
(givenLat) ) * sin( radians
( latitude ) ) ));
RETURN dist;
END
The following query returns no results while it should return something.
SELECT id FROM location_address WHERE haversine(lat, long) < 30;
Because when I do (the same?) query manually it returns results:
SELECT id, (6371 * acos( cos( radians
(lat) ) * cos( radians
( Latitude ) ) * cos( radians
( long ) - radians
(Longitude) ) + sin( radians
(lat) ) * sin( radians
( Latitude ) ) )) AS distance
FROM table HAVING distance < 30
I've of course used the same coordinates to try both queries and I really can't figure out why the function does not produce any results.
I really need a stored function to be able to execute a query.
I still don't exactly know why it works, but passing the column names directly to the function as given parameters worked instead of declaring them in the function itself.
Related
I'm working on a task as part of a project which finds the places within a certain distance of a particular (lat, long) point. I know the way to get the result for a single row in my table which has the (Latitude, Longitude) point and the corresponding distance (given by Coverage_Norm_10km, in my case).
My database table (Sorted_Range_Cap_Data) is of the following form:
Place , Population, Latitude, Longitude, Altitude, Bandwidth_Required, Coverage_Range, Throughput_Range, Coverage_Norm_10km, Throughput_10km
SELECT
Place, (
6371 * acos (
cos ( radians(17.741150) )
* cos( radians(Latitude) )
* cos( radians(Longitude) - radians(73.149712) )
+ sin ( radians(17.741150) )
* sin( radians(Latitude) )
)
) AS Coverage_Norm_10km
FROM Sorted_Range_Cap_Data
HAVING Coverage_Norm_10km < (The current row's "Coverage_Norm_10km" value)
ORDER BY Coverage_Norm_10km
LIMIT 0, 20;
(Source : https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql)
Here, (17.741150, 73.149712) is a (lat, long) point in the first row of my database table. I want to find the set of all places which are within "Coverage_Norm_10km" distance from the (lat, long) point.
I need to repeat this for each row in the table.
Finally, I want to end up with the list of places for each (lat, long) point (every row) in my table.
Any help would be highly appreciated.
I did something similar using the Haversine formula in nodejs, here's my raw query:
var query = "SELECT id, name, latitude, longitude, ( 3959 * acos( cos( radians(" + req.query.latitude + ") ) * cos( radians( latitude ) ) " +
" * cos( radians( longitude ) - radians(" + req.query.longitude + ") ) + sin( radians(" + req.query.latitude + ") ) * sin(radians(latitude)) ) ) AS distance " +
" FROM account " +
" HAVING distance < 10 " +
" ORDER BY distance " +
" LIMIT 0 , 20;";
My accounts table stores each record's latitude/longitude. This would return all accounts within 10 miles of my requested lat/lng.
There's a table called user, and in it, there are some records as following
name -- age -- longitude -- latitude
jack1 24 12.00000 13.0000
jack2 23 16.00000 11.0000
jack3 22 10.00000 11.0000
jack4 25 12.00000 13.0000
...//more records like above
and I have another longitude and latitude, I want to select the name, age, distance from the user order by distance asc, the distance is the user's longitude and latitude with mine, so how can I write this?
You can calculate distance (in miles) this way from latitude & longitude. Say your latitude & longitude is 40.5,80.5 respectively.See related example for idea here Fastest Way to Find Distance Between Two Lat/Long Points
SELECT name, age, (3959 * acos(cos( radians(40.5)) * cos(radians(latitude))
* cos(radians(longitude) - radians(80.5)) + sin( radians(40.5)) *
sin(radians(latitude)))) AS distance
FROM user
ORDER BY distance ASC
Haversine Formula
You can use great circle distance formula. Haversine.
Assuming your lat/lon is 37,-122
SELECT name, age, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) )
* cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin(radians(lat)) ) )
AS distance
FROM `user`
ORDER BY distance
As is already mentioned, the Haversine Formula is what you want to use to calculate distances between two lat/long points. You can implement it as a stored procedure like so:
delimiter //
create function DistanceInKm(
lat1 FLOAT, lon1 FLOAT,
lat2 FLOAT, lon2 FLOAT
) returns float
NO SQL DETERMINISTIC
begin
return degrees(acos(
cos(radians(lat1)) *
cos(radians(lat2)) *
cos(radians(lon2) - radians(lon1)) +
sin(radians(lat1)) * sin(radians(lat2))
)) * 111.045;
END//
delimiter ;
Use 69 instead of 111.045 if you want the distance in miles instead of kilometers.
You can then use this stored procedure in your query in the following way:
select *, DistanceInKm(TARGET_LAT, TARGET_LONG, user.latitude, user.longitude) distance
from user
order by distance asc;
Where TARGET_LAT and TARGET_LONG are the coordinates of the point you are comparing against. Using a stored procedure in the query instead of the formula adds a ton of readability, and also saves you from any bugs introduced by a typo in your formula (so long as you get the stored proc right, of course)
I want to convert the following MySQL to MDB query. I am not at all familiar with access and .mdb databases but I have no choice on this project. I have set up the database in .mdb and can make simple queries to it.
I am trying to convert the following query to mdb query.
SELECT name
, lat
, lng
, ( 3959 * acos( cos( radians('21.222') ) * cos( radians( lat ) )
* cos( radians( lng ) - radians('44.333') )
+ sin( radians('21.222') )
* sin( radians( lat ) ) ) ) AS distance
FROM markers
HAVING distance < '25'
ORDER BY distance ASC LIMIT 1
The query is basically querying a table markers with latitute , longitude information for each record and returns the place which is within 25 miles of the dummy place with latitude,longitude as (21.222,44.333)
Thanks,
Nikhil
You will need to create your own functions for those built in MySQL Mathematic functions, since access won't recognize them. Also the TOP 1 syntax is slightly different. These functions will be only available within the access application itself, so you might have to get really creative how to call this from your PHP script.
Put these math functions into a VBA code module:
Public Function acos(x As Double) As Double
'gets the inverse cosine
acos = Atn(-x / Sqr(-x * x + 1)) + 2 * Atn(1)
End Function
Public Function cos(x As Double) As Double
'gets the cosine
cos = Math.cos(x)
End Function
Public Function radians(degrees As Double) As Double
'returns a degrees measure in radians
Const PI = 3.1415926535
radians = degrees * PI / 180
End Function
Public Function sin(x As Double) As Double
'gets the sine
sin = Math.sin(x)
End Function
Your Access SQL query will look like this (Built using the query builder):
SELECT TOP 1 markers.name, markers.lat, markers.lng, (3959*acos(Cos(radians(21.222))*Cos(radians([lat]))*Cos(radians([lng])-radians(44.333))+Sin(radians(21.222))*Sin(radians([lat])))) AS distance
FROM markers
GROUP BY markers.name, markers.lat, markers.lng, (3959*acos(Cos(radians(21.222))*Cos(radians([lat]))*Cos(radians([lng])-radians(44.333))+Sin(radians(21.222))*Sin(radians([lat]))))
HAVING ((((3959*acos(Cos(radians(21.222))*Cos(radians([lat]))*Cos(radians([lng])-radians(44.333))+Sin(radians(21.222))*Sin(radians([lat])))))<25))
ORDER BY (3959*acos(Cos(radians(21.222))*Cos(radians([lat]))*Cos(radians([lng])-radians(44.333))+Sin(radians(21.222))*Sin(radians([lat]))));
We have a table with places and their latitudes and longitudes.
We are trying to create a function in SQL Server 2008 to list places within next 25 kilometers using a specific latitude and longitude as centre point.
I was wandering if this is a good way to start and test our function and getting current distance between a centre point (current location) and a target location (#latitude/#longitude):
ALTER FUNCTION [dbo].[GetDistanceFromLocation]
(
#myCurrentLatitude float,
#myCurrentLongitude float,
#latitude float,
#longitude float
)
RETURNS int
AS
BEGIN
DECLARE #radiusOfTheEarth int
SET #radiusOfTheEarth = 6371--km
DECLARE #distance int
SELECT #distance = ( #radiusOfTheEarth
* acos( cos( radians(#myCurrentLatitude) )
* cos( radians( #latitude ) )
* cos( radians( #longitude ) - radians(#myCurrentLongitude) ) + sin( radians(#myCurrentLatitude) )
* sin( radians( #latitude ) ) ) )
RETURN #distance
END
Is it correct or we are missing something?
It looks like you are using the great-circle distance formula, which is probably accurate enough for you, although you'll have to be the judge of that.
If you want to check the results of your formula, you can use the geography data type:
declare #geo1 geography = geography::Point(#lat1, #long1, 4326),
#geo2 geography = geography::Point(#lat2, #long2, 4326)
select #geo1.STDistance(#geo2)
and since you are doing a proximity search, you may want to investigate the geography data type further.
Would this be valid?
CREATE FUNCTION [dbo].[GetDistanceFromLocation]
(
#CurrentLatitude float,
#CurrentLongitude float,
#latitude float,
#longitude float
)
RETURNS int
AS
BEGIN
DECLARE #geo1 geography = geography::Point(#lat1, #long1, 4268),
#geo2 geography = geography::Point(#lat2, #long2, 4268)
DECLARE #distance int
SELECT #distance = #geo1.STDistance(#geo2)
RETURN #distance
END
Thanks!
I'm just playing around with a dataset of my region generated by JOSM. I moved it into a mySQL DB with the 0.6 API scheme using Osmosis and now I'm desperately trying the following:
I want to get all streets of a city.
AFAIK there is no tag/relation in the OSM data to determine this so I tried it using a proximity search to get all nodes in a radius around a node representing the city center.
Most of the time I looked at the approaches here
What I got is the following SQL code that should get me the closest 100 nodes around the node with id 36187002 and within a radius of 10km.
set #nodeid = 36187002;
set #dist = 10;
select longitude, latitude into #mylon, #mylat from nodes where id=#nodeid limit 1;
SELECT id, ( 6371 * acos( cos( radians(#mylon) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(#mylat) ) + sin( radians(#mylon) ) * sin( radians( latitude ) ) ) )
AS distance
FROM nodes HAVING distance < #dist ORDER BY distance LIMIT 0 , 100;
Well.. it doesn't work. :( I guess the main problem is that OSM lats/lons are multiplied by 10.000.000 and I don't know how I can correct this function to make it work.
Any ideas about this? All solutions/alternatives are very welcome!
It may be faster to add extra columns to your table for the latitude and longitude expressed as the double data type (so the trigonometric functions stand a chance) - you may want to go further and precalculate the xaxis, yaxis and zaxis as columns (again, stored as double)
So, your new columns are loosely (you may need to add data type conversions as required):
XAxis = cos(radians(Latitude / 10000000)) * cos(radians(Longitude / 10000000))
YAxis = cos(radians(Latitude / 10000000)) * sin(radians(Longitude / 10000000))
ZAxis = sin(radians(Latitude / 10000000))
Then, your proximity search becomes:
set #nodeid = 36187002;
set #dist = 10;
SELECT XAxis, YAxis, ZAxis
INTO #CntXAxis, #CntYAxis, #CntZAxis
FROM nodes
WHERE id=#nodeid limit 1;
SELECT id, ( 6371 * acos(
CASE
WHEN nodes.XAxis * #CntXAxis
+ nodes.YAxis * #CntYAxis
+ nodes.ZAxis * #CntZAxis > 1.0 THEN 1.0
ELSE nodes.XAxis * #CntXAxis
+ nodes.YAxis * #CntYAxis
+ nodes.ZAxis * #CntZAxis
END
) AS Distance
FROM nodes
HAVING Distance < #dist
ORDER BY distance LIMIT 0 , 100;
I modified the query a little and it works.
Here my code:
set #nodeid = 122317;
set #dist = 10;
select lon, lat into #mylon, #mylat from nodes where id=#nodeid limit 1;
SELECT id, ( 6371 * acos(
sin(radians(#mylat)) * sin(radians(lat)) +
cos(radians(#mylat)) * cos( radians(lat)) *
cos(radians(lon) - radians(#mylon))
))
AS distance
FROM nodes having distance <#dist
i´ve got the formula from the german wikipedia and it works fine. I've had in in some ruby code fist, but its also work as an sql-query.
To select some special nodes i added this
(select nodes.id,lat,lon,k,v from nodes join node_tags on nodes.id=node_tags.id where k='public_transport') as stations
as the FROM condition to specify the tags of the nodes. (Of course it changes the lat/log access to stations.lat/stations.log in the code above.