mySQL在y范围内的x km / miles内选择zipcodes

时间:2010-10-25 04:39:27

标签: php sql mysql zipcode postal-code

注意:虽然我使用带有荷兰语邮政编码的邮政编码数据库,但这个问题与国家无关。

我有一个数据库,其中包含荷兰的每个邮政编码及其x和y坐标(纬度/经度)。

我有例如zipcode:$baseZipCode = 1044;,其坐标如下:

x coordinate = 4,808855
y coordinate = 52,406332

现在,我想从$range找到$baseZipCode的所有其他邮政编码。

例如:

SELECT
  zipcode
FROM
  zipcodes
WHERE
  ????? // Need help here

问题是地球不是完全圆的。我找到了许多from a to b计算的教程,但这不是我需要的。

有没有人有任何想法?


更新 感谢Captaintokyo,我发现了这个:

想要找到距离另一个邮政编码或点的某一英里/公里范围内的所有邮政编码和相应距离吗?这个问题需要解决纬度和经度坐标。对地址进行地理编码可为您提供地址的纬度/经度坐标。

首先,您需要一个包含所有zipcodes及其相应的纬度和经度坐标的数据库:

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`)
)

因此,一旦拥有了数据库,就可以找到中心点某个英里半径范围内的所有zipcodes。如果中心点是另一个邮政编码,只需在数据库中查询该邮政编码的纬度和经度坐标即可。然后代码如下:

// 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";

}

(雅虎和谷歌都提供免费的地理编码服务。)

3 个答案:

答案 0 :(得分:4)

你必须使用名为Haversine formula的东西:

$sql = "
    SELECT zipcode
    FROM zipcodes
    WHERE ".mysqlHaversine($lat, $lon, $distance)."
";

公式:

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 '';
}

通常我不使用代码而不了解它的工作方式,但我必须承认这个功能有点超出我的想法......

答案 1 :(得分:2)

虽然Captaintokyo的方法准确,但它也相当慢。我不得不认为使用边界在范围内的所有zipcodes的临时表更有利,然后按距离细化这些结果。

答案 2 :(得分:0)

你想做这样的事情:

SELECT zipcode FROM zipcodes WHERE DistanceFormula(lat, long, 4.808855, 52.406332) < $range

如果您的邮政编码表很大,可能会很慢。您可能还想查看MySQL的地理空间扩展。