mySQL select zipcodes within x km/miles within range of y
Note: Although I use a zipcode database with Dutch zipcodes, this question is country independent.
I have a database with every zipcode in the Netherlands + its x and y coordinate (lat/long).
I have for example zipcode: $baseZipCode = 1044;
with the following coordinates:
x coordinate = 4,808855
y coordinate = 52,406332
Now, I want to find all other zipcodes with $range
from $baseZipCode
.
For example:
SELECT
zipcode
FROM
zipcodes
WHERE
????? // Need help here
The problem is that the earth is not completely round. I find a lot of tutorials with from a to b
calculations but that's not what I need.
Does anyone have any idea?
UPDATE Thanks to Captaintokyo I found this:
Want to find all zipcodes and corresponding distances within a certain mile/kilometer radius from another zipcode or point? This problems require latitude and longitude coordinates to solve. Geocoding the address gives you latitude/longitude coordinates from an address.
First you will need a database of all zipcodes and their corresponding latitude and longitude coordinates:
CREATE TABLE `zipcodes` (
`zipcode` varchar(5) NOT NULL DEFAULT '',
`city` varchar(100) NOT NULL DEFAULT '',
`state` char(2) NOT NULL DEFAULT '',
`latitude` varchar(20) NOT NULL DEFAULT '',
`longitude` varchar(20) NOT NULL DEFAULT '',
KEY `zipcode` (`zipcode`),
KEY `state` (`state`)
)
So once you have the database you want to find all zipcodes within a certain mile radius of a central point. If the central point is another zipcode, simply query the database for the latitude and longitude coordinates of that zipcode. Then the code is as follows:
// ITITIAL POINT
$coords = array('latitude' => "32.8", 'longitude' => "-117.17");
//RADIUS
$radius = 30;
// SQL FOR KILOMETERS
$sql = "SELECT zipcode, ( 6371 * acos( cos( radians( {$coords['latitude']} ) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians( {$coords['longitude']} ) ) + sin( radians( {$coords['latitude']} ) ) * sin( radians( latitude ) ) ) ) AS distance FROM zipcodes HAVING distance <= {$radius} ORDER BY distance";
// SQL FOR MILES
$sql = "SELECT zipcode, ( 3959 * acos( cos( radians( {$coords['latitude']} ) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians( {$coords['longitude']} ) ) + sin( radians( {$coords['latitude']} ) ) * sin( radians( latitude ) ) ) ) AS distance FROM zipcodes HAVING distance <= {$radius} ORDER BY distance";
// OUTPUT THE ZIPCODES AND DISTANCES
$query = mysql_query($sql);
while($row = mysql_fetch_assoc($query)){
echo "{$row['zipcode']} ({$row['distance']})<br>开发者_如何学运维\n";
}
(Both Yahoo and Google offer free geocoding services.)
You have to use something called the Haversine formula:
$sql = "
SELECT zipcode
FROM zipcodes
WHERE ".mysqlHaversine($lat, $lon, $distance)."
";
And the formula:
function mysqlHaversine($lat = 0, $lon = 0, $distance = 0)
{
if($distance > 0)
{
return ('
((6372.797 * (2 *
ATAN2(
SQRT(
SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) *
SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) +
COS(latitude * (PI()/180)) *
COS('.($lat*1).' * (PI()/180)) *
SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2) *
SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2)
),
SQRT(1-(
SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) *
SIN(('.($lat*1).' * (PI()/180)-latitude*(PI()/180))/2) +
COS(latitude * (PI()/180)) *
COS('.($lat*1).' * (PI()/180)) *
SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2) *
SIN(('.($lon*1).' * (PI()/180)-longitude*(PI()/180))/2)
))
)
)) <= '.($distance/1000). ')');
}
return '';
}
Usually I do not use code without understanding the way it works first, but I must confess this function is a little bit over my head...
While Captaintokyo's method is accurate, it's also fairly slow. I can't help but think it'd be more advantageous to use a temporary table of all zipcodes whose boundaries are within the range, then to refine those results by distance.
You want to do something like this:
SELECT zipcode FROM zipcodes WHERE DistanceFormula(lat, long, 4.808855, 52.406332) < $range
It may be slow if your table of zip codes is large. You may also want to check out the geospatial extensions for MySQL.
精彩评论