是地理范围内的一个点 - SQL Server 2008

时间:2013-02-04 09:29:52

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

鉴于以下数据,是否可能,如果是这样,这将是确定第一个表中“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

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:9)

创建数据

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的空间索引非常快

答案 1 :(得分:1)

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

为了计算短距离,您可以使用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 select作为中间步骤来保持计算“可读”。

答案 2 :(得分:1)

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

var x =(lon2-lon1)* Math.cos((lat1 + lat2)/ 2); var 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
)

请注意,这不是最准确的方法,但可能还不错。如果你正在寻找遍布全球的距离,你可能希望采用谷歌的'hasrsine'公式。

将此与Paddy的解决方案进行比较可能值得一看,看看他们的表现如何以及哪种表现最佳。