поиск близости почтового индекса php / mysql - PullRequest
18 голосов
/ 09 марта 2010

Я просто ищу предложения о том, как лучше всего это сделать ...

Мне нужно создать функцию поиска, которая будет искать «пользователей» в радиусе 50 миль от почтового индекса. У меня есть таблица почтовых индексов, которая содержит все почтовые индексы США с указанием их широты и долготы, но я просто пытаюсь найти лучший способ структурировать и запрашивать мои данные ...

Должен ли я добавить столбцы широты / долготы в таблицу пользователей и запросить ее для всех пользователей в радиусе заданного почтового индекса? Или я должен запросить таблицу почтовых индексов для всех почтовых индексов, которые попадают в радиус, а затем запросить таблицу пользователей для всех пользователей с результатами (почтовые индексы)? Или же... ??? Я открыт для любых предложений на данный момент!

Спасибо!

Ответы [ 7 ]

19 голосов
/ 09 марта 2010

Вот лучший способ, который я нашел. Конечно, это потребует, чтобы у вас были все ваши почтовые индексы, закодированные в базе данных.

// 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) и получить список пользователей обратно.

Счастливого кодирования!

3 голосов
/ 09 марта 2010

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

Я нашел это очень круто.

"запросить в таблице почтовых индексов все почтовые индексы, попадающие в радиус, а затем запросить в таблице пользователей всех пользователей с результатами (почтовые индексы)"

Я считаю, что это лучший способ сделать это, если вам не нужно размещать пользователей на карте Google. Если вы просто перечисляете пользователей в диапазоне пробега, должно быть довольно легко запросить базу данных (используя класс) для списка почтовых индексов, а затем выбрать всех пользователей в этих почтовых индексах.

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

Это должно сделать это.

2 голосов
/ 11 ноября 2010

Начните здесь, но обратите внимание, что решение не очень быстрое:

Расчет диапазона и расстояния почтового индекса PHP

Теперь, чтобы сделать это быстро - мы собираемся заменить поиск, чтобы использовать пространственный индекс:)

  1. Использовать MySQL

  2. Добавить столбец в базу данных с именем location и задать для него тип POINT

  3. Убедитесь, что он принимает нулевые значения прямо сейчас

  4. Запустите следующий SQL-запрос

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

  5. Теперь, чтобы столбец не принимал значения NULL

  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;
       }
    
2 голосов
/ 09 марта 2010

Проверьте поиск близости, показанный здесь:

Использование PHP / MySQL с Google Maps

Если ваши данные в той же записи / проекции / формате (как бы это ни называлось), они могут работать для вас.

1 голос
/ 25 апреля 2011

Я бы сначала сделал поиск по всем почтовым индексам в радиусе цели. Затем сравните все возвращенные почтовые индексы с вашими пользовательскими почтовыми индексами. Вытащите подходящих пользователей.

Он нашел почтовые индексы в радиусе, нашел этот вызов 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

Надеюсь, это поможет.

1 голос
/ 09 марта 2010

Я бы подумал, что сначала нужно уменьшить число кандидатов с ограничивающим квадратом, а потом беспокоиться о радиусе. Вы начинаете с координат почтового индекса, затем рассчитываете длину / широту 50 миль во всех 4 направлениях, затем выбираете только кандидатов в этом поле, используя простые критерии «больше / меньше». Если ваша пользовательская база хорошо распределена, это значительно сокращает ваш набор кандидатов, тогда вам нужно только сделать векторную математическую оценку расстояния, чтобы устранить «углы».

0 голосов
/ 09 марта 2010

Лат / лонг, который у вас есть для каждого почтового индекса, является географическим центром для этого почтового индекса, верно? Таким образом, если вы сначала найдете почтовые индексы с географическими центрами в пределах 50 миль, а затем пользователи в этих почтовых индексах, вы можете легко возвращать пользователей на расстоянии более 50 миль. Таким образом, вы жертвовали бы некоторой точностью, делая это таким образом.

Но если у вас много пользователей (больше, чем число почтовых индексов), это будет быстрее, так как вы сначала запросите таблицу меньших почтовых индексов. И вы можете индексировать почтовые индексы в таблице пользователей, поэтому поиск пользователей с определенным почтовым индексом будет быстрым.

Просто некоторые мысли! Итак, если вы ожидаете большого количества пользователей и радиус 50 миль не должен быть точным, я бы нашел почтовые индексы в пределах 50 миль, а затем пользователи в этих почтовых индексах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...