2010-04-23 4 views
4

J'ai testé ma requête de géolocalisation depuis un certain temps et je n'ai trouvé aucun problème jusqu'à maintenant. J'essaie de rechercher toutes les villes dans un rayon donné, souvent je cherche des villes entourant une ville en utilisant les coords de cette ville, mais récemment j'ai essayé de chercher autour d'une ville et j'ai constaté que la ville elle-même n'était pas retournée .Géolocalisation La requête SQL ne trouve pas l'emplacement exact

Je ces villes comme un extrait dans ma base de données:

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 

Mais quand je lance ma requête autour de la ville de Saint-Rémi, avec la requête suivante ...

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 

J'obtiens ces résultats:

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 

La ville de Saint-Rémi manque à la recherche.

donc j'ai essayé une requête modifiée dans l'espoir d'obtenir un meilleur résultat:

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 

Mais j'obtenir le même résultat ...

Cependant, si je modifie les coords de Saint-Rémi slighly en changeant la dernier chiffre du lat ou long de 1, les deux requêtes retourneront Saint-Rémi. De plus, si je centre la requête sur l'une des autres villes ci-dessus, la ville recherchée est renvoyée dans les résultats.

Quelqu'un peut-il faire la lumière sur ce qui peut m'amener à mes questions ci-dessus pour ne pas afficher la ville de Saint-Rémi recherchée? J'ai ajouté un échantillon de la table (avec des champs supplémentaires supprimés) ci-dessous. J'utilise MySQL 5.0.45, merci d'avance. J'utilise 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); 

Répondre

3

Dans votre première requête, je crois que vous avez inversé les longitudes dans la soustraction. La loi sphérique de Cosinus est:

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

Si lat1 est substitué par tblcity.latitude, long1 doit être remplacé par tblcity.longitude. Je pense que vous avez accidentellement remplacé long2 dans votre requête. Est-ce que celui-ci fonctionne mieux?

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 

Je n'ai pas encore examiné votre deuxième requête, mais j'espère que cela aide.

+0

Merci Mike, j'ai essayé la requête que vous avez suggérée et je suis arrivé au même résultat, la ville de Saint-Rémi est toujours omise. Je ne prétendrai pas que je connaissais les mathématiques derrière la requête, je devais Google pour la créer, mais votre équation ci-dessus m'aide à comprendre un peu mieux les maths derrière la requête. Merci! – Iridium52

+0

J'étais paresseux avant, et je ne l'ai pas testé. J'ai été surpris d'apprendre que ça avait échoué, alors je l'ai essayé maintenant. Certes, vous l'avez facilité en fournissant l'entrée SQL pour créer la table. En tout cas, ça a marché! Je ne sais pas quoi dire maintenant ...; o) –

+0

Hmmmm. Votre requête originale a également fonctionné pour moi, même si les longitudes ont été inversées, ce qui est logique puisque la soustraction se terminera à zéro. Maintenant, je ne comprends pas pourquoi cela ne fonctionne pas pour vous. Pourriez-vous remplacer la «distance HAVING <10» par «HAVING city = 'Saint-Rémi'»? Ensuite, nous pourrions au moins voir ce que votre MySQL a calculé comme la distance (la mienne dit 0.0). –

2

Vous utilisez la formule "loi sphérique des cosinus", qui est sujette à des erreurs d'arrondi à faible distance (comme zéro!) - voir this discussion. L'expression longue qui est introduite dans acos() est évaluée à un peu plus de 1.0, ce qui est hors limite.

est ici le problème illustré en utilisant Python pour effectuer les calculs:

>>> 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 
>>> 

Il semble que MySQL est substituait NULL au lieu de soulever une exception. Je sais peu de choses sur MySQL, mais vous devriez être capable de surmonter cela en faisant quelque chose comme ifnull(acos(long_expression), 0.0) ou coalesce(acos(long_expression), 0.0).Vous pouvez également utiliser la formule de la haversine, qui déplace le problème d'arrondi de votre pas de porte vers le côté opposé de la terre.

Mise à jour: Je l'ai testé en utilisant cette formule en Python pour calculer le devrait-être zéro distance entre un point et le même point, pour chacun des 2 triplets uniques 37582 (lat, dans une longitude) file of US zip codes.

Parmi ceux-ci:

  • 31591 (84,1%) a produit une distance
  • 4244 (11,3%) zéro produit une distance de 9,5 cm.
  • 831 (2,2%) ont produit une distance de 13,4 cm.
  • 916 (2,4%) produit une valeur "cos" de 1.0000000000000002 qui provoquerait une exception dans acos() si elle n'est pas détectée et évitée.

Il semble que le test explicite pour lat1 = lat2 and lon1 = lon2 et en évitant la formule dans ce cas (il suffit d'utiliser zéro) pourrait être une bonne idée - il donner une réponse cohérente et éviter la perplexité.