Поиск городов в радиусе X километров (или миль)
-
03-07-2019 - |
Вопрос
Это может быть или не быть ясным, оставьте мне комментарий, если я не в теме, или вам нужна дополнительная информация.Возможно, уже есть решение того, что я хочу в PHP.
Я ищу функцию, которая будет добавлять или вычитать расстояние из значения долготы ИЛИ широты.
Причина:У меня есть база данных со всеми широтами и долготами, и я хочу сформировать запрос для извлечения всех городов в пределах X километров (или миль).Мой запрос будет выглядеть примерно так...
Select * From Cities Where (Longitude > X1 and Longitude < X2) And (Latitude > Y1 and Latitude < Y2)
Where X1 = Longitude - (distance)
Where X2 = Longitude + (distance)
Where Y1 = Latitude - (distance)
Where Y2 = Latitude + (distance)
Я работаю на PHP с базой данных MySql.
Также открыт для любых предложений!:)
Решение
Это запрос MySQL, который сделает именно то, что вы хотите.Имейте в виду, что подобные вещи, как правило, являются приблизительными, поскольку Земля не является идеально сферической и не учитывает горы, холмы, долины и т. д.Мы используем этот код на AcademicHomes.com с PHP и MySQL он возвращает записи в пределах $radius миль от $latitude, $longitude.
$res = mysql_query("SELECT
*
FROM
your_table
WHERE
(
(69.1 * (latitude - " . $latitude . ")) *
(69.1 * (latitude - " . $latitude . "))
) + (
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3)) *
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3))
) < " . pow($radius, 2) . "
ORDER BY
(
(69.1 * (latitude - " . $latitude . ")) *
(69.1 * (latitude - " . $latitude . "))
) + (
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3)) *
(69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3))
) ASC");
Другие советы
РЕДАКТИРОВАТЬ:Если у вас где-то есть список всех городов мира с указанием их широты.и длинный.значения, вы можете выполнить поиск.В этом случае см. мою первую ссылку ниже, где приведена формула для расчета ширины в один градус долготы на широте. :
Честно говоря, сложность этой проблемы такова, что вам будет гораздо лучше использовать такой сервис, как Google Maps, для получения ваших данных.В частности, Земля не является идеальной сферой, и расстояние между двумя градусами меняется по мере того, как вы приближаетесь к экватору или дальше от него.
Видеть http://en.wikipedia.org/wiki/Geographic_coordinate_system примеры того, что я имею в виду, и посмотрите API Карт Google.
В зависимости от того, сколько городов вы включаете, вы можете предварительно рассчитать список.Мы делаем это здесь для внутреннего приложения, где погрешность +100 м слишком велика для нашей настройки.Он работает, имея две ключевые таблицы: location1, location2, distance.Затем мы можем очень быстро вернуть местоположения на расстояние x от местоположения1.
Кроме того, поскольку расчеты можно выполнять в автономном режиме, это не влияет на работу системы.Пользователи также получают более быстрые результаты.
Я попробовал использовать приведенный выше код, и ответы были слишком неправильными, когда расстояние между точками было в диапазоне 20-30 миль, и я в порядке с ошибкой в несколько миль.Поговорил с моим приятелем по картографированию, и вместо этого мы придумали вот это.Код написан на Python, но вы можете легко его перевести.Чтобы избежать постоянного преобразования в радианы, я переделал свою базу данных, преобразовав точки широты и долготы из градусов в радианы.Самое приятное в этом то, что большая часть математических вычислений выполняется один раз.
ra = 3963.1906 # radius @ equator in miles, change to km if you want distance in km
rb = 3949.90275 # radius @ poles in miles, change to km if you want distance in km
ra2 = ra * ra
rb2 = rb * rb
phi = self.lat
big_ol_constant = (math.pow(ra2*math.cos(phi), 2) + pow(rb2*math.sin(phi), 2))/ (pow(ra*math.cos(phi), 2) + pow(rb*math.sin(phi), 2))
sqlWhere = "%(distance)g > sqrt((power(lat - %(lat)g,2) + power(lng-%(lng)g,2)) * %(big_ol_constant)g)" % {
'big_ol_constant': big_ol_constant, 'lat': self.lat, 'lng': self.lng, 'distance': distance}
# This is the Django portion of it, where the ORM kicks in. sqlWhere is what you would put after the WHERE part of your SQL Query.
qs = ZipData.objects.extra(where=[sqlWhere]);
Кажется, это очень точно, когда расстояние друг от друга небольшое, и в пределах 10 миль или около того, когда расстояние увеличивается до 200 миль (конечно, к тому времени у вас возникают проблемы с «по прямой» и «дорогами с твердым покрытием»).
Вот модель ZipData, о которой я упоминал выше.
class ZipData(models.Model):
zipcode = ZipCodeField(null=False, blank=False, verbose_name="ZipCode", primary_key=True)
city = models.CharField(max_length=32, null=False, blank=False)
state = models.CharField(max_length=2)
lat = models.FloatField(null=False, blank=False)
lng = models.FloatField(null=False, blank=False)
Дополнительное примечание: вы можете получить МНОГО географических данных, связанных с почтовыми индексами, по адресу GeoNames.org и у них даже есть некоторые API-интерфейсы веб-сервисов, которые вы также можете использовать.
Есть много (плохих вариантов)
Рассчитайте расстояние, используя математическую формулу (рассматривайте X1-X2 и Y1-Y2) как векторы.
Заранее создайте справочную таблицу со всеми комбинациями и соблюдайте расстояния.
Рассмотрите возможность использования расширения MySQL для ГИС.Вот одна статья Я нашел об этом.
На сайте lessthandot.com есть три разных способа сделать это.вам придется немного пролистать блоги, но они там есть.http://blogs.lessthandot.com/
Функция ниже взята из ботанический ужин(пример приложения ASP.NET MVC доступен на кодплексе) база данных (MSSQL).
ALTER FUNCTION [dbo].[DistanceBetween] (@Lat1 as real,
@Long1 as real, @Lat2 as real, @Long2 as real)
RETURNS real
AS
BEGIN
DECLARE @dLat1InRad as float(53);
SET @dLat1InRad = @Lat1 * (PI()/180.0);
DECLARE @dLong1InRad as float(53);
SET @dLong1InRad = @Long1 * (PI()/180.0);
DECLARE @dLat2InRad as float(53);
SET @dLat2InRad = @Lat2 * (PI()/180.0);
DECLARE @dLong2InRad as float(53);
SET @dLong2InRad = @Long2 * (PI()/180.0);
DECLARE @dLongitude as float(53);
SET @dLongitude = @dLong2InRad - @dLong1InRad;
DECLARE @dLatitude as float(53);
SET @dLatitude = @dLat2InRad - @dLat1InRad;
/* Intermediate result a. */
DECLARE @a as float(53);
SET @a = SQUARE (SIN (@dLatitude / 2.0)) + COS (@dLat1InRad)
* COS (@dLat2InRad)
* SQUARE(SIN (@dLongitude / 2.0));
/* Intermediate result c (great circle distance in Radians). */
DECLARE @c as real;
SET @c = 2.0 * ATN2 (SQRT (@a), SQRT (1.0 - @a));
DECLARE @kEarthRadius as real;
/* SET kEarthRadius = 3956.0 miles */
SET @kEarthRadius = 6376.5; /* kms */
DECLARE @dDistance as real;
SET @dDistance = @kEarthRadius * @c;
return (@dDistance);
END
Я предполагаю, что это может быть полезно.
Вы можете использовать теорему Пифагора для расчета близости двух пар точек широты и долготы.
Если у вас есть два местоположения (Альфа и Бета), вы можете рассчитать их расстояние друг от друга с помощью:
SQRT( POW(Alpha_lat - Beta_lat,2) + POW(Alpha_lon - Beta_lon,2) )
Используя настройку из следующего URL-адреса, я построил приведенный ниже запрос.(Обратите внимание, что я использую codeIgnitor для запроса базы данных)
http://howto-use-mysql-spatial-ext.blogspot.com/2007/11/using-circular-area-selection.html
function getRadius($point="POINT(-29.8368 30.9096)", $radius=2)
{
$km = 0.009;
$center = "GeomFromText('$point')";
$radius = $radius*$km;
$bbox = "CONCAT('POLYGON((',
X($center) - $radius, ' ', Y($center) - $radius, ',',
X($center) + $radius, ' ', Y($center) - $radius, ',',
X($center) + $radius, ' ', Y($center) + $radius, ',',
X($center) - $radius, ' ', Y($center) + $radius, ',',
X($center) - $radius, ' ', Y($center) - $radius, '
))')";
$query = $this->db->query("
SELECT id, AsText(latLng) AS latLng, (SQRT(POW( ABS( X(latLng) - X({$center})), 2) + POW( ABS(Y(latLng) - Y({$center})), 2 )))/0.009 AS distance
FROM crime_listing
WHERE Intersects( latLng, GeomFromText($bbox) )
AND SQRT(POW( ABS( X(latLng) - X({$center})), 2) + POW( ABS(Y(latLng) - Y({$center})), 2 )) < $radius
ORDER BY distance
");
if($query->num_rows()>0){
return($query->result());
}else{
return false;
}
}
Не изобретайте велосипед.Это пространственный запрос.Использовать Встроенные пространственные расширения MySQL для хранения данных координат широты и долготы в собственный тип столбца геометрии MySQL.Затем используйте Расстояние функция для запроса точек, находящихся на заданном расстоянии друг от друга.
Отказ от ответственности:это основано на чтении документации, я сам не пробовал.