mySQL 在 y 范围内选择 x 公里/英里内的邮政编码

Posted

技术标签:

【中文标题】mySQL 在 y 范围内选择 x 公里/英里内的邮政编码【英文标题】:mySQL select zipcodes within x km/miles within range of y 【发布时间】:2010-10-25 04:39:26 【问题描述】:

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

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

例如,我有邮政编码:$baseZipCode = 1044;,其坐标如下:

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

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

例如:

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

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

有人知道吗?


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

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

首先,您需要一个包含所有邮政编码及其对应的经纬度坐标的数据库:

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

因此,一旦您拥有数据库,您就想查找中心点某一英里半径内的所有邮政编码。如果中心点是另一个邮政编码,只需在数据库中查询该邮政编码的纬度和经度坐标。那么代码如下:

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


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

【问题讨论】:

【参考方案1】:

你必须使用一个叫做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 '';

通常我不会在不了解代码的工作原理的情况下使用代码,但我必须承认这个函数有点超出我的想象......

【讨论】:

您对半正弦公式的评论对我帮助很大。但是,您的查询与我找到的查询不同。我用正确的查询更新了我的主题.. 听起来你在暗示我的答案不正确。只是想指出它完美地适用于我的应用程序。恭喜你找到了做同样事情的不同函数。【参考方案2】:

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

【讨论】:

【参考方案3】:

你想做这样的事情:

SELECT zipcode FROM zipcodes WHERE DistanceFormula(lat, long, 4.808855, 52.406332) &lt; $range

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

【讨论】:

以上是关于mySQL 在 y 范围内选择 x 公里/英里内的邮政编码的主要内容,如果未能解决你的问题,请参考以下文章

MySQL中经纬度范围内的查询

MySQL选择范围内的坐标

【在线等】怎么在百度地图上查找方圆多少公里内的地方

显示用户在PHP中当前位置的10公里半径范围内的用户

查找地理点的聚类(距离聚类中心 y 距离内的最小 x 点)的算法

映射范围内的特定办公室