【问题标题】:php/mysql zip code proximity searchphp/mysql 邮政编码邻近搜索
【发布时间】:2010-03-09 16:16:22
【问题描述】:

我只是在寻找有关执行此操作的最佳方法的建议...

我需要创建一个搜索功能,在邮政编码 50 英里半径范围内搜索“用户”。我有一个邮政编码表,其中包含所有美国邮政编码及其纬度/经度,但我只是想找出构建和查询数据的最佳方法......

我是否应该在 users 表中添加纬度/经度列并查询给定邮政编码半径内的所有用户?或者我应该在邮政编码表中查询半径范围内的所有邮政编码,然后在用户表中查询所有用户的结果(邮政编码)?或者... ???在这一点上,我愿意接受任何建议!

谢谢!

【问题讨论】:

  • 您能否更改已接受的答案复选标记?谢谢!

标签: php sql mysql


【解决方案1】:

这是我找到的最好方法。当然,这将要求您将所有邮政编码的纬度/经度编码在数据库中。

// get all the zipcodes within the specified radius - default 20
function zipcodeRadius($lat, $lon, $radius)
{
    $radius = $radius ? $radius : 20;
    $sql = 'SELECT distinct(ZipCode) FROM zipcode  WHERE (3958*3.1415926*sqrt((Latitude-'.$lat.')*(Latitude-'.$lat.') + cos(Latitude/57.29578)*cos('.$lat.'/57.29578)*(Longitude-'.$lon.')*(Longitude-'.$lon.'))/180) <= '.$radius.';';
    $result = $this->db->query($sql);
    // get each result
    $zipcodeList = array();
    while($row = $this->db->fetch_array($result))
    {
        array_push($zipcodeList, $row['ZipCode']);
    }
    return $zipcodeList;
}

您应该可以直接使用此功能。将您想要半径的邮政编码的 $lat 和 $lon 传递给它,包括可选的半径,然后返回邮政编码列表。

您可以很容易地修改它以获取邮政编码为 IN (radius_sql) 的所有用户并返回您的列表用户。

编码愉快!

【讨论】:

  • 这很好,但您建议如何按距离订购?
  • 因此,唯一的方法是将地址(纬度/经度)与周围的地址(纬度/经度)进行比较。话虽如此,您仍然可以通过最接近最远的邮政编码来订购它们。但请记住,这只会指定邮政编码的“区域”,不一定与其他地址接近。我会说添加到选择中: (3958*3.1415926*sqrt((Latitude-'.$lat.')*(Latitude-'.$lat.') + cos(Latitude/57.29578)*cos('.$lat .'/57.29578)*(Longitude-'.$lon.')*(Longitude-'.$lon.'))/180) 作为 LOC 然后在末尾 ORDER BY LOC。
  • 只是为了让人们知道......美国人口普查在这里有这个好处:census.gov/tiger/tms/gazetteer/zips.txt
  • 简单明了 - 当您需要查找“在目的地的半径范围内”(即“邮政编码”而不是“目的地”(即“咖啡店”)时你所拥有的只是纬度/经度。没有空间几何或所有这些......很高兴找到它用于“广告”查找(我们知道“用户”在哪里并且需要查看广告的纬度/经度+radius 延伸到那么远。解决了我们的一个大需求。
【解决方案2】:

http://www.micahcarrick.com/04-19-2005/php-zip-code-range-and-distance-calculation.html

我发现这非常棒。

"在邮政编码表中查询半径内的所有邮政编码,然后在用户表中查询所有用户的结果(邮政编码)"

我发现这是最好的方法,除非您需要将用户放在谷歌地图上。如果您只是列出里程范围内的用户,则应该很容易查询数据库(使用类)以获取邮政编码列表,然后选择这些邮政编码中的所有用户。

Select * from Users where zip_code IN (19125,19081,19107.........);

应该可以的。

【讨论】:

  • 玩了一些东西之后。我认为这将是最好的选择,但我不确定如何按距离对最终结果(用户)进行排序......而且,这就是我从中获取邮政编码数据的网站! ;)
  • 这有一个死链接。
【解决方案3】:

我会首先搜索目标半径内的所有邮政编码。然后将所有返回的邮政编码与您的用户表邮政编码进行比较。拉出匹配的用户。

它在一个半径范围内查找邮政编码,找到了这个 MySQL 调用:

$query = 'SELECT zzip FROM ' . table . 
            ' WHERE (POW((69.1*(zlongitude-"' . 
            $long . '")*cos(' . $long . 
            '/57.3)),"2")+POW((69.1*(zlatitude-"' . 
            $lat . '")),"2"))<(' . $radius . 
            '*' . $radius . ')';

MySQL 为您完成所有数学运算。

我在这里找到了一个使用它的类: http://www.nucleusdevelopment.com/code/do/zipcode

希望对您有所帮助。

【讨论】:

  • 我相信第三行的cos('. $long应该是cos('. $lat
【解决方案4】:

从这里开始,但请注意解决方案不是很快:

PHP Zip Code Range and Distance Calculation

现在为了加快速度 - 我们将替换查找以使用空间索引 :)

  1. 使用 MySQL

  2. 向数据库中添加一个名为 location 的列并使其类型为 POINT

  3. 确保它现在接受空值

  4. 运行以下 SQL 查询

    UPDATE zip_code SET location = PointFromText(CONCAT('POINT(',lon,' ',lat,')'));

  5. 现在,让列不接受空值

  6. 向位置列添加空间索引

  7. 在上述项目的代码中,将函数“get_zips_in_range”替换为以下内容:

    function get_zips_in_range($zip, $range, $sort=1, $include_base) 
         {
    
    
        // returns an array of the zip codes within $range of $zip. Returns
        // an array with keys as zip codes and values as the distance from
        // the zipcode defined in $zip.
    
        $this->chronometer();                     // start the clock
    
        $details = $this->get_zip_point($zip);  // base zip details
        if ($details == false) return false;
    
        // This portion of the routine  calculates the minimum and maximum lat and
        // long within a given range.  This portion of the code was written
        // by Jeff Bearer (http://www.jeffbearer.com). This significanly decreases
        // the time it takes to execute a query.  My demo took 3.2 seconds in
        // v1.0.0 and now executes in 0.4 seconds!  Greate job Jeff!
    
        // Find Max - Min Lat / Long for Radius and zero point and query
        // only zips in that range.
        $lat = $details[0];
        $lon = $details[1];
    
        $return = array();    // declared here for scope
    
        $first = true;
        $radius = $range/69.172;
        $boundary = "POLYGON((";
        for($i=0; $i <= 360; $i += 360/24)
        {
            if($first)
            {
                $first = false;
            }
            else
            {
                $boundary .= ', ';
            }
    
            $clon = $radius*cos(deg2rad($i)) + $lon;
            $clat = $radius*sin(deg2rad($i)) + $lat;
            $boundary .= "$clon $clat" ;
        }
    
        $boundary  .= '))';
    
        $sql = "SELECT zip_code, city, county, state_name, state_prefix, area_code, time_zone, lat, lon FROM zip_code WHERE MBRContains(GeomFromText('$boundary'), location);";
    
        //echo $sql;
        $r = mysql_query($sql);
    
        if (!$r) {    // sql error
    
            $this->last_error = mysql_error();
            return false;
    
        } else {
    
            while ($row = mysql_fetch_row($r)) {
    
                // loop through the results to get the milage from src
                $dist = $this->calculate_mileage($details[0],$row[7],$details[1],$row[8]);
                if ($this->units == _UNIT_KILOMETERS) $dist = $dist * _M2KM_FACTOR;
                $return[str_pad($row[0].', '.$row[1], 5, "0", STR_PAD_LEFT)] = round($dist, $this->decimals);
    
            }
            mysql_free_result($r);
        }
    
        // sort array
        switch($sort)
        {
            case _ZIPS_SORT_BY_DISTANCE_ASC:
                asort($return);
                break;
    
            case _ZIPS_SORT_BY_DISTANCE_DESC:
                arsort($return);
                break;
    
            case _ZIPS_SORT_BY_ZIP_ASC:
                ksort($return);
                break;
    
            case _ZIPS_SORT_BY_ZIP_DESC:
                krsort($return);
                break;
        }
    
        $this->last_time = $this->chronometer();
    
        if (empty($return)) return false;
        return $return;
       }
    

【讨论】:

  • 最后,我使用了 Sphinx,速度非常快...不过还是谢谢!
【解决方案5】:

查看此处的邻近搜索:

Using PHP/MySQL with Google Maps

如果您的数据采用相同的符号/投影/格式(无论叫什么),它可能对您有用。

【讨论】:

  • 链接已失效...如果我只有邮政编码.. 没有 Long-Lati 信息怎么办
  • @Chinu 公平点 - 但这里还有很多其他好的答案,我会要求 OP 删除复选标记,以便我可以删除答案
【解决方案6】:

我会考虑首先使用边界方块减少候选者的数量,然后再考虑半径作为第二步。您从邮政编码的坐标开始,然后在所有 4 个方向上计算 50 英里的经度/纬度,然后使用简单的大于/小于标准仅选择该框中的候选人。如果您的用户群分布得很好,这会大大减少您的候选集,那么您只需进行矢量距离数学即可消除“角落”。

【讨论】:

    【解决方案7】:

    每个邮政编码的纬度/经度是该邮政编码的地理中心,对吗?因此,如果您首先找到地理中心在 50 英里范围内的邮政编码,然后是这些邮政编码中的用户,那么您很容易就可以返回 50 英里以外的用户。所以你这样做会牺牲一些准确性。

    但如果您有很多用户(超过邮政编码的数量),这会更快,因为您会先查询较小的邮政编码表。并且您可以在 users 表中索引邮政编码,因此查找具有特定邮政编码的用户会很快。

    只是一些想法!因此,如果您预计会有很多用户,并且 50 英里的半径不需要精确,我会找到 50 英里内的邮政编码,然后是这些邮政编码内的用户。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多