I\'ve in my database 100 000 addresses (that is records).
Each one of them has its own coordinates (latitude and longitude).
Now, given the geo location of
DELIMITER $
DROP FUNCTION IF EXISTS calc_distance$
CREATE FUNCTION calc_distance(
user_lat FLOAT(9,6),
user_lng FLOAT(9,6),
field_lat FLOAT(9,6),
field_lng FLOAT(9,6)
) RETURNS INTEGER
BEGIN
DECLARE ulat FLOAT;
DECLARE ulng FLOAT;
DECLARE flat FLOAT;
DECLARE flng FLOAT;
SET ulat = RADIANS(user_lat);
SET ulng = RADIANS(user_lng);
SET flat = RADIANS(field_lat);
SET flng = RADIANS(field_lng);
RETURN
6371393
* ACOS(
COS(ulat) * COS(flat) * COS(flng - ulng)
+ SIN(ulat) * SIN(flat)
);
END$
DELIMITER ;
Usage
if (($radius = $search->getRadiusMeters())) {
$query['where'][] = "calc_distance($lat, $lng, addr.lat, addr.lng) < $radius";
}
My approach - and I am using it - is to think like a technician, I am happy with +/- 5%
This solution is not meant to control rockets, boats or a like, and only for distances below ~100km its just a solution like Fermi problem
So lets start for a pragmatic solution:
First: for many problems we can ignore that earth is not flat (distance <~100km) ,
The circumference of the earth is more or less 40000 km (more or less exactly for some reason)
A circle has exactly 360 deg.
So 1 km is: 360/40000 deg => 0.00278 deg
And then just select lat/long within this 0.003 per km, it will be something like where long > 42 - 0.003 and long < 42 + 0.003 - same for lat, where 42 is your lat/long as middpoint. The Database will use the indices.
the problem: you get a square as result not a circle (not real distance)
If you need the circle, script it after getting the results.
I just show the official toilette with in 2 km, so a square is ok, and the accuracy, too. The reason is, that there are streets and houses, so people can't walk directly ...
Edit: Technical / mathematical explain:
For very small triangles (one angle << 5 deg) you can ignore the use of trigonometry. So sin(5 deg) ~= 5/180*PI
Mr UpsideDown, I've been looking for an answer to this question for weeks, and there is a very good solution that surprisingly I didn't find anywhere else. You would have to create a stored procedure, limit the proximity with Java before you apply it to the MySQL query, and the query will return the results in a matter of miliseconds, I'm using this in a table where there are over 900K users and you can search by proximity in under 100ms with this technique (it's explained with PHP, but the MySQL trick that you need is still there):
http://cssshowcase.co.uk/mysql-get-distance-by-latitude-and-longitude-coordinates/
No other solution worked as good and as fast as the above, other solutions on the web made the query take 15-20 seconds, which obviously isn't good.
You can use what is called the Haversine formula.
$sql = "SELECT *, ( 3959 * acos( cos( radians(" . $lat . ") ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(" . $lng . ") ) + sin( radians(" . $lat . ") ) * sin( radians( lat ) ) ) ) AS distance FROM your_table HAVING distance < 5";
Where $lat
and $lng
are the coordinates of your point, and lat/lng are your table columns. The above will list the locations within a 5 nm range. Replace 3959
by 6371
to change to kilometers.
This link could be useful: https://developers.google.com/maps/articles/phpsqlsearch_v3
Edit: I didn't see you mentioned Java. This example is in PHP but the query is still what you need.
I would think MySQL's geo spatial extensions would solve this for you: http://dev.mysql.com/doc/refman/5.7/en/gis-introduction.html
Otherwise you could index on lattitude then query WHERE lattitude > userLat-5miles and lattitude < userLat+5miles. That would massively reduce the number of possible rows to process in the application layer.