Como encontrar um ponto a meio caminho entre dois outros pontos?

8

Estou trabalhando na implementação do formato OSM Mobile Binary para dados de mapa e, como usa um delta de 16 bits para cada ponto ao longo do caminho, não pode representar um caminho se houver uma grande distância entre dois pontos.

A solução documentada é injetar novos pontos no caminho, para manter a diferença menor.

Aqui está a seção relevante do meu código:

// calculate the distance between this point and the previous point,
// multiplied by 1,000,000 so it can be represented as an integer.
int32_t realNodeLatDelta = ((point.latitude - lastPoint.latitude) * 1000000);
int32_t realNodeLonDelta = ((point.longitude - lastPoint.longitude) * 1000000);

// cast as a 16 bit int (reduces filesize by about half)
int16_t nodeLatDelta = realNodeLatDiff;
int16_t nodeLonDelta = realNodeLonDiff;

// check if the cast caused corruption
if (realNodeLatDelta != nodeLatDelta || realNodeLatDelta != nodeLatDelta) {
  // if this point is reached, we need to add new points in the way,
  // which can be represented in a 16 bit delta
}

Não sei como calcular a posição de novos pontos ao longo do caminho? Presumo que precisa ser um grande círculo?

Pontos distantes o suficiente para causar esse problema são raros, mas quando ocorrem, geralmente é um limite político ou similar, que costuma ser bastante longo e precisa ser representado com precisão.

Abhi Beckert
fonte
Sugiro que faça essa pergunta na lista de discussão OSM-dev ( lists.openstreetmap.org/listinfo/dev ), é um pouco específica demais para um fórum geral de GIS.
Igor Brejc
1
Certamente a matemática para encontrar um ponto a meio caminho entre dois outros pontos não é muito específica?
Abhi Beckert
Não é, mas talvez haja outras maneiras de lidar com esse problema, que é específico ao formato.
Igor Brejc
Não há métodos alternativos para lidar com isso. Um número inteiro de 16 bits é o único formato permitido para pontos e (com 6 casas decimais de precisão) um número inteiro de 16 bits é capaz de representar apenas uma diferença de cerca de 3,5 km entre dois pontos. A única alternativa é não usar esse formato (que é a minha solução provisória). Mas eu quero usar algo padrão, se possível.
Abhi Beckert
1
Eu acho que você pode estar em uma ordem de magnitude: as maiores distâncias que você pode encontrar na Terra são cerca de 20.000 km. Com 16 bits, isso se traduz em precisão de 300m.
whuber

Respostas:

13

A melhor precisão é obtida nos modelos elipsoidais. No interesse da simplicidade, você deseja evitar aqueles quando precisa codificar as distâncias. Pagamos um preço: dado que o achatamento da Terra é de cerca de 1/300, o uso de um modelo puramente esférico pode potencialmente introduzir um erro de distância relativa de até 1/300 para rotas muito longas: cerca de 3000 partes por milhão. Vale a pena explorar.

Primeiro, a fórmula esférica : basta converter (lat, lon) em coordenadas cartesianas, calcular a média dos dois pontos cartesianos e depois converter a média de volta em coordenadas esféricas. Aqui está o pseudocódigo:

function cartesian(f,l) { // f = latitude, l = longitude
    return (cos(f)*cos(l), cos(f)*sin(l), sin(f))
}
function spherical(x,y,z) {
    r = sqrt(x^2 + y^2)
    if (r == 0) {
        if (z > 0) return (90, 0) 
        elseif (z < 0) return (-90, 0)
        else return Undefined // (x,y,z) == (0,0,0)
    } else {
        return (atan2(r, z), atan2(x, y)) // atan2 must return *degrees*
    }
}
function midpoint(f0,l0, f1,l1) { 
    return spherical((cartesian(f0,l0) + cartesian(f1,l1))/2)
}

(A aritmética para midpointenvolve uma soma vetorial e uma divisão escalar dessa soma, portanto, está realmente escondendo três somas e três divisões.)

Esse é o ponto médio da geometria esférica. O cálculo requer dois cossenos, dois senos, uma raiz quadrada, dois arco-tangentes e alguma multiplicação e adição: razoavelmente rápido e fácil. Não haverá problemas perto dos pólos ou cruzando o meridiano + -180. O resultado será indefinido quando os dois pontos forem diametralmente opostos.

Uma maneira de medir o erro é calcular o aumento da distância percorrida pelo ponto médio em comparação com a distância entre os pontos originais. Se o aumento for pequeno comparado à distância original, temos pouco a reclamar. Eu calculei esses erros usando a distância elipsoidal precisa para o elipsóide WGS84. Como um exemplo típico dos resultados, aqui está um gráfico de erros relativos quando um dos pontos de extremidade é fixado em (lat, lon) = (45, 0):

Erros relativos

Os contornos estão em uma escala logarítmica (base 10): os contornos -6 mostram pontos onde o erro relativo é 10 ^ (- 6); isto é, uma parte por milhão (ppm). Os contornos -5 (quase invisíveis perto de (-45, 180), o ponto diametralmente oposto) são 10 ppm. Os -7, -8 etc. são frações de um ppm: altamente precisos.

Evidentemente, enquanto não estivermos tentando calcular pontos médios de dois pontos que são quase diametralmente opostos, faremos bem. (Lembre-se de que o cálculo está perfeitamente correto para a esfera; esses erros se devem ao achatamento da esfera.)

Dado que a precisão de 16 bits é de cerca de 16 ppm (um log de base 10 igual a -4,8), parece aceitável usar a fórmula esférica para encontrar pontos médios, desde que os dois pontos estejam a mais de um grau de diametralmente opostos.

E a fórmula linear mais simples? Para estudar isso, vamos comparar a distância entre o ponto médio linear (obtido pela média das duas latitudes e duas longitudes) com o ponto médio esférico, em relação à distância entre os dois pontos finais. A figura a seguir fixa um ponto final em (45, 180) e explora uma região relativamente pequena ao seu redor.

Erros relativos 2

A maioria desses contornos (logaritmos da base 10 mais uma vez) está perto de -2: isso é um erro de uma parte por cem (1%). Para as direções norte-sul, não há erro, mas para todas as outras direções, o erro é inaceitável para muitas aplicações .

Para verificar se a aproximação linear está correta, vamos ampliar o mapa anterior por um fator de 10. Agora ele tem um grau de largura (80 quilômetros a essa latitude) e meio grau de diâmetro (35 quilômetros): estamos olhando na escala de uma cidade grande ou de uma cidade pequena e seus subúrbios.

Agora, os contornos estão entre -3 e -4: são 100 a 1000 partes por milhão (0,01% a 0,1%). Bastante grosseiro e quase imperceptível em uma tela de computador de alta resolução, se você olhar com atenção.

Erros relativos 3

Olhando para trás, é aparente que a fórmula esférica um pouco mais complicada - mas ainda facilmente implementável - alcança maior precisão em todo o mundo do que a fórmula linear simples, mesmo em locais próximos. (Estou enganando um pouco, porque usei duas maneiras diferentes de medir erros, para que não sejam diretamente comparáveis.)

A linha inferior:

- A fórmula linear irá posicionar incorretamente o ponto médio por um erro relativo de 0,01% a 0,1% na escala da cidade; em áreas maiores, o posicionamento incorreto pode estar totalmente errado (1% em até centenas de%).

--A fórmula esférica é absolutamente correta para um modelo de terra esférico. Comparado à fórmula elipsóide mais precisa, ela ainda deve se sair bem, exceto por pontos quase diametralmente opostos.

whuber
fonte
1
Uau, obrigada! Essa é uma informação muito boa. Não preciso de precisão perfeita, é apenas um aplicativo para iPhone para uso pessoal no GPS, principalmente focado nas ruas. Como uma rodovia não é perfeitamente reta por centenas de quilômetros, mesmo a precisão linear é "boa o suficiente". Mas as fronteiras políticas são muitas vezes retas e longas (o lado direito da Austrália Ocidental é de 1.800 km). A fórmula esférica deve ser perfeita para isso. Eu gostaria de poder lhe dar uma recompensa pela sua resposta, você merece.
Abhi Beckert
8

Sua solução funciona para pequenas distâncias, mas não para maiores. A maneira mais fácil de entender por que é olhar para o mapa a seguir (extraído daqui ):

insira a descrição da imagem aqui

É um mapa-múndi em projeção equiretangular - longitudes e latitudes são simplesmente projetadas linearmente nos eixos X e Y. Sua matemática pode ser representada pelo retângulo azul . No entanto, a diagonal azul não representa a menor "linha" entre dois pontos na superfície da Terra. O que você precisa é de um grande círculo (representado pela curva preta ).

Embora você esteja usando C ++, eu recomendo dar uma olhada na C # Geodesy Library de Mike Gavaghan , é de código aberto e de código de alta qualidade, você deve descobrir como fazer o cálculo ao longo do grande círculo. Você também pode dar uma olhada nas fórmulas de Vincenty se estiver interessado em matemática por trás do cálculo.

Igor Brejc
fonte
Obrigado! É exatamente isso que estou procurando. O código de Mike Gavaghan parece fácil de portar para C (na verdade, estou usando Objective-C btw). Eu tinha certeza de que minha matemática linear seria muito difícil, mas precisava ter algo.
Abhi Beckert
-1

A matemática linear simples parece funcionar bem:

double newLat = lastCoord.latitude + ((coord.latitude - lastCoord.latitude) / 2);
double newLon = lastCoord.longitude + ((coord.longitude - lastCoord.longitude) / 2);

Estou inserindo recursivamente metade dos novos pontos em cada seção do caminho, que é muito grande, até que todos sejam pequenos o suficiente.

Não tenho certeza se é necessária uma matemática mais complexa para distâncias maiores (alguém pode confirmar isso para mim?), Mas funciona bem nessa (cerca de 5 km):

exemplo de linha reta longa entre pontos

Abhi Beckert
fonte