【问题标题】:Is a point within a geographical radius - SQL Server 2008是地理半径内的一个点 - SQL Server 2008
【发布时间】:2013-02-04 09:29:51
【问题描述】:

给定以下数据,是否有可能,如果是,哪种方法是确定第一个表中的位置“Shurdington”是否包含在第二个表中任何位置的给定半径范围内的最有效方法.

GeoData 列属于“地理”类型,因此可以选择使用 SQL Server 空间特征以及使用纬度和经度。

Location      GeoData       Latitude    Longitude
===========================================================
Shurdington   XXXXXXXXXX    51.8677979  -2.113189

ID  Location            GeoData     Latitude    Longitude   Radius
==============================================================================
1000    Gloucester      XXXXXXXXXX  51.8907127  -2.274598   10
1001    Leafield        XXXXXXXXXX  51.8360519  -1.537438   10
1002    Wotherton       XXXXXXXXXX  52.5975151  -3.061798   5
1004    Nether Langwith XXXXXXXXXX  53.2275276  -1.212108   20
1005    Bromley         XXXXXXXXXX  51.4152069  0.0292294   10

非常感谢任何帮助。

【问题讨论】:

    标签: sql-server sql-server-2008 sql-server-2008-r2 proximity sqlgeography


    【解决方案1】:

    创建数据

    CREATE TABLE #Data (
        Id int,
        Location nvarchar(50),
        Latitude decimal(10,5),
        Longitude decimal(10,5),
        Radius int
    )
    
    INSERT #Data (Id,Location,Latitude,Longitude,Radius) VALUES 
    (1000,'Gloucester', 51.8907127 ,-2.274598  , 20), -- Increased to 20
    (1001,'Leafield', 51.8360519 , -1.537438  , 10),
    (1002,'Wotherton', 52.5975151,  -3.061798  , 5),
    (1004,'Nether Langwith', 53.2275276 , -1.212108  , 20),
    (1005,'Bromley', 51.4152069 , 0.0292294  , 10)
    

    测试

    将您的兴趣点声明为POINT

    DECLARE @p GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-2.113189 51.8677979)', 4326);
    

    判断它是否在另一个点的半径内:

    -- First create a Point.
    DECLARE @point GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-2.27460 51.89071)', 4326);
    -- Buffer the point (meters) and check if the 1st point intersects
    SELECT @point.STBuffer(50000).STIntersects(@p)
    

    将所有内容组合成一个查询:

    select  *,
            GEOGRAPHY::STGeomFromText('POINT('+ 
                convert(nvarchar(20), Longitude)+' '+
                convert( nvarchar(20), Latitude)+')', 4326)
            .STBuffer(Radius * 1000).STIntersects(@p) as [Intersects]
    from    #Data  
    

    给予:

    Id      Location        Latitude    Longitude   Radius  Intersects
    1000    Gloucester      51.89071    -2.27460    20      1
    1001    Leafield        51.83605    -1.53744    10      0
    1002    Wotherton       52.59752    -3.06180    5       0
    1004    Nether Langwith 53.22753    -1.21211    20      0
    1005    Bromley         51.41521    0.02923     10      0
    

    回复:效率。通过一些正确的索引,看起来 SQL 的空间索引可以非常快

    【讨论】:

    • 喜欢这个例子,虽然奇怪的是距离似乎有点偏离 - 也许这与我对 SQL Server 的空间特征缺乏了解有关,但即便如此,我也会有假设通过添加列 '.STDistance(@p) / 1609.34' 它会返回以英里为单位的距离,但似乎还有很长的路要走 - 或者是我吗?
    • 想通了,从“STGeomFromText”创建“点”时,需要反过来输入纬度/经度,例如长/纬度。
    【解决方案2】:

    如果你想自己做数学,你可以使用基于毕达哥拉斯的 Equirectangular 逼近。公式为:

    var x = (lon2-lon1) * Math.cos((lat1+lat2)/2); 变量 y = (lat2-lat1); var d = Math.sqrt(x*x + y*y) * R;

    就 SQL 而言,这应该在您的第二个表中提供那些在其半径内包含您在第一个表中的条目的位置:

    SELECT *
    FROM Table2 t2
    WHERE EXISTS (
     SELECT 1 FROM Table1 t1
     WHERE 
      ABS (
      SQRT (
        (SQUARE((RADIANS(t2.longitude) - RADIANS(t1.longitude)) * COS((RADIANS(t2.Latitude) + RADIANS(t1.Latitude))/2))) +
        (SQUARE(RADIANS(t1.Latitude) - RADIANS(t2.Latitude)))
        ) * 6371 --Earth radius in km, use 3959 for miles
        )
        <= t2.Radius
    )
    

    请注意,这不是可用的最准确的方法,但可能已经足够好了。如果您正在查看跨越全球的距离,您可能希望使用 Google 的“haversine”公式。

    可能值得将此与 Paddy 的解决方案进行比较,看看它们的一致性如何以及哪个效果最好。

    【讨论】:

      【解决方案3】:

      您计算两点之间的距离并将该距离与给定的半径进行比较。

      对于短距离的计算,您可以使用Wikipedia - Geographical distance - Spherical Earth projected to a plane 处的公式,该公式声称“非常快,并且对于小距离产生相当准确的结果”。

      根据公式,需要经纬度差和平均纬度

      with geo as (select g1.id, g1.latitude as lat1, g1.longitude as long1, g1.radius,
                          g2.latitude as lat2, g2.longitude as long2
                   from geography g1
                   join geography g2 on g2.location = 'shurdington'
                                     and g1.location <> 'shurdington')
           base as (select id,
                           (radians(lat1) - radians(lat2)) as dlat,
                           (radians(long1) - radians(long2)) as dlong,
                           (radians(lat1) + radians(lat2)) / 2 as mlat, radius
                    from geo)
           dist as (select id,
                           6371.009 * sqrt(square(dlat) + square(cos(mlat) * dlong)) as distance,
                           radius
                    from base)
      select id, distance
      from dist
      where distance <= radius
      

      我使用with selects 作为中间步骤来保持计算“可读”。

      【讨论】:

      • 既然“使用 SQL Server 空间功能是一种选择”,为什么要自己做呢?
      • @AakashM 因为这是一个有趣的练习。
      猜你喜欢
      • 2014-04-28
      • 1970-01-01
      • 2012-01-13
      • 1970-01-01
      • 2011-09-17
      • 2016-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多