2点間の距離の計算(緯度、経度)


88

地図上の2つの位置間の距離を計算しようとしています。私はデータに保存しました:経度、緯度、X POS、Y POS。

以前は以下のスニペットを使用しています。

DECLARE @orig_lat DECIMAL
DECLARE @orig_lng DECIMAL
SET @orig_lat=53.381538 set @orig_lng=-1.463526
SELECT *,
    3956 * 2 * ASIN(
          SQRT( POWER(SIN((@orig_lat - abs(dest.Latitude)) * pi()/180 / 2), 2) 
              + COS(@orig_lng * pi()/180 ) * COS(abs(dest.Latitude) * pi()/180)  
              * POWER(SIN((@orig_lng - dest.Longitude) * pi()/180 / 2), 2) )) 
          AS distance
--INTO #includeDistances
FROM #orig dest

ただし、これからのデータは信用できません。わずかに不正確な結果が出ているようです。

必要な場合のサンプルデータ

Latitude        Longitude     Distance 
53.429108       -2.500953     85.2981833133896

誰かが私のコードを手伝ってくれませんか?これを達成するための新しい方法が素晴らしいとしたら、私がすでに持っているものを修正してもよろしいですか?

結果の測定単位を明記してください。


正弦への引数を追加の/ 2で除算しないでください。また、あなたが地球の半径でより精度を有するだけでなく、使用可能性が、いくつかのデータが(赤道で且つ極に異なる半径を有する)楕円体で地球を近似するGPSシステム(WGS-84)により、例えば使用
アキSuihkonen

@Waller、なぜGeography / Geometry(Spatial)タイプを使用してこれを達成しないのですか?
Habib 2012年

3
Mathematicaで計算を確認しました。それはあなたの計算ではないことを示唆している、法定マイルの距離(5280フィート)は42.997であると考えてやや不正確むしろそれは、乱暴に不正確
ハイパフォーマンスマーク

回答:


126

SQL Server 2008を使用しているので、geography使用できるデータ型があり、これはまさにこの種のデータ用に設計されています。

DECLARE @source geography = 'POINT(0 51.5)'
DECLARE @target geography = 'POINT(-3 56)'

SELECT @source.STDistance(@target)

与える

----------------------
538404.100197555

(1 row(s) affected)

ロンドン(近く)からエジンバラ(近く)までは約538 kmです。

当然、最初に行うべき学習の量がありますが、一度知ってしまえば、独自のHaversine計算を実装するよりもはるかに簡単です。さらに、多くの機能を利用できます。


既存のデータ構造を保持したい場合でも、メソッドを使用してSTDistance適切なgeographyインスタンスを作成することにより、を使用できますPoint

DECLARE @orig_lat DECIMAL(12, 9)
DECLARE @orig_lng DECIMAL(12, 9)
SET @orig_lat=53.381538 set @orig_lng=-1.463526

DECLARE @orig geography = geography::Point(@orig_lat, @orig_lng, 4326);

SELECT *,
    @orig.STDistance(geography::Point(dest.Latitude, dest.Longitude, 4326)) 
       AS distance
--INTO #includeDistances
FROM #orig dest

6
@nezamいいえ-経度は、子午線の西の場所では負、東の場所では正になります
AakashM 2014年

あなたは私の日を救った!..どうもありがとう!
Dhrumil Bhankhar、2015

1
組み込み関数の使用は非常に遅いようです。たとえば、100,000アイテムのループでは、ユーザー定義関数の場合、1.4秒ではなく23秒かかります(Duraiの回答を参照)。
NickG 2016

1
@AakashMの空間インデックスに関する推奨事項+ ...を確認したいだけで、ETLアプリケーションの場合、空間インデックスを実装した後の違いは数桁優れていました
Bill Anton

3
FYI:POINT(LONGITUDE LATITUDE)に対してgeography :: Point(LATITUDE、
LONGITUDE

41

以下の関数は、2つの地理座標間の距離をマイルで示します。

create function [dbo].[fnCalcDistanceMiles] (@Lat1 decimal(8,4), @Long1 decimal(8,4), @Lat2 decimal(8,4), @Long2 decimal(8,4))
returns decimal (8,4) as
begin
declare @d decimal(28,10)
-- Convert to radians
set @Lat1 = @Lat1 / 57.2958
set @Long1 = @Long1 / 57.2958
set @Lat2 = @Lat2 / 57.2958
set @Long2 = @Long2 / 57.2958
-- Calc distance
set @d = (Sin(@Lat1) * Sin(@Lat2)) + (Cos(@Lat1) * Cos(@Lat2) * Cos(@Long2 - @Long1))
-- Convert to miles
if @d <> 0
begin
set @d = 3958.75 * Atan(Sqrt(1 - power(@d, 2)) / @d);
end
return @d
end 

以下の関数は、2つの地理座標間の距離をキロメートル単位で示します。

CREATE FUNCTION dbo.fnCalcDistanceKM(@lat1 FLOAT, @lat2 FLOAT, @lon1 FLOAT, @lon2 FLOAT)
RETURNS FLOAT 
AS
BEGIN

    RETURN ACOS(SIN(PI()*@lat1/180.0)*SIN(PI()*@lat2/180.0)+COS(PI()*@lat1/180.0)*COS(PI()*@lat2/180.0)*COS(PI()*@lon2/180.0-PI()*@lon1/180.0))*6371
END

以下の関数は、SQL Server 2008で導入された地理データタイプを 使用して、2つの地理座標間の距離をキロメートル単位で示します。

DECLARE @g geography;
DECLARE @h geography;
SET @g = geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656)', 4326);
SET @h = geography::STGeomFromText('POINT(-122.34900 47.65100)', 4326);
SELECT @g.STDistance(@h);

使用法:

select [dbo].[fnCalcDistanceKM](13.077085,80.262675,13.065701,80.258916)

参照: Ref1Ref2


2
郵便番号までの距離で並べ替えられたさまざまなイベントの郵便番号に対して35K郵便番号の距離計算を行う必要がありました。地理データタイプを使用して計算するには、座標のリストが大きすぎます。上記の単一行のトリガー関数ベースのソリューションを使用するように切り替えたところ、実行速度が大幅に向上しました。したがって、地理タイプを使用して距離を計算するだけではコストがかかるようです。バイヤーは注意してください。
Tombala

クエリのWHERE句で距離を計算するのに非常に役立ちます。「set @ d =」式の周囲にABS()をラップする必要がありました。これは、関数が負の距離を返す場合があったためです。
Joe Irby

1
2つの等しい点を比較すると、「キロメートル単位の2つの地理座標間の距離」の関数が失敗し、エラー「無効な浮動小数点演算が発生しました」
RRM

2
これはすばらしいことですが、「decimal(8,4)」では十分な精度が得られないため、短距離では機能しません。
影響力のある

1
@influentは正しい、これは短距離(私の場合は5マイル)には役立ちません
Roger

15

マイクロソフトが他のすべての回答者の頭脳に侵入し、それらに可能な限り複雑なソリューションを記述させたようです。追加の関数/宣言ステートメントなしの最も簡単な方法は次のとおりです。

SELECT geography::Point(LATITUDE_1, LONGITUDE_1, 4326).STDistance(geography::Point(LATITUDE_2, LONGITUDE_2, 4326))

単にあなたのデータを置き換える代わりにLATITUDE_1LONGITUDE_1LATITUDE_2LONGITUDE_2例えば:

SELECT geography::Point(53.429108, -2.500953, 4326).STDistance(geography::Point(c.Latitude, c.Longitude, 4326))
from coordinates c

1
参考までに:STDistance()は、地理データが定義されている空間参照系の線形測定単位で距離を返します。SRID 4326を使用しています。つまり、STDistance()はメートル単位で距離を返します。
ブライアンスタンプ

4

SQL 2008以降を使用しているので、GEOGRAPHYデータ型をチェックアウトすることをお勧めします。SQLには、地理空間クエリのサポートが組み込まれています。

たとえば、座標の地理空間表現が入力されるGEOGRAPHYタイプの列がテーブルにあるとします(例については、上記のMSDNリファレンスを参照してください)。このデータ型は、地理空間クエリのホスト全体を実行できるメソッドを公開します(たとえば、2点間の距離を見つける)。


追加するために、geographyフィールドタイプを試しましたが、Duraiの関数(経度と緯度の値を直接使用)を使用し方がはるかに高速であることがわかりました。こちらの私の例をご覧ください: stackoverflow.com/a/37326089/391605
Mike Gledhill

4
Create Function [dbo].[DistanceKM] 
( 
      @Lat1 Float(18),  
      @Lat2 Float(18), 
      @Long1 Float(18), 
      @Long2 Float(18)
)
Returns Float(18)
AS
Begin
      Declare @R Float(8); 
      Declare @dLat Float(18); 
      Declare @dLon Float(18); 
      Declare @a Float(18); 
      Declare @c Float(18); 
      Declare @d Float(18);
      Set @R =  6367.45
            --Miles 3956.55  
            --Kilometers 6367.45 
            --Feet 20890584 
            --Meters 6367450 


      Set @dLat = Radians(@lat2 - @lat1);
      Set @dLon = Radians(@long2 - @long1);
      Set @a = Sin(@dLat / 2)  
                 * Sin(@dLat / 2)  
                 + Cos(Radians(@lat1)) 
                 * Cos(Radians(@lat2))  
                 * Sin(@dLon / 2)  
                 * Sin(@dLon / 2); 
      Set @c = 2 * Asin(Min(Sqrt(@a))); 

      Set @d = @R * @c; 
      Return @d; 

End
GO

使用法:

dbo.DistanceKM(37.848832506474、37.848732506474、27.83935546875、27.83905546875)を選択します

出力:

0,02849639

コメント付きフロートを使用して@Rパラメータを変更できます。


完全に機能します
Tejasvi Hegde

1

前の回答に加えて、SELECT内の距離を計算する方法を次に示します。

CREATE FUNCTION Get_Distance
(   
    @La1 float , @Lo1 float , @La2 float, @Lo2 float
)
RETURNS TABLE 
AS
RETURN 
    -- Distance in Meters
    SELECT GEOGRAPHY::Point(@La1, @Lo1, 4326).STDistance(GEOGRAPHY::Point(@La2, @Lo2, 4326))
    AS Distance
GO

使用法:

select Distance
from Place P1,
     Place P2,
outer apply dbo.Get_Distance(P1.latitude, P1.longitude, P2.latitude, P2.longitude)

スカラー関数も機能しますが、大量のデータを計算する場合は非常に非効率的です。

これが誰かの役に立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.