Запросы на поиск мест в пределах заданной широты / долготы

Итак, я пытаюсь отобразить списки мест в пределах заданной широты / долготы. У меня нет проблем с этим:

Места в пределах одной мили (список мест ...)

Используя что-то вроде

SELECT * FROM places WHERE lat < $latmax AND lat > $latmin AND lng < $lngmax AND lng > $lngmin

Но затем я хочу перечислить места в пределах двух миль, НО не в пределах одной мили - то есть я не хочу повторять результаты первого запроса.

Вот одна из версий того, что я пробовал:

$milesperdegree = 0.868976242 / 60.0 * 1.2;

// 1 mile -- this works
$degrees = $milesperdegree * 1;
$latmin = $lat - $degrees;
$latmax = $lat + $degrees;
$lngmin = $lng - $degrees;
$lngmax = $lng + $degrees;

$query = "SELECT * FROM places WHERE lat < $latmax AND lat > $latmin AND lng < $lngmax AND lng > $lngmin";

// 2 miles -- this doesn't work
$degrees_2 = $milesperdegree * 2;
$latmin_2 = $lat - $degrees_2;
$latmax_2 = $lat + $degrees_2;
$lngmin_2 = $lat - $degrees_2;
$lngmax_2 = $lat + $degrees_2;

$query = "SELECT * FROM places WHERE ";
$query .= "lat BETWEEN $latmax AND $latmax_2 AND lng BETWEEN $lngmax AND $lngmax_2 OR ";
$query .= "lat BETWEEN $latmin AND $latmin_2 AND lng BETWEEN $lngmin AND $lngmin_2 OR ";
$query .= "lat BETWEEN $latmax AND $latmax_2 AND lng BETWEEN $lngmin AND $lngmin_2 OR ";
$query .= "lat BETWEEN $latmin AND $latmin_2 AND lng BETWEEN $lngmax AND $lngmax_2";

Это не так. Я предполагаю, что это просто какая-то логика, которую я не могу понять в воскресенье днем, но я, вероятно, тоже делаю что-то еще не так. Любой вклад приветствуется.


person nickfindley    schedule 27.02.2011    source источник
comment
Вы понимаете, что этот сценарий просто создает рамку вокруг точки (вместо круга)? Кроме того, $ milesperdegree - это функция (математически говоря) широты. Для этого есть хороший сценарий - хотя и в JavaScript - ссылка   -  person Czechnology    schedule 27.02.2011
comment
Я не понял круг / квадрат. И я предполагаю, что переменные долготы будут $ lng +/- x миль, а не $ milesperdegree?   -  person nickfindley    schedule 27.02.2011
comment
Поскольку Земля не является идеальной сферой, длина градуса не фиксирована. Если ваши скрипты работают только на небольшой территории (например, в городе), вы можете работать со средним значением для этой области. В противном случае для точности потребуется больше математики. См. широту и longitude в Википедии.   -  person Czechnology    schedule 27.02.2011
comment
Что касается части круга / квадрата - простое применение теоремы Пифагора исправит это.   -  person Czechnology    schedule 27.02.2011
comment
Кстати, +1 за BETWEEN ... AND, не знал этого;)   -  person Czechnology    schedule 27.02.2011
comment
@nickfindley, я добавил круговое решение, см. мой ответ.   -  person Czechnology    schedule 27.02.2011
comment
@Czechnology На самом деле расстояние между градусами не имеет ничего общего с тем, что Земля не является идеальной сферой (хотя это имеет крошечный эффект, это довольно незначительно). Это скорее функция расстояния от центра. Центр круга - это северный / южный полюс. Длина любой дуги в x градусов увеличивается с удалением от центра. Должно быть ясно, глядя на эту схему транспортира: farlaboratories.com/dyna-a- prot.html   -  person Endophage    schedule 27.02.2011
comment
@Endophage, вы, конечно, абсолютно правы, в этом и главная причина. Я думал о милях на градус широты, где разница в основном связана с формой Земли. Но, конечно, угол от экватора / полюсов гораздо важнее.   -  person Czechnology    schedule 27.02.2011


Ответы (3)


Мы реализуем его более или менее аналогично приведенному ниже коду (отказ от ответственности: я вырезал это из файла и удалил код, не имеющий отношения к рассматриваемой проблеме. Я не запускал это, но вы должны уловить идею.

$maxLat = $city->latitude + ($max_distance / 69); // 69 Miles/Degree
$minLat = $city->latitude - ($max_distance / 69);

$maxLon = $city->longitude + ($max_distance / (69.172 * cos($city->latitude * 0.0174533)));
$minLon = $city->longitude - ($max_distance / (69.172 * cos($city->latitude * 0.0174533)));

// Simplify terms to speed query
$originLatRadCos = cos($city->latitude * 0.0174533);
$originLatRadSin = sin($city->latitude * 0.0174533);
$originLonRad = $city->longitude * 0.0174533;

$city_distance_query = "
SELECT city_id, 
  3963 * acos(($originLatRadSin * sin( latitude * 0.0174533)) + 
  ($originLatRadCos * cos(latitude * 0.0174533) * cos((longitude * 0.0174533) -
  $originLonRad))) AS distanceFromOrigin
FROM cities
WHERE
 latitude < $maxLat AND latitude > $minLat AND longitude < $maxLon AND longitude > $minLon";

Остальная часть запроса

SELECT cities.city_name, CityDistance.distanceFromOrigin,
FROM cities 
INNER JOIN ($city_distance_query) AS CityDistance ON CityDistance.city_id=cities.city_id
WHERE (distanceFromOrigin < $distance OR distanceFromOrigin IS NULL) 
person Roger W.    schedule 27.02.2011
comment
Об этом точно не догадался. Спасибо всем. - person nickfindley; 28.02.2011
comment
Большое спасибо за пост. Очень меня спасли! Очень легко адаптируется и понятно. - person Paul T. Rawkeen; 26.07.2012

Я думаю, вам не хватает скобок, и вы немного перепутали логические операторы. Как насчет этого.

$query  = "SELECT * FROM places WHERE ";
$query .= "((lat BETWEEN $latmin_2 AND $latmax_2) AND NOT (lat BETWEEN $latmin AND $latmax)) AND ";
$query .= "((lng BETWEEN $lngmin_2 AND $lngmax_2) AND NOT (lng BETWEEN $lngmin AND $lngmax)) AND ";

ИЗМЕНИТЬ

Чтобы решить проблему круга / квадрата:

$query  = "SELECT * FROM places WHERE ";
$query .= "(POW((lat - $lat) * $avgMilesPerLatDeg,2) + ".
           "POW((lng - $lng) * $avgMilesPerLngDeg,2) BETWEEN 1 AND 4)";
// the four at the end is 2 squared

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

person Czechnology    schedule 27.02.2011
comment
Попасть туда! Но должен признать, что не совсем понимаю, что происходит, по крайней мере, математически. Что это за запрос, чтобы получить места на расстоянии от двух до трех миль? - person nickfindley; 28.02.2011
comment
Три мили - это самое большое расстояние, на которое я бы пошел в этом приложении - достаточно просто находиться примерно по соседству. Хотя я ценю то, что эти знания были отброшены для использования в будущем. - person nickfindley; 28.02.2011
comment
@nickfindley, это действительно просто пифагорейский a^2 + b^2 = c^2, в нашем случае n <= (x-a)^2 + (y-b)^2 <= m. Таким образом, для мест от двух до трех миль вам понадобится ... BETWEEN POW(2,2) AND POW(3,2), что эквивалентно ... BETWEEN 4 AND 9 - person Czechnology; 28.02.2011

Вот что, я думаю, вам нужно: Рассчитайте расстояние в MySQL, используя широту / долготу. Это даст вам круг и возможность иметь необходимое вам исключение.

Следуйте инструкциям, описанным в сообщении, но вместо

HAVING `distance`<= 10

Вам нужно будет поставить

HAVING `distance` BETWEEN 1 AND 2

Это даст вам материал в пределах допустимого диапазона.

PS: Если у вас есть база данных с большим количеством записей - вам нужно будет проверить, как она будет работать, и выполнить некоторую оптимизацию (если производительность неприемлема)

person Sergey    schedule 27.02.2011