计算两点之间的距离(纬度,经度)


88

我正在尝试计算地图上两个位置之间的距离。我已在数据中存储:经度,纬度,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)使用的基准面,该基准面可以通过椭球(在赤道和极点具有不同的半径)来近似地球
Aki Suihkonen 2012年

@Waller,为什么不使用Geography / Geometry(Spatial)类型来实现这一目标?
哈比卜2012年

3
我用Mathematica检查了您的计算;它认为以法定英里(5280英尺)为单位的距离为42.997,这表明您的计算并不是有点不准确,而是非常不准确
高性能Mark

Answers:


128

由于您使用的是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-经络对于本初子午线以西的地点为负,而对于其子午线以东的地点为正
AakashM 2014年

您救了我的一天!。。非常感谢!
Dhrumil Bhankhar 2015年

1
使用内置函数似乎非常慢。例如,在100,000个项目的循环中,我的用户定义函数需要23秒而不是1.4秒(请参阅Durai的答案)。
NickG '16

1
只是想了解一下并确认@AakashM推荐的空间索引+ ...对于ETL应用程序,实现空间索引后,差异要好几个数量级
Bill Anton

3
仅供参考:POINT(经纬度),而
geoge

41

以下函数给出了两个地理坐标之间的距离(以英里为单位)

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 server 2008中引入的Geography数据类型给出两个地理坐标之间的距离(以千米为单位)

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 '16

对于在查询的WHERE子句中计算距离非常有用。我确实必须在“ set @ d =”表达式周围包裹ABS(),因为我发现某些情况下函数返回负距离。
Joe Irby

1
如果我们比较两个相等的点,则“两个地理坐标之间的距离以公里为单位”的功能将失败,从而给您带来错误“发生了无效的浮点运算”
RRM

2
这很好,但不适用于短距离,因为“ decimal(8,4)”不能提供足够的精度。
进水

1
@influent是正确的,这对于短距离(在我的情况下为5英里)没有用
罗杰(Roger

15

看起来Microsoft入侵了所有其他响应者的大脑,使他们编写了尽可能复杂的解决方案。这是没有任何其他功能/声明语句的最简单方法:

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

2
供参考:STDistance()以定义地理数据的空间参考系统的线性度量单位返回距离。您正在使用SRID 4326,这意味着STDistance()返回以米为单位的距离。
布莱恩·史坦普

4

当您使用SQL 2008或更高版本时,建议您检出GEOGRAPHY数据类型。SQL内置了对地理空间查询的支持。

例如,您的表中将有一个GEOGRAPHY类型的列,该列将填充坐标的地理空间表示(有关示例,请参阅上面链接的MSDN参考)。然后,此数据类型公开了使您能够执行大量地理空间查询的方法(例如,找到2个点之间的距离)


只是添加一下,我尝试了地理字段类型,但是发现使用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)

标量函数也可以使用,但是在计算大量数据时它们的效率非常低。

我希望这可以帮助某人。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.