Làm cách nào để tính khoảng cách giữa hai điểm được chỉ định bởi vĩ độ và kinh độ?
Để làm rõ, tôi muốn khoảng cách bằng km; các điểm sử dụng hệ thống WGS84 và tôi muốn hiểu tính chính xác tương đối của các phương pháp tiếp cận có sẵn.
Làm cách nào để tính khoảng cách giữa hai điểm được chỉ định bởi vĩ độ và kinh độ?
Để làm rõ, tôi muốn khoảng cách bằng km; các điểm sử dụng hệ thống WGS84 và tôi muốn hiểu tính chính xác tương đối của các phương pháp tiếp cận có sẵn.
Câu trả lời:
Liên kết này có thể hữu ích cho bạn, vì nó chi tiết việc sử dụng công thức Haversine để tính khoảng cách.
Trích đoạn:
Tập lệnh này [trong Javascript] tính toán khoảng cách vòng tròn lớn giữa hai điểm - nghĩa là khoảng cách ngắn nhất trên bề mặt trái đất - sử dụng công thức 'Haversine'.
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)
}
Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
thay vì Math.asin(Math.sqrt(h))
, đó sẽ là việc thực hiện trực tiếp công thức mà bài viết Wikipedia sử dụng? Có hiệu quả hơn và / hoặc ổn định hơn về số lượng?
(sin(x))²
bằng(sin(-x))²
Tôi cần tính toán rất nhiều khoảng cách giữa các điểm cho dự án của mình, vì vậy tôi đã tiếp tục và cố gắng tối ưu hóa mã, tôi đã tìm thấy ở đây. Trung bình trong các trình duyệt khác nhau, triển khai mới của tôi chạy nhanh hơn 2 lần so với câu trả lời được đánh giá cao nhất.
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
}
Bạn có thể chơi với jsPerf của tôi và xem kết quả tại đây .
Gần đây tôi cần phải làm tương tự trong python, vì vậy đây là một triển khai python :
from math import cos, asin, sqrt, pi
def distance(lat1, lon1, lat2, lon2):
p = 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...
Và vì lợi ích của sự hoàn chỉnh: Haversine trên wiki.
// 2 * R; R = 6371 km
đại diện cho? và các phương pháp hiện tại cung cấp câu trả lời trong km hoặc dặm? cần tài liệu tốt hơn. Cảm ơn
Đây là một triển khai C #:
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;
}
}
double dlon = Radians(lon2 - lon1);
vàdouble dlat = Radians(lat2 - lat1);
RADIUS
nhu cầu giá trị là 6371 như trong câu trả lời khác?
Đây là một triển khai java của công thức 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));
}
Lưu ý rằng ở đây chúng tôi đang làm tròn câu trả lời cho km gần nhất.
6371000
như bán kính trái đất? (trung bình bán kính trái đất là 6371000 mét) hoặc chuyển đổi từ km sang mét từ chức năng của bạn?
0.621371
Cảm ơn rất nhiều cho tất cả điều này. Tôi đã sử dụng mã sau đây trong ứng dụng iPhone Objective-C của mình:
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;
}
Vĩ độ và Kinh độ là số thập phân. Tôi đã không sử dụng min () cho cuộc gọi asin () vì khoảng cách mà tôi đang sử dụng quá nhỏ đến mức họ không yêu cầu.
Nó đã đưa ra câu trả lời không chính xác cho đến khi tôi chuyển các giá trị bằng Radian - bây giờ nó khá giống với các giá trị thu được từ ứng dụng Bản đồ của Apple :-)
Cập nhật thêm:
Nếu bạn đang sử dụng iOS4 trở lên thì Apple cung cấp một số phương pháp để thực hiện việc này để có thể đạt được chức năng tương tự với:
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {
MKMapPoint start, finish;
start = MKMapPointForCoordinate(place1);
finish = MKMapPointForCoordinate(place2);
return MKMetersBetweenMapPoints(start, finish) / 1000;
}
()
khoảng đó, tôi nhận được 3869,75. Không có chúng, tôi nhận được 3935,75, gần như những gì một tìm kiếm trên web xuất hiện.
Đây là một hàm PHP đơn giản sẽ đưa ra một xấp xỉ rất hợp lý (dưới tỷ lệ lỗi +/- 1%).
<?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;
}
?>
Như đã nói trước; trái đất KHÔNG phải là một hình cầu. Nó giống như một quả bóng chày cũ, cũ mà Mark McGwire đã quyết định tập luyện cùng - nó đầy vết lõm và va đập. Các tính toán đơn giản hơn (như thế này) coi nó như một quả cầu.
Các phương pháp khác nhau có thể chính xác hơn hoặc ít hơn tùy theo vị trí của bạn trên hình bầu dục không đều này VÀ điểm của bạn cách nhau bao xa (càng gần thì biên độ sai số tuyệt đối càng nhỏ). Kỳ vọng của bạn càng chính xác, toán học càng phức tạp.
Để biết thêm thông tin: khoảng cách địa lý wikipedia
Tôi đăng ở đây ví dụ làm việc của tôi.
Liệt kê tất cả các điểm trong bảng có khoảng cách giữa một điểm được chỉ định (chúng tôi sử dụng một điểm ngẫu nhiên - lat: 45.20327, dài: 23.7806) dưới 50 KM, với vĩ độ và kinh độ, trong MySQL (các trường của bảng là tọa độ_lat và tọa độ_long):
Liệt kê tất cả có DISTANCE <50, tính bằng Kilômét (được coi là bán kính Trái đất 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
Ví dụ trên đã được thử nghiệm trong MySQL 5.0.95 và 5.5.16 (Linux).
Trong các câu trả lời khác, việc thực hiện trong r mất tích
Tính khoảng cách giữa hai điểm khá đơn giản với distm
hàm từ geosphere
gói:
distm(p1, p2, fun = distHaversine)
Ở đâu:
p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid
Vì trái đất không phải là hình cầu hoàn hảo, công thức Vincenty cho ellipsoids có lẽ là cách tốt nhất để tính khoảng cách. Vì vậy, trong geosphere
gói bạn sử dụng sau đó:
distm(p1, p2, fun = distVincentyEllipsoid)
Dĩ nhiên bạn không nhất thiết phải sử dụng geosphere
gói, bạn cũng có thể tính khoảng cách trong cơ sở R
với một hàm:
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)
}
Haversine chắc chắn là một công thức tốt cho hầu hết các trường hợp, các câu trả lời khác đã bao gồm nó vì vậy tôi sẽ không lấy chỗ trống. Nhưng điều quan trọng cần lưu ý là bất kể công thức nào được sử dụng (có, không chỉ một). Bởi vì phạm vi chính xác rất lớn có thể cũng như thời gian tính toán cần thiết. Sự lựa chọn công thức đòi hỏi một chút suy nghĩ hơn là một câu trả lời đơn giản không có trí tuệ.
Bài đăng này từ một người ở nasa, là bài tốt nhất tôi tìm thấy khi thảo luận về các lựa chọn
http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
Ví dụ, nếu bạn chỉ là sắp xếp hàng theo khoảng cách trong bán kính 100 dặm. Công thức trái đất phẳng sẽ nhanh hơn nhiều so với 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;
Lưu ý rằng chỉ có một cosin và một căn bậc hai. Vs 9 trong số họ trên công thức Haversine.
Bạn có thể sử dụng bản dựng trong CLLocationDistance để tính toán điều này:
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;
}
Trong trường hợp của bạn nếu bạn muốn số km chỉ cần chia cho 1000.
Tôi không muốn thêm một câu trả lời nữa, nhưng Google maps API v.3 có hình dạng hình cầu (và hơn thế nữa). Sau khi chuyển đổi WGS84 của bạn thành độ thập phân, bạn có thể làm điều này:
<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));
Không có thông tin nào về cách tính toán chính xác của Google hoặc thậm chí mô hình nào được sử dụng (mặc dù nó nói "hình cầu" thay vì "Geoid". Nhân tiện, khoảng cách "đường thẳng" rõ ràng sẽ khác với khoảng cách nếu một người đi trên bề mặt trái đất là thứ mà mọi người dường như đang đoán trước.
Python implimentation Origin là trung tâm của Hoa Kỳ tiếp giáp.
from haversine import haversine
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, miles=True)
Để có được câu trả lời bằng km chỉ cần đặt dặm = false.
Có thể có một giải pháp đơn giản hơn và chính xác hơn: Chu vi của trái đất là 40.000Km tại xích đạo, khoảng 37.000 trên chu kỳ Greenwich (hoặc bất kỳ kinh độ) nào. Như vậy:
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));
};
Tôi đồng ý rằng nó nên được tinh chỉnh vì, bản thân tôi đã nói rằng đó là một hình elip, do đó bán kính được nhân với cosin thay đổi. Nhưng nó chính xác hơn một chút. So với Google Maps và nó đã giảm lỗi đáng kể.
Tất cả các câu trả lời trên giả định trái đất là một hình cầu. Tuy nhiên, một xấp xỉ chính xác hơn sẽ là một hình cầu bắt buộc.
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
Đây là triển khai SQL để tính khoảng cách tính bằng 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;
Để biết thêm chi tiết trong việc triển khai bằng cách lập trình langugage, bạn chỉ cần xem qua tập lệnh php được cung cấp ở đây
Đây là một bản thực hiện của công thức 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;
}
Như đã chỉ ra, một tính toán chính xác cần tính đến việc trái đất không phải là một hình cầu hoàn hảo. Dưới đây là một số so sánh của các thuật toán khác nhau được cung cấp ở đây:
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
Trong khoảng cách nhỏ, thuật toán của Keerthana dường như trùng khớp với thuật toán của Google Maps. Google Maps dường như không tuân theo bất kỳ thuật toán đơn giản nào, cho thấy rằng nó có thể là phương pháp chính xác nhất ở đây.
Dù sao, đây là một triển khai Javascript của thuật toán Keerthana:
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));
}
Kịch bản lệnh này [trong PHP] tính toán khoảng cách giữa hai điểm.
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;
}
}
Thực hiện Java theo công thức Haversine
double calculateDistance(double latPoint1, double lngPoint1,
double latPoint2, double lngPoint2) {
if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) {
return 0d;
}
final double EARTH_RADIUS = 6371.0; //km value;
//converting to radians
latPoint1 = Math.toRadians(latPoint1);
lngPoint1 = Math.toRadians(lngPoint1);
latPoint2 = Math.toRadians(latPoint2);
lngPoint2 = Math.toRadians(lngPoint2);
double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2)
+ Math.cos(latPoint1) * Math.cos(latPoint2)
* Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2);
distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance));
return distance; //km value
}
Để tính khoảng cách giữa hai điểm trên một quả cầu, bạn cần thực hiện phép tính Great Circle .
Có một số thư viện C / C ++ để trợ giúp trình chiếu bản đồ tại MapTools nếu bạn cần điều chỉnh lại khoảng cách của mình đến một bề mặt phẳng. Để làm điều này, bạn sẽ cần chuỗi chiếu của các hệ tọa độ khác nhau.
Bạn cũng có thể tìm thấy MapWindow một công cụ hữu ích để trực quan hóa các điểm. Ngoài ra, nguồn mở của nó là một hướng dẫn hữu ích về cách sử dụng thư viện proj.dll, dường như là thư viện chiếu mã nguồn mở lõi.
Đây là cách thực hiện câu trả lời được chấp nhận chuyển sang Java trong trường hợp có ai cần nó.
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);
}
Dưới đây là VB.NET triển khai, việc triển khai này sẽ cho bạn kết quả bằng KM hoặc Miles dựa trên giá trị Enum mà bạn vượt qua.
Public Enum DistanceType
Miles
KiloMeters
End Enum
Public Structure Position
Public Latitude As Double
Public Longitude As Double
End Structure
Public Class Haversine
Public Function Distance(Pos1 As Position,
Pos2 As Position,
DistType As DistanceType) As Double
Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371)
Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude)
Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude)
Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2)
Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)))
Dim result As Double = R * c
Return result
End Function
Private Function toRadian(val As Double) As Double
Return (Math.PI / 180) * val
End Function
End Class
Tôi cô đọng tính toán xuống bằng cách đơn giản hóa công thức.
Đây là Ruby.
include Math
earth_radius_mi = 3959
radians = lambda { |deg| deg * PI / 180 }
coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } }
# from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) }
def haversine_distance(from, to)
from, to = coord_radians[from], coord_radians[to]
cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng])
sines_product = sin(to[:lat]) * sin(from[:lat])
return earth_radius_mi * acos(cosines_product + sines_product)
end
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) {
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;
var miles = d / 1.609344;
if ( units == 'km' ) {
return d;
} else {
return miles;
}}
giải pháp của Chuck, hợp lệ hàng dặm cũng có.
Đây là triển khai java của tôi cho khoảng cách tính toán thông qua độ thập phân sau một số tìm kiếm. Tôi đã sử dụng bán kính trung bình của thế giới (từ wikipedia) tính bằng km. Nếu bạn muốn kết quả dặm sau đó sử dụng bán kính trên thế giới trong dặm.
public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2)
{
double earthRadius = 6371.0d; // KM: use mile here if you want mile result
double dLat = toRadian(lat2 - lat1);
double dLng = toRadian(lng2 - lng1);
double a = Math.pow(Math.sin(dLat/2), 2) +
Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) *
Math.pow(Math.sin(dLng/2), 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return earthRadius * c; // returns result kilometers
}
public static double toRadian(double degrees)
{
return (degrees * Math.PI) / 180.0d;
}
Trong Mysql sử dụng hàm sau truyền các tham số như sử dụng POINT(LONG,LAT)
CREATE FUNCTION `distance`(a POINT, b POINT)
RETURNS double
DETERMINISTIC
BEGIN
RETURN
GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters
END;
function getDistanceFromLatLonInKm(position1, position2) {
"use strict";
var deg2rad = function (deg) { return deg * (Math.PI / 180); },
R = 6371,
dLat = deg2rad(position2.lat - position1.lat),
dLng = deg2rad(position2.lng - position1.lng),
a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(deg2rad(position1.lat))
* Math.cos(deg2rad(position1.lat))
* Math.sin(dLng / 2) * Math.sin(dLng / 2),
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
console.log(getDistanceFromLatLonInKm(
{lat: 48.7931459, lng: 1.9483572},
{lat: 48.827167, lng: 2.2459745}
));
đây là một ví dụ trong postgres sql (ở km, cho phiên bản dặm, thay thế 1,609344 bởi phiên bản 0,8684)
CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat
float, blng float)
RETURNS float AS
$BODY$
DECLARE
v_distance float;
BEGIN
v_distance = asin( sqrt(
sin(radians(blat-alat)/2)^2
+ (
(sin(radians(blng-alng)/2)^2) *
cos(radians(alat)) *
cos(radians(blat))
)
)
) * cast('7926.3352' as float) * cast('1.609344' as float) ;
RETURN v_distance;
END
$BODY$
language plpgsql VOLATILE SECURITY DEFINER;
alter function geodistance(alat float, alng float, blat float, blng float)
owner to postgres;
Đây là một mã khác được chuyển đổi thành mã Ruby :
include Math
#Note: from/to = [lat, long]
def get_distance_in_km(from, to)
radians = lambda { |deg| deg * Math.PI / 180 }
radius = 6371 # Radius of the earth in kilometer
dLat = radians[to[0]-from[0]]
dLon = radians[to[1]-from[1]]
cosines_product = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(radians[from[0]]) * Math.cos(radians[to[1]]) * Math.sin(dLon/2) * Math.sin(dLon/2)
c = 2 * Math.atan2(Math.sqrt(cosines_product), Math.sqrt(1-cosines_product))
return radius * c # Distance in kilometer
end
Có một ví dụ điển hình ở đây để tính khoảng cách với PHP http://www.geodatasource.com/developers/php :
function distance($lat1, $lon1, $lat2, $lon2, $unit) {
$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 == "N") {
return ($miles * 0.8684);
} else {
return $miles;
}
}