web-dev-qa-db-fra.com

Algorithme permettant de trouver toutes les positions de latitude et de longitude situées à une certaine distance d'une position donnée de latitude et de longitude

Avec une base de données de lieux avec des positions Latitude + Longitude, telles que 40.8120390, -73.4889650, comment pourrais-je trouver tous les lieux situés à une distance donnée d'un emplacement spécifique?

Il ne semble pas très efficace de sélectionner tous les lieux de la base de données, puis de les parcourir un par un, en prenant la distance du lieu de départ pour voir s’ils se situent à la distance spécifiée. Existe-t-il un bon moyen de réduire les emplacements initialement sélectionnés dans la base de données? Une fois que j'ai (ou pas?) Un ensemble d'emplacements restreint, est-ce que je les parcoure encore un par un pour vérifier la distance, ou existe-t-il une meilleure façon?

La langue dans laquelle je fais cela n'a pas vraiment d'importance. Merci!

72
Valera

Commencez par comparer la distance entre les latitudes. Chaque degré de latitude est séparé d’environ 111 km. La plage varie (en raison de la forme légèrement ellipsoïde de la Terre) de 110,567 km (68,703 miles) à l'équateur à 111,799 km (69,407 km) aux pôles. La distance entre deux lieux sera égale ou supérieure à la distance entre leurs latitudes.

Notez que ce n'est pas vrai pour les longitudes - la longueur de chaque degré de longitude dépend de la latitude. Toutefois, si vos données sont liées à une zone (un seul pays, par exemple), vous pouvez également calculer des limites minimale et maximale pour les longitudes.


Continuez, calculez une distance rapide et précise avec une terre sphérique:

La distance du grand cercle d entre deux points de coordonnées {lat1, lon1} et {lat2, lon2} est donnée par:

d = acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2))

Une formule mathématiquement équivalente, moins sujette aux erreurs d’arrondi sur de courtes distances, est la suivante:

d = 2*asin(sqrt((sin((lat1-lat2)/2))^2 +
    cos(lat1)*cos(lat2)*(sin((lon1-lon2)/2))^2))

d est la distance en radians

distance_km ≈ radius_km * distance_radians ≈ 6371 * d

(6371 km est le rayon moyen de la terre )

Les exigences de calcul de cette méthode sont minimales. Cependant, le résultat est très précis pour les petites distances.


Ensuite, si vous vous trouvez à une distance donnée, utilisez une méthode plus précise.

GeographicLib est l'implémentation la plus précise que je connaisse, bien que formule inverse de Vincenty puisse également être utilisé.


Si vous utilisez un SGBDR, définissez la latitude comme clé primaire et la longitude comme clé secondaire. Recherchez une plage de latitude ou une plage de latitude/longitude, comme décrit ci-dessus, puis calculez les distances exactes pour le jeu de résultats.

Notez que les versions modernes de tous les principaux SGBDR prennent en charge les types de données géographiques et les requêtes de manière native.

37
Lior Kogan

En fonction de la latitude, de la longitude et de la distance de l'utilisateur actuel, la requête SQL est donnée ci-dessous.

SELECT * FROM(
    SELECT *,(((acos(sin((@latitude*pi()/180)) * sin((Latitude*pi()/180))+cos((@latitude*pi()/180)) * cos((Latitude*pi()/180)) * cos(((@longitude - Longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344) as distance FROM Distances) t
WHERE distance <= @distance

@latitude et @longitude sont la latitude et la longitude du point. La latitude et la longitude sont les colonnes du tableau des distances. La valeur de pi est 22/7

12
yogihosting

extensions PostgreSQL GIS peut être utile - dans ce cas, il peut déjà implémenter une grande partie des fonctionnalités que vous envisagez de mettre en œuvre.

6
Gian

Essayez ceci pour une bonne solution: Recherche par géolocalisation

5
Nikita Koksharov

Tank Yogihosting

J'ai dans ma base de données un groupe de tables de Open Streep Maps et j'ai testé avec succès.

Distance de travail très bien en mètres.

SET @orig_lat=-8.116137;
SET @orig_lon=-34.897488;
SET @dist=1000;

SELECT *,(((acos(sin((@orig_lat*pi()/180)) * sin((dest.latitude*pi()/180))+cos((@orig_lat*pi()/180))*cos((dest.latitude*pi()/180))*cos(((@orig_lon-dest.longitude)*pi()/180))))*180/pi())*60*1.1515*1609.344) as distance FROM nodes AS dest HAVING distance < @dist ORDER BY distance ASC LIMIT 100;
4
Helmut Kemper
2
Alix Axel

Comme biziclop l'a mentionné, une sorte d'arborescence de l'espace métrique serait probablement votre meilleure option. J'ai l'habitude d'utiliser des kd-trees et des quadruples pour effectuer ce type de requêtes d'intervalle et ils sont incroyablement rapides; ils ne sont pas si difficiles à écrire. Je suggérerais d'examiner l'une de ces structures, car elles vous permettent également de répondre à d'autres questions intéressantes telles que "quel est le point le plus proche de mon ensemble de données par rapport à cet autre point?"

2
templatetypedef

Ce dont vous avez besoin, c'est d'une recherche spatiale. Vous pouvez utiliser recherche spatiale Solr . Il a également intégré le type de données lat/long, cochez ici .

1
Zimbabao

Puisque vous dites que toute langue est acceptable, le choix naturel est PostGIS:

SELECT * FROM places
WHERE ST_DistanceSpheroid(geom, $location, $spheroid) < $max_metres;

Si vous voulez utiliser le datum WGS, vous devez définir $spheroid à 'SPHEROID["WGS 84",6378137,298.257223563]'

En supposant que vous ayez indexé places par la colonne geom, cela devrait être raisonnablement efficace.

0
Toby Speight

Vous pouvez convertir latitude-longitude au format UTM, qui est un format métrique pouvant vous aider à calculer les distances. Ensuite, vous pouvez facilement décider si un point tombe à un emplacement spécifique.

0
Hamdi