Calculando a distância entre dois pontos, usando latitude longitude?

94

Aqui está minha tentativa, é apenas um trecho do meu código:

final double RADIUS = 6371.01;
double temp = Math.cos(Math.toRadians(latA))
            * Math.cos(Math.toRadians(latB))
            * Math.cos(Math.toRadians((latB) - (latA)))
            + Math.sin(Math.toRadians(latA))
            * Math.sin(Math.toRadians(latB));
    return temp * RADIUS * Math.PI / 180;

Estou usando esta fórmula para obter a latitude e longitude:

x = Deg + (Min + Sec / 60) / 60)
m4design
fonte

Respostas:

217

O código Java fornecido por Dommer acima fornece resultados ligeiramente incorretos, mas os pequenos erros se somam se você estiver processando, digamos, uma trilha de GPS. Aqui está uma implementação do método Haversine em Java que também leva em consideração as diferenças de altura entre dois pontos.

/**
 * Calculate distance between two points in latitude and longitude taking
 * into account height difference. If you are not interested in height
 * difference pass 0.0. Uses Haversine method as its base.
 * 
 * lat1, lon1 Start point lat2, lon2 End point el1 Start altitude in meters
 * el2 End altitude in meters
 * @returns Distance in Meters
 */
public static double distance(double lat1, double lat2, double lon1,
        double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}
David George
fonte
7
Por que não Math.toRadians () em vez de deg2rad ()? Seria realmente autossuficiente.
Aron Lorincz
2
@Bala - Ruim, tá no comentário no código do meu computador mas faltando aqui. Distância em metros.
David George
4
@ ÁronNemmondommegavezetéknevem Eu atualizei o método para usar sua ótima sugestão.
David George
2
Alguns comentários aqui: stackoverflow.com/questions/28510115/…
David George
Qualquer relevância particular para nomes de variáveis aec ? São estes os lados de um triângulo ângulo reto com rótulos tradicionais a, be c?
AlainD
76

Aqui está uma função Java que calcula a distância entre dois pontos de latitude / longitude , postados abaixo, para o caso de desaparecer novamente.

    private double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
      double theta = lon1 - lon2;
      double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
      dist = Math.acos(dist);
      dist = rad2deg(dist);
      dist = dist * 60 * 1.1515;
      if (unit == 'K') {
        dist = dist * 1.609344;
      } else if (unit == 'N') {
        dist = dist * 0.8684;
        }
      return (dist);
    }
    
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts decimal degrees to radians             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    private double deg2rad(double deg) {
      return (deg * Math.PI / 180.0);
    }
    
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts radians to decimal degrees             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    private double rad2deg(double rad) {
      return (rad * 180.0 / Math.PI);
    }
    
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'M') + " Miles\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'K') + " Kilometers\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'N') + " Nautical Miles\n");
dommer
fonte
1
O mapa do Google mostra 200 km para 12,915700, 77,632046, 11,665154, 78,145657 onde o código acima mostra 149,82 km. Algo ainda está incorreto.
Samy
1
@Samy a função acima fornece a distância em linha reta.
Rahul_Pawar
1
Meu reino para que os desenvolvedores rotulem suas variáveis com as unidades . double dist => double distInMiles (ou similar) (não estou criticando a pessoa que postou esta resposta, estou votando positivamente ....... mas sim o implementador original do código)
granadaCoder
13

Nota: esta solução só funciona para distâncias curtas.

Tentei usar a fórmula postada de dommer para um aplicativo e descobri que funcionava bem para longas distâncias, mas em meus dados eu estava usando todas as distâncias muito curtas, e a postagem de dommer se saiu muito mal. Eu precisava de velocidade, e os cálculos geográficos mais complexos funcionavam bem, mas eram muito lentos. Portanto, no caso de você precisar de velocidade e todos os cálculos que está fazendo forem curtos (talvez <100m ou mais). Eu achei essa pequena aproximação para funcionar muito bem. ele assume que o mundo é plano para você, então não o use para longas distâncias, ele funciona aproximando a distância de uma única latitude e longitude na latitude dada e retornando a distância pitagórica em metros.

public class FlatEarthDist {
    //returns distance in meters
    public static double distance(double lat1, double lng1, 
                                      double lat2, double lng2){
     double a = (lat1-lat2)*FlatEarthDist.distPerLat(lat1);
     double b = (lng1-lng2)*FlatEarthDist.distPerLng(lat1);
     return Math.sqrt(a*a+b*b);
    }

    private static double distPerLng(double lat){
      return 0.0003121092*Math.pow(lat, 4)
             +0.0101182384*Math.pow(lat, 3)
                 -17.2385140059*lat*lat
             +5.5485277537*lat+111301.967182595;
    }

    private static double distPerLat(double lat){
            return -0.000000487305676*Math.pow(lat, 4)
                -0.0033668574*Math.pow(lat, 3)
                +0.4601181791*lat*lat
                -1.4558127346*lat+110579.25662316;
    }
}
zahmde
fonte
13

Leitores futuros que encontrarem este artigo da SOF.

Obviamente, a pergunta foi feita em 2010 e agora é 2019. Mas surge no início de uma pesquisa na Internet. A pergunta original não descarta o uso de biblioteca de terceiros (quando escrevi esta resposta).

public double calculateDistanceInMeters(double lat1, double long1, double lat2,
                                     double long2) {


    double dist = org.apache.lucene.util.SloppyMath.haversinMeters(lat1, long1, lat2, long2);
    return dist;
}

e

<dependency>
  <groupId>org.apache.lucene</groupId>
  <artifactId>lucene-spatial</artifactId>
  <version>8.2.0</version>
</dependency>

https://mvnrepository.com/artifact/org.apache.lucene/lucene-spatial/8.2.0

Por favor, leia a documentação sobre "SloppyMath" antes de mergulhar!

https://lucene.apache.org/core/8_2_0/core/org/apache/lucene/util/SloppyMath.html

granadaCoder
fonte
5

Aqui está uma página com exemplos de javascript para vários cálculos esféricos. O primeiro da página deve fornecer o que você precisa.

http://www.movable-type.co.uk/scripts/latlong.html

Aqui está o código Javascript

var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad(); 
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
        Math.sin(dLon/2) * Math.sin(dLon/2); 
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
var d = R * c;

Onde 'd' manterá a distância.

Chris Taylor
fonte
"A" pode ser negativo?
Xi Wei
1
package distanceAlgorithm;

public class CalDistance {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
    CalDistance obj=new CalDistance();
    /*obj.distance(38.898556, -77.037852, 38.897147, -77.043934);*/
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "M") + " Miles\n");
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "K") + " Kilometers\n");
        System.out.println(obj.distance(32.9697, -96.80322, 29.46786, -98.53506, "N") + " Nautical Miles\n");       
    }   
    public double distance(double lat1, double lon1, double lat2, double lon2, String sr) {


          double theta = lon1 - lon2;
          double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
          dist = Math.acos(dist);
          dist = rad2deg(dist);
          dist = dist * 60 * 1.1515;
          if (sr.equals("K")) {
            dist = dist * 1.609344;
          } else if (sr.equals("N")) {
            dist = dist * 0.8684;
            }
          return (dist);
        }
    public double deg2rad(double deg) {
          return (deg * Math.PI / 180.0);
        }
    public double rad2deg(double rad) {
          return (rad * 180.0 / Math.PI);
        }


    }
MAnoj Sarnaik
fonte
1

foram fornecidas muitas respostas excelentes, no entanto, encontrei algumas deficiências de desempenho, então, deixe-me oferecer uma versão com desempenho em mente. Cada constante é pré-calculada e as variáveis ​​x, y são introduzidas para evitar o cálculo do mesmo valor duas vezes. Espero que ajude

    private static final double r2d = 180.0D / 3.141592653589793D;
    private static final double d2r = 3.141592653589793D / 180.0D;
    private static final double d2km = 111189.57696D * r2d;
    public static double meters(double lt1, double ln1, double lt2, double ln2) {
        final double x = lt1 * d2r;
        final double y = lt2 * d2r;
        return Math.acos( Math.sin(x) * Math.sin(y) + Math.cos(x) * Math.cos(y) * Math.cos(d2r * (ln1 - ln2))) * d2km;
    }
Stan Sokolov
fonte
0

Resposta ligeiramente atualizada de @David George:

public static double distance(double lat1, double lat2, double lon1,
                              double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}

public static double distanceBetweenLocations(Location l1, Location l2) {
    if(l1.hasAltitude() && l2.hasAltitude()) {
        return distance(l1.getLatitude(), l2.getLatitude(), l1.getLongitude(), l2.getLongitude(), l1.getAltitude(), l2.getAltitude());
    }
    return l1.distanceTo(l2);
}

A função distance é a mesma, mas eu criei uma pequena função wrapper, que pega 2 objetos Location . Graças a isso, só uso a função de distância se os dois locais realmente tiverem altitude, porque às vezes não. E pode levar a resultados estranhos (se o local não souber sua altitude, 0 será retornado). Nesse caso, volto para a função distanceTo clássica .

Makalele
fonte
-1

Este artigo da Wikipedia fornece as fórmulas e um exemplo. O texto está em alemão, mas os cálculos falam por si.

zelo
fonte