2012-10-23 7 views
60

Tôi đang cố tính 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.Tính khoảng cách giữa hai điểm (Vĩ độ, Kinh độ)

Tôi đã từng 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 

Tôi không tin tưởng vào dữ liệu sắp ra, dường như kết quả không chính xác.

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

Latitude  Longitude  Distance 
53.429108  -2.500953  85.2981833133896 

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

Đề nghị ghi rõ những gì đơn vị đo lường kết quả của bạn đang ở.

+0

Hệ thống cơ sở dữ liệu nào? – AakashM

+0

MSSQL 2008 R2 .. – Waller

+0

Bạn không nên chia đối số để sine bằng 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_ được sử dụng, ví dụ:bởi hệ thống GPS (WGS-84) xấp xỉ Trái đất bởi một ellipsoid (với bán kính khác nhau tại đường xích đạo và cực) –

Trả lời

90

Vì bạn đang sử dụng SQL Server 2008, bạn có sẵn kiểu dữ liệu geography. 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) 

Telling chúng tôi đó là khoảng 538 km từ (gần) London để (gần) Edinburgh.

Đương nhiên sẽ có một số lượng học tập để làm đầu tiên, nhưng một khi bạn biết nó dễ dàng 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 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 tại của bạn, bạn vẫn có thể sử dụng STDistance, bằng cách xây dựng phù hợp geography trường hợp sử dụng Point phươ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 
+1

là bắt buộc đối với kinh độ phải âm? – Nezam

+5

@nezam no - kinh độ sẽ là tiêu cực đối với các địa điểm phía Tây của [Kinh tuyến chính] (http://en.wikipedia.org/wiki/Prime_meridian), và tích cực cho các địa điểm phía đông của nó – AakashM

+0

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

3

Khi bạn đang sử dụng SQL 2008 hoặc sau đó, tôi khuyên bạn nên kiểm tra ra các loại GEOGRAPHY dữ liệu. SQL đã xây dựng trong hỗ trợ cho các truy vấn không gian địa lý.

ví dụ: bạn sẽ có một cột trong bảng loại GEOGRAPHY của bạn sẽ được điền vào với một đại 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ụ). Kiểu dữ liệu này sau đó đưa ra 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)

+0

Chỉ cần thêm, tôi đã thử loại trường địa lý, nhưng được tìm thấy bằng cách sử dụng chức năng của Durai (trực tiếp sử dụng các giá trị kinh độ và vĩ độ) để * nhiều * nhanh hơn. Xem ví dụ của tôi ở đây: http://stackoverflow.com/a/37326089/391605 –

31

Hàm dưới đây mang đến 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 mang đến cho khoảng cách giữa hai geocoordinates 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 mang đến cho khoảng cách giữa hai geocoordinates bằng km sử dụng Geography kiểu dữ liệu mà đã được giới thiệu trong máy chủ 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); 

Cách sử dụng:

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

tham khảo:Ref1, Ref2

+1

Tôi cần thực hiện tính toán khoảng cách cho mã zip 35K đối với các mã zip của sự kiện khác nhau được sắp xếp theo khoảng cách đến mã zip . Nó quá lớn trong danh sách các tọa độ để thực hiện các phép tính sử dụng kiểu dữ liệu địa lý. Khi tôi chuyển sang sử dụng các chức năng trig đơn dựa trên giải pháp 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 một khoảng cách có vẻ là tốn kém. Người mua hãy cẩn thận. – Tombala

+0

Rất hữu ích để tính 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 đã tìm thấy một số trường hợp mà hàm trả về một khoảng cách âm. –

+0

Chức năng cho "khoảng cách giữa hai geocoordinates tính bằng km" nếu chúng ta so sánh 2 điểm bằng nhau, nó cho bạn lỗi "Thao tác dấu chấm động không hợp lệ đã xảy ra" – RRM

2
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 

Cách sử dụng:

chọn dbo.DistanceKM (37,848832506474, 37,848732506474, 27,83935546875, 27,83905546875)

Đầu ra:

0,02849639

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

0

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 

Cách sử dụng: chức năng

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

vô hướng cũng làm việc nhưng họ rất kém hiệu quả khi tính toán số lượng lớn dữ liệu.

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