distance calculations in mysql queries - php

I have to query a database of thousands of entries and order this by the distance from a specified point.
The issue is that each entry has a latitude and longitude and I would need to retrieve each entry to calculate its distance. With a large database, I don't want to retrieve each row, this may take some time.
Is there any way to build this into the mysql query so that I only need to retrieve the nearest 15 entries.
E.g.
`SELECT events.id, caclDistance($latlng, events.location) AS distance FROM events ORDER BY distance LIMIT 0,15`
function caclDistance($old, $new){
//Calculates the distance between $old and $new
}

Option 1:
Do the calculation on the database by switching to a database that supports GeoIP.
Option 2:
Do the calculation on the databaseusing a stored procedure like this:
CREATE FUNCTION calcDistance (latA double, lonA double, latB double, LonB double)
RETURNS double DETERMINISTIC
BEGIN
SET #RlatA = radians(latA);
SET #RlonA = radians(lonA);
SET #RlatB = radians(latB);
SET #RlonB = radians(LonB);
SET #deltaLat = #RlatA - #RlatB;
SET #deltaLon = #RlonA - #RlonB;
SET #d = SIN(#deltaLat/2) * SIN(#deltaLat/2) +
COS(#RlatA) * COS(#RlatB) * SIN(#deltaLon/2)*SIN(#deltaLon/2);
RETURN 2 * ASIN(SQRT(#d)) * 6371.01;
END//
If you have an index on latitude and longitude in your database, you can reduce the number of calculations that need to be calculated by working out an initial bounding box in PHP ($minLat, $maxLat, $minLong and $maxLong), and limiting the rows to a subset of your entries based on that (WHERE latitude BETWEEN $minLat AND $maxLat AND longitude BETWEEN $minLong AND $maxLong). Then MySQL only needs to execute the distance calculation for that subset of rows.
If you're simply using a stored procedure to calculate the distance) then SQL still has to look through every record in your database, and to calculate the distance for every record in your database before it can decide whether to return that row or discard it.
Because the calculation is relatively slow to execute, it would be better if you could reduce the set of rows that need to be calculated, eliminating rows that will clearly fall outside of the required distance, so that we're only executing the expensive calculation for a smaller number of rows.
If you consider that what you're doing is basically drawing a circle on a map, centred on your initial point, and with a radius of distance; then the formula simply identifies which rows fall within that circle... but it still has to checking every single row.
Using a bounding box is like drawing a square on the map first with the left, right, top and bottom edges at the appropriate distance from our centre point. Our circle will then be drawn within that box, with the Northmost, Eastmost, Southmost and Westmost points on the circle touching the borders of the box. Some rows will fall outside that box, so SQL doesn't even bother trying to calculate the distance for those rows. It only calculates the distance for those rows that fall within the bounding box to see if they fall within the circle as well.
Within your PHP (guess you're running PHP from the $ variable name), we can use a very simple calculation that works out the minimum and maximum latitude and longitude based on our distance, then set those values in the WHERE clause of your SQL statement. This is effectively our box, and anything that falls outside of that is automatically discarded without any need to actually calculate its distance.
There's a good explanation of this (with PHP code) on the Movable Type website that should be essential reading for anybody planning to do any GeoPositioning work in PHP.
EDIT
The value 6371.01 in the calcDistance stored procedure is the multiplier to give you a returned result in kilometers. Use appropriate alternative multipliers if you want to result in miles, nautical miles, meters, whatever

SELECT events.id FROM events
ORDER BY pow((lat - pointlat),2) + pow((lon - pointlon),2) ASC
LIMIT 0,15
You dont have to calculate the absolute distance in meters using the radius of the earth and so forth.
To get the closest points you only need the points ordered with relative distance.

Is this what you're looking for? http://zcentric.com/2010/03/11/calculate-distance-in-mysql-with-latitude-and-longitude/

i think stored procedures are what you're looking for.

If your question is a "find my nearest" or "store finder" type question then you can google for those terms. Generally though, that type of data is accompanied by a postal code of some description, and it is possible to narrow down the list (as Mark Maker points out) by association with postal code.
Every case is different, and this may not apply to you, just throwing it out there.

Related

Find data from table using radius check in geomatry column

I have table "vehicle_location" and "coordinates" column in table datatype is geomatry
and in my controller i am getting lat and long and radius in request so i want to find vehicle location data using radius
Have a look at the formula explained on Wikipedia: https://en.wikipedia.org/wiki/Great-circle_distance
You'll find someone asking about the same question here: Measuring the distance between two coordinates in PHP
Ideally, it would be good to be able to reduce the calculation of distances to only cars that are not to far from your location. So typically, I would start by an SQL query that only returns the vehicules which have latitude and longitude values in the nearby, according to the given radius.
Then, in a second step, you can calculate all the distances between these cars and your position, with the algorithm (which takes some calculation time) and sort them after.
The ideal thing to do is to try and do the calculation directly in SQL if possible, so that you can sort them and filter them with the radius. If it gets to complicated then do the calculation and sorting in PHP.

Select locations based on distance between geocodes using SQL query

I have an SQL database containing hotel information, some of which is the geocoded lat/lng generated by Googles geocoder.
I want to be able to select (directly using an SQL query) all the hotels within a certain range. This range will never be more than 50km so I dont need to go as detailed as alot of answers on here are suggesting (taking into account earth curvature and the fact its not a perfect sphere isnt an issue over the distances im searching).
Im thinking a simple Pythagorian formula would suffice, but I dont know what the latitude and longitude figures represent (and therefore how to convert to metres) and also ive read on a couple of 'simple' solutions to my problem that there are issues with their formulas and calculating distances between two locations either side of the meridian line (as I am based in London this will be a big issue for me!!)
Any help would be great, thankyou!
----Helpful Information-----
My database stores the geocoded data in the following format:
geo_lat: 51.5033630,
geo_lon; -0.1276250
This is a select clause that will get your distance into kilometers. From there you can use a where clause to filter it down to less than 25 kilometers or whatever you want. If you want it in miles just take off the * 1.609344 conversion.
$latitude = [current_latitude];
$longitude = [current_longitude];
SELECT
((((acos(sin((".$latitude."*pi()/180)) * sin((`geo_lat`*pi()/180))+cos((".$latitude."*pi()/180)) * cos((`geo_lat`*pi()/180)) * cos(((".$longitude."- `geo_lon`)* pi()/180))))*180/pi())*60*1.1515) * 1.609344) as distance
FROM
[table_name]
WHERE distance
You can use a simple map projection and straight distances for example equirectangular projection. In the formula on this website you can also use a simplier formula without the square root:http://www.movable-type.co.uk/scripts/latlong.html. Of course you can use a bounding box to filter the query:How to calculate the bounding box for a given lat/lng location?, https://gis.stackexchange.com/questions/19760/how-do-i-calculate-the-bounding-box-for-given-a-distance-and-latitude-longitude.

Is current location within radius of items stored in a table?

I have a table that stores longitude / latitude / radius ( miles ) per row. What I am trying to figure out is how to select ONLY the rows that my current point is within.
This is the opposite of querying locations within a given radius of my current location, instead, I actually want to query locations that my current point is within.
Any help here would be greatly appreciated.
This is easily a math problem. The distance between 2 points (x1, y1) and (x2, y2) can be calculated as follows:
Simple Pythagoras. Query your database, point one is the point you want to find, point two is the one you get from the database, if the result of the above statement is smaller then the radius, it's in your range. (assuming all units match. You'll probably want to convert the lat/lon difference to km/whatever unit your radius is)
Note that if you want to have an accurate radius calculation for good ol' earth latitudes/longitudes, you'd have to use more complicated functions than the simple pythagorean distance mentioned in the accepted answer; look for "great circle distance", as e.g. posted in another thread already; however, for your purposes, the pythagorean distance should be close enough I guess; it delivers a reasonably good approximation, except if your latitude/longitude positions are somewhere near the poles.
And, just for completeness, your problem is actually not the opposite to the question "What locations are in a certain radius around my location?" - it's the exact same, only from the other direction - but since distance calculations are symmetric, it actually doesn't matter which direction you check!

Query database with conditions on negative longitude

Small problem about Google Maps and storing/retrieving coordinates. I run a DB on MySql where I store longitude/latitudes of points in 2 separate float fields. I would like to perform queries to find the points fitting the bounds of a given google map object.
I get the min/max lng/lat from the current google map and perform a query through PHP. Everything works fine, except when I have negative values for the longitude. Which doesn't return any result whilst values fitting the min/max lng exist... My query looks like this:
SELECT * FROM points WHERE ((lng BETWEEN :lng_min AND :lng_max) AND (lat BETWEEN :lat_min AND :lat_max))
I checked each single variable sent to the query / stored in the database and each one is a valid float. This doesn't seem to make any sense. Am I missing something?
An issue here is where in your query a negative value causes problems. For instance, if lng_min < 0 and lng_max >0, then you should be retrieving data as usually. However, if lng_max<0 and lng_min>0 (it can happen, as the earth is round and transitioning from -180 degrees to +180 degrees longitude is only a few footsteps away), then it makes sense for the database not to retrieve any values at all. If the latter is the case, then you should previously check if lng_max <0 and lng_min>0 and make an origin shift (e.g. (a+180) % 360) for all longitudes in the query. In any case, a few examples of lng_min/lng_max pairs where the query fails would be helpful.

Finding cities close to one another using longitude and latitude [duplicate]

This question already has answers here:
MySQL Great Circle Distance (Haversine formula)
(9 answers)
Closed 2 years ago.
Each user in my db is associated to a city (with it's longitude and latitude)
How would I go about finding out which cities are close to one another?
i.e. in England, Cambridge is fairly close to London.
So If I have a user who lives in Cambridge. Users close to them would be users living in close surrounding cities, such as London, Hertford etc.
Any ideas how I could go about this? And also, how would I define what is close? i.e. in the UK close would be much closer than if it were in the US as the US is far more spread out.
Ideas and suggestions. Also, do you know any services that provide this sort of functionality?
Thanks
If you can call an external web service, you can use the GeoNames API for locating nearby cities within some radius that you define:
http://www.geonames.org/export/web-services.html
Getting coordinates from City names is called reverse geo coding. Google maps has a nice Api fot that.
There is also the Geonames project where you get huge databases of cities, zip codes etc and their cooridnates
However if you already have the coordinates, its a simple calculation to get the distance.
The tricky thing is to get a nice performant version of it. You probably have it stored in a mysql database, so you need to do it there and fast.
It is absolutely possible. I once did a project including that code, I will fetch it and post it here.
However to speed things up I would recommend first doing a rectangular selection around the center coordinates. This is very, very fast using bee tree indexes or even better stuff like multidimensional range search. Then inside that you can then calculate the exact distances on a limited set of data.
Outside that recangular selection the directions are so vast that it does not need to be displayed or calculated so accurately. Or just display the country, continent or something like that.
I am still at the office but when i get home i can fetch the codes for you. Int he meantime it would be good if you could inform me how you store your data.
Edit: in the mean time here you have a function which looks right to me (i did it without a function in one query...)
CREATE FUNCTION `get_distance_between_geo_locations`(`lat1` FLOAT, `long1` FLOAT, `lat2` FLOAT, `long2` FLOAT)
RETURNS FLOAT
LANGUAGE SQL
DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE distance FLOAT DEFAULT -1;
DECLARE earthRadius FLOAT DEFAULT 6371.009;
-- 3958.761 --miles
-- 6371.009 --km
DECLARE axis FLOAT;
IF ((lat1 IS NOT NULL) AND (long1 IS NOT NULL) AND (lat2 IS NOT NULL) AND (long2 IS NOT NULL)) THEN -- bit of protection against bad data
SET axis = (SIN(RADIANS(lat2-lat1)/2) * SIN(RADIANS(lat2-lat1)/2) + COS(RADIANS(lat1)) * COS(RADIANS(lat2)) * SIN(RADIANS(long2-long1)/2) * SIN(RADIANS(long2-long1)/2));
SET distance = earthRadius * (2 * ATAN2(SQRT(axis), SQRT(1-axis)));
END IF;
RETURN distance;
END;
i quoted this from here: http://sebastian-bauer.ws/en/2010/12/12/geo-koordinaten-mysql-funktion-zur-berechnung-des-abstands.html
and here is another link: http://www.andrewseward.co.uk/2010/04/sql-function-to-calculate-distance.html
The simplest way to do this would be to calculate a bounding box from the latitude and longitude of the city and a distance (by converting the distance to degrees of longitude).
Once you have that box (min latitude, max latitude, min longitude, max longitude), query for other cities whose latitude and longitude are inside the bounding box. This will get you an approximate list, and should be quite fast as it will be able to use any indexes you might have on the latitude and longitude columns.
From there you can narrow the list down if desired using a real "distance between points on a sphere" function.
You need a spatial index or GIS functionality. What database are you using? MySQL and PostgreSQL both have GIS support which would allow you to find the N nearest cities using an SQL query.
Another option you might want to consider would be to put all of the cities into a spatial search tree like a kd-tree. Kd-trees efficiently support nearest-neighbor searches, as well as fast searches for all points in a given bounding box. You could then find nearby cities by searching for a few of the city's nearest neighbors, then using the distance to those neighbors to get an estimate size for a bounding box to search in.

Categories