Calcular a distância entre dois pontos no google maps V3

Respostas:

460

Se você quiser calcular por conta própria, poderá usar a fórmula Haversine:

var rad = function(x) {
  return x * Math.PI / 180;
};

var getDistance = function(p1, p2) {
  var R = 6378137; // Earth’s mean radius in meter
  var dLat = rad(p2.lat() - p1.lat());
  var dLong = rad(p2.lng() - p1.lng());
  var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(rad(p1.lat())) * Math.cos(rad(p2.lat())) *
    Math.sin(dLong / 2) * Math.sin(dLong / 2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  var d = R * c;
  return d; // returns the distance in meter
};
Mike Williams
fonte
4
Por que você sugere usar Math.atan2 (Math.sqrt (a), Math.sqrt (1-a)) em vez do Math.asin mais simples (Math.sqrt (a))?
Emanuele Paolini
3
@EmanuelePaolini - Matematicamente, atan2 (sqrt (a), sqrt (1-a)) = asin (sqrt (a)) = acos (sqrt (1-a)), mas a versão atan2 permanece numericamente melhor condicionada para todos os valores de uma.
ChrisV
23
Rapazes. Questão. Por que você gosta tanto de usar nomes de variáveis ​​com 1 letra, resolvendo problemas que exigem imaginação, onde um bom nome de variável pode ser útil? Apenas pedindo :)
pie6k
2
Não deveria ser var R = 6371; para o Km?
Alexander Fradiani
5
As funções p1.lat()e p1.lng()assumem que seus dados de entrada são google.maps.LatLngobjetos. Se você tiver apenas dados brutos , {lat: __, lon: __}use p1.lat, por exemplo.
Don McCurdy
309

Na verdade, parece haver um método no GMap3. É um método estático do google.maps.geometry.sphericalespaço para nome.

Ele usa como argumento dois LatLngobjetos e utilizará um raio de Terra padrão de 6378137 metros, embora o raio padrão possa ser substituído por um valor personalizado, se necessário.

Certifique-se de incluir:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&v=3&libraries=geometry"></script>

na sua seção de cabeça.

A chamada será:

google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);
Emil Badh
fonte
10
Então, por que há uma diferença de 1% na resposta dada pela fórmula esférica computateDistanceBetween do Google e pela distância de Haversine?
Matt S
7
@RamenRecon Não tenho certeza, mas um problema seria que eles usem valores diferentes para o raio da Terra.
Emil Badh
11
@RamenRecon sim, Emil está correto nisso. A documentação diz: O raio padrão é o raio da Terra de 6378137 metros. Mas Mike no Haversine acima usa 6371 km .
21813 Laszlo
O link acima agora está quebrado, mas a explicação do método não é um problema.
precisa saber é o seguinte
2
@ ABCD.ca Não é o meu número. Esta pergunta é sobre a versão 3 da Biblioteca do Google Maps. Você perguntou por que seu cálculo era diferente do deles. É porque eles usam um valor diferente para o raio da Terra do que você. A referência para o número? developers.google.com/maps/documentation/javascript/… logo abaixo do título.
Emil Badh
30

Exemplo usando latitude / longitude de GPS de 2 pontos.

var latitude1 = 39.46;
var longitude1 = -0.36;
var latitude2 = 40.40;
var longitude2 = -3.68;

var distance = google.maps.geometry.spherical.computeDistanceBetween(new google.maps.LatLng(latitude1, longitude1), new google.maps.LatLng(latitude2, longitude2));       
joan16v
fonte
3
Os resultados da distância são expressos em metros.
joan16v
1
@ joan16v como solicitar google.maps.geometry no node.js. Eu quero usar o código acima no node.js. qual módulo devo instalar e quais arquivos devo exigir.
Kris12
15

Basta adicionar isso ao início do seu código JavaScript:

google.maps.LatLng.prototype.distanceFrom = function(latlng) {
  var lat = [this.lat(), latlng.lat()]
  var lng = [this.lng(), latlng.lng()]
  var R = 6378137;
  var dLat = (lat[1]-lat[0]) * Math.PI / 180;
  var dLng = (lng[1]-lng[0]) * Math.PI / 180;
  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
  Math.cos(lat[0] * Math.PI / 180 ) * Math.cos(lat[1] * Math.PI / 180 ) *
  Math.sin(dLng/2) * Math.sin(dLng/2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  var d = R * c;
  return Math.round(d);
}

e use a função assim:

var loc1 = new GLatLng(52.5773139, 1.3712427);
var loc2 = new GLatLng(52.4788314, 1.7577444);
var dist = loc2.distanceFrom(loc1);
alert(dist/1000);
Plamen Todorov
fonte
Solução Excel, mas quero saber em que unidades está retornando o resultado. Eu tenho 3.013 .. é isso em milhas, km ??
Gowthami Gattineni
O valor retornado está em metros. Portanto, dist / 1000 fornece o valor em km.
Praveen Janakarajan
13
//p1 and p2 are google.maps.LatLng(x,y) objects

function calcDistance(p1, p2) {
          var d = (google.maps.geometry.spherical.computeDistanceBetween(p1, p2) / 1000).toFixed(2);
          console.log(d);              
}
Aishwat Singh
fonte
3
Esta é a melhor resposta. Por que adicionar uma função quando a API do Google já tem as funções
felixfbecker
Existe uma variante java disponível para esta API? Não consegui encontrá-lo depois de muita pesquisa.
Sanketh 10/06
@felixfbecker, porque você pode estar trabalhando em um ambiente em que não pode inserir a API do google maps em uma scripttag e chamar esses métodos. Como reagir nativo.
Nnanyielugo 5/09
11

Aqui está a implementação c # deste fórum

 public class DistanceAlgorithm
{
    const double PIx = 3.141592653589793;
    const double RADIO = 6378.16;

    /// <summary>
    /// This class cannot be instantiated.
    /// </summary>
    private DistanceAlgorithm() { }

    /// <summary>
    /// Convert degrees to Radians
    /// </summary>
    /// <param name="x">Degrees</param>
    /// <returns>The equivalent in radians</returns>
    public static double Radians(double x)
    {
        return x * PIx / 180;
    }

    /// <summary>
    /// Calculate the distance between two places.
    /// </summary>
    /// <param name="lon1"></param>
    /// <param name="lat1"></param>
    /// <param name="lon2"></param>
    /// <param name="lat2"></param>
    /// <returns></returns>
    public static double DistanceBetweenPlaces(
        double lon1,
        double lat1,
        double lon2,
        double lat2)
    {
        double dlon =  Radians(lon2 - lon1);
        double dlat =  Radians(lat2 - lat1);

        double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
        double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        return (angle * RADIO) * 0.62137;//distance in miles
    }

}    
Ahmad Naveed
fonte
5
Isso não se aplica à questão original de como fazê-lo no Google Maps.
Niklas Wulff
Não há função incorporada para calcular a distância diretamente, você deve usar os serviços de diretório para dois pontos e extrair a distância do XML / JSON retornado.
Naveed Ahmad
1
Meu comentário foi sobre o fato de que seria melhor fornecer uma solução em javascript, pois o iniciador de threads não disse se estava usando php, .net ou html estático.
Niklas Wulff
11

Com o Google você pode fazer isso usando a api esférica , google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);.

No entanto, se a precisão de uma projeção esférica ou de uma solução haversine não for precisa o suficiente para você (por exemplo, se você estiver próximo ao poste ou computando distâncias maiores), use uma biblioteca diferente.

Encontrei mais informações sobre o assunto na Wikipedia aqui .

Um truque para verificar se a precisão de um algoritmo é adequada é preencher o raio máximo e mínimo da Terra e verificar se a diferença pode causar problemas para o seu caso de uso. Muitos mais detalhes podem ser encontrados em neste artigo

No final, a API do Google ou o haversine servirão à maioria dos propósitos sem problemas.

iwein
fonte
9

Usando PHP, você pode calcular a distância usando esta função simples:

// para calcular a distância entre duas lat e lon

função calcular_distância ($ lat1, $ lon1, $ lat2, $ lon2, $ unit = 'N') 
{ 
  $ theta = $ lon1 - $ lon2; 
  $ dist = sin (deg2rad ($ lat1)) * sin (deg2rad ($ lat2)) + cos (deg2rad ($ lat1)) * cos (deg2rad ($ lat2)) * cos (deg2rad ($ theta)); 
  $ dist = acos ($ dist); 
  $ dist = rad2deg ($ dist); 
  $ milhas = $ dist * 60 * 1,1515;
  $ unit = strtoupper ($ unit);

  if ($ unit == "K") {
    ida e volta ($ milhas * 1,609344); 
  } senão se ($ unit == "N") {
      ida e volta ($ milhas * 0,8684);
    } outro {
        devolver $ milhas;
      }
}

// função termina aqui
Ravinder Singh
fonte
2
Existe uma condição na função de que, se você passar na unidade K, ela fornecerá a distância em km. Verifique-o.
Dead Man
essa função funciona muito bem e fornece distância da localização da estrela a todos os locais. ele pode percorrer de uma maneira que primeiro encontre o primeiro local mais nítido e se torne a fonte ou o início e depois encontre o próximo mais próximo, mas não o primeiro, e assim por diante?
Waheed ur Rehman
8

SOLUÇÃO OFFLINE - Algoritmo de Haversine

Em Javascript

var _eQuatorialEarthRadius = 6378.1370;
var _d2r = (Math.PI / 180.0);

function HaversineInM(lat1, long1, lat2, long2)
{
    return (1000.0 * HaversineInKM(lat1, long1, lat2, long2));
}

function HaversineInKM(lat1, long1, lat2, long2)
{
    var dlong = (long2 - long1) * _d2r;
    var dlat = (lat2 - lat1) * _d2r;
    var a = Math.pow(Math.sin(dlat / 2.0), 2.0) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r) * Math.pow(Math.sin(dlong / 2.0), 2.0);
    var c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
    var d = _eQuatorialEarthRadius * c;

    return d;
}

var meLat = -33.922982;
var meLong = 151.083853;


var result1 = HaversineInKM(meLat, meLong, -32.236457779983745, 148.69094705162837);
var result2 = HaversineInKM(meLat, meLong, -33.609020205923713, 150.77061469270831);

C #

using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");

        var meLat = -33.922982;
        double meLong = 151.083853;


        var result1 = HaversineInM(meLat, meLong, -32.236457779983745, 148.69094705162837);
        var result2 = HaversineInM(meLat, meLong, -33.609020205923713, 150.77061469270831);

        Console.WriteLine(result1);
        Console.WriteLine(result2);
    }

    static double _eQuatorialEarthRadius = 6378.1370D;
    static double _d2r = (Math.PI / 180D);

    private static int HaversineInM(double lat1, double long1, double lat2, double long2)
    {
        return (int)(1000D * HaversineInKM(lat1, long1, lat2, long2));
    }

    private static  double HaversineInKM(double lat1, double long1, double lat2, double long2)
    {
        double dlong = (long2 - long1) * _d2r;
        double dlat = (lat2 - lat1) * _d2r;
        double a = Math.Pow(Math.Sin(dlat / 2D), 2D) + Math.Cos(lat1 * _d2r) * Math.Cos(lat2 * _d2r) * Math.Pow(Math.Sin(dlong / 2D), 2D);
        double c = 2D * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1D - a));
        double d = _eQuatorialEarthRadius * c;

        return d;
    }
}

Referência: https://en.wikipedia.org/wiki/Great-circle_distance

MarceloBarbosa
fonte
3

Tinha que fazer isso ... O caminho do script de ação

//just make sure you pass a number to the function because it would accept you mother in law...
public var rad = function(x:*) {return x*Math.PI/180;}

protected  function distHaversine(p1:Object, p2:Object):Number {
    var R:int = 6371; // earth's mean radius in km
    var dLat:Number = rad(p2.lat() - p1.lat());
    var dLong:Number = rad(p2.lng() - p1.lng());

    var a:Number = Math.sin(dLat/2) * Math.sin(dLat/2) +
                Math.cos(rad(p1.lat())) * Math.cos(rad(p2.lat())) * Math.sin(dLong/2) * Math.sin(dLong/2);
    var c:Number = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    var d:Number = R * c;

    return d;
}
Netcfmx
fonte
3

No meu caso, era melhor calcular isso no SQL Server, pois eu queria pegar a localização atual e procurar todos os códigos postais a uma certa distância da localização atual. Eu também tinha um banco de dados que continha uma lista de códigos postais e seus lats longos. Felicidades

--will return the radius for a given number
create function getRad(@variable float)--function to return rad
returns float
as
begin
declare @retval float 
select @retval=(@variable * PI()/180)
--print @retval
return @retval
end
go

--calc distance
--drop function dbo.getDistance
create function getDistance(@cLat float,@cLong float, @tLat float, @tLong float)
returns float
as
begin
declare @emr float
declare @dLat float
declare @dLong float
declare @a float
declare @distance float
declare @c float

set @emr = 6371--earth mean 
set @dLat = dbo.getRad(@tLat - @cLat);
set @dLong = dbo.getRad(@tLong - @cLong);
set @a = sin(@dLat/2)*sin(@dLat/2)+cos(dbo.getRad(@cLat))*cos(dbo.getRad(@tLat))*sin(@dLong/2)*sin(@dLong/2);
set @c = 2*atn2(sqrt(@a),sqrt(1-@a))
set @distance = @emr*@c;
set @distance = @distance * 0.621371 -- i needed it in miles
--print @distance
return @distance;
end 
go


--get all zipcodes within 2 miles, the hardcoded #'s would be passed in by C#
select *
from cityzips a where dbo.getDistance(29.76,-95.38,a.lat,a.long) <3
order by zipcode
user2004796
fonte
Não tenho certeza se isso é eficiente para o uso do lado do cliente.
Nizar B.
Pode não ser uma solução de front-end, mas definitivamente o que eu estava procurando. Obrigado.
st_stefanov
3
//JAVA
    public Double getDistanceBetweenTwoPoints(Double latitude1, Double longitude1, Double latitude2, Double longitude2) {
    final int RADIUS_EARTH = 6371;

    double dLat = getRad(latitude2 - latitude1);
    double dLong = getRad(longitude2 - longitude1);

    double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(getRad(latitude1)) * Math.cos(getRad(latitude2)) * Math.sin(dLong / 2) * Math.sin(dLong / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return (RADIUS_EARTH * c) * 1000;
    }

    private Double getRad(Double x) {
    return x * Math.PI / 180;
    }
borchvm
fonte
1

É muito fácil usar o serviço Google Distance Matrix

O primeiro passo é ativar o serviço Matriz de distâncias no console da API do Google. retorna distâncias entre um conjunto de locais. E aplique esta função simples

function initMap() {
        var bounds = new google.maps.LatLngBounds;
        var markersArray = [];

        var origin1 = {lat:23.0203, lng: 72.5562};
        //var origin2 = 'Ahmedabad, India';
        var destinationA = {lat:23.0436503, lng: 72.55008939999993};
        //var destinationB = {lat: 23.2156, lng: 72.6369};

        var destinationIcon = 'https://chart.googleapis.com/chart?' +
            'chst=d_map_pin_letter&chld=D|FF0000|000000';
        var originIcon = 'https://chart.googleapis.com/chart?' +
            'chst=d_map_pin_letter&chld=O|FFFF00|000000';
        var map = new google.maps.Map(document.getElementById('map'), {
          center: {lat: 55.53, lng: 9.4},
          zoom: 10
        });
        var geocoder = new google.maps.Geocoder;

        var service = new google.maps.DistanceMatrixService;
        service.getDistanceMatrix({
          origins: [origin1],
          destinations: [destinationA],
          travelMode: 'DRIVING',
          unitSystem: google.maps.UnitSystem.METRIC,
          avoidHighways: false,
          avoidTolls: false
        }, function(response, status) {
          if (status !== 'OK') {
            alert('Error was: ' + status);
          } else {
            var originList = response.originAddresses;
            var destinationList = response.destinationAddresses;
            var outputDiv = document.getElementById('output');
            outputDiv.innerHTML = '';
            deleteMarkers(markersArray);

            var showGeocodedAddressOnMap = function(asDestination) {
              var icon = asDestination ? destinationIcon : originIcon;
              return function(results, status) {
                if (status === 'OK') {
                  map.fitBounds(bounds.extend(results[0].geometry.location));
                  markersArray.push(new google.maps.Marker({
                    map: map,
                    position: results[0].geometry.location,
                    icon: icon
                  }));
                } else {
                  alert('Geocode was not successful due to: ' + status);
                }
              };
            };

            for (var i = 0; i < originList.length; i++) {
              var results = response.rows[i].elements;
              geocoder.geocode({'address': originList[i]},
                  showGeocodedAddressOnMap(false));
              for (var j = 0; j < results.length; j++) {
                geocoder.geocode({'address': destinationList[j]},
                    showGeocodedAddressOnMap(true));
                //outputDiv.innerHTML += originList[i] + ' to ' + destinationList[j] + ': ' + results[j].distance.text + ' in ' +                    results[j].duration.text + '<br>';
                outputDiv.innerHTML += results[j].distance.text + '<br>';
              }
            }

          }
        });
      }

Onde origin1 é sua localização e destinoA é um local de destinação, você pode adicionar acima de dois ou mais dados.

Rad Documentação Completa com um exemplo

TarangP
fonte
1
  /**
   * Calculates the haversine distance between point A, and B.
   * @param {number[]} latlngA [lat, lng] point A
   * @param {number[]} latlngB [lat, lng] point B
   * @param {boolean} isMiles If we are using miles, else km.
   */
  function haversineDistance(latlngA, latlngB, isMiles) {
    const squared = x => x * x;
    const toRad = x => (x * Math.PI) / 180;
    const R = 6371; // Earth’s mean radius in km

    const dLat = toRad(latlngB[0] - latlngA[0]);
    const dLon = toRad(latlngB[1] - latlngA[1]);

    const dLatSin = squared(Math.sin(dLat / 2));
    const dLonSin = squared(Math.sin(dLon / 2));

    const a = dLatSin +
              (Math.cos(toRad(latlngA[0])) * Math.cos(toRad(latlngB[0])) * dLonSin);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    let distance = R * c;

    if (isMiles) distance /= 1.609344;

    return distance;
  }

Encontrei uma versão online 80% correta, mas inserida no parâmetro errado e inconsistente no uso das entradas, esta versão corrigiu completamente

Pencilcheck
fonte
0

Para calcular a distância no Google Maps, você pode usar a API de rotas. Essa será uma das maneiras mais fáceis de fazer isso. Para obter dados do Google Server, você pode usar Retrofit ou Volley. Ambos têm sua própria vantagem. Dê uma olhada no código a seguir, onde usei o retrofit para implementá-lo:

private void build_retrofit_and_get_response(String type) {

    String url = "https://maps.googleapis.com/maps/";

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    RetrofitMaps service = retrofit.create(RetrofitMaps.class);

    Call<Example> call = service.getDistanceDuration("metric", origin.latitude + "," + origin.longitude,dest.latitude + "," + dest.longitude, type);

    call.enqueue(new Callback<Example>() {
        @Override
        public void onResponse(Response<Example> response, Retrofit retrofit) {

            try {
                //Remove previous line from map
                if (line != null) {
                    line.remove();
                }
                // This loop will go through all the results and add marker on each location.
                for (int i = 0; i < response.body().getRoutes().size(); i++) {
                    String distance = response.body().getRoutes().get(i).getLegs().get(i).getDistance().getText();
                    String time = response.body().getRoutes().get(i).getLegs().get(i).getDuration().getText();
                    ShowDistanceDuration.setText("Distance:" + distance + ", Duration:" + time);
                    String encodedString = response.body().getRoutes().get(0).getOverviewPolyline().getPoints();
                    List<LatLng> list = decodePoly(encodedString);
                    line = mMap.addPolyline(new PolylineOptions()
                                    .addAll(list)
                                    .width(20)
                                    .color(Color.RED)
                                    .geodesic(true)
                    );
                }
            } catch (Exception e) {
                Log.d("onResponse", "There is an error");
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Throwable t) {
            Log.d("onFailure", t.toString());
        }
    });

}

Acima está o código da função build_retrofit_and_get_response para calcular a distância. Abaixo está a interface Retrofit correspondente:

package com.androidtutorialpoint.googlemapsdistancecalculator;


import com.androidtutorialpoint.googlemapsdistancecalculator.POJO.Example;

import retrofit.Call;
import retrofit.http.GET;
import retrofit.http.Query;

public interface RetrofitMaps {


/*
 * Retrofit get annotation with our URL
 * And our method that will return us details of student.
 */
@GET("api/directions/json?key=AIzaSyC22GfkHu9FdgT9SwdCWMwKX1a4aohGifM")
Call<Example> getDistanceDuration(@Query("units") String units, @Query("origin") String origin, @Query("destination") String destination, @Query("mode") String mode);

}

Espero que isso explique sua consulta. Muito bem sucedida :)

Fonte: Calculadora de distâncias do Google Maps

Navneet Goel
fonte
Não - calcula a distância da viagem (nas estradas, etc.), não a distância geodésica ponto a ponto.
Yarin