Oblicz odległość między dwoma punktami w Google Maps V3

Odpowiedzi:

460

Jeśli chcesz to obliczyć samodzielnie, możesz użyć formuły 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
źródło
4
Dlaczego sugerujesz użycie Math.atan2 (Math.sqrt (a), Math.sqrt (1-a)) zamiast najprostszego Math.asin (Math.sqrt (a))?
Emanuele Paolini
3
@EmanuelePaolini - Matematycznie atan2 (sqrt (a), sqrt (1-a)) = asin (sqrt (a)) = acos (sqrt (1-a)), ale wersja atan2 pozostaje lepiej uwarunkowana liczbowo dla wszystkich wartości za.
ChrisV,
23
Chłopaki Pytanie. Dlaczego tak bardzo podoba Ci się używanie 1-literowych nazw zmiennych do rozwiązywania problemów wymagających wyobraźni, w których dobra nazwa zmiennej może być pomocna? Tylko pytam :)
pie6k 26.04.16
2
Czy nie powinno to być var ​​R = 6371; za Km?
Alexander Fradiani
5
Funkcje p1.lat()i p1.lng()zakładają, że dane wejściowe są google.maps.LatLngobiektami. Jeśli masz po prostu surowe dane , to na przykład {lat: __, lon: __}zamiast tego skorzystasz p1.lat.
Don McCurdy
308

Wydaje się, że w GMap3 jest jakaś metoda. Jest to statyczna metoda google.maps.geometry.sphericalprzestrzeni nazw.

Jako argument przyjmuje dwa LatLngobiekty i wykorzysta domyślny promień Ziemi wynoszący 6378137 metrów, chociaż domyślny promień można w razie potrzeby zastąpić wartością niestandardową.

Upewnij się, że podałeś:

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

w twojej części głowy.

Połączenie będzie:

google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);
Emil Badh
źródło
10
Dlaczego więc istnieje 1% różnica w odpowiedzi podanej przez sferyczne obliczenie GoogleDistanceBetween i formułę odległości Haversine?
Matt S
7
@RamenRecon Nie jestem pewien, ale wątpliwe jest, że używają różnych wartości dla promienia ziemi.
Emil Badh,
11
@RamenRecon tak, Emil ma rację. Dokumentacja mówi: Domyślnym promieniem jest promień Ziemi wynoszący 6378137 metrów. Ale Mike w Haversine powyżej wykorzystuje zamiast tego 6371 km .
Laszlo
Powyższy link jest teraz zepsuty, ale wyjaśnienie metody nie stanowi większego problemu.
GChorn
2
@ ABCD.ca To nie jest mój numer. To pytanie dotyczy wersji 3 Biblioteki Map Google. Zapytałeś, dlaczego twoje obliczenia różnią się od ich obliczeń. To dlatego, że używają innej wartości promienia ziemi niż ty. Numer referencyjny? developers.google.com/maps/documentation/javascript/… Bezpośrednio pod nagłówkiem.
Emil Badh
30

Przykład zastosowania szerokości / długości geograficznej GPS 2 punktów.

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
źródło
3
Wyniki odległości wyrażone są w metrach.
joan16v,
1
@ joan16v jak wymagać google.maps.geometry w node.js. Chcę użyć powyższego kodu w node.js. który moduł powinienem zainstalować i jakich plików powinienem wymagać.
kisor
15

Po prostu dodaj to na początku swojego kodu 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);
}

a następnie użyj funkcji w ten sposób:

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
źródło
Doskonałe rozwiązanie, ale chcę wiedzieć, w jakich jednostkach zwraca wynik. Mam 3.013 .. czy to w milach, km?
Gowthami Gattineni
Zwrócona wartość jest w metrach. Zatem dist / 1000 daje wartość w 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
źródło
3
to najlepsza odpowiedź. Po co dodawać funkcję, skoro Google API ma już funkcje
felixfbecker
Czy jest dostępny wariant Java dla tego API? Nie mogłem go znaleźć po wielu poszukiwaniach.
Sanketh,
@felixfbecker, ponieważ możesz pracować w środowisku, w którym nie możesz wstawić interfejsu API Google Maps do scripttagu i wywoływać tych metod. Jak reakcyjny natywny.
Nnanyielugo,
11

Oto implementacja c # tej forumuli

 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
źródło
5
Nie dotyczy to pierwotnego pytania, jak to zrobić w Mapach Google.
Niklas Wulff
Nie ma wbudowanej funkcji bezpośredniego obliczania odległości, musisz użyć usług katalogowych dla dwóch punktów i wyodrębnić odległość ze zwróconego XML / JSON.
Naveed Ahmad
1
Mój komentarz dotyczył faktu, że lepiej byłoby podać rozwiązanie w javascript, ponieważ starter wątku nie powiedział, czy używa php, .net lub statycznego HTML.
Niklas Wulff
11

Z Google można to zrobić za pomocą sferycznego api , google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);.

Jeśli jednak precyzja projekcji sferycznej lub rozwiązania typu haverine nie jest dla Ciebie wystarczająco precyzyjna (np. Jeśli jesteś blisko bieguna lub obliczasz większe odległości), powinieneś użyć innej biblioteki.

Większość informacji na ten temat znalazłem na Wikipedii tutaj .

Sztuką, aby sprawdzić, czy dokładność dowolnego algorytmu jest wystarczająca, jest wypełnienie maksymalnego i minimalnego promienia ziemi i sprawdzenie, czy różnica może powodować problemy w twoim przypadku użycia. Wiele innych szczegółów można znaleźć w tym artykule

W końcu google api lub haversine będą bez problemu służyć większości celów.

iwein
źródło
9

Za pomocą PHP możesz obliczyć odległość za pomocą tej prostej funkcji:

// aby obliczyć odległość między dwoma lat i lon

funkcja oblicz_dystans ($ 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); 
  $ mile = $ dist * 60 * 1,1515;
  $ unit = strtoupper ($ unit);

  if ($ unit == "K") {
    powrót ($ mile * 1,609344); 
  } else if ($ unit == "N") {
      powrót ($ mile * 0,8684);
    } else {
        zwrócić mile $;
      }
}

// funkcja kończy się tutaj
Ravinder Singh
źródło
2
W tej funkcji jest warunek, że jeśli przejdziesz jednostkę tak, Kto da ci odległość w km. Sprawdź to.
Dead Man,
ta funkcja działa bardzo dobrze i zapewnia odległość od lokalizacji gwiazdy do wszystkich lokalizacji. czy może przejść w taki sposób, że najpierw znajdzie pierwszą najbliższą lokalizację i stanie się źródłem lub początkiem, a następnie znajdzie najbliższą, ale nie pierwszą, i tak dalej dla wszystkich?
Waheed ur Rehman
8

ROZWIĄZANIE OFFLINE - Algorytm Haversine

W 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);

DO#

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

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

MarceloBarbosa
źródło
3

Musiałem to zrobić ... Sposób skryptu akcji

//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
źródło
3

W moim przypadku najlepiej było to obliczyć w programie SQL Server, ponieważ chciałem wziąć bieżącą lokalizację, a następnie wyszukać wszystkie kody pocztowe w pewnej odległości od bieżącej lokalizacji. Miałem również DB, który zawierał listę kodów pocztowych i ich długości. Twoje zdrowie

--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
użytkownik2004796
źródło
Nie jestem pewien, czy jest to skuteczne w przypadku użycia po stronie klienta.
Nizar B.,
Może nie jest to rozwiązanie front-end, ale zdecydowanie to, czego szukałem. Dzięki.
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
źródło
1

Korzystanie z usługi Google Distance Matrix jest dość łatwe

Pierwszym krokiem jest aktywacja usługi Distance Matrix z konsoli API Google. zwraca odległości między zestawem lokalizacji. I zastosuj tę prostą funkcję

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

          }
        });
      }

Gdzie origin1 jest Twoją lokalizacją i miejscem docelowymA jest lokalizacją docelową. Możesz dodać powyżej dwóch lub więcej danych.

Rad Pełna dokumentacja z przykładem

TarangP
źródło
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;
  }

Znalazłem wersję online, która jest w 80% poprawna, ale podłączyłem zły parametr i jest niespójny w użyciu danych wejściowych, ta wersja naprawiła to całkowicie

Ołówek
źródło
0

Aby obliczyć odległość w Mapach Google, możesz użyć interfejsu API wskazówek. To będzie jeden z najłatwiejszych sposobów. Aby uzyskać dane z Google Server, możesz użyć Retrofit lub Volley. Oba mają swoje zalety. Spójrz na następujący kod, w którym użyłem modernizacji, aby go zaimplementować:

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

}

Powyżej znajduje się kod funkcji build_retrofit_and_get_response do obliczania odległości. Poniżej znajduje się odpowiedni interfejs modernizacji:

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

}

Mam nadzieję, że to wyjaśnia twoje zapytanie. Wszystkiego najlepszego :)

Źródło: Google Maps Distance Calculator

Navneet Goel
źródło
Nie - oblicza odległość podróży (po drogach itp.), A nie odległość geodezyjną od punktu do punktu.
Yarin