¿Cómo agrupo puntos cercanos con posiciones GPS?


10

Soy una persona de TI, así que no sé demasiado sobre proyecciones, etc., espero que me puedan ayudar.

He creado una aplicación para Android que recopila una posición GPS, por lo que tengo la latitud y la longitud en un momento dado. Quiero guardar juntos los elementos que están cerca uno del otro, en grupos de áreas de terreno del mismo tamaño físico; El problema es que no conozco los puntos de antemano y pueden provenir de cualquier posición del mundo .

Mi primera idea (para explicar un poco más el problema) fue usar los decimales de la latitud y la longitud para la agrupación; por ejemplo, un grupo será las posiciones con latitud entre 35.123 y 35.124, y la longitud entre 60.101 y 60.102. Entonces, si obtengo una posición como lat = 35.1235647 y lon = 60.1012254598, este punto irá a ese grupo.

Esta solución estaría bien para una representación cartesiana en 2D, ya que tendría áreas de 0.001 unidades de ancho y alto; sin embargo, como el tamaño de 1 grado de longitud es diferente en diferentes latitudes, no puedo usar este enfoque.

¿Alguna idea?


¿Por qué no puede almacenar la posición como en, y luego hacer el procesamiento más tarde? También en el ecuador, 1 grado de longitud es de aproximadamente 111 km, por lo que 0.001 grado será un poco más de 1 km. ¿De verdad quieres tus contenedores tan grandes?
Devdatta Tengshe

El 0.001 grado fue solo un ejemplo de mi idea. Por supuesto que tendré que adaptarlo a los requisitos. No puedo hacer un procesamiento posterior, ya que es plannes ser una aplicación en tiempo real, y no puedo decirles a mis usuarios "espera hasta mañana, porque tengo que agrupar los puntos" Gracias por las ideas de todos modos;)
Kuu

Respuestas:


6

Una forma rápida y sucia utiliza una subdivisión esférica recursiva . Comenzando con una triangulación de la superficie de la tierra, divida recursivamente cada triángulo desde un vértice hasta el centro de su lado más largo. (Lo ideal sería dividir el triángulo en dos partes de igual diámetro o partes de igual área, pero debido a que implican algunos cálculos complicados, simplemente divido los lados exactamente por la mitad: esto hace que los diversos triángulos eventualmente varíen un poco en tamaño, pero eso no parece crítico para esta aplicación).

Por supuesto, mantendrá esta subdivisión en una estructura de datos que permite identificar rápidamente el triángulo en el que se encuentra cualquier punto arbitrario. Un árbol binario (basado en las llamadas recursivas) funciona bien: cada vez que se divide un triángulo, el árbol se divide en el nodo de ese triángulo. Los datos relacionados con el plano de división se retienen, de modo que puede determinar rápidamente en qué lado del plano se encuentra cualquier punto arbitrario: eso determinará si viajará hacia la izquierda o la derecha hacia abajo del árbol.

(¿Dije dividir "plano"? Sí, si modelar la superficie de la tierra como una esfera y usar coordenadas geocéntricas (x, y, z), entonces la mayoría de nuestros cálculos tienen lugar en tres dimensiones, donde los lados de los triángulos son piezas de intersecciones de la esfera con planos a través de su origen. Esto hace que los cálculos sean rápidos y fáciles).


Ilustraré mostrando el procedimiento en un octante de una esfera; Los otros siete octantes se procesan de la misma manera. Tal octante es un triángulo 90-90-90. En mis gráficos dibujaré triángulos euclidianos que abarcan las mismas esquinas: no se ven muy bien hasta que se vuelven pequeños, pero se pueden dibujar fácil y rápidamente. Aquí está el triángulo euclidiano correspondiente al octante: es el comienzo del procedimiento.

Figura 1

Como todos los lados tienen la misma longitud, se elige uno al azar como el "más largo" y se subdivide:

Figura 2

Repita esto para cada uno de los nuevos triángulos:

figura 3

Después de n pasos tendremos 2 ^ n triángulos. Aquí está la situación después de 10 pasos, mostrando 1024 triángulos en el octante (y 8192 en la esfera en general):

Figura 4

Como ilustración adicional, generé un punto aleatorio dentro de este octante y recorrí el árbol de subdivisión hasta que el lado más largo del triángulo alcanzó menos de 0.05 radianes. Los triángulos (cartesianos) se muestran con el punto de la sonda en rojo.

Figura 5

Incidentalmente, para reducir la ubicación de un punto a un grado de latitud (aproximadamente), notarás que esto es aproximadamente 1/60 radianes y, por lo tanto, cubre alrededor de (1/60) ^ 2 / (Pi / 2) = 1/6000 de superficie total Como cada subdivisión reduce aproximadamente a la mitad el tamaño del triángulo, unas 13 a 14 subdivisiones del octante harán el truco. Eso no es mucho cálculo, como veremos a continuación, lo que hace que sea eficiente no almacenar el árbol en absoluto, sino realizar la subdivisión sobre la marcha. Al principio, observaría en qué octante se encuentra el punto, que está determinado por los signos de sus tres coordenadas, que se pueden registrar como un número binario de tres dígitos, y en cada paso desea recordar si el punto se encuentra en la izquierda (0) o derecha (1) del triángulo. Eso le da otro número binario de 14 dígitos. Puede usar estos códigos para agrupar puntos arbitrarios.

(En general, cuando dos códigos están cerca como números binarios reales, los puntos correspondientes están cerca; sin embargo, los puntos aún pueden estar cerca y tener códigos notablemente diferentes. Considere dos puntos separados por un metro por separado del Ecuador, por ejemplo: sus códigos deben diferir incluso antes del punto binario, porque están en octantes diferentes. Este tipo de cosas es inevitable con cualquier partición fija del espacio).


Utilicé Mathematica 8 para implementar esto: puede tomarlo tal cual o como pseudocódigo para una implementación en su entorno de programación favorito.

Determine en qué lado del plano 0-ab se encuentra el punto p :

side[p_, {a_, b_}] := If[Det[{p, a, b}] >=  0, left, right];

Refinar el triángulo abc basado en el punto p.

refine[p_, {a_, b_, c_}] := Block[{sides, x, y, z, m},
  sides = Norm /@ {b - c, c - a, a - b} // N;
  {x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
  m = Normalize[Mean[{y, z}]];
  If[side[p, {x, m}] === right, {y, m, x}, {x, m, z}] 
  ]

La última figura se dibujó mostrando el octante y, además, representando la siguiente lista como un conjunto de polígonos:

p = Normalize@RandomReal[NormalDistribution[0, 1], 3]        (* Random point *)
{a, b, c} = IdentityMatrix[3] . DiagonalMatrix[Sign[p]] // N (* First octant *)
NestWhileList[refine[p, #] &, {a, b, c}, Norm[#[[1]] - #[[2]]] >= 0.05 &, 1, 16]

NestWhileListaplica repetidamente una operación ( refine) mientras se aplica una condición (el triángulo es grande) o hasta que se haya alcanzado un conteo máximo de operación (16).

Para mostrar la triangulación completa del octante, comencé con el primer octante e iteré el refinamiento diez veces. Esto comienza con una ligera modificación de refine:

split[{a_, b_, c_}] := Module[{sides, x, y, z, m},
  sides = Norm /@ {b - c, c - a, a - b} // N;
  {x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
  m = Normalize[Mean[{y, z}]];
  {{y, m, x}, {x, m, z}}
  ]

La diferencia es que splitdevuelve las dos mitades de su triángulo de entrada en lugar de en la que se encuentra un punto dado. La triangulación completa se obtiene iterando esto:

triangles = NestList[Flatten[split /@ #, 1] &, {IdentityMatrix[3] // N}, 10];

Para verificar, calculé una medida del tamaño de cada triángulo y miré el rango. (Este "tamaño" es proporcional a la figura en forma de pirámide subtendida por cada triángulo y el centro de la esfera; para triángulos pequeños como estos, este tamaño es esencialmente proporcional a su área esférica).

Through[{Min, Max}[Map[Round[Det[#], 0.00001] &, triangles[[10]] // N, {1}]]]

{0.00523, 0.00739}

Por lo tanto, los tamaños varían hacia arriba o hacia abajo en aproximadamente un 25% de su promedio: eso parece razonable para lograr una forma aproximadamente uniforme de agrupar puntos.

Al escanear sobre este código, notará que no hay trigonometría : el único lugar donde será necesario, si es que lo hará, será en la conversión de ida y vuelta entre coordenadas esféricas y cartesianas. El código tampoco proyecta la superficie de la tierra en ningún mapa, evitando así las distorsiones concomitantes. De lo contrario, solo usa el promedio ( Mean), el Teorema de Pitágoras ( Norm) y un determinante 3 por 3 ( Det) para hacer todo el trabajo. (Hay algunos comandos simples de manipulación de listas como RotateLefty Flatten, también, junto con una búsqueda del lado más largo de cada triángulo).


1

Esta es difícil, ya que las proyecciones introducen distorsión al pasar del sistema de coordenadas geográficas 3D WGS84 a una proyección 2D plana. A escala global, es probable que tenga distorsiones en algún lugar del sistema.

Creo que su mejor apuesta es proyectar a la proyección Universal Transverse Mercator . Hasta donde sé, esto es lo más cerca que puede llegar a una proyección global con la menor cantidad de distorsión.

Si puede relajar los requisitos que los grupos tienen que definir en áreas del mismo tamaño exacto, así como los requisitos del procesamiento en tiempo real, existen algoritmos de agrupamiento como DBSCAN y una familia de derivados que pueden ayudar a producir agrupaciones.


1
UTM no es una "proyección global": la demostración es que casi cualquier par de coordenadas válidas, como (500000, 5000000), corresponde a al menos 120 puntos distintos ampliamente separados en el sistema UTM. Y, desafortunadamente, los algoritmos de agrupamiento no satisfacen las necesidades del OP de poder agrupar puntos en tiempo real basándose únicamente en su ubicación (y no en su proximidad a otros puntos).
whuber

@whuber re: "proyección global" - correcto. Es por eso que dije "lo más cerca que puede llegar a una proyección global". Si conoce un mejor sistema de proyección que sea más apropiado, déjelo en los comentarios y editaré mi respuesta. Además, OP no tenía requisitos en tiempo real en la publicación inicial. Editaré mi respuesta para tener esto en cuenta.
Sean Barbeau

Sean, (1) Mi solución al problema de proyección global es no usar uno. No existe una proyección global sin singularidades. (2) Es cierto, la aclaración en tiempo real apareció en un comentario. Sin embargo, el texto que sigue a "mi primera idea" hace un buen trabajo al enfatizar que este problema es dividir la superficie de la tierra en lugar de agrupar un conjunto de ubicaciones. Ese es el punto que estaba (no muy efectivamente) tratando de comunicar en mi comentario anterior.
whuber
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.