Обновление : я написал более подробный пост блога на эту конкретную тему в 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) попадают в эти максимальные и минимальные границы, хотя на самом деле они не находятся в пределах запрошенного радиуса. Впоследствии они могут быть отфильтрованы через PHP.
КомуРешив эту последнюю проблему, вы можете зациклить все результаты и рассчитать точное расстояние между вамиr корневое местоположение и найденные близкие совпадения, чтобы вычислить, действительно ли они находятся в пределах вашего радиуса.Для этого вы можете использовать этот код:
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-fetch, но не достаточно близко, чтобы быть в пределах ваших возможностей.