Finding places within a particular distance of a point using MySQL - mysql

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.

Related

Sorting Lat and Long Array By Distance in mysql

I have an array of Lats and Longs that i would like to sort by their distance between each other. I dont have a start or an end lat and long, i just want to sort the array by ascending distance.
What i have doesnt work because i dont have the $lat and $long.
SELECT DISTINCT dist.lat,
dist.lon,
( 111.045 * Degrees(Acos(Cos(Radians($lat)) * Cos(
Radians(dist.lat)) * Cos(
Radians(dist.lon) -
Radians($lon)) +
Sin(Radians($lat)) * Sin(
Radians(dist.lat)))) )AS
distance_in_km
FROM dist
ORDER BY distance_in_km ASC;
What I have:
[[23.454445,-12.33342],[34.45665,-34.555566],[21.222233,-43.444453],
[23.444555,-44.556666],[32.445554,-33.44555788],[23.445598,-67.88909],[54.556677,-66.776655]]
I want to sort this array by proximity.
P.S. These coordinates are not real ones.

select longitude and latitude from table in MySQL

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)

mysql spatial field

one of the value updated to a field in a mysql table is 'location' of type 'point'.
When we select the field
select *,AsText(location) from somtable
How can we use this field in our script ? a we need to find the radius or the distance between 2 points.
Usually, you'll want to return the distance between the two locations as another field, not the location, because this is the point itself and not a distance. Something like:
For example:
$sql = 'SELECT *, ( 3959 * acos( cos( radians(' . $lat1 . ') ) * cos( radians(location.lat) ) * cos( radians(location.lng) - radians(' . $lng1 . ') ) + sin( radians(' . $lat1 . ') ) * sin( radians(location.lat) ) ) ) AS distanceFromUser FROM somtable WHERE....';
But if you're rolling with a Point datatype, then use some MySQL spatial functions instead. One of the popular ones is MBRContains. This makes your query something like:
select * from somtable where
MBRContains(
GeomFromText('Polygon((swLat swLon, neLat swLon, neLat neLon, swLat neLon, swLat swLon))'), location );
The other coordinates are specifying a bounding box in which your location Point must exist. So in the second case, you are just returning points inside the box. In the first case, you are returning the distance to every point in your table.
This link allows for a detailed example for radius:
http://howto-use-mysql-spatial-ext.blogspot.in/2007/11/using-circular-area-selection.html
It builds on #dRocking's example of MBRContains, and also explains how to build the polygon from a given point.

Large MySQL DB (21MM records) with location data - each location has lat and long - need to run 'nearby' query

We have a large location DB - with lat long specified for each row. The DB is hosted in MySQL.
We need to run two type of queries:
places nearby (sort by distance)
places nearby by category (where category is a column)
With the number of records growing, this query seems to slow down drastically.
SELECT *, ( 3959 * acos( cos( radians(40.759105) ) * cos( radians( Latitude ) ) * cos( radians( longitude) - radians(-73.984654) ) + sin( radians(40.759105) ) * sin( radians( Latitude ) ) ) ) as distance FROM mcw_in WHERE Latitude <> '' ORDER BY distance LIMIT 0,20
How can I create an index in MySQL to address the slowness? Is there any other solution - like using any geospatial data types?
MySQL Manual :: Introduction to MySQL Spatial Support
MySQL Manual :: Creating Spatial Indexes
But really this won't work in MySQL since they haven't really implemented the functions.
If you are open to it, I would reccomend using PostGIS or Spatialiate (running on Postgresql and SQLLite respectively) or even mongodb or geocouch. These have a much larger suite of implemented spatial functions. If you look at the MySQL documentation it mostly says "not implemented" for the spatial functions.
It is better to use range queries by defining a bounding box surrounding the center. The following query searches the nearest 20 locations within distance $dist from the center ($lat0, $lng0), with the result sorted by the distance. You need two indexes, one on 'lat' and one on 'lng'. Some explanations can be found here.
SELECT *,
    ( 6371 * acos(
    cos(radians($lat0)) * cos(radians(lat)) * cos(radians(lng) - radians($lng0)) +
    sin(radians($lat0)) * sin(radians(lat))
    ) ) AS distance
FROM `locations`
WHERE lat < degrees( asin( sin(radians($lat0)) * cos($dist / 6371) +
        cos(radians($lat0)) * sin($dist / 6371) * cos(radians(0)) ))
  AND lat > degrees( asin( sin(radians($lat0)) * cos($dist / 6371) +
        cos(radians($lat0)) * sin($dist / 6371) * cos(radians(180)) ))
  AND lng < $lng0 - degrees( atan2(sin(radians(90)) * sin(radians($dist / 6371)) * cos(radians($lat0)),
        cos(radians($dist / 6371)) - sin(radians($lat0)) * sin(radians($lat0))) )
  AND lng > $lng0 + degrees( atan2(sin(radians(90)) * sin(radians($dist / 6371)) * cos(radians($lat0)),
        cos(radians($dist / 6371)) - sin(radians($lat0)) * sin(radians($lat0))) )
ORDER BY distance LIMIT 20;

OpenStreetMap Proximity search using mySQL

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.