緯度と経度で指定された 2 点間の距離を計算するにはどうすればよいですか?
明確にするために、距離をキロメートルでお願いします。ポイントは WGS84 システムを使用しており、利用可能なアプローチの相対的な精度を理解したいと思います。
緯度と経度で指定された 2 点間の距離を計算するにはどうすればよいですか?
明確にするために、距離をキロメートルでお願いします。ポイントは WGS84 システムを使用しており、利用可能なアプローチの相対的な精度を理解したいと思います。
このリンクは、距離を計算するためのHaversine式の使用について詳しく説明しているため、役立つ場合があります。
抜粋:
このスクリプト[Javascript]は、「Haversine」式を使用して、2点間の大円距離(つまり、地表上の最短距離)を計算します。
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)
}
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;
}
}
以下は、Haversine 式の Java 実装です。
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));
}
ここでは、答えを最も近い km に丸めていることに注意してください。
これまでありがとうございました。Objective-C の iPhone アプリで次のコードを使用しました。
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;
}
緯度と経度は 10 進数です。私が使用している距離が非常に小さいため、asin() 呼び出しには min() を使用しませんでした。
ラジアンで値を渡すまで、間違った答えが返されました-今では、Appleのマップアプリから取得した値とほとんど同じです:-)
余分な更新:
iOS4 以降を使用している場合、Apple はこれを行うためのいくつかの方法を提供しているため、次の方法で同じ機能を実現できます。
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {
MKMapPoint start, finish;
start = MKMapPointForCoordinate(place1);
finish = MKMapPointForCoordinate(place2);
return MKMetersBetweenMapPoints(start, finish) / 1000;
}
これは単純な PHP 関数であり、非常に妥当な概算 (誤差範囲 +/-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;
}
?>
前に言ったように; 地球は球体ではありません。それはマーク・マグワイアが練習することに決めた昔の古い野球のようなものです - それはへこみと隆起でいっぱいです. より単純な計算 (このような) では、球体のように扱われます。
この不規則な卵形のどこにいるか、およびポイントがどれだけ離れているかに応じて、さまざまな方法が多かれ少なかれ正確になる場合があります (それらが近ければ近いほど、絶対誤差マージンは小さくなります)。期待値が正確であるほど、計算は複雑になります。
詳細情報:ウィキペディアの地理的距離
ここに私の作業例を投稿します。
指定されたポイント (ランダムなポイントを使用 - 緯度: 45.20327、経度: 23.7806) 間の距離が 50 KM 未満のテーブル内のすべてのポイントを、MySQL で緯度と経度とともに一覧表示します (テーブル フィールドは coord_lat と coord_long です)。
DISTANCE<50 を持つすべてをキロメートルでリストします (地球半径 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
上記の例は、MySQL 5.0.95 および 5.5.16 (Linux) でテストされました。
他の回答では、 rの実装がありません。
パッケージのdistm
関数を使用すると、2 点間の距離を簡単に計算できます。geosphere
distm(p1, p2, fun = distHaversine)
どこ:
p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid
地球は完全な球形ではないため、楕円体の Vincenty 式がおそらく距離を計算する最良の方法です。したがって、geosphere
使用するパッケージでは次のようになります。
distm(p1, p2, fun = distVincentyEllipsoid)
もちろん、必ずしもパッケージを使用する必要はありません。関数を使用geosphere
してベースの距離を計算することもできます。R
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)
}
pip install haversine
Python 実装
出身地は、本土アメリカ合衆国の中心です。
from haversine import haversine, Unit
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, unit=Unit.MILES)
答えをキロメートル単位で取得するには、設定するだけですunit=Unit.KILOMETERS
(これがデフォルトです)。
上記のすべての答えは、地球が球体であることを前提としています。ただし、より正確な近似は、扁平回転楕円体の近似になります。
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
さらに別の回答を追加するのは好きではありませんが、Google マップ API v.3 には球状のジオメトリ (およびその他) があります。WGS84 を 10 進度に変換した後、次の操作を実行できます。
<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));
Google の計算がどれほど正確であるか、またどのモデルが使用されているかについては何も言われていません (ただし、「ジオイド」ではなく「球状」と書かれています。ちなみに、「直線」距離は、人が地球上を移動する場合の距離とは明らかに異なります。誰もが推測しているように見える地球の表面。
これは、Haversine 式のtypescript実装です。
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;
}
これは、距離を km で計算するための SQL 実装です。
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;
プログラミング言語による実装の詳細については、ここに記載されている php スクリプトを参照してください。
球上の2点間の距離を計算するには、大円の計算を行う必要があります。
平面への距離を再投影する必要がある場合に、 MapToolsでの地図投影に役立つC /C++ライブラリがいくつかあります。これを行うには、さまざまな座標系の投影文字列が必要になります。
MapWindowは、ポイントを視覚化するための便利なツールでもあります。また、そのオープンソースとして、コアオープンソースプロジェクションライブラリのように見えるproj.dllライブラリの使用方法に関する便利なガイドです。
これはpostgres sqlの例です(km単位、マイルバージョンの場合、1.609344を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;
誰かがそれを必要とする場合に備えて、Javaに移植された受け入れられた回答の実装を次に示します。
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);
}
Rベースパッケージで利用可能な関数を使用して、2つの空間ポイント間のhaversine distance(km)を計算するRでカスタム関数を作成しました。
custom_hav_dist <- function(lat1, lon1, lat2, lon2) {
R <- 6371
Radian_factor <- 0.0174533
lat_1 <- (90-lat1)*Radian_factor
lat_2 <- (90-lat2)*Radian_factor
diff_long <-(lon1-lon2)*Radian_factor
distance_in_km <- 6371*acos((cos(lat_1)*cos(lat_2))+
(sin(lat_1)*sin(lat_2)*cos(diff_long)))
rm(lat1, lon1, lat2, lon2)
return(distance_in_km)
}
サンプル出力
custom_hav_dist(50.31,19.08,54.14,19.39)
[1] 426.3987
PS: 距離をマイルで計算するには、関数 (6371) の R を 3958.756 に置き換えます (海里の場合は 3440.065 を使用します)。
Mysqlでは、次の関数を使用してパラメーターを次のように渡します 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;
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;
}
}
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(position2.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}
));
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;
}}
マイルにも有効なチャックのソリューション。
//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;
}