Поиск близости к почтовому индексу php/mysql

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

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

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

Спасибо!

Ответы

Ответ 1

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

// 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 zipcode, для которого вы хотите использовать радиус, включите дополнительный радиус и получите список zipcodes.

Вы можете очень легко изменить это, чтобы получить всех пользователей, где zipcode IN (radius_sql) и вернуть пользователей списка.

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

Ответ 2

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

Я нашел это очень удивительным.

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

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

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

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

Ответ 3

Ознакомьтесь с предлагаемым здесь поиском близости:

Использование PHP/MySQL с Картами Google

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

Ответ 4

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

Интервал Zip-кода и расчет расстояний

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

  • Использовать MySQL

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

  • Убедитесь, что он принимает значения null прямо сейчас

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

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

  • Теперь сделайте столбец не принятым nulls

  • Добавить пространственный индекс в столбец местоположения

  • В коде из вышеуказанного проекта замените функцию '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;
       }
    

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

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