web-dev-qa-db-fra.com

Trouver efficacement la paire de coordonnées la plus proche d'un ensemble dans Python

Le problème

Imaginez que je me tenais dans un aéroport. Étant donné une paire de coordonnées géographiques, comment déterminer efficacement l'aéroport dans lequel je me trouvais?

Entrées

  • Une paire de coordonnées (x,y) Représentant l'emplacement où je me trouvais.
  • Un ensemble de paires de coordonnées [(a1,b1), (a2,b2)...] où chaque paire de coordonnées représente un aéroport.

sortie souhaitée

Une paire de coordonnées (a,b) De l'ensemble de paires de coordonnées d'aéroport représentant l'aéroport le plus proche du point (x,y).

Solution inefficace

Voici ma tentative inefficace de résoudre ce problème. Il est clairement linéaire dans la longueur de l'ensemble des aéroports.

shortest_distance = None
shortest_distance_coordinates = None

point = (50.776435, -0.146834)

for airport in airports:
    distance = compute_distance(point, airport)
    if distance < shortest_distance or shortest_distance is None:
        shortest_distance = distance
        shortest_distance_coordinates = airport

La question

Comment cette solution peut-elle être améliorée? Cela pourrait impliquer un moyen de pré-filtrer la liste des aéroports en fonction des coordonnées de l'emplacement où nous nous trouvons actuellement, ou de les trier dans un certain ordre au préalable.

12
Kieran

Si vos coordonnées ne sont pas triées, votre recherche ne peut être que légèrement améliorée en supposant qu'elle est (latitude,longitude) en filtrant d'abord la latitude comme pour la terre

1 degré de latitude sur la sphère est de 111,2 km ou 69 miles

mais cela ne donnerait pas une énorme accélération.

Si vous triez d'abord les aéroports par latitude, vous pouvez utiliser une recherche binaire pour trouver le premier aéroport qui pourrait correspondre à (airport_lat >= point_lat-tolerance) et ensuite comparer uniquement jusqu'au dernier qui pourrait correspondre (airport_lat <= point_lat+tolerance) - mais prenez soin de 0 degré égal à 360. Bien que vous ne puissiez pas utiliser cette bibliothèque directement, les sources de bisect sont un bon début pour implémenter une recherche binaire.

Bien que techniquement, la recherche soit toujours O (n), vous avez beaucoup moins de calculs de distance réels (en fonction de la tolérance) et peu de comparaisons de latitude. Vous aurez donc une accélération énorme.

2
janbrohl

En utilisant un arbre k-dimensionnel:

>>> from scipy import spatial
>>> airports = [(10,10),(20,20),(30,30),(40,40)]
>>> tree = spatial.KDTree(airports)
>>> tree.query([(21,21)])
(array([ 1.41421356]), array([1]))

Où 1,41421356 est la distance entre le point interrogé et le voisin le plus proche et 1 est l'indice du voisin.

Voir: http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.KDTree.query.html#scipy.spatial.KDTree.query

13
Juddling

De cela question SO :

import numpy as np
def closest_node(node, nodes):
    nodes = np.asarray(nodes)
    deltas = nodes - node
    dist_2 = np.einsum('ij,ij->i', deltas, deltas)
    return np.argmin(dist_2)

node est un Tuple avec deux valeurs (x, y) et nodes est un tableau de tuples avec deux valeurs ([(x_1, y_1), (x_2, y_2),])

2
J. C. Rocamonde