Поиск близости к почтовому индексу 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 миль, а затем пользователи в этих почтовых индексах.