Calcular la distancia entre un punto y una línea virtual de dos lat / lngs

14

Consulte el ejemplo y la imagen correspondiente.

Me gustaría lograr lo siguiente: proporcionar dos ubicaciones (lat / lng), que se muestran a continuación como A y B . A partir de esto, se trazaría una línea virtual y luego se calcularía la distancia entre esta línea y C (en cualquier medida).

dibujo

Lo he logrado actualmente en la API v3 de Google Maps, pero me gustaría poder realizar esto detrás de escena en el idioma que elija. Cualquier sugerencia / idea sería muy apreciada!

Prisionero
fuente
¿Es AB una línea de gran círculo ?
Kirk Kuykendall
@Kirk, No, AB es solo una línea recta
Prisionero
@ Michael, ese es un punto interesante. ¡Tendré que echarle un vistazo!
Prisionero el
@Prisoner @Kirk Literalmente, una "línea recta" pasará por debajo de la superficie de la tierra. En general, su proyección radial de regreso a la superficie será, de hecho, un segmento de un gran círculo (usando un modelo de tierra esférica).
whuber
1
@Prisoner ¡Esa es una información adicional extremadamente útil! Sí, estás en lo correcto. Todavía tiene que compensar el hecho de que el uso (lat, lon) distorsiona de manera diferencial las distancias este-oeste en comparación con el norte-sur. Como @Jose aconseja, proyecte las coordenadas. Esto puede ser tan simple como multiplicar previamente las longitudes por el coseno de la latitud promedio y luego pretender que estás en un plano euclidiano.
whuber

Respuestas:

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 longitud más corta es la distancia que necesita, a menos que me equivoque?

Peludo
fuente
Sí, estoy buscando la distancia más corta desde C hasta el segmento de línea. ¿Es esto lo que calcula esta matemática?
Prisionero
1
Realmente funcionó bien, pasé los tres puntos (A, B, C) en lo siguiente: i.imgur.com/bK9oB.jpg y regresó con el lat / lng de X. ¡Gran trabajo!
Prisionero
1
@Hairy, una última cosa, ¿cómo haría para modificar esto para ir al punto más cercano (no solo a la línea), así que si lo hubiera pasado del punto de unión de una línea, ¿cómo podría hacer que verifique la distancia al ¿punto?
Prisionero
1
@Hairy Buen comienzo, pero parece que con demasiada frecuencia este código regresa Nonecuando existe una solución legítima. El problema es que el último condicional asume X1 <X2 e Y1 <Y2, lo que no siempre se puede asegurar. Se necesita una mejor prueba de intermediación.
whuber
1
@Hairy Parece que este intercambio entre usted y @prisoner ha sido productivo. Me gustaría enfatizar que no he tenido nada que ver con (o incluso ningún control sobre) cualquier cambio en la votación o puntos que puedan haber ocurrido y que mi comentario fue solo para ayudarlo a mejorar su respuesta.
whuber
11

Tal vez lo estoy haciendo demasiado complicado, pero lo que quieres es la distancia de un punto a una línea. Esa es la distancia desde un punto a lo largo de AB que une AB con C con una línea ortogonal a AB. Este vector perpendicular a AB viene dado por

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

(He utilizado los corchetes para definir un vector o una matriz de dos elementos). La distancia entre C [xp, yp] y el punto A es

u=[x1-xp, y1-xp]

La distancia entre la línea y C es solo la proyección de u sobre v. Si suponemos que mod (v) = 1 (solo lo normalizamos), entonces

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

La única complicación es que probablemente desee asegurarse de que sus coordenadas no sean pares lat / log WGS84, sino proyectadas (o utilice coordenadas geodésicas). Puede usar OGR o Proj4 para esto.

Jose
fuente
3
+ varios millones de pseudopuntos por no usar funciones trigonométricas, por cierto. Demasiadas personas sacan ArcTan cuando deberían mirar esto: en.wikipedia.org/wiki/Dot_product
Herb
@Jose, gracias por la respuesta! Estoy usando el lat / long de la API de Google Maps. La parte de matemáticas es bastante nueva para mí, así que lo intentaré y veré qué se me ocurre. ¿Algún consejo con las matemáticas? Por ejemplo, [x2-x1, - (y2-y1)], ¿a qué se traduce eso?
Prisionero
He agregado una pequeña edición para esto. Básicamente, es una notación de matriz, pero si almacena sus coordenadas en las variables x1, x2, y1, y2 y xp, yp, solo necesita escribir el lado derecho de la última ecuación que proporcioné. Este es un código C, Java, JS, Python, etc. bastante válido :)
Jose
1
@Jose Estás calculando la distancia desde C hasta la línea AB. Según la figura, creo que el OP quiere la distancia de C al segmento de línea AB. Esto requiere un trabajo adicional para verificar si la proyección de C en la línea AB se encuentra entre A y B o no. En el último caso, use la más corta de las dos longitudes CA y CB.
whuber
1
@Prisoner La principal diferencia es que una línea se extiende para siempre (solo se define por un vector de dirección y un punto, o por dos puntos), mientras que un segmento entre A y B es el bit de la línea infinita que va entre A y B (tiene una longitud finita)
Jose
4

Siendo un poco reacio a todas estas matemáticas también, lo abordaría desde un ángulo diferente. Lo convertiría en una línea 'real', en lugar de una línea virtual, y luego usaría las herramientas existentes.

Si A y B comparten un atributo, puede conectarlos dibujando una línea (Kosmo GIS tiene una herramienta que creará líneas a partir de puntos, y creo que también hay un complemento QGIS para esto). Una vez que tenga las líneas, una función 'cerca' en la capa de puntos 'C' le dará la distancia a la línea. ¡Deje que el software maneje las matemáticas por usted!

Darren Cope
fuente
¡Gracias por el comentario, pero Hairy salió triunfante en este!
Prisionero
1
(+1) Haces un excelente punto. Los algoritmos de geometría computacional son notoriamente difíciles de lograr completamente en la práctica (como podemos ver en todo el código ofrecido hasta ahora, lo cual es útil e ilustrativo pero aún no funciona por completo). El uso de un procedimiento SIG de alto nivel a menudo es una buena manera de asegurarse de que está obteniendo la respuesta que espera y de que es correcta (siempre que confíe en su SIG ;-).
whuber
1

Si estabas usando Java en Android, es solo una línea con la función de biblioteca

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

distanceToLine:

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

Calcula la distancia en la esfera entre el punto p y el segmento de línea de principio a fin.

Parámetros: p - el punto a medir

inicio: el comienzo del segmento de línea

end - el final del segmento de línea

Retornos: la distancia en metros (suponiendo tierra esférica)

Simplemente agregue la biblioteca a su

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