Pergunta

Dada uma coordenada (lat, long), estou tentando calcular uma caixa delimitadora quadrada que é uma determinada distância (por exemplo, 50 km) de distância do coordenadas. Assim como entrada Tenho lat, long e à distância e como saída gostaria duas coordenadas; sendo um deles o Sudoeste (canto inferior esquerdo) de canto e sendo um deles o (superior direito) canto nordeste. Eu vi um par de respostas aqui que tentam abordar esta questão em Python, mas eu estou procurando uma implementação Java em particular.

Só para ficar claro, tenho a intenção de usar o algoritmo na Terra e só então eu não preciso acomodar um raio variável.

Ele não tem que ser extremamente preciso (+/- 20% é bom) e isso só vai ser usado para calcular caixas delimitadoras em pequenas distâncias (não mais de 150 km). Então, eu estou feliz em sacrificar alguma precisão para um algoritmo eficiente. Qualquer ajuda é muito apreciada.

Edit: eu deveria ter sido mais claro, eu realmente sou depois de um quadrado, não um círculo. Eu entendo que a distância entre o centro de alguns pontos quadrados e vários ao longo do perímetro da praça não é um valor constante como é com um círculo. Acho que o que eu quero dizer é uma praça onde se você desenhar uma linha do centro para qualquer um dos quatro pontos no perímetro que resulta em uma perpendicular linha para um lado do perímetro, então essas 4 linhas têm o mesmo comprimento.

Foi útil?

Solução

Eu escrevi um artigo sobre encontrar as coordenadas delimitadoras:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

O artigo explica as fórmulas e também fornece uma implementação Java. (É por isso que também mostra a fórmula de IronMan para a longitude min / max é impreciso.)

Outras dicas

double R = 6371;  // earth radius in km

double radius = 50; // km

double x1 = lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));

double x2 = lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));

double y1 = lat + Math.toDegrees(radius/R);

double y2 = lat - Math.toDegrees(radius/R);

Embora eu recomendaria também JTS.

import com.vividsolutions.jts.geom.Envelope;

...
Envelope env = new Envelope(centerPoint.getCoordinate());
env.expandBy(distance_in_degrees); 
...

Agora env contém o envelope. Não é realmente um "quadrado" (o que isso significa na superfície de uma esfera), mas deve fazer.

Você deve observar que a distância em graus dependerá da latitude do ponto central. No equador, 1 grau de latitude é de cerca de 111 km, mas em Nova York, que é apenas cerca de 75 km.

A coisa muito legal é que você pode jogar todos os seus pontos em um com.vividsolutions.jts.index.strtree.STRtree e usá-lo para os pontos de calcular rapidamente dentro daquele envelope.

Todas as respostas anteriores são apenas parcialmente correta . Especialmente na região, como a Austrália, eles sempre incluem poste e calcular um grande retângulo mesmo para 10kms.

Especialmente o algoritmo por Jan Philip Matuschek em http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex inclui um muito grande retângulo de (-37, -90, -180, 180) para quase todos os pontos na Austrália. Isto atinge um grande usuários de banco de dados e distância têm que ser calculado para todos os usuários em quase a metade do país.

Eu achei que o Drupal API Algoritmo Terra pelo Rochester Institute of Technology funciona melhor em torno do pólo, bem como em outros lugares e é muito mais fácil de implementar.

https: // www .rit.edu / drupal / api / drupal / locais% 21all% 21modules% 21location% 21earth.inc / 7,54

Use earth_latitude_range e earth_longitude_range do algoritmo acima para calcular retângulo delimitador

Aqui está a implementação é Java

    /**
 * Get bouding rectangle using Drupal Earth Algorithm
 * @see https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
 * @param lat
 * @param lng
 * @param distance
 * @return
 */
default BoundingRectangle getBoundingRectangleDrupalEarthAlgo(double lat, double lng, int distance) {
    lng = Math.toRadians(lng);
    lat = Math.toRadians(lat);
    double radius = earth_radius(lat);
    List<Double> retLats = earth_latitude_range(lat, radius, distance);
    List<Double> retLngs = earth_longitude_range(lat, lng, radius, distance);
    return new BoundingRectangle(retLats.get(0), retLats.get(1), retLngs.get(0), retLngs.get(1));
}


/**
 * Calculate latitude range based on earths radius at a given point
 * @param latitude
 * @param longitude
 * @param distance
 * @return
 */
default List<Double> earth_latitude_range(double lat, double radius, double distance) {
      // Estimate the min and max latitudes within distance of a given location.

      double angle = distance / radius;
      double minlat = lat - angle;
      double maxlat = lat + angle;
      double rightangle = Math.PI / 2;
      // Wrapped around the south pole.
      if (minlat < -rightangle) {
        double overshoot = -minlat - rightangle;
        minlat = -rightangle + overshoot;
        if (minlat > maxlat) {
          maxlat = minlat;
        }
        minlat = -rightangle;
      }
      // Wrapped around the north pole.
      if (maxlat > rightangle) {
        double overshoot = maxlat - rightangle;
        maxlat = rightangle - overshoot;
        if (maxlat < minlat) {
          minlat = maxlat;
        }
        maxlat = rightangle;
      }
      List<Double> ret = new ArrayList<>();
      ret.add((minlat));
      ret.add((maxlat));
      return ret;
    }

/**
 * Calculate longitude range based on earths radius at a given point
 * @param lat
 * @param lng
 * @param earth_radius
 * @param distance
 * @return
 */
default List<Double> earth_longitude_range(double lat, double lng, double earth_radius, int distance) {
      // Estimate the min and max longitudes within distance of a given location.
      double radius = earth_radius * Math.cos(lat);

      double angle;
      if (radius > 0) {
        angle = Math.abs(distance / radius);
        angle = Math.min(angle, Math.PI);
      }
      else {
        angle = Math.PI;
      }
      double minlong = lng - angle;
      double maxlong = lng + angle;
      if (minlong < -Math.PI) {
        minlong = minlong + Math.PI * 2;
      }
      if (maxlong > Math.PI) {
        maxlong = maxlong - Math.PI * 2;
      }

      List<Double> ret = new ArrayList<>();
      ret.add((minlong));
      ret.add((maxlong));
      return ret;
    }

/**
 * Calculate earth radius at given latitude
 * @param latitude
 * @return
 */
default Double earth_radius(double latitude) {
      // Estimate the Earth's radius at a given latitude.
      // Default to an approximate average radius for the United States.
      double lat = Math.toRadians(latitude);

      double x = Math.cos(lat) / 6378137.0;
      double y = Math.sin(lat) / (6378137.0 * (1 - (1 / 298.257223563)));

      //Make sure earth's radius is in km , not meters
      return (1 / (Math.sqrt(x * x + y * y)))/1000;
    }

E use o cálculo da distância fórmula documentado através do Google Maps a distância calcular

https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#outputting-data-as-xml-using-php

Para pesquisar por quilômetros em vez de milhas, substitua 3959 com 6371. Para (LAT, LNG) = (37, -122) e uma tabela de marcadores com colunas lat e GNL , a fórmula é:

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;

Aqui é uma solução simples que eu usei para gerar delimitadora coordenadas caixa que eu uso com GeoNames citieJSON API para chegar perto grandes cidades de um gps coordenada decimal.

Este é um método Java do meu repositório GitHub: FusionTableModifyJava

Eu tive um local decimal GPS e eu precisava encontrar a maior cidade / estado "próximo" esse local. Eu precisava de uma caixa delimitadora relativamente precisas para passar para o webservice citiesJSON GeoNames para voltar a maior cidade do que a caixa delimitadora. Eu passo a localização e o "raio" Eu estou interessado em (em km) e dá de volta o norte, sul, leste, oeste coordenadas decimais necessários para passar para citiesJSON.

(eu encontrei esses recursos úteis para fazer minha pesquisa:

calcular a distância, rolamento e mais entre latitude / longitude pontos.

Longitude - Wikipedia )

Não é super precisos, mas o suficiente preciso para o que eu estava usando para isso:

    // Compute bounding Box coordinates for use with Geonames API.
    class BoundingBox
    {
        public double north, south, east, west;
        public BoundingBox(String location, float km)
        {
             //System.out.println(location + " : "+ km);
            String[] parts = location.replaceAll("\\s","").split(","); //remove spaces and split on ,

            double lat = Double.parseDouble(parts[0]);
            double lng = Double.parseDouble(parts[1]);

            double adjust = .008983112; // 1km in degrees at equator.
            //adjust = 0.008983152770714983; // 1km in degrees at equator.

            //System.out.println("deg: "+(1.0/40075.017)*360.0);


            north = lat + ( km * adjust);
            south = lat - ( km * adjust);

            double lngRatio = 1/Math.cos(Math.toRadians(lat)); //ratio for lng size
            //System.out.println("lngRatio: "+lngRatio);

            east = lng + (km * adjust) * lngRatio;
            west = lng - (km * adjust) * lngRatio;
        }

    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top