Рассчитать расстояние между двумя точками в Google Maps V3


319

Как рассчитать расстояние между двумя маркерами в Google maps V3? (Аналогично distanceFromфункции в v2.)

Спасибо..


Ответы:


460

Если вы хотите рассчитать это самостоятельно, то вы можете использовать формулу 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
};

4
Почему вы предлагаете использовать Math.atan2 (Math.sqrt (a), Math.sqrt (1-a)) вместо самого простого Math.asin (Math.sqrt (a))?
Эмануэле Паолини

3
@EmanuelePaolini - математически atan2 (sqrt (a), sqrt (1-a)) = asin (sqrt (a)) = acos (sqrt (1-a)), но версия atan2 остается численно лучше обусловленной для всех значений а.
ChrisV

23
Ребята. Вопрос. Почему вам так нравится использовать однобуквенные имена переменных для решения задач, требующих воображения, когда хорошее имя переменной может быть полезным? Просто спрашиваю :)
pie6k

2
Не должно ли это быть var R = 6371; за км?
Александр Фрадиани

5
Функции p1.lat()и p1.lng()предполагают, что ваши входные данные являются google.maps.LatLngобъектами. Например, если у вас есть необработанные данные, {lat: __, lon: __}вы можете использовать их p1.lat.
Дон

309

Кажется, что в GMap3 есть метод. Это статический метод google.maps.geometry.sphericalпространства имен.

Он принимает в качестве аргументов два LatLngобъекта и будет использовать радиус Земли по умолчанию, равный 6378137 метрам, хотя радиус по умолчанию может быть переопределен с помощью пользовательского значения при необходимости.

Убедитесь, что вы включили:

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

в разделе вашей головы.

Звонок будет:

google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);

10
Так почему же разница в ответе, заданном сферическим computeDistanceBetween Google и формулой расстояния Хаверсайна, составляет 1%?
Мэтт С

7
@RamenRecon Я не уверен, но вопрос в том, что они используют разные значения радиуса Земли.
Эмиль Бадх

11
@RamenRecon да, Эмиль прав в этом. Документация гласит: Радиус по умолчанию - радиус Земли 6378137 метров. Но Майк в Haversine выше использует 6371 км вместо этого.
Ласло

Ссылка выше теперь не работает, но объяснение метода делает это не большой проблемой.
GChorn

2
@ ABCD.ca Это не мой номер. Этот вопрос касается версии 3 библиотеки Google Maps. Вы спросили, почему ваш расчет отличается от их. Это потому, что они используют другое значение радиуса Земли, чем вы. Ссылка на номер? developers.google.com/maps/documentation/javascript/… Прямо под заголовком.
Эмиль Бадх

30

Пример использования GPS широты / долготы 2 точки.

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

3
Результаты на расстоянии выражены в метрах.
joan16v

1
@ joan16v как запросить google.maps.geometry в node.js. Я хочу использовать приведенный выше код в node.js. Какой модуль я должен установить и какие файлы мне нужно.
кисор

15

Просто добавьте это в начало вашего кода 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);
}

и затем используйте функцию как это:

var loc1 = new GLatLng(52.5773139, 1.3712427);
var loc2 = new GLatLng(52.4788314, 1.7577444);
var dist = loc2.distanceFrom(loc1);
alert(dist/1000);

Решение Excellect, но я хочу знать, в каких единицах он возвращает результат. Я получил 3.013 .. это в милях, км ??
Гаутами Гаттинени

Возвращаемое значение в метрах. Следовательно dist / 1000 дает вам значение в км.
Правин Джанакараджан

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

3
это лучший ответ. Зачем добавлять функцию, если Google API уже имеет эти функции
felixfbecker

Есть ли вариант Java для этого API? Я не смог найти его после долгих поисков.
Санкет

@felixfbecker, потому что вы можете работать в среде, где вы не можете вставить API карт Google в scriptтег и вызывать эти методы. Вроде реакция-родная.
Nnanyielugo

11

Вот реализация этого форума c #

 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
    }

}    

5
Это не относится к первоначальному вопросу о том, как это сделать в Google Maps.
Никлас Вульф

Нет встроенной функции для непосредственного расчета расстояния, вы должны использовать службы каталогов для двух точек и извлечь расстояние из возвращенного XML / JSON.
Навид Ахмад

1
Мой комментарий был о том, что было бы лучше предоставить решение в javascript, так как автор потока не сказал, использует ли он / она php, .net или статический html.
Никлас Вульф

11

С помощью Google вы можете сделать это с помощью сферического апи , google.maps.geometry.spherical.computeDistanceBetween (latLngA, latLngB);.

Однако, если точность сферической проекции или решения haversine не достаточно точна для вас (например, если вы близки к полюсу или вычисляете большие расстояния), вам следует использовать другую библиотеку.

Большую информацию по этому вопросу я нашел в Википедии здесь .

Уловка, чтобы увидеть, является ли точность любого данного алгоритма адекватной, состоит в том, чтобы заполнить максимальный и минимальный радиус Земли и посмотреть, может ли разница вызвать проблемы для вашего варианта использования. Еще много деталей можно найти в этой статье

В конце концов, Google Api или haversine будут служить большинству целей без проблем.


9

Используя PHP, вы можете рассчитать расстояние с помощью этой простой функции:

// рассчитать расстояние между двумя широтами

функция 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); 
  $ миль = $ dist * 60 * 1.1515;
  $ unit = strtoupper ($ unit);

  if ($ unit == "K") {
    возврат ($ мили * 1.609344); 
  } else if ($ unit == "N") {
      возврат ($ миль * 0,8684);
    } еще {
        вернуть $ миль;
      }
}

// функция заканчивается здесь

2
В функции есть условие, что если вы пройдете единицу как, Kто это даст вам расстояние в км. Проверь это.
Мертвец

эта функция работает очень хорошо и дает расстояние от звездного местоположения до всех местоположений. может ли он пройти таким образом, чтобы сначала найти первое наиболее близкое местоположение и стать источником или началом, а затем найти ближайший, но не первый источник, и так далее для всех?
Вахид ур Рехман

8

Автономное решение - алгоритм Haversine

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

Ссылка: https://en.wikipedia.org/wiki/Great-circle_distance


3

Пришлось сделать это ... Сценарий действия пути

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

3

В моем случае было лучше рассчитать это в SQL Server, так как я хотел взять текущее местоположение, а затем искать все почтовые индексы на определенном расстоянии от текущего местоположения. У меня также была БД, которая содержала список почтовых индексов и их длинных номеров. ура

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

Не уверен, что это эффективно для использования на стороне клиента.
Низар Б.

Может быть, не интерфейсное решение, но определенно то, что я искал. Спасибо.
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;
    }

1

С сервисом Google Distance Matrix довольно легко

Первый шаг - активировать сервис Distance Matrix из консоли Google API. он возвращает расстояния между множеством мест. И применить эту простую функцию

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

          }
        });
      }

Где origin1 - это ваше местоположение, а destinationA - это место назначения. Вы можете добавить два или более данных выше.

Rad Полная документация с примером


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

Я нашел в сети версию, которая на 80% верна, но подключена с неверным параметром и несовместима с использованием входных данных, эта версия полностью исправлена


0

Чтобы рассчитать расстояние на Google Картах, вы можете использовать Directions API. Это будет один из самых простых способов сделать это. Чтобы получить данные с сервера Google, вы можете использовать Retrofit или Volley. У обоих есть свои преимущества. Взгляните на следующий код, где я использовал модификацию для его реализации:

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

}

Выше приведен код функции build_retrofit_and_get_response для расчета расстояния. Ниже приведен соответствующий интерфейс Retrofit:

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

}

Я надеюсь, что это объясняет ваш запрос. Всего наилучшего :)

Источник: Google Maps Расстояние Калькулятор


Нет - это рассчитывает расстояние в пути (на дорогах и т. Д.), А не расстояние от точки до точки геодезической.
Ярин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.