开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜