Calcul de la distance entre un point et une ligne virtuelle de deux lat / lngs

14

Veuillez vous référer à l'exemple et à l'image correspondante.

Je voudrais atteindre les objectifs suivants: fournir deux emplacements (lat / lng), qui sont présentés ci - dessous en A et B . À partir de cela, une ligne virtuelle serait tracée, puis la distance entre cette ligne et C serait calculée (dans n'importe quelle mesure).

dessin

J'ai atteint cet objectif actuellement dans Google Maps API v3, mais je voudrais également pouvoir effectuer cela en coulisses dans la langue de mon choix. Tous les conseils / idées seraient grandement appréciés!

Prisonnier
la source
AB est-il une ligne Great Circle ?
Kirk Kuykendall
@Kirk, Non, AB n'est qu'une ligne droite
Prisoner
@Michael, c'est un point intéressant. Je vais devoir y jeter un œil!
Prisonnier
@Prisoner @Kirk Littéralement, une "ligne droite" passera sous la surface de la terre. En général, sa projection radiale sur la surface sera en effet un segment d'un grand cercle (en utilisant un modèle de terre sphérique).
whuber
1
@Prisoner C'est une information supplémentaire extrêmement utile! Oui tu as raison. Il faut encore compenser le fait que l'utilisation (lat, lon) fausse différemment les distances est-ouest par rapport au nord-sud. Comme le conseille @Jose, projetez les coordonnées. Cela peut être aussi simple que de multiplier les longitudes par le cosinus de la latitude moyenne, puis de faire semblant d'être sur un plan euclidien.
whuber

Réponses:

6
def get_perp( X1, Y1, X2, Y2, X3, Y3):
    """************************************************************************************************ 
    Purpose - X1,Y1,X2,Y2 = Two points representing the ends of the line segment
              X3,Y3 = The offset point 
    'Returns - X4,Y4 = Returns the Point on the line perpendicular to the offset or None if no such
                        point exists
    '************************************************************************************************ """
    XX = X2 - X1 
    YY = Y2 - Y1 
    ShortestLength = ((XX * (X3 - X1)) + (YY * (Y3 - Y1))) / ((XX * XX) + (YY * YY)) 
    X4 = X1 + XX * ShortestLength 
    Y4 = Y1 + YY * ShortestLength
    if X4 < X2 and X4 > X1 and Y4 < Y2 and Y4 > Y1:
        return X4,Y4
    return None

La longueur la plus courte est la distance dont vous avez besoin, sauf erreur de ma part?

Poilu
la source
Oui, je recherche la distance la plus courte entre C et le segment de ligne. Est-ce ce que ce calcul calcule?
Prisonnier le
1
Cela a bien fonctionné, j'ai passé les trois points (A, B, C) suivants: i.imgur.com/bK9oB.jpg et il est revenu avec le lat / lng de X. Excellent travail!
Prisonnier le
1
@Hairy, Une dernière chose, comment pourrais-je modifier ceci pour aller au point le plus proche (pas seulement la ligne), donc si je l'avais mis au-delà du point de jonction d'une ligne, comment pourrais-je le faire vérifier la distance jusqu'au point?
Prisonnier
1
@Hairy Bon début, mais il semble que trop souvent ce code revient Nonequand une solution légitime existe. Le problème est que le dernier conditionnel suppose X1 <X2 et Y1 <Y2, ce qui ne peut pas toujours être assuré. Un meilleur test d'interdépendance est nécessaire.
whuber
1
@Hairy Il semble que cet échange entre vous et @prisoner ait été productif. Je voudrais souligner que je n'ai rien à voir avec (ou même aucun contrôle sur) les changements de vote ou les points qui ont pu se produire et que mon commentaire était uniquement destiné à vous aider à améliorer votre réponse.
whuber
11

Peut-être que je rends les choses trop compliquées, mais ce que vous voulez, c'est la distance d'un point à une ligne. C'est la distance d'un point le long de AB qui relie AB à C avec une ligne orthogonale à AB. Ce vecteur perpendiculaire à AB est donné par

v=[x2-x1, -(y2-y1)] # Point A is [x1,y1] Point B is [x2,y2]

(J'ai utilisé les crochets pour définir un vecteur ou un tableau à deux éléments). La distance entre C [xp, yp] et le point A est

u=[x1-xp, y1-xp]

La distance entre la ligne et C n'est que la projection de u sur v. Si nous supposons que mod (v) = 1 (il suffit de le normaliser), alors

distance = u*v = abs( (x2-x1)*(y1-yp) - (x1-xp)*(y2-y1) )

La seule complication est que vous voulez probablement vous assurer que vos coordonnées ne sont pas des paires lat / log WGS84, mais projetées (ou utilisez des coordonnées géodésiques). Vous pouvez utiliser OGR ou Proj4 pour cela.

Jose
la source
3
+ plusieurs millions de pseudo-points pour ne pas utiliser les fonctions trigonométriques, soit dit en passant. Trop de gens retirent ArcTan alors qu'ils devraient regarder ceci: en.wikipedia.org/wiki/Dot_product
Herb
@Jose, merci pour la réponse! J'utilise le lat / long de l'API Google Maps. La partie mathématique est assez nouvelle pour moi, donc je vais essayer et voir ce que je peux trouver. Des conseils avec les maths? Par exemple [x2-x1, - (y2-y1)], qu'est-ce que cela signifie?
Prisonnier
J'ai ajouté une courte modification pour cela. Fondamentalement, c'est une notation de tableau, mais si vous stockez vos coordonnées dans les variables x1, x2, y1, y2 et xp, yp, il vous suffit d'écrire le côté droit de la dernière équation que j'ai fournie. C'est à peu près du code C, Java, JS, Python, etc. valide :)
Jose
1
@Jose Vous calculez la distance de C à la ligne AB. Sur la base de la figure, je crois que l'OP veut la distance de C au segment de ligne AB. Cela nécessite un travail supplémentaire pour vérifier si la projection de C sur la ligne AB se situe entre A et B ou non. Dans ce dernier cas, utilisez la plus courte des deux longueurs CA et CB.
whuber
1
@Prisoner La principale différence est qu'une ligne s'étend pour toujours (elle n'est définie que par un vecteur de direction et un point, ou par deux points), tandis qu'un segment entre A et B est le bit de la ligne infinie qui va entre A et B (il a une longueur finie)
Jose
4

Étant un peu opposé à toutes ces mathématiques aussi, j'y viendrais sous un angle différent. Je voudrais en faire une ligne «réelle» plutôt qu'une ligne virtuelle, puis utiliser les outils existants.

Si A et B partagent un attribut, vous pouvez les connecter en traçant une ligne (Kosmo GIS dispose d'un outil qui créera des lignes à partir de points, et je pense qu'il existe également un plugin QGIS pour cela). Une fois que vous avez les lignes, une fonction «proche» sur la couche de points «C» vous donnera la distance à la ligne. Laissez le logiciel gérer les mathématiques pour vous!

Darren Cope
la source
Merci pour le commentaire mais Hairy a trouvé des atouts sur celui-ci!
Prisonnier
1
(+1) Vous faites un excellent point. Les algorithmes de géométrie computationnelle sont notoirement difficiles à obtenir complètement à droite dans la pratique (comme nous pouvons le voir à partir de tout le code proposé jusqu'à présent, ce qui est utile et illustratif mais ne fonctionne pas encore complètement). L'utilisation d'une procédure SIG de haut niveau est souvent un bon moyen de s'assurer que vous obtenez la réponse que vous attendez et qu'elle est correcte (à condition de faire confiance à votre SIG ;-).
whuber
1

Si vous utilisiez Java sur Android, sa seule ligne avec la fonction de bibliothèque

import static com.google.maps.android.PolyUtil.distanceToLine;

distanceToLine:

public static double distanceToLine(LatLng p, LatLng start,LatLng end)

Calcule la distance sur la sphère entre le point p et le segment de ligne du début à la fin.

Paramètres: p - le point à mesurer

start - le début du segment de ligne

end - la fin du segment de ligne

Renvoie: la distance en mètres (en supposant la terre sphérique)

Ajoutez simplement une bibliothèque à votre

dependencies {
    compile 'com.google.maps.android:android-maps-utils:0.5+'
}
indy
la source