Tính khoảng cách giữa hai điểm (Vĩ độ, Kinh độ)


89

Tôi đang cố gắng tính toán khoảng cách giữa hai vị trí trên bản đồ. Tôi đã lưu trữ trong dữ liệu của mình: Kinh độ, Vĩ độ, X POS, Y POS.

Trước đây tôi đã sử dụng đoạn mã dưới đây.

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

Tuy nhiên, tôi không tin tưởng dữ liệu đến từ điều này, nó có vẻ như đưa ra kết quả hơi không chính xác.

Một số dữ liệu mẫu trong trường hợp bạn cần

Latitude        Longitude     Distance 
53.429108       -2.500953     85.2981833133896

Ai có thể giúp tôi với mã của tôi, tôi không phiền nếu bạn muốn sửa chữa những gì tôi đã có nếu bạn có một cách mới để đạt được điều này, điều này sẽ rất tuyệt.

Vui lòng cho biết đơn vị đo lường kết quả của bạn.


Bạn không nên chia đối số thành sin cho phần bổ sung / 2. Ngoài ra bạn có thể có độ chính xác hơn trong bán kính trái đất, cũng như sử dụng một số Datum sử dụng ví dụ bởi hệ thống GPS (WGS-84) mà xấp xỉ Trái đất bởi một ellipsoid (với bán kính khác nhau tại xích đạo và cực)
Aki Suihkonen

@Waller, tại sao bạn không sử dụng loại Địa lý / Hình học (Không gian) để đạt được điều này?
Habib

3
Tôi đã kiểm tra phép tính của bạn với Mathematica; nó nghĩ rằng khoảng cách trong dặm quy chế (5280 feet) là 42,997, điều này gợi ý rằng tính toán của bạn không phải là một chút không chính xác , chứ không phải nó là cực kỳ chính xác .
hiệu suất cao

Câu trả lời:


129

Vì bạn đang sử dụng SQL Server 2008 nên bạn có geographysẵn kiểu dữ liệu được thiết kế cho chính xác loại dữ liệu này:

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

SELECT @source.STDistance(@target)

Cho

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

(1 row(s) affected)

Cho chúng tôi biết nó là khoảng 538 km từ (gần) Luân Đôn đến (gần) Edinburgh.

Đương nhiên sẽ có một số việc học phải làm trước, nhưng một khi bạn biết điều đó thì sẽ dễ hơn nhiều so với việc thực hiện phép tính Haversine của riêng bạn; cộng với việc bạn nhận được rất nhiều chức năng.


Nếu bạn muốn giữ lại cấu trúc dữ liệu hiện có của mình, bạn vẫn có thể sử dụng STDistance, bằng cách xây dựng các geographyphiên bản phù hợp bằng Pointphương pháp:

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 không - kinh độ sẽ là tiêu cực đối với những nơi Tây của Thủ Meridian , và tích cực cho những nơi đông của nó
AakashM

Bạn đã cứu ngày của tôi! .. Cảm ơn rất nhiều!
Dhrumil Bhankhar

1
Việc sử dụng chức năng tích hợp có vẻ rất chậm. Ví dụ: trong vòng lặp 100.000 mục, mất 23 giây thay vì 1,4 giây cho chức năng do người dùng xác định của tôi (xem câu trả lời của Durai).
NickG

1
Chỉ muốn kêu vang trong và khuyến nghị xác nhận @AakashM 's cho chỉ số không gian + ... cho một ứng dụng ETL, sự khác biệt là vài bậc độ lớn hơn sau khi thực hiện các chỉ số không gian
Bill Anton

3
FYI: POINT (LONGITUDE LATITUDE) trong khi địa lý :: Point (LATITUDE, LONGITUDE, 4326)
Mzn

42

Dưới đây chức năng cung cấp cho khoảng cách giữa hai geocoordinates trong dặm

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 

Hàm dưới đây cho biết khoảng cách giữa hai tọa độ địa lý tính bằng km

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

Hàm dưới đây cho biết khoảng cách giữa hai tọa độ địa lý tính bằng km sử dụng kiểu dữ liệu Địa lý được giới thiệu trong sql server 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);

Sử dụng:

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

Tham khảo: Ref1 , Ref2


2
Tôi cần tính khoảng cách cho mã zip 35K dựa trên mã zip của các sự kiện khác nhau được sắp xếp theo khoảng cách đến mã zip. Danh sách tọa độ quá lớn để thực hiện tính toán bằng cách sử dụng kiểu dữ liệu địa lý. Khi tôi chuyển sang sử dụng giải pháp dựa trên các hàm cắt một dòng ở trên, nó chạy nhanh hơn nhiều. Vì vậy, sử dụng các loại địa lý chỉ để tính toán khoảng cách dường như là tốn kém. Người mua hãy cẩn thận.
Tombala

Rất hữu ích để tính toán khoảng cách trong mệnh đề WHERE của truy vấn. Tôi đã phải bọc một ABS () xung quanh biểu thức "set @ d =", bởi vì tôi thấy một số trường hợp hàm trả về một khoảng cách âm.
Joe Irby

1
Hàm cho "khoảng cách giữa hai tọa độ địa lý tính bằng km" không thành công nếu chúng tôi so sánh 2 điểm bằng nhau, nó sẽ báo cho bạn lỗi "Đã xảy ra thao tác dấu phẩy động không hợp lệ"
RRM

2
Điều này rất tuyệt nhưng không hoạt động trong khoảng cách ngắn vì "số thập phân (8,4)" không cung cấp đủ độ chính xác.
có ảnh hưởng

1
@influent là đúng, đây không phải là hữu ích cho khoảng cách ngắn (5 dặm trong trường hợp của tôi)
Roger

15

Có vẻ như Microsoft đã xâm chiếm bộ não của tất cả những người được hỏi khác và khiến họ viết ra những giải pháp phức tạp nhất có thể. Đây là cách đơn giản nhất mà không cần thêm bất kỳ hàm / câu lệnh khai báo nào:

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

Đơn giản chỉ cần thay thế dữ liệu của bạn thay vì LATITUDE_1, LONGITUDE_1, LATITUDE_2, LONGITUDE_2ví dụ như:

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

2
để tham khảo: STDistance () trả về khoảng cách theo đơn vị đo tuyến tính của hệ quy chiếu không gian mà dữ liệu địa lý của bạn được xác định. Bạn đang sử dụng SRID 4326, có nghĩa là STDistance () trả về khoảng cách tính bằng mét.
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

Sử dụng:

chọn dbo.DistanceKM (37.848832506474, 37.848732506474, 27.83935546875, 27.83905546875)

Kết quả đầu ra:

0,02849639

Bạn có thể thay đổi tham số @R với các phao nhận xét.


Hoạt động hoàn hảo
Tejasvi Hegde

4

Khi bạn đang sử dụng SQL 2008 trở lên, tôi khuyên bạn nên kiểm tra kiểu dữ liệu GEOGRAPHY . SQL đã tích hợp hỗ trợ cho các truy vấn không gian địa lý.

ví dụ: bạn có một cột trong bảng loại GEOGRAPHY sẽ được điền với biểu diễn không gian địa lý của các tọa độ (xem tham chiếu MSDN được liên kết ở trên để biết ví dụ). Sau đó, kiểu dữ liệu này hiển thị các phương thức cho phép bạn thực hiện toàn bộ các truy vấn không gian địa lý (ví dụ: tìm khoảng cách giữa 2 điểm)


Chỉ để nói thêm, tôi đã thử kiểu trường địa lý, nhưng thấy việc sử dụng hàm Durai (trực tiếp sử dụng các giá trị kinh độ và vĩ độ) sẽ nhanh hơn nhiều . Xem ví dụ của tôi tại đây: stackoverflow.com/a/37326089/391605
Mike Gledhill

1

Ngoài các câu trả lời trước, đây là một cách để tính toán khoảng cách bên trong một 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

Sử dụng:

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

Các hàm vô hướng cũng hoạt động nhưng chúng rất kém hiệu quả khi tính toán một lượng lớn dữ liệu.

Tôi hy vọng điều này có thể giúp ai đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.