Ответ 1
Обновление. Я написал более подробный блог-блог об этом конкретном предмете на http://www.mullie.eu/geographic-searches/
-
Прокрутите все доступные города, используя API Карт Google, чтобы получить их широту и долготу. Сохраните их где-нибудь (база данных). - Остерегайтесь, Google не будет принимать огромное количество звонков, поэтому дросселируйте ваши звонки.
Затем, выбирая город, вы можете использовать код, похожий на код ниже, чтобы захватить города с определенным диапазоном:
public static function getNearby($lat, $lng, $type = 'cities', $limit = 50, $distance = 50, $unit = 'km')
{
// radius of earth; @note: the earth is not perfectly spherical, but this is considered the 'mean radius'
if ($unit == 'km') $radius = 6371.009; // in kilometers
elseif ($unit == 'mi') $radius = 3958.761; // in miles
// latitude boundaries
$maxLat = (float) $lat + rad2deg($distance / $radius);
$minLat = (float) $lat - rad2deg($distance / $radius);
// longitude boundaries (longitude gets smaller when latitude increases)
$maxLng = (float) $lng + rad2deg($distance / $radius / cos(deg2rad((float) $lat)));
$minLng = (float) $lng - rad2deg($distance / $radius / cos(deg2rad((float) $lat)));
// get results ordered by distance (approx)
$nearby = (array) FrontendDB::getDB()->retrieve('SELECT *
FROM table
WHERE lat > ? AND lat < ? AND lng > ? AND lng < ?
ORDER BY ABS(lat - ?) + ABS(lng - ?) ASC
LIMIT ?;',
array($minLat, $maxLat, $minLng, $maxLng, (float) $lat, (float) $lng, (int) $limit));
return $nearby;
}
Заметки о приведенном выше коде:
- Используется собственная оболочка базы данных, поэтому преобразуем ее в mysql_query, PDO,...
- Это не будет точно. Мы не можем делать точные сферические вычисления в БД, поэтому мы взяли верхний и нижний пределы широты и долготы. Это в основном означает, что местоположение, которое немного дальше вашего расстояния (например, на крайнем северо-востоке, прямо за пределами фактического радиуса (на самом деле это почти круг), но все же внутри максимальной широты и долготы (потому что мы сравниваем это на квадратные пределы в базе данных). Это просто даст приблизительный, но ореховый 100% точный выбор городов с вашим радиусом.
Я попытаюсь проиллюстрировать это:
_________________
| / \ |
| Y / \ |
| / \ |
|( X )|
| \ / |
| \ / |
|______\_/______|
Вышеуказанный круг (несколько) является фактическим радиусом, в котором вы хотите найти местоположения внутри, в зависимости от местоположения X. Это слишком сложно сделать прямо из вашей БД, поэтому то, что мы на самом деле извлекаем из БД, это окружающая площадь, Как вы можете видеть, возможно, что местоположения (например, Y) попадают в эти границы max и min, хотя на самом деле они не имеют требуемого радиуса. Затем они могут быть отфильтрованы через PHP.
Чтобы решить эту последнюю проблему, вы можете закодировать все результаты и рассчитать точное расстояние между вашим корневым местоположением и найденными совпадениями, чтобы рассчитать, действительно ли они находятся в пределах вашего радиуса. Для этого вы можете использовать этот код:
public static function getDistance($lat1, $lng1, $lat2, $lng2, $unit = 'km')
{
// radius of earth; @note: the earth is not perfectly spherical, but this is considered the 'mean radius'
if ($unit == 'km') $radius = 6371.009; // in kilometers
elseif ($unit == 'mi') $radius = 3958.761; // in miles
// convert degrees to radians
$lat1 = deg2rad((float) $lat1);
$lng1 = deg2rad((float) $lng1);
$lat2 = deg2rad((float) $lat2);
$lng2 = deg2rad((float) $lng2);
// great circle distance formula
return $radius * acos(sin($lat1) * sin($lat2) + cos($lat1) * cos($lat2) * cos($lng1 - $lng2));
}
Это будет вычислять (квази) точное расстояние между местоположением X и местоположением Y, а затем вы можете отфильтровать именно те города, которые были достаточно близко, чтобы передать грубую db-выборку, но не достаточно близко, чтобы фактически быть в пределах вашего границы.