การคำนวณระยะทางระหว่างสองจุด (ละติจูดลองจิจูด)


89

ฉันกำลังพยายามคำนวณระยะห่างระหว่างสองตำแหน่งบนแผนที่ ฉันเก็บไว้ในข้อมูลของฉัน: Longitude, Latitude, 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 เพิ่มเติม นอกจากนี้คุณยังสามารถมีความแม่นยำมากขึ้นในรัศมีโลกเช่นเดียวกับการใช้Datumบางอย่างที่ใช้เช่นระบบ GPS (WGS-84) ที่ประมาณโลกด้วยวงรี (โดยมีรัศมีต่างกันที่เส้นศูนย์สูตรและถึงขั้ว)
Aki Suihkonen

@ ช่างภาพทำไมคุณไม่ใช้ประเภทภูมิศาสตร์ / เรขาคณิต (เชิงพื้นที่) เพื่อให้บรรลุสิ่งนี้
Habib

3
ฉันตรวจสอบการคำนวณของคุณด้วย Mathematica คิดว่าระยะทางเป็นไมล์ตามกฎหมาย (5280 ฟุต) คือ 42.997 ซึ่งแสดงให้เห็นว่าการคำนวณของคุณไม่แม่นยำเล็กน้อยแต่มันไม่แม่นยำอย่างมาก
High Performance Mark

คำตอบ:


130

เนื่องจากคุณใช้ 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 กม.

โดยปกติแล้วจะมีการเรียนรู้จำนวนหนึ่งที่ต้องทำก่อน แต่เมื่อคุณรู้แล้วมันง่ายกว่าการคำนวณ 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 no - ลองจิจูดจะเป็นลบสำหรับตำแหน่งทางตะวันตกของPrime Meridianและเป็นบวกสำหรับสถานที่ทางตะวันออกของมัน
AakashM

คุณช่วยวันของฉัน! .. ขอบคุณมาก!
Dhrumil Bhankhar

1
การใช้ฟังก์ชันในตัวดูเหมือนจะช้ามาก เช่นในการวนซ้ำ 100,000 รายการใช้เวลา 23 วินาทีแทนที่จะเป็น 1.4 วินาทีสำหรับฟังก์ชันที่ผู้ใช้กำหนด (ดูคำตอบของ Durai)
NickG

1
เพียงแค่ต้องการที่จะตีระฆังและยืนยันคำแนะนำของ @AakashM สำหรับดัชนีเชิงพื้นที่ + ... สำหรับแอปพลิเคชัน ETL ความแตกต่างคือคำสั่งขนาดต่างๆที่ดีขึ้นหลังจากใช้ดัชนีเชิงพื้นที่
Bill Anton

3
FYI: POINT (LONGITUDE LATITUDE) ในขณะที่ภูมิศาสตร์ :: Point (LATITUDE, LONGITUDE, 4326)
Mzn

42

ฟังก์ชันด้านล่างให้ระยะห่างระหว่างพิกัดภูมิศาสตร์สองหน่วยเป็นไมล์

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 

ฟังก์ชันด้านล่างให้ระยะห่างระหว่างพิกัดภูมิศาสตร์สองตำแหน่งเป็นกิโลเมตร

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 2008

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)

อ้างอิง: Ref1 , Ref2


2
ฉันต้องการคำนวณระยะทางสำหรับรหัสไปรษณีย์ 35K เทียบกับรหัสไปรษณีย์ของเหตุการณ์ต่างๆที่เรียงลำดับตามระยะทางไปยังรหัสไปรษณีย์ รายการพิกัดมีขนาดใหญ่เกินไปที่จะทำการคำนวณโดยใช้ชนิดข้อมูลภูมิศาสตร์ เมื่อฉันเปลี่ยนไปใช้โซลูชันที่ใช้ฟังก์ชัน Trig แบบบรรทัดเดียวข้างต้นมันทำงานได้เร็วขึ้นมาก ดังนั้นการใช้ประเภทภูมิศาสตร์เพียงเพื่อคำนวณระยะทางดูเหมือนจะมีราคาแพง ผู้ซื้อระวัง
Tombala

มีประโยชน์มากสำหรับการคำนวณระยะทางในส่วนคำสั่ง WHERE ของแบบสอบถาม ฉันต้องพัน ABS () รอบ ๆ นิพจน์ "set @ d =" เพราะฉันพบบางกรณีที่ฟังก์ชันส่งกลับระยะเป็นลบ
Joe Irby

1
ฟังก์ชันสำหรับ "ระยะห่างระหว่างพิกัดภูมิศาสตร์สองตำแหน่งเป็นกิโลเมตร" ล้มเหลวหากเราเปรียบเทียบ 2 จุดที่เท่ากันจะทำให้คุณมีข้อผิดพลาด "เกิดการทำงานของจุดลอยตัวที่ไม่ถูกต้อง"
RRM

2
วิธีนี้ดีมาก แต่ใช้ไม่ได้ในระยะทางสั้น ๆ เนื่องจาก "ทศนิยม (8,4)" ไม่ได้ให้ความแม่นยำเพียงพอ
มีอิทธิพล

1
@influent ถูกต้องสิ่งนี้ไม่มีประโยชน์สำหรับระยะทางสั้น ๆ (5 ไมล์ในกรณีของฉัน)
Roger

16

ดูเหมือนว่า Microsoft จะบุกรุกสมองของผู้ตอบแบบสอบถามรายอื่น ๆ ทั้งหมดและทำให้พวกเขาเขียนวิธีแก้ปัญหาที่ซับซ้อนให้มากที่สุด นี่เป็นวิธีที่ง่ายที่สุดโดยไม่ต้องใช้ฟังก์ชันเพิ่มเติม / ประกาศคำสั่ง:

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

เพียงแค่ใช้แทนข้อมูลของคุณแทนLATITUDE_1, LONGITUDE_1, LATITUDE_2, LONGITUDE_2เช่น:

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

2
สำหรับการอ้างอิง: STDistance () ส่งกลับระยะทางในหน่วยเชิงเส้นของการวัดของระบบอ้างอิงเชิงพื้นที่ซึ่งข้อมูลภูมิศาสตร์ของคุณถูกกำหนดไว้ คุณกำลังใช้ SRID 4326 ซึ่งหมายความว่า STDistance () ส่งกลับระยะทางเป็นเมตร
Bryan Stump

5
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 ระยะทาง KM (37.848832506474, 37.848732506474, 27.83935546875, 27.83905546875)

ผลลัพธ์:

0,02849639

คุณสามารถเปลี่ยนพารามิเตอร์ @R ด้วยการแสดงความคิดเห็น


ทำงานได้อย่างสมบูรณ์
Tejasvi Hegde

4

ในขณะที่คุณใช้ SQL 2008 หรือใหม่กว่าขอแนะนำให้ตรวจสอบGEOGRAPHYประเภทข้อมูลSQL ได้สร้างขึ้นเพื่อรองรับการสืบค้นข้อมูลเชิงพื้นที่

เช่นคุณมีคอลัมน์ในตารางประเภท GEOGRAPHY ซึ่งจะถูกเติมด้วยการแสดงพิกัดเชิงพื้นที่ (ดูตัวอย่างการอ้างอิง MSDN ที่ลิงก์ด้านบน) จากนั้นประเภทข้อมูลนี้จะแสดงวิธีการที่ช่วยให้คุณสามารถดำเนินการแบบสอบถามเชิงพื้นที่ทั้งหมดได้ (เช่นการหาระยะห่างระหว่าง 2 จุด)


เพียงเพื่อเพิ่มผมพยายามภูมิศาสตร์ชนิดของฟิลด์ แต่ก็พบว่าการใช้ฟังก์ชั่นของ Durai (โดยตรงโดยใช้เส้นแวงและค่ารุ้ง) ที่จะมากได้เร็วขึ้น ดูตัวอย่างของฉันที่นี่: stackoverflow.com/a/37326089/391605
Mike Gledhill

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.