Calcular la distancia entre dos puntos en google maps V3

Respuestas:

460

Si desea calcularlo usted mismo, puede usar la fórmula de 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
fuente
44
¿Por qué sugiere usar Math.atan2 (Math.sqrt (a), Math.sqrt (1-a)) en lugar del Math.asin más simple (Math.sqrt (a))?
Emanuele Paolini
3
@EmanuelePaolini - Matemáticamente, atan2 (sqrt (a), sqrt (1-a)) = asin (sqrt (a)) = acos (sqrt (1-a)), pero la versión atan2 permanece numéricamente mejor condicionada para todos los valores de a.
ChrisV
23
Chicos Pregunta. ¿Por qué te gusta tanto usar nombres de variables de 1 letra para resolver problemas que requieren un poco de imaginación donde un buen nombre de variable podría ser útil? Solo pregunto :)
pie6k
2
¿No debería ser var R = 6371; por km?
Alexander Fradiani
55
Las funciones p1.lat()y p1.lng()asumen que sus datos de entrada son google.maps.LatLngobjetos. Si solo tiene datos en bruto como {lat: __, lon: __}entonces, en su lugar p1.lat, los usaría , por ejemplo.
Don McCurdy
309

En realidad parece haber un método en GMap3. Es un método estático del google.maps.geometry.sphericalespacio de nombres.

Toma como argumentos dos LatLngobjetos y utilizará un radio de la Tierra predeterminado de 6378137 metros, aunque el radio predeterminado se puede anular con un valor personalizado si es necesario.

Asegúrate de incluir:

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

en tu sección de la cabeza.

La llamada será:

google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);
Emil Badh
fuente
10
Entonces, ¿por qué hay una diferencia del 1% en la respuesta dada por la fórmula esférica computeDistanceBetween y la distancia de Haversine de Google?
Matt S
77
@RamenRecon No estoy seguro, pero una pregunta sería que usan valores diferentes para el radio de la Tierra.
Emil Badh
11
@RamenRecon sí, Emil tiene razón en esto. La documentación dice: El radio predeterminado es el radio de la Tierra de 6378137 metros. Pero Mike en el Haversine anterior usa 6371 km en su lugar.
Laszlo
El enlace anterior ahora está roto, pero la explicación del método hace que no sea un gran problema.
GChorn
2
@ ABCD.ca No es mi número. Esta pregunta es sobre la versión 3 de la Biblioteca de Google Maps. Usted preguntó por qué su cálculo difería del de ellos. Es porque usan un valor diferente para el radio de la Tierra que el que tú usas. La referencia para el número? developers.google.com/maps/documentation/javascript/… Justo debajo del título.
Emil Badh
30

Ejemplo usando latitud / longitud GPS de 2 puntos.

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
fuente
3
Los resultados de la distancia se expresan en metros.
joan16v
1
@ joan16v cómo requerir google.maps.geometry en node.js. Quiero usar el código anterior en node.js. qué módulo debo instalar y qué archivos debo requerir.
kisor
15

Simplemente agregue esto al comienzo de su 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);
}

y luego use la función de esta manera:

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
fuente
Excelente solución pero quiero saber en qué unidades está devolviendo el resultado. Tengo 3.013 ... ¿es eso en millas, km?
Gowthami Gattineni
El valor devuelto está en metros. Por lo tanto dist / 1000 le da el valor en 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
fuente
3
Esta es la mejor respuesta. ¿Por qué añadir una función cuando la API de Google ya cuenta con las funciones
felixfbecker
¿Hay una variante de Java disponible para esta API? No pude encontrarlo después de mucho buscar.
Sanketh
@felixfbecker, porque podría estar trabajando en un entorno en el que no puede insertar la API de Google Maps en una scriptetiqueta y llamar a esos métodos. Como reaccionar nativo.
Nnanyielugo
11

Aquí está la implementación de C # de este foro.

 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
    }

}    
Naveed Ahmad
fuente
55
Esto no se aplica a la pregunta original de cómo hacerlo en Google Maps.
Niklas Wulff
No hay una función incorporada para calcular la distancia directamente, debe usar los servicios de directorio para dos puntos y extraer la distancia del XML / JSON devuelto.
Naveed Ahmad
1
Mi comentario fue sobre el hecho de que hubiera sido mejor proporcionar una solución en javascript, ya que el iniciador de subprocesos no dijo si estaba usando php, .net o html estático.
Niklas Wulff
11

Con Google puede hacerlo utilizando la API esférica , google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);.

Sin embargo, si la precisión de una proyección esférica o una solución de Haversine no es lo suficientemente precisa para usted (por ejemplo, si está cerca del poste o calcula distancias más largas), debe usar una biblioteca diferente.

La mayoría de la información sobre el tema que encontré en Wikipedia aquí .

Un truco para ver si la precisión de cualquier algoritmo es adecuada es completar el radio máximo y mínimo de la tierra y ver si la diferencia puede causar problemas en su caso de uso. Muchos más detalles se pueden encontrar en este artículo

Al final, la API de Google o Haversine servirán para la mayoría de los propósitos sin problemas.

iwein
fuente
9

Usando PHP, puede calcular la distancia usando esta función simple:

// para calcular la distancia entre dos lat y lon

función Calculate_distance ($ 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); 
  $ millas = $ dist * 60 * 1.1515;
  $ unidad = strtoupper ($ unidad);

  if ($ unit == "K") {
    devolución ($ millas * 1.609344); 
  } else if ($ unit == "N") {
      retorno ($ millas * 0.8684);
    } más {
        devolver $ millas;
      }
}

// la función termina aquí
Ravinder Singh
fuente
2
Hay una condición en la función que si pasa la unidad como Kentonces le dará la distancia en km. Revisalo.
Dead Man
Esta función funciona muy bien y proporciona la distancia desde la ubicación de la estrella a todas las ubicaciones. ¿puede b atravesar de alguna manera primero encuentra la primera ubicación más cercana y se convierte en la fuente o el inicio y luego encuentra la siguiente más cercana pero no la primera fuente y así sucesivamente?
Waheed ur Rehman
8

SOLUCIÓN OFFLINE - Algoritmo de Haversine

En 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;
    }
}

Referencia: https://en.wikipedia.org/wiki/Great-circle_distance

MarceloBarbosa
fuente
3

Tenía que hacerlo ... La forma del guión de acción

//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
fuente
3

En mi caso, era mejor calcular esto en SQL Server, ya que quería tomar la ubicación actual y luego buscar todos los códigos postales a una cierta distancia de la ubicación actual. También tenía una base de datos que contenía una lista de códigos postales y sus largos. Salud

--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
usuario2004796
fuente
No estoy seguro de que esto sea eficiente para un uso del lado del cliente.
Nizar B.
Puede que no sea una solución front-end, pero definitivamente lo que estaba buscando. Gracias.
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
fuente
1

Es bastante fácil usar el servicio Google Distance Matrix

El primer paso es activar el servicio Distance Matrix desde la consola API de Google. devuelve distancias entre un conjunto de ubicaciones. Y aplica esta simple función

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>';
              }
            }

          }
        });
      }

Donde origen1 es su ubicación y destino A es ubicación de destino. Puede agregar más de dos o más datos.

Rad Documentación completa con un ejemplo

TarangP
fuente
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;
  }

Encontré una versión en línea que es correcta en un 80% pero está conectada al parámetro incorrecto y es inconsistente en el uso de las entradas, esta versión lo solucionó por completo

Pencilcheck
fuente
0

Para calcular la distancia en Google Maps, puede usar la API de indicaciones. Esa será una de las formas más fáciles de hacerlo. Para obtener datos del servidor de Google, puede usar Retrofit o Volley. Ambos tienen su propia ventaja. Eche un vistazo al siguiente código donde he usado la modificación para implementarlo:

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());
        }
    });

}

Arriba está el código de la función build_retrofit_and_get_response para calcular la distancia. A continuación se muestra la interfaz de actualización correspondiente:

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 esto explique tu consulta. Todo lo mejor :)

Fuente: Calculadora de distancia de Google Maps

Navneet Goel
fuente
No, esto calcula la distancia de viaje (en carreteras, etc.), no la distancia geodésica punto a punto.
Yarin