Obtenha a distância entre dois pontos geográficos


108

Quero fazer um aplicativo que verifique o lugar mais próximo onde o usuário está. Posso obter facilmente a localização do usuário e já tenho uma lista de locais com latitude e longitude.

Qual seria a melhor maneira de saber o local mais próximo da lista em relação à localização do usuário atual.

Não consegui encontrar nada nas APIs do google.

Respostas:


164
Location loc1 = new Location("");
loc1.setLatitude(lat1);
loc1.setLongitude(lon1);

Location loc2 = new Location("");
loc2.setLatitude(lat2);
loc2.setLongitude(lon2);

float distanceInMeters = loc1.distanceTo(loc2);

Referência: http://developer.android.com/reference/android/location/Location.html#distanceTo(android.location.Location)


2
Provavelmente mais lento do que usar Location.DistanceBetween (), pois está usando objetos Location, mas funciona muito bem para meus propósitos.
ZoltanF

Qual classe eu tenho que importar para o local import android.location.Location;ou qual
Pranav MS

@PranavMS sim android.location.Location;
AndrewS

Eu acho que distanceTo retorna a distância entre o primeiro e o último ponto, mas em uma linha retal, então se você tomar qualquer outra direção do ponto a até b nunca será calculado, pois o caminho é diferente, isso é quando a distância entre se junta, você pode salve cada distância entre os pontos a serem criados e então com os resultados do parâmetro final [] obtenha a distância correta.
Gastón Saillén

122

http://developer.android.com/reference/android/location/Location.html

Observe distanceTo ou distanceBetween. Você pode criar um objeto Location a partir de uma latitude e longitude:

Location location = new Location("");
location.setLatitude(lat);
location.setLongitude(lon);

37
distanceBetween é um método estático que leva 2 conjuntos de pontos lat long, então você nem precisa instanciar um objeto Location =)
Stan Kurdziel

4
Tenho certeza de que ele quis dizer isso como distanceTométodo.
laph

Isso é ótimo e super útil, mas para que serve o provedor String no construtor?
miss.serena

33

Uma solução aproximada (baseada em uma projeção equirretangular), muito mais rápida (requer apenas 1 trigonometria e 1 raiz quadrada).

Esta aproximação é relevante se seus pontos não estiverem muito distantes. Ele sempre superestimará em comparação com a distância real do haversine. Por exemplo, não adicionará mais do que 0,05382% à distância real se a latitude delta ou longitude entre seus dois pontos não exceder 4 graus decimais .

A fórmula padrão (Haversine) é a exata (ou seja, funciona para qualquer par de longitude / latitude na Terra), mas é muito mais lenta , pois precisa de 7 raízes trigonométricas e 2 raízes quadradas. Se seus dois pontos não estiverem muito distantes e a precisão absoluta não for fundamental, você pode usar esta versão aproximada (Equirretangular), que é muito mais rápida, pois usa apenas um trigonométrico e uma raiz quadrada.

// Approximate Equirectangular -- works if (lat1,lon1) ~ (lat2,lon2)
int R = 6371; // km
double x = (lon2 - lon1) * Math.cos((lat1 + lat2) / 2);
double y = (lat2 - lat1);
double distance = Math.sqrt(x * x + y * y) * R;

Você pode otimizar ainda mais :

  1. Remover a raiz quadrada se você simplesmente comparar a distância com outra (nesse caso, compare ambas as distâncias quadradas);
  2. Fatorar o cosseno se você calcular a distância de um ponto mestre a muitos outros (nesse caso, você faz a projeção equirretangular centrada no ponto mestre, de modo que pode calcular o cosseno uma vez para todas as comparações).

Para obter mais informações, consulte: http://www.movable-type.co.uk/scripts/latlong.html

Há uma boa implementação de referência da fórmula Haversine em vários idiomas em: http://www.codecodex.com/wiki/Calculate_Distance_Between_Two_Points_on_a_Globe


grande homem obrigado. Mas se eu precisar obter um conjunto de locais em torno de um local em um perímetro, devo usar um loop while para verificar cada local em relação ao pesquisado e manter apenas aqueles que estão no perímetro?
themhz

Você pode, mas essa é uma abordagem de força bruta em O(n). Para uma O(1)solução, use um índice espacial 2D para aparar as correspondências potenciais antes de calcular a solução exata. Estamos deixando o escopo desta questão :)
Laurent Grégoire

este é um ótimo resumo de ótimas otimizações possíveis .. thx! Exatamente o que eu estava procurando
Sam Vloeberghs

Eu só queria saber se essa fórmula funciona para grandes distâncias
Sandipan Majhi

Veja a resposta, mas resumindo: não , não funciona para grandes distâncias. Quanto maior a distância entre os dois pontos, maior será o erro em comparação com a fórmula Haversine exata .
Laurent Grégoire

11

Existem alguns métodos que você pode usar, mas para determinar qual é o melhor, primeiro precisamos saber se você está ciente da altitude do usuário, bem como da altitude dos outros pontos.

Dependendo do nível de precisão que você procura, você pode consultar as fórmulas Haversine ou Vincenty ...

Essas páginas detalham as fórmulas e, para os menos inclinados à matemática, também fornecem uma explicação de como implementá-las no script!

Fórmula Haversine: http://www.movable-type.co.uk/scripts/latlong.html

Fórmula Vincenty: http://www.movable-type.co.uk/scripts/latlong-vincenty.html

Se você tiver algum problema com qualquer um dos significados das fórmulas, apenas comente e farei o possível para respondê-los :)


4

Existem duas maneiras de obter distância entre LatLng.

public static void distanceBetween (double startLatitude, double startLongitude, double endLatitude, double endLongitude, float[] results)

Veja isso

e em segundo lugar

public float distanceTo (Location dest) como respondido por praveen.


3
private float getDistance(double lat1, double lon1, double lat2, double lon2) {
        float[] distance = new float[2];
        Location.distanceBetween(lat1, lon1, lat2, lon2, distance);
        return distance[0];
    }

1

Basta usar o seguinte método, passar lat e long e obter a distância em metros:

private static double distance_in_meter(final double lat1, final double lon1, final double lat2, final double lon2) {
    double R = 6371000f; // Radius of the earth in m
    double dLat = (lat1 - lat2) * Math.PI / 180f;
    double dLon = (lon1 - lon2) * Math.PI / 180f;
    double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.cos(latlong1.latitude * Math.PI / 180f) * Math.cos(latlong2.latitude * Math.PI / 180f) *
                    Math.sin(dLon/2) * Math.sin(dLon/2);
    double c = 2f * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    double d = R * c;
    return d;
}

2
latlong1 e latlong2 não estão definidos
Menino

1
o que é latlong1 e latlong2?
Nisal Malinda Livera

0

você pode obter a distância e o tempo usando a API do Google Maps API do Google Maps

basta passar o JSON baixado para este método, você obterá a distância e o tempo em tempo real entre dois latlong's

void parseJSONForDurationAndKMS(String json) throws JSONException {

    Log.d(TAG, "called parseJSONForDurationAndKMS");
    JSONObject jsonObject = new JSONObject(json);
    String distance;
    String duration;
    distance = jsonObject.getJSONArray("routes").getJSONObject(0).getJSONArray("legs").getJSONObject(0).getJSONObject("distance").getString("text");
    duration = jsonObject.getJSONArray("routes").getJSONObject(0).getJSONArray("legs").getJSONObject(0).getJSONObject("duration").getString("text");

    Log.d(TAG, "distance : " + distance);
    Log.d(TAG, "duration : " + duration);

    distanceBWLats.setText("Distance : " + distance + "\n" + "Duration : " + duration);


}

0

a = sin² (Δφ / 2) + cos φ1 ⋅ cos φ2 ⋅ sin² (Δλ / 2)

c = 2 ⋅ atan2 (√a, √ (1 − a))

distância = R ⋅ c

onde φ é a latitude, λ é a longitude, R é o raio da Terra (raio médio = 6.371km);

observe que os ângulos precisam estar em radianos para passar para as funções trigonométricas!

fun distanceInMeter(firstLocation: Location, secondLocation: Location): Double {
    val earthRadius = 6371000.0
    val deltaLatitudeDegree = (firstLocation.latitude - secondLocation.latitude) * Math.PI / 180f
    val deltaLongitudeDegree = (firstLocation.longitude - secondLocation.longitude) * Math.PI / 180f
    val a = sin(deltaLatitudeDegree / 2).pow(2) +
            cos(firstLocation.latitude * Math.PI / 180f) * cos(secondLocation.latitude * Math.PI / 180f) *
            sin(deltaLongitudeDegree / 2).pow(2)
    val c = 2f * atan2(sqrt(a), sqrt(1 - a))
    return earthRadius * c
}


data class Location(val latitude: Double, val longitude: Double)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.