PDA

Ver la Versión Completa : [ ARTICULO ] Calcular la distancia entre dos coordenadas


WillyWeb
08/06/15, 22:48:50
Comenté en este tema (http://www.htcmania.com/showthread.php?t=1021127) lo de medir la distancia entre dos coordenadas geodésicas y luego se me pasó explicar cuál era la forma menos incorrecta de hacerlo ... mil disculpas :oh:

Vamos al tema ... :palomitas:

Calcular la distancia en línea recta ente dos puntos es relativamente sencillo ...

Se aplica el "Teorema de Pitágoras (http://es.wikipedia.org/wiki/Teorema_de_Pitágoras)" (aquello de H^2=A^2+B^2) teniendo en cuenta lo siguiente:

H es la distancia que nos interesa
A es la diferencia entre las coordenadas X de los puntos (o las Y)
B es la diferencia entre las coordenadas Y de los puntos (o las X)

Creo que la siguiente imagen lo explica visualmente bastante bien ...

http://www.htcmania.com/attachment.php?attachmentid=731050&stc=1&d=1433842363

Y se considera como buena la equivalencia entre grados y kilómetros de 111,12 km/gr (http://es.wikipedia.org/wiki/Grado_de_latitud).

Una vez calculado el valor de H se multiplica por 111,12 y tenemos la distancia en Km ... lo dicho, sencillo. X-D

Pues resulta que esto, tan fácil de entender y trasladar a Tasker, no nos vale para calcular la distancia entre dos puntos "sobre la superficie de la tierra" porque no tiene en cuenta que la tierra es "casi" redonda (http://es.wikipedia.org/wiki/Forma_de_la_Tierra) :cry:

A muy cortas distancias el error no será demasiado significativo, pero a medida que nos alejemos del ecuador y/o la distancia entre los puntos aumente, el error puede llegar a ser considerable.

Para calcular la distancia más corta ente dos puntos de la superficie de un cuerpo esférico (la ortodrómica (http://es.wikipedia.org/wiki/Ortodrómica)) se tiene que usar la "Trigonometría Esférica (http://es.wikipedia.org/wiki/Trigonometría_esférica)", considerablemente más farragosa de manejar que el Teorema de Pitágoras.

http://www.htcmania.com/attachment.php?attachmentid=731051&stc=1&d=1433842363

Existen varios procedimientos para calcular esa distancia ortodrómica, cada uno con precisiones y complejidades diferentes. Parece ser que los tres más utilizados son: "Teorema del Coseno (http://es.wikipedia.org/wiki/Teorema_del_coseno)", "Fórmula de Haversine (http://es.wikipedia.org/wiki/Fórmula_del_Haversine)" y "Fórmulas de Vincenty (http://es.wikipedia.org/wiki/Fórmulas_de_Vincenty)" (este es para caerse de culo :risitas:).

Después de dar unas pocas vueltas al tema he descartado el sistema de Vincenty por su complejidad y la poca diferencia de precisión con el de Haversine. Y he dejado aparcado el de Haversine un poco forzado también por una pequeña limitación que me he encontrado en Tasker a la hora de hacer cálculos con muchos decimales. Aunque la librería de soporte matemático que usa Tasker (http://softwaremonkey.org/Code/MathEval) parece que puede manejar con soltura número grandes y pequeños los resultados que Tasker proporciona al usuario siempre tienen un máximo de tres decimales, y eso es una faena en este caso porque los sistemas más precisos necesitan varios pasos intermedios en los que perder decimales afecta al resultado. :rolleyes:

El sistema de los cosenos es relativamente fácil de implementar en una única fórmula de manera que no sea necesario hacer pasos intermedios y de esa forma evitar las limitaciones matemáticas de Tasker. Creo que también sería posible hacerlo en una única fórmula con el sistema de Haversine, pero no me he entretenido en ello. ;-)

En definitiva, y como no se trata de colar un ICBM por la ventana de nadie, me parece que la relación dificultad/precisión del teorema del coseno es muy buena (a pesar de ser el sistema menos preciso de los tres estudiados) y algo mejor que con el teorema de pitágoras.

Esta es la fórmula resultante después de desarrollar el teorema del coseno ...

Distancia (A, B) = R * arccos (sen (LatA) * sen (LatB) + cos (LatA) * cos (LatB) * cos (LonA-LonB))

A y B son los dos puntos en cuestión definidos por sus coordenadas geodésicas (latitud/longitud) que son justo del tipo de las que nos proporciona Android. LatA y LatB son las latitudes. LonA y LonB son las longitudes. Las funciones trigonométricas* no tienen misterio. Y el valor R es el radio de la tierra ... y aquí viene otro dolor de cabeza :-(

* Las funciones trigonométricas de Tasker necesitan los ángulos en radianes (http://es.wikipedia.org/wiki/Radián). Eso no es un problema porque la librería matemática de Tasker dispone de todo lo necesario para hacer una conversión precisa.

Resulta que aunque la tierra es "casi" redonda no es una esfera, es un geoide (http://es.wikipedia.org/wiki/Geoide). Tomar este detalle en cuenta añade un nivel de complejidad del que todo el mundo escapa como de la peste, así que se toma como aproximación un esferoide oblato (http://es.wikipedia.org/wiki/Esferoide) (achatado en el eje vertical), que como todo esferoide que se precie tiene un radio menor y otro mayor.

Vale ... ¿y cuánto miden esos radios en el caso de la tierra? ... pues resulta que nuestro sistema de coordenadas usa como DATUM (http://es.wikipedia.org/wiki/Datum) (el modelo matemático de la superficie de la tierra) el WGS84 (http://es.wikipedia.org/wiki/WGS84) (el del GPS (http://es.wikipedia.org/wiki/Sistema_de_posicionamiento_global)), y en ese modelo el esferoide que define la tierra tiene un radio mayor de 6.378,137 Km y un radio menor de 6.356,752 Km.

Vale ... ¿y cuál cogemos? ... pues la media, pero no cualquier media, lo correcto es coger la media cuadrática (http://es.wikipedia.org/wiki/Media_cuadrática) que es la que mejor tiene en cuenta la forma exacta de ese dichoso esferoide.

En definitiva, el radio medio cuadrático de la tierra es de 6.372,795477598 Km ... toma ya. :oh:

Y llegados a este punto (para qué me meteré yo en estos charcos :() ya podemos escribir una tarea en Tasker que nos haga el cálculo de la distancia entre dos coordenadas geodésicas con una precisión, que sin ser digna de la NASA, es más que aceptable.

Esta tarea espera las coordenadas en %par1 y %par2 y nos devolverá un resultado en metros en la variable que le pongamos.

Aquí está la tarea ...

DistLatLon (666)
A1: Devolver [ Valor:ERROR Detener:Encendido ] Si (if) [ %par1 No ajust. | %par2 No ajust. ]
A2: Establecer variable [ Nombre:%radio A:6372795.477598 ]
A3: Separar variable [ Nombre:%par1 Separador:, ]
A4: Separar variable [ Nombre:%par2 Separador:, ]
A5: Establecer variable [ Nombre:%dist A:%radio * acos( sin(torad(%par11)) * sin(torad(%par21)) + cos(torad(%par11)) * cos(torad(%par21)) * cos(torad(%par12-%par22)) ) Calcular:Encendido ]
A6: Devolver [ Valor:%dist Detener:Encendido ]

Hasta la próxima ... :cucu:

WillyWeb
18/06/15, 18:42:52
De los tres sistemas propuestos me decidí por el "Teorema del Coseno" por su simplicidad y porque se podía implementar en una sola ecuación que evita la pequeña limitación en el manejo de los decimales que encontré en la librería matemática de Tasker.

La tarea que propuse hace uso exclusivamente de acciones de Tasker. Esta otra hace exactamente lo mismo pero usando JavaScript...

DistLatLonJS (777)
A1: Devolver [ Valor:ERROR Detener:Encendido ] Si (if) [ %par1 No ajust. | %par2 No ajust. ]
A2: JavaScriptlet [ Código:
pos1=par[0].split(",");
pos2=par[1].split(",");

radio=6372795.77598;
gra2rad=Math.PI/180;
lata=pos1[0]*gra2rad;
lona=pos1[1]*gra2rad;
latb=pos2[0]*gra2rad;
lonb=pos2[1]*gra2rad;
lond=lona-lonb;

dist=radio * Math.acos(Math.sin(lata) * Math.sin(latb) + Math.cos(lata) * Math.cos(latb) * Math.cos(lond));

var dist=dist.toFixed(3);
Librerías: Salida Automática:Encendido Cuenta atrás (segundos):1 ]
A3: Devolver [ Valor:%dist Detener:Encendido ]

Lo bueno de este sistema... el número de decimales del resultado es ajustable cambiando el valor dentro del paréntesis de "toFixed".

Lo malo de este sistema... esta tarea es considerablemente más lenta que su equivalente con acciones de Tasker.

Hasta la próxima ... :cucu:

WillyWeb
21/06/15, 20:09:48
Aquí está la tarea para calcular la distancia entre dos coordenadas usando la "Fórmula de Haversine". He usado JavaScript porque no es posible implementarla directamente en Tasker ya que su librería matemática no tiene la función trigonométrica "atan2 (https://en.wikipedia.org/wiki/Atan2)".

DistHaversine (333)
A1: Devolver [ Valor:ERROR Detener:Encendido ] Si (if) [ %par1 No ajust. | %par2 No ajust. ]
A2: JavaScriptlet [ Código:
pos1=par[0].split(",");
pos2=par[1].split(",");

radio=6371000;
gra2rad=Math.PI/180;
lat1=pos1[0]*gra2rad;
lat2=pos2[0]*gra2rad;
latd=(pos2[0]-pos1[0])*gra2rad;
lond=(pos2[1]-pos1[1])*gra2rad;

a = Math.sin(latd/2) * Math.sin(latd/2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(lond/2) * Math.sin(lond/2);

c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

d = radio * c;

var dist=d.toFixed(3);
Librerías: Salida Automática:Encendido Cuenta atrás (segundos):1 ]
A3: Devolver [ Valor:%dist Detener:Encendido ]


Como en la otra implementación que hice en JavaScript el número de decimales del resultado es ajustable cambiando el valor dentro del paréntesis de "toFixed".

Nota: En esta ecuación se usa un radio terrestres de 6.371 Km. Curiosamente si se sustituye este valor por el radio medio cuadrático (6.372,795477598 Km) que propuse para el "Teorema del Coseno" los resultados son casi iguales. :loco:

Por lo que he visto los resultados con las "Fórmulas de Vincenty" difieren muy poco de los que se obtienen con la "Fórmula de Harversine", en cambio la complejidad del algoritmo es mucho mayor y el esfuerzo de adaptarla a Tasker/JavaScript no merece la pena, así que aquí me planto. :silbando:

Hasta otra ... :cucu:

Caravantes
29/02/16, 23:39:08
Aunque el hilo es de hace meses, no lo había visto hasta ahora.

Fantástico trabajo. Gracias.

Perplejo estoy por ese dominio de las matemáticas. Con dificultades, mi uso de Tasker había llegado hasta Pitágoras, no más. Nunca se me ocurrió plantearme lo de la esfera. Excelente cuestión, y muy bien resuelta.

Mx WaR HaBiB
01/03/16, 22:19:04
muy bueno WillyWeb, y pensar que hace unos días decías que no eras de los Máster :risitas:

es algo muy complejo :oh: lo descrito en este tema... a mi parecer.

gracias por compartir, muy buen manejo.

5u4573
26/12/17, 20:46:39
Hola WillyWeb, encontre estas excelentes tareas que has desarrollado pero disculpa mi ignoracia, no encuentro la forma de ingresar las coordenadas, asi como el formato de estas.

sera mucha molestia si pones un ejemplo?

saludos.

WillyWeb
26/12/17, 21:54:00
sera mucha molestia si pones un ejemplo?.

Para nada. :D

El formato de las coordenadas es el que usa Tasker en las variables de localización %LOC y %LOCN (http://tasker.dinglisch.net/userguide/es/variables.html). Y el mismo que se usa en Google Maps. Es decir LAT,LON estando ambos valores en grados sexagesimales (https://es.wikipedia.org/wiki/Grado_sexagesimal) y usando el punto como separador decimal.

48.8583562,2.2945427 ... Torre Eiffel
40.6891802,-74.0446935 ... Estatua de la Libertad

Felices Fiestas :nav3: