SQL-запрос геолокации не находит точное местоположение - PullRequest
4 голосов
/ 23 апреля 2010

Я уже некоторое время тестирую свой запрос геолокации, и до сих пор не обнаружил никаких проблем с ним.

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

В моей базе данных есть выдержка из этих городов:

city            latitude    longitude  
Saint-Mathieu   45.316708   -73.516253  
Saint-Édouard   45.233374   -73.516254  
Saint-Michel    45.233374   -73.566256  
Saint-Rémi      45.266708   -73.616257  

Но когда я запускаю свой запрос по городу Сен-Реми со следующим запросом ...

SELECT tblcity.city, tblcity.latitude, tblcity.longitude, 
truncate((degrees(acos( sin(radians(tblcity.latitude)) 
* sin(radians(45.266708)) 
+ cos(radians(tblcity.latitude)) 
* cos(radians(45.266708)) 
* cos(radians(tblcity.longitude - -73.616257) ) ) ) 
* 69.09*1.6),1) as distance 
FROM tblcity HAVING distance < 10 ORDER BY distance desc 

Я получаю эти результаты:

city            latitude    longitude     distance  
Saint-Mathieu   45.316708   -73.516253    9.5  
Saint-Édouard   45.233374   -73.516254    8.6  
Saint-Michel    45.233374   -73.566256    5.3  

Город Сен-Реми отсутствует в поиске.

Поэтому я попытался изменить запрос, надеясь получить лучший результат:

SELECT tblcity.city, tblcity.latitude, tblcity.longitude, 
truncate(( 6371 * acos( cos( radians( 45.266708 ) ) 
* cos( radians( tblcity.latitude ) ) 
* cos( radians( tblcity.longitude ) 
- radians( -73.616257 ) ) 
+ sin( radians( 45.266708 ) ) 
* sin( radians( tblcity.latitude ) ) ) ),1) AS distance 
FROM tblcity HAVING distance < 10 ORDER BY distance desc 

Но я получаю тот же результат ...

Однако, если я изменю Saint-Координаты Rémi немного изменив последнюю цифру лат или long на 1, оба запроса вернут Сен-Реми.Кроме того, если я сконцентрирую запрос на каком-либо из других городов, указанных выше, искомый город возвращается в результатах.

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

Я использую MySQL 5.0.45, заранее спасибо.

CREATE TABLE `tblcity` ( 
`IDCity` int(1) NOT NULL auto_increment, 
`City` varchar(155) NOT NULL default '', 
`Latitude` decimal(9,6) NOT NULL default '0.000000', 
`Longitude` decimal(9,6) NOT NULL default '0.000000', 
PRIMARY KEY (`IDCity`) 
) ENGINE=MyISAM AUTO_INCREMENT=52743 DEFAULT CHARSET=latin1 AUTO_INCREMENT=52743; 

INSERT INTO `tblcity` (`city`, `latitude`, `longitude`) VALUES 
('Saint-Mathieu', 45.316708, -73.516253), 
('Saint-Édouard', 45.233374, -73.516254), 
('Saint-Michel', 45.233374, -73.566256), 
('Saint-Rémi', 45.266708, -73.616257); 

Ответы [ 2 ]

3 голосов
/ 23 апреля 2010

В вашем первом запросе, я полагаю, вы перевернули долготы в вычитании. Сферический закон косинусов:

d = acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(long2−long1))*R

Если lat1 заменен на tblcity.latitude, long1 должен быть заменен на tblcity.longitude. Я думаю, что вы случайно заменили long2 в своем запросе. Этот работает лучше?

SELECT tblcity.city, tblcity.latitude, tblcity.longitude, 
truncate((degrees(acos( sin(radians(tblcity.latitude)) 
* sin(radians(45.266708)) 
+ cos(radians(tblcity.latitude)) 
* cos(radians(45.266708)) 
* cos(radians(-73.616257 - tblcity.longitude) ) ) ) 
* 69.09*1.6),1) as distance 
FROM tblcity HAVING distance < 10 ORDER BY distance desc 

Я еще не изучил ваш второй запрос, но, надеюсь, это поможет.

2 голосов
/ 18 июля 2010

Вы используете формулу "сферического закона косинусов", которая допускает ошибку округления на малых расстояниях (например, ноль!) - см. это обсуждение . Длинное выражение, которое передается в acos (), оценивается чуть больше 1,0, что выходит за пределы.

Вот проблема, проиллюстрированная с использованием Python для расчетов:

>>> from math import sin, cos, acos, radians
>>> lat = radians(45.266708)
>>> long_expression = sin(lat) * sin(lat) + cos(lat) * cos(lat) * cos(0.0)
>>> repr(long_expression)
'1.0000000000000002'
>>> acos(long_expression)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: math domain error
>>>

Похоже, что MySQL заменяет NULL вместо создания исключения. Я мало знаю о MySQL, но вы должны быть в состоянии преодолеть это, делая что-то вроде ifnull(acos(long_expression), 0.0) или coalesce(acos(long_expression), 0.0).

В качестве альтернативы вы могли бы использовать формулу haversine, которая переносит проблему округления от ступеньки к противоположной стороне земли.

Обновление: Я протестировал использование этой формулы в Python для расчета расстояния между точкой и той же точкой, которое должно быть равно нулю, для каждого из 37582 уникальных (широта, долгота) 2- кортежи в файле с почтовыми индексами США .

Из них:

  • 31591 (84,1%) дали нулевое расстояние
  • 4244 (11,3%) дали расстояние 9,5 см.
  • 831 (2,2%) дали расстояние 13,4 см.
  • 916 (2,4%) выдает значение cos, равное 1.0000000000000002, которое может вызвать исключение в acos (), если его не обнаружить и избежать.

Похоже, что явное тестирование для lat1 = lat2 and lon1 = lon2 и избегание формулы в этом случае (просто используйте ноль) может быть хорошей идеей - это даст последовательный ответ и позволит избежать недоумения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...