Get nearest GPS Points from MySQL - mysql

I have a table with float latitude and float longitude (like 47.960237,13.796564).
Now i want to select roughly points that are about 2km around a specific point.
How do i do that fast and with less cpu usage?
UPDATE
Maybe i have wrong expressed my problem. Is there a direct method to use a BETWEEN and direct coordinates like x between 47.95 and 47.94 and y between 13.78 and 13.76 (according to my example point)

You should change your table to use points
http://dev.mysql.com/doc/refman/5.1/de/point-property-functions.html
With the help of those, you may use this query
SELECT *,3956 * 2 * ASIN(SQRT( POWER(SIN((#orig_lat -abs( dest.lat)) * pi()/180 / 2),2) + COS(#orig_lat * pi()/180 ) * COS( abs (dest.lat) * pi()/180) * POWER(SIN((#orig_lon – dest.lon) * pi()/180 / 2), 2) )) as distanceFROM TABLE desthaving distance < #distORDER BY distance limit 10

Try this query
SELECT ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI() / 180)
* COS(lat * PI() / 180) * COS(($lon – lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS `distance`
FROM `members`
HAVING `distance`<=’10′ ORDER BY `distance` ASC

MySQL seem to have spatial extensions, I have never used MySql, but if these are anything like the spatial extensions SqlServer then they will have the functions you need including the use of fast spatial indexes.
Based on a quick read of the MySQL docs, store the data as points, then use the MBRContains(g1,g2) function, add a SPATIAL indexes however I think only MyISAM surports spatial indexes.
Hopefull there is a MySQL function to find the nearest pointk, if not feed all the close points into a 2nd query that uses trig (as per other answers) to find the bast point.

Related

How to search nearby location with kilometer in MySQL

I have the database like as below structure. And how can I get location_id in list within 5 kilometer. There have latitude and longitude numbers are already in the database table. Please see my database structure image.
- school_id
- location_id
- school_name
- lat
- lng
Here is the database structure image:
I have already searched from this link
How to find nearest location using latitude and longitude from sql database?
and i don't understand the code.
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) *
cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin(
radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25
ORDER BY distance LIMIT 0 , 20;
The constant literal 3959 represents an approximation of the radius of the earth, in miles. That's why the "great circle distance" expression is returning a value in miles.
To get distance in kilometers, just replace 3959 with 6371, an approximation of the earth's radius in km.
Reference: https://en.wikipedia.org/wiki/Great-circle_distance
What the query is doing is calculating a distance (in miles) between two points on the earth, represented by degrees latitude and degrees longitude.
One the points is represented by literal values in the GCD expression (37.000000,-122.000000). The other point is (lat,lng) (degrees latitude and degrees longitude) from the row in the database.
The query cranks through every row in the table, and evaluates the GCD expression to calculate a distance. (The length of shortest line along the surface of the sphere between the two points.)
The HAVING distance < 25 clause excludes any row where the calculated distance is either greater than or equal to 25 or NULL.
The ORDER BY distance clause returns the rows in sequence by ascending values of distance, the closest points first.
The LIMIT 20 clause restricts the return to the first twenty rows.
FOLLOWUP
Within five kilometers of what? The Santa Monica Pier Aquarium?
That's latitude 34.010396, longitude -118.496029.
We can set user-defined variables (to avoid spreading literals in our query text):
SET #lat = 34.010396 ;
SET #lng = -118.496029 ;
Our SQL text include in the SELECT list the columns we want to return from our table. We'll also included a complicated looking "Great Circle Distance" expression that returns a distance in kilometers.
Something Like this:
SELECT m.school_id
, m.location_id
, m.school_name
, m.lat
, m.lng
, ( ACOS( COS( RADIANS( #lat ) )
* COS( RADIANS( m.lat ) )
* COS( RADIANS( m.lng ) - RADIANS( #lng ) )
+ SIN( RADIANS( #lat ) )
* SIN( RADIANS( m.lat ) )
)
* 6371
) AS distance_in_km
FROM mytable m
ORDER BY distance_in_km ASC
LIMIT 100
The GCD formula in the expression is calculating a distance between two points.
In this query, one of the points is a constant (#lat,#lng), which we previously set to the coordinates of the Santa Monica Pier Aquarium.
The other point is (m.lat,m.lng), the latitude and longitude from the row in the table.
So in this query, distance_in_km represents the distance between (lat,lng) of the row in the table and the Santa Monica Pier Aquarium.
Because distance_in_km value is not available at the time the rows are accessed, we can't reference that in a WHERE clause.
But we can reference it in a HAVING clause. That's simliar to a WHERE in that it filters out rows, but is much different, because it is evaluated much later in the query execution. And it can reference expressions that aren't available when the rows are being accessed, when the WHERE clause is evaluated.
We can modify our query to include the HAVING clause. In this case, we're limiting to rows that are within 100 kilometers, and we'll return only the closest 12 rows...
FROM mytable m
HAVING distance_in_km <= 100
ORDER BY distance_in_km ASC
LIMIT 12
If we want to find the distance to some point other than the Santa Monica Pier, we set #lat and #lng for that point, and re-execute the SQL.
SELECT *, ((ACOS(SIN(inputLat * PI() / 180) *
SIN(tableColLat * PI() / 180) + COS(inputLat * PI() / 180) *
COS(tableColLat * PI() / 180) * COS((inputLng - tableColLng) * PI() / 180)) * 180 / PI()) * 60 * 1.1515)
as distance FROM gm_shop HAVING distance <= 5 ORDER BY distance ASC;
distance is you can change. it is KM
When I solved a similar problem instead of worrying about curvature of the earth and angles. I just used the Pythagorean distance. It's a close enough approximation IMHO.
i.e. getting all schools that are in an approximate 5km Pythagorean distances.
select sqrt(pow(lat - curlat,2) + pow(lng - curlng,2)) as distance from markers having distance < XXXXX
You'll have to calculate the approximate value for XXXX. I degree is approximately 111km. If you aren't doing any extreme latitudes you shouldn't have to worry about adjusting it. So you could set XXXX at 0.045

How do I perform "AS" query in django orm

I was trying to convert this sql query into django orm query but I got stuck how do I perform sqrt and other mathematical calculation in a single ORM query.
SELECT latitude, longitude, SQRT(
POW(69.1 * (latitude - [input_lat]), 2) +
POW(69.1 * ([input_lng] - longitude) * COS(latitude / 57.3), 2)) AS distance
FROM MyTable HAVING distance < 25 ORDER BY distance
What I tried -
MyTable.objects.extra(select={"distance": X}).extra(where=["distance > 25"]).order_by('distance')
What should I write in place of X? am I going in right direction ?
if I write this
MyTable.objects.extra(select={"distance": "SQRT(POW(69.1 * (latitude - 37), 2) + POW(69.1 * (-122 - longitude) * COS(latitude / 573), 2))"}).extra(where=["distance > 25"]).order_by('distance')
it throws an error, OperationalError: no such function: SQRT. which I guess my DB(sqlite3) doesn't support this function. how to overcome this problem?
MyTable has 5 columns including latitude and longitude.
we will get the value of input_lat and input_lng from enduser.

Methods for geographic distance search in MySQL

I'm looking for the fasted way to search for Points that are within a certain distance from another given Point. I have a MyISAM table with Points spatially indexed representing geographic locations (latitude, longitude).
If MySQL supported it, I think ST_DWithin would do the job. But it doesn't, so I got the following expression that uses a buffer to generate a circle and then look for points that fall within this circle :
ST_Within(geopoint, ST_Buffer(Point(#lat, #lng), #radius))
It seems to be working fine and I believe it uses the index. But is it a good enough solution? How precise is ST_Within and ST_Buffer for geography purposes?
UPDATE: I concluded that MySQL doesn't offer support for Geography coordinates and that all operations are done on a Euclidean plane (even if you specify the SRID). Depending on the location, that eventually leads to big imprecisions. So the coordinates need to be transformed prior to using MySQL Spatial functions.
We do something similar to this at work.
We get about 1 million queries per hour and when we were using spatial indexes, it would basically take the database down and queries would get put in a pending state. Some queries were pending for about 8,000 seconds (about 2 hours). So we had to find another way, and this was the best that we could come up with, it now no longer backs up the database, and returns results in milliseconds.
What we do is first we have a distance function which looks like this:
CREATE FUNCTION `distance`(`lat1` DECIMAL(10,7), `lon1` DECIMAL(10,7), `lat2` DECIMAL(10,7), `lon2` DECIMAL(10,7)) RETURNS double
BEGIN
DECLARE X DOUBLE;
DECLARE PI DECIMAL(21, 20);
SET PI = 3.14159265358979323846;
SET X = SIN(lat1 * PI / 180)
* SIN(lat2 * PI / 180)
+ COS(lat1 * PI / 180)
* COS(lat2 * PI / 180)
* COS((lon2 * PI / 180) - (lon1 * PI / 180));
SET X = ATAN((SQRT( 1- POWER( X, 2))) / X);
RETURN (1.852 * 60.0 * ((X / PI) * 180)) / 1.609344;
END
Remove / 1.609344 on the return line to get kilometers
We then have a procedure to calculate the distance between your location and the surrounding area. From what we tested this was the fastest (simplified version of what we have):
CREATE PROCEDURE `MyRadius`(IN `p_lat` DOUBLE, IN `p_long` DOUBLE, IN `radius` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
SELECT distance(p_lat, p_long, g.latitude, g.longitude) as distance, country, region, city
from geocity g
having distance <= radius
order by distance asc limit 100;
END
You may want to change the order clause, because I am not sure how you want to order it.

Using WHERE clause to find POI within a range of distance from Longitude and Latitude

I'm using following sql code to find out 'ALL' poi closest to the set coordinates, but I would want to find out specific poi instead of all of them. When I try to use the where clause I get an error and it doesn't work and this is where I'm currently stuck, since I only use one table for all the coordinates off all poi's.
SET #orig_lat=55.4058;
SET #orig_lon=13.7907;
SET #dist=10;
SELECT
*,
3956 * 2 * ASIN(SQRT(POWER(SIN((#orig_lat -abs(latitude)) * pi()/180 / 2), 2)
+ COS(#orig_lat * pi()/180 ) * COS(abs(latitude) * pi()/180)
* POWER(SIN((#orig_lon - longitude) * pi()/180 / 2), 2) )) as distance
FROM geo_kulplex.sweden_bobo
HAVING distance < #dist
ORDER BY distance limit 10;
The problem is that you can not reference an aliased column (distancein this case) in a select or where clause. For example, you can't do this:
select a, b, a + b as NewCol, NewCol + 1 as AnotherCol from table
where NewCol = 2
This will fail in both: the select statement when trying to process NewCol + 1 and also in the where statement when trying to process NewCol = 2.
There are two ways to solve this:
1) Replace the reference by the calculated value itself. Example:
select a, b, a + b as NewCol, a + b + 1 as AnotherCol from table
where a + b = 2
2) Use an outer select statement:
select a, b, NewCol, NewCol + 1 as AnotherCol from (
select a, b, a + b as NewCol from table
) as S
where NewCol = 2
Now, given your HUGE and not very human-friendly calculated column :) I think you should go for the last option to improve readibility:
SET #orig_lat=55.4058;
SET #orig_lon=13.7907;
SET #dist=10;
SELECT * FROM (
SELECT
*,
3956 * 2 * ASIN(SQRT(POWER(SIN((#orig_lat -abs(latitude)) * pi()/180 / 2), 2)
+ COS(#orig_lat * pi()/180 ) * COS(abs(latitude) * pi()/180)
* POWER(SIN((#orig_lon - longitude) * pi()/180 / 2), 2) )) as distance
FROM geo_kulplex.sweden_bobo
) AS S
WHERE distance < #dist
ORDER BY distance limit 10;
Edit: As #Kaii mentioned below this will result in a full table scan. Depending on the amount of data you will be processing you might want to avoid that and go for the first option, which should perform faster.
The reason why you cant use your alias in the WHERE clause is the order in which MySQL executes things:
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
When executing your WHERE clause, the value for your column alias is not yet calculated. This is a good thing, because it would waste a lot of performance. Imagine many (1,000,000) rows -- to use your calculation in the WHERE clause, each of those 1,000,000 would first have to be fetched and calculated so the WHERE condition can compare the calculation results to your expectation.
You can do this explicitly by either
using HAVING (thats the reason why HAVING has another name as WHERE - its a different thing)
using a subquery as illustrated by #MostyMostacho (will effectively do the same with some overhead)
put the complex calculation in the WHERE clause (will effectively give the same performance result as HAVING)
All those will perform almost equally bad: each row is fetched first, the distance calculated and finally filtered by distance before sending the result to the client.
You can gain much (!) better performance by mixing a simple WHERE clause for distance approximation (filtering rows to fetch first) with the more precise euclidian formula in a HAVING clause.
find rows that could match the #distance = 10 condition using a WHERE clause based on simple X and Y distance (bounding box) -- this is a cheap operation.
filter those results using the formula for euclidian distance in a HAVING clause -- this is an expensive operation.
Look at this query to understand what i mean:
SET #orig_lat=55.4058;
SET #orig_lon=13.7907;
SET #dist=10;
SELECT
*,
3956 * 2 * ASIN(SQRT(POWER(SIN((#orig_lat -abs(latitude)) * pi()/180 / 2), 2)
+ COS(#orig_lat * pi()/180 ) * COS(abs(latitude) * pi()/180)
* POWER(SIN((#orig_lon - longitude) * pi()/180 / 2), 2) )) as distance
FROM geo_kulplex.sweden_bobo
/* WHERE clause to pre-filter by distance approximation .. filter results
later with precise euclidian calculation. can use indexes. */
WHERE
/* i'm unsure about geo stuff ... i dont think you want a
distance of 10° here, please adjust this properly!! */
latitude BETWEEN (#orig_lat - #dist) AND (#orig_lat + #dist)
AND longitude BETWEEN (#orig_lon - #dist) AND (#orig_lon + #dist)
/* HAVING clause to filter result using the more precise euclidian distance */
HAVING distance < #dist
ORDER BY distance limit 10;
For those who are interested in the constant:
3956 is the radius of the earth in miles, so the resulting distance is measured in miles
6371 is the radius of the earth in kilometers, so use this constant to measure distance in kilometers
Find more information in the wiki about the Haversine formula

Selecting data from database based on numbers

I'm trying to figure out how to select data from a MySQL table based of closeness to a number. Here's what I mean.
I'm writing an application that stores the coordinates of places (longitude and latitude) what I'd like to be able to do is select data from the database based on the location of where the user is. So, say, for example, I've got three locations in the database: [(-70.425, 45.836), (-74.234, 41.639), (-75.747, 41.836)], and the user's location is (-74.345, 41.625). I'd like to be able to select the entries so that they spread out according to distance from the user, getting the three entries in this order: [(-74.234, 41.639), (-75.747, 41.836), (-70.425, 45.836)].
Is this even possible in MySQL, or am I going to have to select a few entries from the database and do the calculation in my programming language?
Take a look at this article
http://zcentric.com/2010/03/11/calculate-distance-in-mysql-with-latitude-and-longitude/
To quote
"So you have a whole table full of members or places with latitude and longitude’s associated with them. Just replace the $lat and $lon with the center point you want to find distances from. You can also change the distance<=10 to a number you want to search from. This will limit your results to all results that are under 10 miles from the starting point
SELECT ((ACOS(SIN($lat * PI() / 180) * SIN(lat * PI() / 180) + COS($lat * PI() / 180) * COS(lat * PI() / 180) * COS(($lon – lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) ASdistanceFROMmembersHAVINGdistance<=’10′ ORDER BYdistanceASC
"
Note $lon and $lat would be your php fvariables. lat and lon (sans $) are the column names in this example i.e.
Table would be members with columns lat and lon