Como calcular a caixa delimitadora para uma distância e latitude / longitude

13

Preciso calcular uma caixa ou círculo delimitador para uma determinada latitude WGS84 e longitude e distância WGS84, mas não tenho ideia de por onde começar!

A distância desde o início Lat / Lon seria 10 km ou menos.

Seria possível alguém me dar algumas dicas / Exemplo de como fazer isso

nmtoken
fonte
Para círculos que não cobrem nenhum dos pólos, uma resposta detalhada é dada em gis.stackexchange.com/questions/19221/… . Mas essa não é a história completa, como sugerem as respostas atuais: você pode fazer trocas de complexidade de programas com precisão de velocidade. Observe também que há um problema de "contorno" ao especificar caixas delimitadoras quando você trabalha em lat-lon (as dificuldades ocorrem no meridiano de + -180 graus). Para uma solução para isso, consulte gis.stackexchange.com/questions/17788/… .
whuber
Você realmente precisa de uma caixa ou 4 pontos próximos a um determinado ponto seriam suficientes? Dado um ponto p, encontre 4 pontos d de distância nas direções NE, SW, SE e NW a partir de p.
22412 Kirk Kuykendall
@Kirk - Se você tem as coordenadas dos 4 pontos, então você tem a caixa ...
martinstoeckli
@martinstoeckli certo, eu estava apenas esperando simplificar o problema por não ter que visualizar como uma caixa projetada em uma esfera se parece. Observe também que o problema pode ser generalizado para deixar claro que os lados da caixa não precisam cair na mesma latitude / longitude (em outras palavras, uma caixa girada).
22412 Kirk Kuykendall
@ Kirk - Ahh bem, se você precisar exatamente, então você está certo, é claro. Penso que a caixa só é útil para encontrar os possíveis candidatos rapidamente. Para verificar se dois pontos estão dentro de uma certa distância (círculo), a fórmula mais complexa da navalha pode ser usada.
22812 martinstoeckli

Respostas:

15

WGS-o que? WGS-84? Dependendo da precisão de que você precisa, talvez você precise saber muito mais informações - meu palpite é que foi por isso que você recebeu votos negativos, apesar de ninguém se importar em deixar um comentário dizendo o porquê.

Aqui estão duas maneiras:

Impreciso, mas provavelmente "bom o suficiente"

Um grau de latitude é de aproximadamente 10001.965729 / 90 quilômetros (distância do equador ao polo, dividido por noventa graus) ou 111.113 quilômetros, usando o dado WGS-84. Esta é uma aproximação devido à forma da Terra e porque as distâncias mudam à medida que você se aproxima dos pólos (uma razão para usar a latitude, não a longitude - eventualmente a distância de um grau de longitude é zero!) A Terra também não é perfeita esfera. Ambas são razões para usar uma abordagem mais complexa baseada em projeção e em dados, na minha segunda resposta.

10001.965729km = 90 degrees
1km = 90/10001.965729 degrees = 0.0089982311916 degrees
10km = 0.089982311915998 degrees

Isso está usando graus decimais, não graus / minutos / segundos.

Portanto, sua caixa delimitadora será seu ponto, mais e menos 0,08999 graus. Como alternativa, você pode usar esse número como um raio, fornecendo um círculo delimitador .

Qualquer pessoa GIS lendo isso estremecerá. Porém, será mais preciso, dependendo de onde você está no mundo. Por um raio de 10 km, tudo bem.

Muito mais preciso, mas mais código

Use uma biblioteca de projeção e especifique seu dado, etc. Eu recomendo o Proj4; é amplamente usado para que o Google retorne muitos resultados para perguntas sobre o assunto, e existem invólucros Delphi . Se você tiver problemas para usá-lo, poste outra pergunta aqui no SO - está fora do escopo desta. O site do Proj4 tem exemplos usando as APIs básicas e, embora estejam em C, devem ser facilmente traduzíveis. A referência da API é o melhor lugar para começar, seguida pelas perguntas frequentes .

Eu usaria o WGS-84 como um dado básico (representação da Terra), a menos que você saiba um específico que deseja usar ou que foi usado para criar suas coordenadas. É comumente usado e bastante preciso.

Se sua posição for do Google Maps (por exemplo), especifique uma projeção Mercator. Você pode usar outra projeção ou usar, por exemplo, coordenadas UTMem vez de latitude e longitude, dependendo da fonte dos seus dados e se você deseja alta precisão para uma pequena área local. (O UTM tem várias zonas, todas as quais alteram a distorção, de modo que, dentro dessa zona, é altamente preciso; se você usar uma zona para coordenadas fora dela, a distorção aumentará bastante à medida que você se afasta. Se você visualizar toda a Terra projetada a partir de uma zona, pode ser irreconhecível. Mas dentro de uma zona, as traduções UTM serão as melhores possíveis. As coordenadas são geralmente especificadas em metros, e não em graus, por isso pode ser mais útil para você, já que você precisa de 10 km raio. 10km é fácil dentro de uma única zona, você só precisa escolher a zona apropriada com base na coordenada central. A única parte complicada é quando você se aproxima de uma borda: é uma situação comum e está bem, apenasconsistente em como você escolhe qual deles usar . O Proj4 também permite traduzir projeções, para que você possa ir do Mercator WGS-84 lat / long para uma zona UTM n , por exemplo, ou para e de duas zonas UTM.)

David
fonte
2
No local onde moramos, um pesquisador me disse uma vez que usa 1 grau aproximadamente igual a 108 km para seus cálculos mentais. Mentalmente, 10 km são aproximadamente 0,1 graus. Como essas aproximações são aproximadas, é melhor tratá-las com precisão de 1 dígito significativo (no máximo 2 ou 3), em vez de 0,089982311915998, pois isso implica em um nível de precisão.
Stephen Quan
1
Realmente não é difícil calcular os graus mais precisos, levando em consideração a latitude. Como o computador faz o cálculo, não há nada ganho com uma aproximação (veja a primeira função no meu exemplo).
martinstoeckli
3

Supondo que você queira fazer uma consulta em um banco de dados, provavelmente desejará fazer uma pesquisa rápida (imprecisa) e depois calcular exatamente a distância para os locais resultantes. Esse é o seu cenário?

A seguinte função (em PHP, desculpe) calculará aproximadamente as diferenças de latitude e longitude. Essas diferenças dependem da latitude do seu ponto de pesquisa. Use-os (com uma pequena tolerância) para fazer uma pesquisa rápida no banco de dados. A caixa pode ser calculada simplesmente com latitude + -deltaLatitude e longitude + -deltaLongitude.

deltaLatitude[rad] = distance[m] / earthRadius[m]
deltaLongitude[rad] = distance[m] / (cos(latitude[rad]) * $earthRadius[m])

/**
 * Calculates the deltas in latitude and longitude to use, for a db search
 * around a location in the database.
 * @param float $distance Radius to use for the search [m]
 * @param float $latitude Latitude of the location, we need the angle deltas for [deg decimal]
 * @param float $deltaLatitude Calculated delta in latitude [deg]
 * @param float $deltaLongitude Calculated delta in longitude [deg]
 * @param float $earthRadius Mean earth radius in [m]
 */
public static function angleFromSphericDistance($distance, $latitude,
  &$deltaLatitude, &$deltaLongitude, $earthRadius = 6371000)
{
  $lat = deg2rad($latitude);

  $radiusOnLatitude = cos($lat) * $earthRadius;
  $deltaLatitude = $distance / $earthRadius;
  $deltaLongitude = $distance / $radiusOnLatitude;

  $deltaLatitude = rad2deg($deltaLatitude);
  $deltaLongitude = rad2deg($deltaLongitude);
}

Com a fórmula haversine , você pode calcular distâncias na esfera. Use-o para cada um dos locais encontrados, para obter a distância "exata". Dessa forma, você pode testar se os dois locais estão dentro de um determinado raio (um círculo em vez da caixa).

/**
 * Calculates the great-circle distance between two points, with
 * the Haversine formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
public static function haversineGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $latDelta = $latTo - $latFrom;
  $lonDelta = $lonTo - $lonFrom;

  $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
    cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
  return $angle * $earthRadius;
}
martinstoeckli
fonte
3

Para testar se um lat / lon está dentro ou fora de um círculo delimitador, você precisa calcular a distância entre o lat / lon de referência e o ponto lat / lon que deseja testar. Como sua distância é de 10 km ou menos, eu tentaria usar a aproximação Equiretangular para obter a distância em vez de Haversine por causa da simplicidade. Para obter a distância em km:

x = (lonRef - lon) * cos ( latRef )
y = latRef - lat
distance = EarthRadius * sqrt( x*x + y*y )

Nota importante: lat / lon nesta fórmula está em radianos e não em graus. O valor típico de EarthRadius é 6371 km, que retornará a distância em unidades de km. Agora é um teste simples se a sua distância está dentro ou fora do círculo. Se um círculo delimitador funcionar, eu aceitaria isso.

Para um retângulo delimitador, eu assumiria que você deseja que o retângulo seja definido paralelamente ao equador. Em seguida, eu calculava os cantos da caixa delimitadora usando cálculos de alcance / rolamento (rolamentos sendo 45 graus, 135 graus, 225 graus e 315 graus). A partir daí, eu assumiria que você não está em volta dos polos e usaria um ponto no teste de polígono.

TreyA
fonte
2

Abaixo está o código T-SQL que eu uso para criar a caixa delimitadora no SQL-Server 2012. No meu caso, recebo valores decimais para Lat, Long. Eu uso isso para limitar rapidamente o número de linhas antes de usar a STDistancefunção SQL para verificar se os resultados estão realmente a uma distância específica. As funções geográficas são muito caras no SQL Server, portanto, ao criar uma caixa delimitadora, sou capaz de reduzir bastante o número de vezes que ele precisa ser executado.

DECLARE @Lat DECIMAL(20, 13) = 35.7862
   ,@Long DECIMAL(20, 13) = -80.3095
   ,@Radius DECIMAL(7, 2) = 5
   ,@Distance DECIMAL(10, 2)
   ,@Earth_Radius INT = 6371000;

SET @Distance = @Radius * 1609.344;

DECLARE @NorthLat DECIMAL(20, 13) = @Lat + DEGREES(@distance / @Earth_Radius)
   ,@SouthLat DECIMAL(20, 13) = @Lat - DEGREES(@distance / @Earth_Radius)
   ,@EastLong DECIMAL(20, 13) = @Long + DEGREES(@distance / @Earth_Radius / COS(RADIANS(@Lat)))
   ,@WestLong DECIMAL(20, 13) = @Long - DEGREES(@distance / @Earth_Radius / COS(RADIANS(@Lat)));

SELECT *
    FROM CustomerPosition AS cp
    WHERE (
            cp.Lat >= @SouthLat
            AND cp.Lat <= @NorthLat )
        AND (
              cp.Long >= @WestLong
              AND cp.Long <= @EastLong )
Vladimir Oselsky
fonte