Cum pot calcula distanța dintre două puncte specificate de latitudine și longitudine?
Pentru clarificare, am'd distanța în kilometri; punctele folosi sistemul WGS84 și am'd dori să înțeleagă relativă precizie de abordări disponibile.
Acest link ar putea fi de ajutor pentru tine, ca-l descrie modul de utilizare a formula Haversine pentru a calcula distanța.
Fragment:
Acest script [Javascript] calculează mare-cerc de distanțe între două puncte – care este distanța cea mai scurtă de-a lungul suprafeței pământului – utilizarea 'Haversine' formula.
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2-lat1); // deg2rad below
var dLon = deg2rad(lon2-lon1);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2)
;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI/180)
}
Am nevoie pentru a calcula o mulțime de distanțe între punctele de proiectul meu, așa că am mers mai departe și a încercat pentru a optimiza codul, am găsit-o aici. În medie, în diferite browsere noul meu implementare se execută de 2 ori mai repede decât cel mai upvoted răspuns.
function distance(lat1, lon1, lat2, lon2) {
var p = 0.017453292519943295; // Math.PI / 180
var c = Math.cos;
var a = 0.5 - c((lat2 - lat1) * p)/2 +
c(lat1 * p) * c(lat2 * p) *
(1 - c((lon2 - lon1) * p))/2;
return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}
Puteți juca cu jsPerf și a vedea rezultate.
Recent am avut nevoie să facă același lucru în python, deci, aici este o implementare python:
from math import cos, asin, sqrt
def distance(lat1, lon1, lat2, lon2):
p = 0.017453292519943295 #Pi/180
a = 0.5 - cos((lat2 - lat1) * p)/2 + cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2
return 12742 * asin(sqrt(a)) #2*R*asin...
Și pentru motive de exhaustivitate: Haversine de pe wiki.
Aici este o C# punerii în Aplicare:
static class DistanceAlgorithm
{
const double PIx = 3.141592653589793;
const double RADIUS = 6378.16;
/// <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 * RADIUS;
}
}
Aici este o implementare java de formula Haversine.
public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
double venueLat, double venueLng) {
double latDistance = Math.toRadians(userLat - venueLat);
double lngDistance = Math.toRadians(userLng - venueLng);
double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
+ Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
* Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}
Rețineți că aici suntem rotunjire răspunsul la cea mai apropiată km.
Multumesc foarte mult pentru toate astea. Am folosit următorul cod în Objective-C pentru iPhone app:
const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km
double convertToRadians(double val) {
return val * PIx / 180;
}
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {
double dlon = convertToRadians(place2.longitude - place1.longitude);
double dlat = convertToRadians(place2.latitude - place1.latitude);
double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
double angle = 2 * asin(sqrt(a));
return angle * RADIO;
}
Latitudinea și Longitudinea sunt în zecimal. Am't utilizarea min() pentru asin() suna ca distantele pe care am'm utilizând sunt atât de mici încât acestea nu't nevoie de ea.
A dat răspunsuri incorecte, până când am trecut în valorile în Radiani - acum's destul de mult la fel ca și valorile obținute de la Apple's Map app :-)
Plus update:
Dacă utilizați iOS4 sau mai târziu, atunci Apple a oferi unele metode pentru a face acest lucru astfel încât functionalitate la fel s-ar fi realizat cu:
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {
MKMapPoint start, finish;
start = MKMapPointForCoordinate(place1);
finish = MKMapPointForCoordinate(place2);
return MKMetersBetweenMapPoints(start, finish) / 1000;
}
Aceasta este o simplă funcție PHP care vă va oferi o aproximare rezonabilă (sub +/-1% marja de eroare).
<?php
function distance($lat1, $lon1, $lat2, $lon2) {
$pi80 = M_PI / 180;
$lat1 *= $pi80;
$lon1 *= $pi80;
$lat2 *= $pi80;
$lon2 *= $pi80;
$r = 6372.797; // mean radius of Earth in km
$dlat = $lat2 - $lat1;
$dlon = $lon2 - $lon1;
$a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$km = $r * $c;
//echo '<br/>'.$km;
return $km;
}
?>
Așa cum a spus înainte; pământul NU este o sferă. Este ca un vechi, vechi de baseball care Mark McGwire a decis să practice cu - este plin de urme de lovituri și umflături. Cele mai simple calcule (ca aceasta) va trata ca pe o sferă.
Diferite metode pot fi mai mult sau mai puțin precise în funcție de unde vă aflați pe această neregulate ovoid ȘI cât de departe în puncte (cu cât sunt mai aproape cele mai mici absolută marja de eroare). Mai precis așteptările dumneavoastră, mai complexe de matematica.
Pentru mai multe informatii: wikipedia distanță geografică
Voi posta aici exemplu de lucru.
Lista cu toate punctele din tabel având distanța între un punct desemnat (vom folosi un punct aleatoriu - lat:45.20327, mult timp:23.7806) mai puțin de 50 KM, cu latitudine & longitudine, în MySQL (tabelul câmpuri sunt coord_lat și coord_long):
Lista toate având DISTANȚA<50, în Kilometri (considerat Pământ rază de 6371 KM):
SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta
FROM obiective
WHERE coord_lat<>''
AND coord_long<>''
HAVING distanta<50
ORDER BY distanta desc
Exemplul de mai sus a fost testat în MySQL 5.0.95 și 5.5.16 (Linux).
În alte răspunsuri o punere în aplicare în [etichetă:r] lipsește.
Calcularea distanței între două puncte este destul de simplu cu distm
funcția de `geosferei pachetului:
distm(p1, p2, fun = distHaversine)
în cazul în care:
p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid
Ca pământul nu este perfect sferic, Vincenty formula pentru ellipsoids este, probabil, cel mai bun mod de a calcula distanțele. Astfel, în anii `geosferei pachetului utilizați atunci:
distm(p1, p2, fun = distVincentyEllipsoid)
Off desigur, nu't trebuie neapărat să folosească `geosferei pachet, puteți calcula, de asemenea, distanța în baza " R " cu o funcție:
hav.dist <- function(long1, lat1, long2, lat2) {
R <- 6371
diff.long <- (long2 - long1)
diff.lat <- (lat2 - lat1)
a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
b <- 2 * asin(pmin(1, sqrt(a)))
d = R * b
return(d)
}
La haversine este cu siguranta o buna formula pentru, probabil, cele mai multe cazuri, alte răspunsuri includ deja, așa că nu sunt de gând să ia spațiu. Dar este important să rețineți că, indiferent de formula în care este folosit (da, nu doar una). Din cauza gamă foarte mare de acuratețe posibil, precum și timpul de calcul necesar. Alegerea de formula necesită un pic mai mult decât un simplu brainer nu răspunde.
Această postare de la o persoană la nasa, este cel mai bun l-am găsit la discutarea opțiuni
http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
De exemplu, dacă sunteți doar de sortare rânduri de la distanță într-o 100 de mile. Pământul plat formula va fi mult mai rapid decât haversine.
HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/
a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;
Observați că nu există doar un singur cosinus și o rădăcină pătrată. Vs 9 dintre ele pe formula Haversine.
Eu nu't, cum ar fi adăugarea încă un răspuns, dar Google maps API v. 3 a geometriei sferice (și mai mult). După conversia WGS84 în grade zecimale puteți face acest lucru:
<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script>
distance = google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(fromLat, fromLng),
new google.maps.LatLng(toLat, toLng));
Nici un cuvânt despre cât de exacte Google's calculele sunt sau macar ce model este folosit (deși se spune "sferică" mai degrabă decât "geoid". Apropo, "linie dreaptă" distanta va fi, evident, diferite de la distanță dacă cineva se deplasează pe suprafața pământului, care este ceea ce toată lumea pare a fi îndrăzneț.
Puteți utiliza construi în CLLocationDistance pentru a calcula:
CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2];
[self distanceInMetersFromLocation:location1 toLocation:location2]
- (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 {
CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2];
return distanceInMeters;
}
În cazul tău, dacă vrei kilometri împărțiți de 1000.
Python implimentation Originea este centru de contiguă Statele Unite ale americii.
from haversine import haversine
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, miles=True)
Pentru a obține răspunsul în kilometri pur și simplu setați km=false.
Ar putea fi o soluție mai simplă, și mai corect: perimetrul de pământ este de 40.000 Km la ecuator, aproximativ 37.000 de pe Greenwich (sau orice longitudine) ciclu. Astfel:
pythagoras = function (lat1, lon1, lat2, lon2) {
function sqr(x) {return x * x;}
function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);}
var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0);
var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0;
var dy = 37000000.0 * (lat1 - lat2) / 360.0;
return Math.sqrt(sqr(dx) + sqr(dy));
};
Sunt de acord că ar trebui să fie bine reglate ca, eu însumi am spus că-l's un elipsoid, astfel încât raza să fie înmulțită cu cosinusul variază. Dar's un pic mai precis. Comparativ cu Google Maps și a făcut reduce semnificativ de eroare.
Toate raspunsurile de mai sus presupune pământul este o sferă. Cu toate acestea, o aproximare mai exactă ar fi aceea de un sferoid turtit.
a= 6378.137#equitorial radius in km
b= 6356.752#polar radius in km
def Distance(lat1, lons1, lat2, lons2):
lat1=math.radians(lat1)
lons1=math.radians(lons1)
R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1
x1=R*math.cos(lat1)*math.cos(lons1)
y1=R*math.cos(lat1)*math.sin(lons1)
z1=R*math.sin(lat1)
lat2=math.radians(lat2)
lons2=math.radians(lons2)
R1=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2
x2=R*math.cos(lat2)*math.cos(lons2)
y2=R*math.cos(lat2)*math.sin(lons2)
z2=R*math.sin(lat2)
return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5
După cum a subliniat, un calcul precis ar trebui să ia în considerare faptul că pământul nu este o sferă perfectă. Aici sunt unele comparații diferitelor algoritmi oferit aici:
geoDistance(50,5,58,3)
Haversine: 899 km
Maymenn: 833 km
Keerthana: 897 km
google.maps.geometry.spherical.computeDistanceBetween(): 900 km
geoDistance(50,5,-58,-3)
Haversine: 12030 km
Maymenn: 11135 km
Keerthana: 10310 km
google.maps.geometry.spherical.computeDistanceBetween(): 12044 km
geoDistance(.05,.005,.058,.003)
Haversine: 0.9169 km
Maymenn: 0.851723 km
Keerthana: 0.917964 km
google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km
geoDistance(.05,80,.058,80.3)
Haversine: 33.37 km
Maymenn: 33.34 km
Keerthana: 33.40767 km
google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km
Pe distante mici, Keerthana's algoritmul nu pare să coincidă cu cea a Google Maps. Google Maps nu pare să urmeze orice algoritm simplu, sugerând că aceasta poate fi metoda cea mai precisă de aici.
Oricum, aici este o punere în aplicare Javascript de Keerthana's algoritm:
function geoDistance(lat1, lng1, lat2, lng2){
const a = 6378.137; // equitorial radius in km
const b = 6356.752; // polar radius in km
var sq = x => (x*x);
var sqr = x => Math.sqrt(x);
var cos = x => Math.cos(x);
var sin = x => Math.sin(x);
var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat))));
lat1 = lat1 * Math.PI / 180;
lng1 = lng1 * Math.PI / 180;
lat2 = lat2 * Math.PI / 180;
lng2 = lng2 * Math.PI / 180;
var R1 = radius(lat1);
var x1 = R1*cos(lat1)*cos(lng1);
var y1 = R1*cos(lat1)*sin(lng1);
var z1 = R1*sin(lat1);
var R2 = radius(lat2);
var x2 = R2*cos(lat2)*cos(lng2);
var y2 = R2*cos(lat2)*sin(lng2);
var z2 = R2*sin(lat2);
return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2));
}
Aici este Implementarea SQL pentru a calcula distanța, în km,
SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) *
cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) *
sin( radians(latitude) ) ) ) AS distance FROM user HAVING
distance < 5 ORDER BY distance LIMIT 0 , 5;
Pentru detalii suplimentare în punerea în aplicare de programare limba, poti sa te duci doar prin script php dat aici
Aici este un mașina de scris punerea în aplicare a formula Haversine
static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
var deg2Rad = deg => {
return deg * Math.PI / 180;
}
var r = 6371; // Radius of the earth in km
var dLat = deg2Rad(lat2 - lat1);
var dLon = deg2Rad(lon2 - lon1);
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = r * c; // Distance in km
return d;
}
Acest script [PHP] calculează distanțele între două puncte.
public static function getDistanceOfTwoPoints($source, $dest, $unit='K') {
$lat1 = $source[0];
$lon1 = $source[1];
$lat2 = $dest[0];
$lon2 = $dest[1];
$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);
$miles = $dist * 60 * 1.1515;
$unit = strtoupper($unit);
if ($unit == "K") {
return ($miles * 1.609344);
}
else if ($unit == "M")
{
return ($miles * 1.609344 * 1000);
}
else if ($unit == "N") {
return ($miles * 0.8684);
}
else {
return $miles;
}
}
Pentru a calcula distanța dintre două puncte de pe sfera aveți nevoie pentru a face Cerc Mare de calcul.
Există o serie de C/C++ biblioteci pentru a ajuta cu proiecție de hartă la MapTools dacă aveți nevoie pentru a reproiecta distanțele de la o suprafață plană. Pentru a face acest lucru veți avea nevoie de proiecție șir de diverse sisteme de coordonate.
Puteți găsi, de asemenea, MapWindow un instrument util de a vizualiza puncte. De asemenea, ca open source sale un util ghid pentru cum să utilizați proj.dll biblioteca, care pare a fi nucleul open source proiecție bibliotecă.
Aici's a acceptat răspunsul punerea în aplicare adaptată pentru Java în cazul în care cineva are nevoie de ea.
package com.project529.garage.util;
/**
* Mean radius.
*/
private static double EARTH_RADIUS = 6371;
/**
* Returns the distance between two sets of latitudes and longitudes in meters.
* <p/>
* Based from the following JavaScript SO answer:
* http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula,
* which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%).
*/
public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) {
double dLat = toRadians(lat2 - lat1);
double dLon = toRadians(lon2 - lon1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = EARTH_RADIUS * c;
return d;
}
public double toRadians(double degrees) {
return degrees * (Math.PI / 180);
}