Como agrupar pontos próximos com posições de GPS?

10

Sou uma pessoa de TI, então não sei muito sobre projeções e assim por diante, espero que você possa me ajudar.

Eu fiz um aplicativo para Android que coleta uma posição GPS, então tenho a latitude e a longitude em um determinado momento. Quero salvar juntos aqueles elementos que estão próximos um do outro, em grupos de áreas de terreno do mesmo tamanho físico; o problema é que eu não conheço os pontos de antemão e eles podem vir de qualquer posição no mundo .

Minha primeira idéia (para explicar um pouco mais o problema) foi usar os decimais da latitude e longitude para agrupar; por exemplo, um grupo serão as posições com latitude entre 35.123 e 35.124 e a longitude entre 60.101 e 60.102. Portanto, se eu conseguir uma posição como lat = 35.1235647 e lon = 60.1012254598, esse ponto será direcionado para esse grupo.

Essa solução seria adequada para uma representação 2D cartesiana, pois eu teria áreas de 0,001 unidades de largura e altura; no entanto, como o tamanho de 1 grau de longitude é diferente em latitudes diferentes, não posso usar essa abordagem.

Qualquer ideia?

Kuu
fonte
Por que você não pode armazenar a posição como em e fazer o processamento posteriormente? Também no equador, 1 grau de longitude é de aproximadamente 111 km, então 0,001 grau será um pouco mais de 1 km. Você realmente quer suas caixas tão grandes?
Devdatta Tengshe
O grau 0,001 foi apenas um exemplo da minha ideia. Claro que terei que adaptá-lo aos requisitos. Não posso fazer um pós-processamento, pois planeja ser um aplicativo em tempo real, e não posso dizer aos meus usuários " esperem até amanhã, porque tenho que agrupar os pontos" Obrigado pelas idéias de qualquer maneira;)
Kuu

Respostas:

6

Uma maneira rápida e suja usa uma subdivisão esférica recursiva . Começando com uma triangulação da superfície da Terra, divida recursivamente cada triângulo de um vértice até o meio do lado mais longo. (Idealmente, você dividirá o triângulo em duas partes de diâmetro igual ou partes de área igual, mas como essas envolvem algum cálculo complicado, eu apenas divido os lados exatamente ao meio: isso faz com que os vários triângulos acabem variando um pouco de tamanho, mas isso não parece crítico para este aplicativo.)

Obviamente, você manterá essa subdivisão em uma estrutura de dados que permite identificar rapidamente o triângulo no qual reside qualquer ponto arbitrário. Uma árvore binária (com base nas chamadas recursivas) funciona bem: cada vez que um triângulo é dividido, a árvore é dividida no nó desse triângulo. Os dados referentes ao plano de divisão são mantidos, para que você possa determinar rapidamente de que lado do avião está qualquer ponto arbitrário: que determinará se deve viajar para a esquerda ou para a direita na árvore.

(Eu disse dividir o "plano"? Sim - se modelar a superfície da Terra como uma esfera e usar coordenadas geocêntricas (x, y, z), a maioria dos nossos cálculos ocorrerá em três dimensões, onde os lados dos triângulos são pedaços de interseções da esfera com planos através de sua origem. Isso torna os cálculos rápidos e fáceis.)


Ilustrarei mostrando o procedimento em um octante de uma esfera; os outros sete octantes são processados ​​da mesma maneira. Tal octante é um triângulo 90-90-90. Nos meus gráficos, desenharei triângulos euclidianos nos mesmos cantos: eles não parecem muito bons até ficar pequenos, mas podem ser desenhados com facilidade e rapidez. Aqui está o triângulo euclidiano correspondente ao octante: é o início do procedimento.

figura 1

Como todos os lados têm o mesmo comprimento, um é escolhido aleatoriamente como o "mais longo" e subdividido:

Figura 2

Repita isso para cada um dos novos triângulos:

Figura 3

Após n etapas, teremos 2 ^ n triângulos. Aqui está a situação após 10 etapas, mostrando 1024 triângulos no octante (e 8192 na esfera geral):

Figura 4

Como ilustração adicional, gerei um ponto aleatório dentro deste octante e viajei pela árvore de subdivisão até o lado mais longo do triângulo atingir menos de 0,05 radianos. Os triângulos (cartesianos) são mostrados com o ponto de sonda em vermelho.

Figura 5

Aliás, para restringir a localização de um ponto a um grau de latitude (aproximadamente), você deve notar que isso é cerca de 1/60 radiano e, portanto, cobre cerca de (1/60) ^ 2 / (Pi / 2) = 1/6000 da superfície total. Como cada subdivisão reduz pela metade o tamanho do triângulo, cerca de 13 a 14 subdivisões do octante farão o truque. Isso não é muita computação - como veremos abaixo - tornando eficiente não armazenar a árvore, mas executar a subdivisão em tempo real. No começo, você anotaria em que ponto o ponto se encontra - que é determinado pelos sinais de suas três coordenadas, que podem ser registradas como um número binário de três dígitos - e a cada passo você deseja se lembrar se o ponto está à esquerda (0) ou à direita (1) do triângulo. Isso fornece outro número binário de 14 dígitos. Você pode usar esses códigos para agrupar pontos arbitrários.

(Geralmente, quando dois códigos estão próximos como números binários reais, os pontos correspondentes estão próximos; no entanto, os pontos ainda podem estar próximos e têm códigos notavelmente diferentes. Considere dois pontos um metro separados do Equador, por exemplo: seus códigos devem diferir mesmo antes do ponto binário, porque eles estão em diferentes octantes. Esse tipo de coisa é inevitável com qualquer particionamento fixo de espaço.)


Eu usei o Mathematica 8 para implementar isso: você pode usá- lo como está ou como pseudocódigo para uma implementação em seu ambiente de programação favorito.

Determine em que lado do plano 0-ab ponto p se encontra:

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

Refine o triângulo abc com base no ponto 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}] 
  ]

A última figura foi desenhada exibindo o octante e, além disso, renderizando a seguinte lista como um 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 uma operação ( refine) enquanto uma condição pertence (o triângulo é grande) ou até que uma contagem máxima de operações seja atingida (16).

Para exibir a triangulação completa do octante, comecei com o primeiro octante e repeti o refinamento dez vezes. Isso começa com uma ligeira modificação 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}}
  ]

A diferença é que splitretorna as duas metades de seu triângulo de entrada, e não aquela em que um determinado ponto se encontra. A triangulação completa é obtida iterando isto:

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

Para verificar, calculei uma medida do tamanho de cada triângulo e olhei para o intervalo. (Esse "tamanho" é proporcional à figura em forma de pirâmide subtendida por cada triângulo e pelo centro da esfera; para triângulos pequenos como esse, esse tamanho é essencialmente proporcional à sua área esférica.)

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

{0,00523, 0,00739}

Assim, os tamanhos variam cerca de 25% em relação à sua média: isso parece razoável para se obter uma maneira aproximadamente uniforme de agrupar pontos.

Ao digitalizar esse código, você não notará trigonometria : o único local necessário, se for o caso, será a conversão entre coordenadas esféricas e cartesianas. O código também não projeta a superfície da Terra em nenhum mapa, evitando as distorções correspondentes. Caso contrário, ele usa apenas a média ( Mean), o Teorema de Pitágoras ( Norm) e um determinante de 3 por 3 ( Det) para fazer todo o trabalho. (Há alguns simples comandos lista de manipulação, como RotateLefte Flatten, também, juntamente com uma busca para o lado mais longo de cada triângulo.)

whuber
fonte
1

Essa é uma tarefa difícil, pois as projeções introduzem distorção ao passar do sistema de coordenadas geográficas 3D WGS84 para uma projeção 2D plana. Em uma escala global, você provavelmente terá distorções em algum lugar do sistema.

Acho que sua melhor aposta é projetar na projeção Universal Transverse Mercator . Até onde eu sei, é o mais próximo possível de uma projeção global com o mínimo de distorção.

Se você pode relaxar os requisitos de que os grupos precisam ser definidos em áreas exatamente do mesmo tamanho e os requisitos de processamento em tempo real, existem algoritmos de cluster, como o DBSCAN e uma família de derivados, que podem ajudar a produzir agrupamentos.

Sean Barbeau
fonte
1
O UTM não é uma "projeção global": a demonstração é que quase qualquer par de coordenadas válidas, como (500000, 5000000), corresponde a pelo menos 120 pontos distintos e amplamente separados no sistema UTM. E, infelizmente, os algoritmos de agrupamento não atendem às necessidades do OP de poder agrupar pontos em tempo real com base apenas em sua localização (e não na proximidade de outros pontos).
whuber
@whuber re: "projeção global" - certo. Por isso eu disse "o mais próximo possível de uma projeção global". Se você conhece um sistema de projeção melhor e mais apropriado, deixe-o nos comentários e editarei minha resposta. Além disso, o OP não tinha requisitos em tempo real na postagem inicial. Vou editar minha resposta para levar isso em conta.
Sean Barbeau 12/03
Sean, (1) Minha solução para o problema de projeção global não é usar um. Não existe nenhuma projeção global sem singularidades. (2) É verdade que o esclarecimento em tempo real apareceu em um comentário. O texto que segue a "minha primeira idéia" faz um bom trabalho, ao enfatizar que esse problema é o de dividir a superfície da Terra em vez de agrupar um conjunto de locais. Esse é o ponto que eu estava (não muito efetivamente) tentando entender no meu comentário anterior para você.
whuber