Find all nearest zip codes with lat and long from a particular latitude and longitude in sql query - mysql

I've a database table in which there are 4 columns.
1. id
2. person_name
3. country
4. zip_code
Now I want all the zip codes with their real latitude and longitude which come in a given radius of 10 mile from a given lat long.
suppose my latitude and longitudes are (19.24947300,72.85681400) and distance is 10 mile, then what SQL query should I make to return all nearest zip codes and their lat longs.
I only have the following 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
But it requires the lat longs of all zip codes in the table but I want them in run time.

I'm not sure I understand what you mean by
But it requires the lat longs of all zip codes in the table but I want them in run time.
Since you want a SQL query, I assume that you have a table with every zip code's latitude and longitude.
You probably shouldn't do all that math in your SQL query. I would first pull every zip code that is in a 10 mile x 10 mile square centered in your queried position and then, if you really need just those that are positively in a 10 mile radius, check in your program (I assume you are using PHP, since you have variables starting with $).
SELECT zip, latitude, longitude FROM zip_positions
WHERE latitude > 19.24947300 - DISTANCE and latitude < 19.24947300 + DISTANCE
AND longitude > 72.85681400 - DISTANCE and longitude < 72.85681400 + DISTANCE
where DISTANCE is a rough equivalent to 10 miles in latitude/longitude that you would have to calculate.
Then you would, if you need it, check in PHP (or whatever language you're using) every result to see if it is indeed less than 10 miles from the point queried. I can't help you as to how to precisely convert latitude and longitude to distance in miles.

I assume this abandoned question was resolved, but let me share my opinions here in case someone else is wrestling with this one. I present 3 options for letting closet zip from DB using lat long as the lookup:
DB LOOKUP OPTION 1 (QUICK AND DIRTY):
SET #lat = 41.675868;
SET #long = -73.864484;
SET #tol = .05;
SELECT * FROM mytable.ZipCodes
WHERE latitude >= #lat AND latitude <= #lat + #tol
AND longitude >= #long AND longitude <= #long + #tol
ORDER BY (ABS(latitude-longitude) - ABS(#lat-#long)) ASC
LIMIT 1
OPTION #2 (VARIATION ON #1)
$qry = "SELECT zipcode,cityname,stateabbr FROM dealer-site.ZipCodes WHERE (latitude >= {$lat} AND latitude <= {$lat}+1) AND (ABS(longitude) >= ABS({$long}) AND ABS(longitude) < ABS({$long})+1) ORDER BY latitude ASC LIMIT 1”;
OPTION #3 BEST METHOD CALCULATE . I WOULD TRY TO USE THIS:
SET #latitude = 41.675868;
SET #longitude = -73.864484;
SELECT
*
FROM
(SELECT
*,
(((ACOS(SIN((#latitude * PI() / 180)) *
SIN((latitude * PI() / 180)) + COS((#latitude * PI() / 180)) * COS((latitude * PI() / 180)) * COS(((#longitude - longitude) * PI() / 180)))) * 180 / PI()) * 60 * 1.1515 * 1.609344) AS distance
FROM
mytable.ZipCodes) ZipCodes
WHERE
distance <= 5
LIMIT 10
PHP MYSQL (LEGACY CALL) :
$qry = "SELECT zipcode
FROM (SELECT *,
(((ACOS(SIN(({$lat} * PI() / 180)) *
SIN((`latitude` * PI() / 180)) + COS(({$lat} * PI() / 180)) * COS((`latitude` * PI() / 180)) *
COS((({$long} - `longitude`) * PI() / 180)))) * 180 / PI()) * 60 * 1.1515 * 1.609344)
AS distance
FROM
`MyDB`.ZipCodes) ZipCodes
WHERE distance <= 5
LIMIT 1
";

Related

passing longitude and latitude and distance as parameters in node js mysql query

Hello I wrote this long mysql query that takes user longitude ,latitude and meters as input and shows the locations within 100 meters.
However I always get a mysql error near ? when I type http://localhost:3002/closest/20.8877387/5.847748474/100 in the browser. Could someone give me a hint?
app.get('/closest/:lat/:lon/meters', (request, response) => {
const lat = request.params.lat;
const lon = request.params.lon;
pool.query('SELECT *, ( ( ( Acos(Sin(( WHERE lat =? * Pi() / 180 )) * Sin(( map.lat* Pi() / 180 )) + Cos (( WHERE lat =? * Pi() / 180 )) * Cos(( map.lat* Pi() / 180 )) * Cos (( ( WHERE lon = ? - map.lon ) * Pi() / 180 ))) ) * 180 / Pi () ) * 60 * 1.1515 * 1.609344 * 1000 ) AS `METERS` FROM base.map WHERE "METERS" <= ?',lat,lon, (error, result) => {
if (error) throw error;
response.send(result);
});
});
You don't seem to set the values of your ? parameters. You need to provide them as an array as the second parameter to .query(sqlStatement, parameterArray, callback). You have four ?s in your query, so the array you provide must have four elements. [lat, lat, lon, radius] correspond to the purpose of your four ?s. The ? in your query represent conditional parameters. You want
Your query says something like this.
SELECT bigFatFormula AS METERS FROM tbl WHERE METERS < someDistance
That WHERE clause doesn't work. You have to say
SELECT * FROM
(SELECT bigFatFormula AS METERS FROM tbl) result
WHERE METERS < someDistance
In your query's WHERE clause, your METERS alias isn't in scope. The nested query puts it in scope.
Your query contains a mess of nested WHERE clauses. That doesn't work.
Your get handler's first line needs to say this, with a colon on meters.
app.get('/closest/:lat/:lon/:meters', (request, response) => {
So, you want something like this.
const lat = Number(request.params.lat);
const lon = Number(request.params.lon);
const radius = Number(request.params.meters);
var q = '
SELECT * FROM (
SELECT *, ( ( ( Acos(Sin(( ? * Pi() / 180 ))
* Sin(( map.lat* Pi() / 180 ))
+ Cos (( lat =? * Pi() / 180 )) *
Cos(( map.lat* Pi() / 180 )) *
Cos ((( ? - map.lon ) * Pi() / 180 ))) )
* 180 / Pi () ) * 60 * 1.1515 * 1.609344 * 1000 )
AS METERS
FROM base.map
) resultset WHERE METERS <= ?';
pool.query(q, [lat, lat, lon, radius], (error, result) => {
I haven't debugged this.
Pro tip: Your query will be very slow if you have lots of rows in your table. Read this for a suggestion.
Pro tip: You might want to use a stored function for your lat/lng - to - distance function, to make your query easier to read for the next person. Here's a stable function.
Pro tip: It's often a good idea to debug complex queries like this using a SQL client program before trying to embed them into programs.

MySQL query to get route between 2 lat-lng coordinates

I'm looking for a query to get the route between two points.
I have this link, let's say:
example.com/any? lat1 = 31.0000 & lng1 = 31.0000 & lat2 = 31.0000 & lng2 = 31.0000
So inside the function, I want to write a query that goes to the table example,
id | lat1 | lng1 | lat2 | lng2 | time | distance_between_inKm
1 | 31.0000 | 31.0000 | 31.0000 | 31.0000 | 30 | 20
So now I want to go to the link and get the nearest || Closest* route or row
I'm getting these from link lat1 & lng1 and lat2 & lng2 so I want to get the closest for the two.
So what I want is that I have a table contains static data for polylines and info for 2 points so I want to use them like Google Matrix api but from my server, let's say:
Update
I tried this query but it ignore the AND:
SELECT id,distance_between,time_between,start_latitude, start_longitude,end_latitude,end_longitude, SQRT(
POW(69.1 * (start_latitude - 31.908482676577364), 2) +
POW(69.1 * (35.1666776731924 - start_longitude) * COS(start_latitude / 57.3), 2))
AND
SQRT(
POW(69.1 * (end_latitude - 31.966051), 2) +
POW(69.1 * (35.894587 - end_longitude) * COS(end_latitude / 57.3), 2))
AS distance
FROM matrix_api USE INDEX (start_latitude,start_longitude,end_latitude,end_longitude,distance_between,time_between) HAVING distance < 0.2 ORDER BY distance LIMIT 1;
So I want something like that give input 1 and 2 and give me the row that has the 2
Close for the start latlngs and the end latlngs at the same time
Update
I tried this too, no luck still:
SELECT id,distance_between,time_between,start_latitude, start_longitude,end_latitude,end_longitude,
SQRT(
POW(69.1 * (start_latitude - 32.016659), 2) +
POW(69.1 * (35.727839 - start_longitude) * COS(start_latitude / 57.3), 2) AND
POW(69.1 * (end_latitude - 31.966051), 2) +
POW(69.1 * (35.894587 - end_longitude) * COS(end_latitude / 57.3), 2)
)
AS distance
FROM matrix_api USE INDEX (start_latitude,start_longitude,end_latitude,end_longitude,distance_between,time_between) HAVING distance < 0.2 ORDER BY distance LIMIT 1;
Sample input:
lat1=0.0000 lng1=0.0000
lat2=0.0000 lng2=0.0000
Output:
The row that I already have in table Distance between: 0.0 And time between: 0.0
So inside table we have the lat1 and lng1 lat2 lng2 And distance between. I want to get the row with nearest lat1 lng1 in same time nearest to lat2 lng2. But here the 2 are in same row saved so I want it like Google API Matrix closest distance between inside the row I need or found.
A small change in your query:
SELECT id,distance_between,time_between
,start_latitude, start_longitude,end_latitude,end_longitude
FROM
(
SELECT id,distance_between,time_between
,start_latitude, start_longitude,end_latitude,end_longitude,
SQRT(
POW(69.1 * (start_latitude - 31.908482676577364), 2) +
POW(69.1 * (35.1666776731924 - start_longitude) * COS(start_latitude / 57.3), 2)
) as startlatlngDistance
,SQRT(
POW(69.1 * (end_latitude - 31.966051), 2) +
POW(69.1 * (35.894587 - end_longitude) * COS(end_latitude / 57.3), 2))
AS endlatlngDistance
FROM matrix_api
USE INDEX (start_latitude,start_longitude,end_latitude
,end_longitude,distance_between,time_between)
) as A
WHERE startlatlngDistance < 0.2 AND endlatlngDistance < 0.2
ORDER BY startlatlngDistance LIMIT 1;

Null returns for distance

Total SQL newb here. I've created two SQL tables, one containing information about hotels and the other containing information about attractions. I'm trying to create a query where I can input a postcode of an attraction, and have the database return the distance of hotels within a 1, 5 and 10 mile radius.
SELECT
hotels.*,
attractions.*,
( ( ACOS( SIN(hotels.Hotel_Lat * PI() / 180)
* SIN(attractions.Attraction_Lat * PI() / 180)
+
COS(hotels.Hotel_Long * PI() / 180)
* COS(attractions.Attraction_Long * PI() / 180)
* COS((hotels.Hotel_Long - attractions.Attraction_Long) * PI() / 180)
) * 180 / PI()
) * 60 * 1.1515
) as distance
FROM hotels
JOIN attractions
This query returns 'Null' for distances. My tables are populated with lat longs as well as postcodes. Any ideas why please? I have negative value longitudes if that makes a difference?
Using MySQL Community server 5.6.17.
Sample Data - There are 7 rows of data in hotels.
Hotel_Name | Hotel_Address | Hotel_Lat | Hotel_Long
a ST158DH 52.906438 -2.145523
b ST161LF 52.827959 -2.129709
Attraction_Name | Attraction_Postcode | Attraction_Lat | Attraction_Long
a ST180BA 52.839509 -2.056964
b ST180TG 52.832820 -2.091124
There are four attractions in total.
Using the Haversine formula, which is well behaved even at very small distances, try this code (or equivalent for MySQL):
select
Hotel_Name
,Hotel_Address
,Attraction_Name
,Attraction_Address
,round(2 * atn2(sqrt(a),sqrt(1-a)), 4) as DeltaRad
,round(2 * atn2(sqrt(a),sqrt(1-a)) * (180.0 / pi()), 2) as DeltaDeg
,round(2 * atn2(sqrt(a),sqrt(1-a)) * (180.0 / pi()) * 60, 2) as DeltaNMs
,round(2 * atn2(sqrt(a),sqrt(1-a)) * 6371.0, 2) as DeltaKMs
from (
select
Hotel_Name
,Hotel_Address
,Attraction_Name
,Attraction_Address
, SinHalfDeltaLatRad * SinHalfDeltaLatRad
+ CosCosLat
* SinHalfDeltaLngRad * SinHalfDeltaLngRad as a
from (
SELECT
Hotel_Name
,Hotel_Address
,Attraction_Name
,Attraction_Address
,sin((h.Lat - a.Lat) * PI() / 180.0 / 2.0) as SinHalfDeltaLatRad
,sin((h.Lng - a.Lng) * PI() / 180.0 / 2.0) as SinHalfDeltaLngRad
,cos(a.Lat) * cos(a.Lat) as CosCosLat
FROM hotels as h
cross JOIN attractions as a
) t
) t
Yielding for your sample data:
Hotel_Name Hotel_Address Attraction_Name Attraction_Address DeltaRad DeltaDeg DeltaNMs DeltaKMs
-------------------- ------------- -------------------- ------------------ ---------------------- ---------------------- ---------------------- ----------------------
HotelA ST158DH AttractionA ST180BA 0.0018 0.1 6.02 11.15
HotelB ST161LF AttractionA ST180BA 0.0011 0.06 3.75 6.94
HotelA ST158DH AttractionB ST180TG 0.0015 0.09 5.2 9.63
HotelB ST161LF AttractionB ST180TG 0.0006 0.03 1.97 3.64

Explanation of SQL query

I am having some issues with this SQL query. This actually comes from a PROCEDURE that is dealing with spatial calculations. The procedure finds locations within a certain radius. Really I am having a hard time understanding what is going on. I just need to rewrite it for my purposes if someone could help explain a couple of things. I will be forever grateful to whoever attempts to help. Thanks so much in advance!
Link To Source: http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
Full Procedure - I Just Need Some Explanation Help On The Actual SELECT Statement - Is Here Just For Some Clarification So Whoever Is Reading Can Get A Better Understanding
CREATE PROCEDURE geodist (IN userid int, IN dist int)BEGINdeclare mylon double; declare mylat double;declare lon1 float; declare lon2 float;declare lat1 float; declare lat2 float;
-- get the original lon and lat for the userid
select longitude, latitude into mylon, mylat from users5where id=userid limit 1;
-- calculate lon and lat for the rectangle:
set lon1 = mylon-dist/abs(cos(radians(mylat))*69);set lon2 = mylon+dist/abs(cos(radians(mylat))*69);set lat1 = mylat-(dist/69); set lat2 = mylat+(dist/69);
-- run the query:
SELECT destination.*,3956 * 2 * ASIN(SQRT( POWER(SIN((orig.lat -dest.lat) * pi()/180 / 2), 2) +COS(orig.lat * pi()/180) * COS(dest.lat * pi()/180) *POWER(SIN((orig.lon -dest.lon) * pi()/180 / 2), 2) )) asdistance FROM users destination, users originWHERE origin.id=userid
and destination.longitude between lon1 and lon2 and destination.latitude between lat1 and lat2
having distance < dist ORDER BY Distance limit 10;END $$
Full Query The Part I Need a Little Clarification On:
SELECT destination.*,3956 * 2 * ASIN(SQRT( POWER(SIN((orig.lat -dest.lat) * pi()/180 / 2), 2) +COS(orig.lat * pi()/180) * COS(dest.lat * pi()/180) *POWER(SIN((orig.lon -dest.lon) * pi()/180 / 2), 2) )) as distance FROM users destination, users origin WHERE origin.id=userid
and locations.longitude between lon1 and lon2 and locations.latitude between lat1 and lat2
having distance < dist ORDER BY Distance;
First Part:
SELECT destination.*
Question: What is with the period and * after the word destination? Sure we are selecting destination but is this concatenating? Like prefixing the word destination to every column in the table?
Next Part:
3956 * 2 * ASIN(SQRT( POWER(SIN((orig.lat -dest.lat) * pi()/180 / 2), 2) +COS(orig.lat * pi()/180) * COS(dest.lat * pi()/180) *POWER(SIN((orig.lon -dest.lon) * pi()/180 / 2), 2) )) as distance
Question: Is this just performing the calculation and storing it as the keyword destination? The keyword "as" is throwing me off here. Is it searching the database for a column destination?
Third Part:
FROM users destination, users origin WHERE origin.id=userid
Question: a little confused on where the query is headed. users destination? users origin? What is this grabbing?
Last Part:
having distance < dist ORDER BY Distance;
Question: Confused about the "having" keyword and where having distance comes into play. Is it trying to grab distance from the table because this is something that is obviously calculated on the fly.
First Part
* selects all columns. You can specify all columns for a particular table using the dot notation as you would use to specify an individual column for a table. For example:
SELECT t1.* FROM t1 NATURAL JOIN t2
Will only SELECT columns from t1 even though t2 is used in the JOIN.
Next Part
This just does a mathematical calculation on some columns and aliases the result in distance (not "destination"). You can reference the result as distance in the result set. The AS keyword sets the column alias, but it is optional.
Third Part
destination and origin are aliases. They are leaving off the optional AS keyword. It would be the same to write:
FROM users AS destination, users AS origin
The users table is being renamed for the duration of the query so it can be referenced by the alias but also to avoid the collision of two of the same table name in the query which would be invalid.
Last Part
distance is an alias for the mathematical calculation above. HAVING is like WHERE, but it has to be used for aggregation (e.g. GROUP BY). I could be wrong, but I don't think it's necessary in this query.
I feel like I'm missing something here... Why doesn't the code use the 'mylon' and 'mylat' in the math part of the final query?
So, this:
SELECT destination.*,
3956 * 2 * ASIN(SQRT( POWER(SIN((orig.lat -
destination.lat) * pi()/180 / 2), 2) +
COS(orig.lat * pi()/180) * COS(destination.lat * pi()/180) *
POWER(SIN((orig.lon -destination.lon) * pi()/180 / 2), 2) ))
as distance
FROM users AS destination, users AS orig
WHERE origin.id=userid
AND destination.longitude BETWEEN lon1 AND lon2
AND destination.latitude BETWEEN lat1 AND lat2
HAVING distance < dist ORDER BY Distance limit 10;
becomes this:
SELECT destination.*,
3956 * 2 * ASIN(SQRT( POWER(SIN((mylat -
dest.lat) * pi()/180 / 2), 2) +
COS(mylat * pi()/180) * COS(dest.lat * pi()/180) *
POWER(SIN((mylon -dest.lon) * pi()/180 / 2), 2) ))
as distance
FROM users AS destination
WHERE
destination.longitude BETWEEN lon1 AND lon2
AND destination.latitude BETWEEN lat1 AND lat2
HAVING distance < dist ORDER BY Distance limit 10;
edit: my best guess is that "origin.id=userid" should read "origin.id=destination.id". Am I correct in thinking that this join is done to filter out all users who are outside of the "distance box" -- in order to avoid calculating the actual geo distance?

Find nearest latitude/longitude with an SQL query

I have latitude and longitude and I want to pull the record from the database, which has nearest latitude and longitude by the distance, if that distance gets longer than specified one, then don't retrieve it.
Table structure:
id
latitude
longitude
place name
city
country
state
zip
sealevel
SELECT latitude, longitude, SQRT(
POW(69.1 * (latitude - [startlat]), 2) +
POW(69.1 * ([startlng] - longitude) * COS(latitude / 57.3), 2)) AS distance
FROM TableName HAVING distance < 25 ORDER BY distance;
where [starlat] and [startlng] is the position where to start measuring the distance.
Google's solution:
Creating the Table
When you create the MySQL table, you want to pay particular attention to the lat and lng attributes. With the current zoom capabilities of Google Maps, you should only need 6 digits of precision after the decimal. To keep the storage space required for your table at a minimum, you can specify that the lat and lng attributes are floats of size (10,6). That will let the fields store 6 digits after the decimal, plus up to 4 digits before the decimal, e.g. -123.456789 degrees. Your table should also have an id attribute to serve as the primary key.
CREATE TABLE `markers` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 60 ) NOT NULL ,
`address` VARCHAR( 80 ) NOT NULL ,
`lat` FLOAT( 10, 6 ) NOT NULL ,
`lng` FLOAT( 10, 6 ) NOT NULL
) ENGINE = MYISAM ;
Populating the Table
After creating the table, it's time to populate it with data. The sample data provided below is for about 180 pizzarias scattered across the United States. In phpMyAdmin, you can use the IMPORT tab to import various file formats, including CSV (comma-separated values). Microsoft Excel and Google Spreadsheets both export to CSV format, so you can easily transfer data from spreadsheets to MySQL tables through exporting/importing CSV files.
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Frankie Johnnie & Luigo Too','939 W El Camino Real, Mountain View, CA','37.386339','-122.085823');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Amici\'s East Coast Pizzeria','790 Castro St, Mountain View, CA','37.38714','-122.083235');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Kapp\'s Pizza Bar & Grill','191 Castro St, Mountain View, CA','37.393885','-122.078916');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Round Table Pizza: Mountain View','570 N Shoreline Blvd, Mountain View, CA','37.402653','-122.079354');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Tony & Alba\'s Pizza & Pasta','619 Escuela Ave, Mountain View, CA','37.394011','-122.095528');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Oregano\'s Wood-Fired Pizza','4546 El Camino Real, Los Altos, CA','37.401724','-122.114646');
Finding Locations with MySQL
To find locations in your markers table that are within a certain radius distance of a given latitude/longitude, you can use a SELECT statement based on the Haversine formula. The Haversine formula is used generally for computing great-circle distances between two pairs of coordinates on a sphere. An in-depth mathemetical explanation is given by Wikipedia and a good discussion of the formula as it relates to programming is on Movable Type's site.
Here's the SQL statement that will find the closest 20 locations that are within a radius of 25 miles to the 37, -122 coordinate. It calculates the distance based on the latitude/longitude of that row and the target latitude/longitude, and then asks for only rows where the distance value is less than 25, orders the whole query by distance, and limits it to 20 results. To search by kilometers instead of miles, replace 3959 with 6371.
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 < 28
ORDER BY distance LIMIT 0, 20;
This one is to find latitudes and longitudes in a distance less than 28 miles.
Another one is to find them in a distance between 28 and 29 miles:
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 < 29 and distance > 28
ORDER BY distance LIMIT 0, 20;
https://developers.google.com/maps/articles/phpsqlsearch_v3#creating-the-map
The original answers to the question are good, but newer versions of mysql (MySQL 5.7.6 on) support geo queries, so you can now use built in functionality rather than doing complex queries.
You can now do something like:
select *, ST_Distance_Sphere( point ('input_longitude', 'input_latitude'),
point(longitude, latitude)) * .000621371192
as `distance_in_miles`
from `TableName`
having `distance_in_miles` <= 'input_max_distance'
order by `distance_in_miles` asc
The results are returned in meters. So if you want in KM simply use .001 instead of .000621371192 (which is for miles).
MySql docs are here
Here is my full solution implemented in PHP.
This solution uses the Haversine formula as presented in http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL.
It should be noted that the Haversine formula experiences weaknesses around the poles. This answer shows how to implement the vincenty Great Circle Distance formula to get around this, however I chose to just use Haversine because it's good enough for my purposes.
I'm storing latitude as DECIMAL(10,8) and longitude as DECIMAL(11,8). Hopefully this helps!
showClosest.php
<?PHP
/**
* Use the Haversine Formula to display the 100 closest matches to $origLat, $origLon
* Only search the MySQL table $tableName for matches within a 10 mile ($dist) radius.
*/
include("./assets/db/db.php"); // Include database connection function
$db = new database(); // Initiate a new MySQL connection
$tableName = "db.table";
$origLat = 42.1365;
$origLon = -71.7559;
$dist = 10; // This is the maximum distance (in miles) away from $origLat, $origLon in which to search
$query = "SELECT name, latitude, longitude, 3956 * 2 *
ASIN(SQRT( POWER(SIN(($origLat - latitude)*pi()/180/2),2)
+COS($origLat*pi()/180 )*COS(latitude*pi()/180)
*POWER(SIN(($origLon-longitude)*pi()/180/2),2)))
as distance FROM $tableName WHERE
longitude between ($origLon-$dist/cos(radians($origLat))*69)
and ($origLon+$dist/cos(radians($origLat))*69)
and latitude between ($origLat-($dist/69))
and ($origLat+($dist/69))
having distance < $dist ORDER BY distance limit 100";
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_assoc($result)) {
echo $row['name']." > ".$row['distance']."<BR>";
}
mysql_close($db);
?>
./assets/db/db.php
<?PHP
/**
* Class to initiate a new MySQL connection based on $dbInfo settings found in dbSettings.php
*
* #example $db = new database(); // Initiate a new database connection
* #example mysql_close($db); // close the connection
*/
class database{
protected $databaseLink;
function __construct(){
include "dbSettings.php";
$this->database = $dbInfo['host'];
$this->mysql_user = $dbInfo['user'];
$this->mysql_pass = $dbInfo['pass'];
$this->openConnection();
return $this->get_link();
}
function openConnection(){
$this->databaseLink = mysql_connect($this->database, $this->mysql_user, $this->mysql_pass);
}
function get_link(){
return $this->databaseLink;
}
}
?>
./assets/db/dbSettings.php
<?php
$dbInfo = array(
'host' => "localhost",
'user' => "root",
'pass' => "password"
);
?>
It may be possible to increase performance by using a MySQL stored procedure as suggested by the "Geo-Distance-Search-with-MySQL" article posted above.
I have a database of ~17,000 places and the query execution time is 0.054 seconds.
Just in case you are lazy like me, here's a solution amalgamated from this and other answers on SO.
set #orig_lat=37.46;
set #orig_long=-122.25;
set #bounding_distance=1;
SELECT
*
,((ACOS(SIN(#orig_lat * PI() / 180) * SIN(`lat` * PI() / 180) + COS(#orig_lat * PI() / 180) * COS(`lat` * PI() / 180) * COS((#orig_long - `long`) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS `distance`
FROM `cities`
WHERE
(
`lat` BETWEEN (#orig_lat - #bounding_distance) AND (#orig_lat + #bounding_distance)
AND `long` BETWEEN (#orig_long - #bounding_distance) AND (#orig_long + #bounding_distance)
)
ORDER BY `distance` ASC
limit 25;
Easy one ;)
SELECT * FROM `WAYPOINTS` W ORDER BY
ABS(ABS(W.`LATITUDE`-53.63) +
ABS(W.`LONGITUDE`-9.9)) ASC LIMIT 30;
Just replace the coordinates with your required ones. The values have to be stored as double. This ist a working MySQL 5.x example.
Cheers
Try this, it show the nearest points to provided coordinates (within 50 km). It works perfectly:
SELECT m.name,
m.lat, m.lon,
p.distance_unit
* DEGREES(ACOS(COS(RADIANS(p.latpoint))
* COS(RADIANS(m.lat))
* COS(RADIANS(p.longpoint) - RADIANS(m.lon))
+ SIN(RADIANS(p.latpoint))
* SIN(RADIANS(m.lat)))) AS distance_in_km
FROM <table_name> AS m
JOIN (
SELECT <userLat> AS latpoint, <userLon> AS longpoint,
50.0 AS radius, 111.045 AS distance_unit
) AS p ON 1=1
WHERE m.lat
BETWEEN p.latpoint - (p.radius / p.distance_unit)
AND p.latpoint + (p.radius / p.distance_unit)
AND m.lon BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
ORDER BY distance_in_km
Just change <table_name>. <userLat> and <userLon>
You can read more about this solution here: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
You're looking for things like the haversine formula. See here as well.
There's other ones but this is the most commonly cited.
If you're looking for something even more robust, you might want to look at your databases GIS capabilities. They're capable of some cool things like telling you whether a point (City) appears within a given polygon (Region, Country, Continent).
Check this code based on the article Geo-Distance-Search-with-MySQL:
Example: find the 10 nearest hotels to my current location in a 10 miles radius:
#Please notice that (lat,lng) values mustn't be negatives to perform all calculations
set #my_lat=34.6087674878572;
set #my_lng=58.3783670308302;
set #dist=10; #10 miles radius
SELECT dest.id, dest.lat, dest.lng, 3956 * 2 * ASIN(SQRT(POWER(SIN((#my_lat -abs(dest.lat)) * pi()/180 / 2),2) + COS(#my_lat * pi()/180 ) * COS(abs(dest.lat) * pi()/180) * POWER(SIN((#my_lng - abs(dest.lng)) * pi()/180 / 2), 2))
) as distance
FROM hotel as dest
having distance < #dist
ORDER BY distance limit 10;
#Also notice that distance are expressed in terms of radius.
Find nearest Users to my:
Distance in meters
Based in Vincenty's formula
i have User table:
+----+-----------------------+---------+--------------+---------------+
| id | email | name | location_lat | location_long |
+----+-----------------------+---------+--------------+---------------+
| 13 | xxxxxx#xxxxxxxxxx.com | Isaac | 17.2675625 | -97.6802361 |
| 14 | xxxx#xxxxxxx.com.mx | Monse | 19.392702 | -99.172596 |
+----+-----------------------+---------+--------------+---------------+
sql:
-- my location: lat 19.391124 -99.165660
SELECT
(ATAN(
SQRT(
POW(COS(RADIANS(users.location_lat)) * SIN(RADIANS(users.location_long) - RADIANS(-99.165660)), 2) +
POW(COS(RADIANS(19.391124)) * SIN(RADIANS(users.location_lat)) -
SIN(RADIANS(19.391124)) * cos(RADIANS(users.location_lat)) * cos(RADIANS(users.location_long) - RADIANS(-99.165660)), 2)
)
,
SIN(RADIANS(19.391124)) *
SIN(RADIANS(users.location_lat)) +
COS(RADIANS(19.391124)) *
COS(RADIANS(users.location_lat)) *
COS(RADIANS(users.location_long) - RADIANS(-99.165660))
) * 6371000) as distance,
users.id
FROM users
ORDER BY distance ASC
radius of the earth : 6371000 ( in meters)
simpledb.execSQL("CREATE TABLE IF NOT EXISTS " + tablename + "(id INTEGER PRIMARY KEY AUTOINCREMENT,lat double,lng double,address varchar)");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2891001','70.780154','craftbox');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2901396','70.7782428','kotecha');");//22.2904718 //70.7783906
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2863155','70.772108','kkv Hall');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.275993','70.778076','nana mava');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2667148','70.7609386','Govani boys hostal');");
double curentlat=22.2667258; //22.2677258
double curentlong=70.76096826;//70.76096826
double curentlat1=curentlat+0.0010000;
double curentlat2=curentlat-0.0010000;
double curentlong1=curentlong+0.0010000;
double curentlong2=curentlong-0.0010000;
try{
Cursor c=simpledb.rawQuery("select * from '"+tablename+"' where (lat BETWEEN '"+curentlat2+"' and '"+curentlat1+"') or (lng BETWEEN '"+curentlong2+"' and '"+curentlong1+"')",null);
Log.d("SQL ", c.toString());
if(c.getCount()>0)
{
while (c.moveToNext())
{
double d=c.getDouble(1);
double d1=c.getDouble(2);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
It sounds like you want to do a nearest neighbour search with some bound on the distance. SQL does not support anything like this as far as I am aware and you would need to use an alternative data structure such as an R-tree or kd-tree.
MS SQL Edition here:
DECLARE #SLAT AS FLOAT
DECLARE #SLON AS FLOAT
SET #SLAT = 38.150785
SET #SLON = 27.360249
SELECT TOP 10 [LATITUDE], [LONGITUDE], SQRT(
POWER(69.1 * ([LATITUDE] - #SLAT), 2) +
POWER(69.1 * (#SLON - [LONGITUDE]) * COS([LATITUDE] / 57.3), 2)) AS distance
FROM [TABLE] ORDER BY 3
Sounds like you should just use PostGIS, SpatialLite, SQLServer2008, or Oracle Spatial. They can all answer this question for you with spatial SQL.
+----+-----------------------+---------+--------------+---------------+
| id | email | name | location_lat | location_long |
+----+-----------------------+---------+--------------+---------------+
| 7 | test#gmail.com | rembo | 23.0249256 | 72.5269697 |
| 25 | test1#gmail.com. | Rajnis | 23.0233221 | 72.5342112 |
+----+-----------------------+---------+--------------+---------------+
$lat = 23.02350629;
$long = 72.53230239;
DB::
SELECT
("
SELECT
*
FROM
(
SELECT
,
(
( ( acos( sin(( ". $ lat ." * pi() / 180)) * sin(( lat * pi() / 180)) + cos(( ". $ lat ." pi() / 180 )) * cos(( lat * pi() / 180)) * cos((( ". $ long ." - LONG) * pi() / 180))) ) * 180 / pi() ) * 60 * 1.1515 * 1.609344
)
as distance
FROM
users
)
users
WHERE
distance <= 2");
In extreme cases this approach fails, but for performance, I've skipped the trigonometry and simply calculated the diagonal squared.
Mysql query for search coordinates with distance limit and where condition
SELECT id, ( 3959 * acos( cos( radians('28.5850154') ) * cos( radians(latitude) ) * cos( radians( longitude ) - radians('77.07207489999999') ) + sin( radians('28.5850154') ) * sin( radians( latitude ) ) ) ) AS distance FROM `vendors` HAVING distance < 5;
This problem is not very hard at all, but it gets more complicated if you need to optimize it.
What I mean is, do you have 100 locations in your database or 100 million? It makes a big difference.
If the number of locations is small, get them out of SQL and into code by just doing ->
Select * from Location
Once you get them into code, calculate the distance between each lat/lon and your original with the Haversine formula and sort it.