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.

Chmouel Boudjnah
fonte

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)

Praveen
fonte
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);
haseman
fonte
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

Laurent Grégoire
fonte
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 :)

Dwaine Bailey
fonte
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.

Zar E Ahmer
fonte
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];
    }
Levon Petrosyan
fonte
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;
}
farhad.kargaran
fonte
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);


}
Sai Gopi Me
fonte
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)
Kourosh
fonte