SQL Geometry查找半径中的所有点

时间:2014-02-10 06:10:56

标签: sql spatial spatial-query

我精通SQL,但对使用SQL Geometry功能不熟悉。我有一个非常基本的问题需要解决,但我没有在网上找到任何解释如何使用几何对象的好资源。 (Technet是学习新事物的糟糕方式......)

我在笛卡尔平面上有一组2d点,我试图找到半径集合内的所有点。

我使用如下语法创建并填充了一个表:

更新[Things] set [Location] = geometry :: Point(@ X,@ Y,0)

(@ X,@ Y只是x和y值,0是所有允许设置过滤的对象共享的任意数字,如果我理解正确的话)

这是我离开轨道的地方......我是否尝试使用它构建某种多边形集合和查询,或者是否有一些简单的方法来检查多个半径的交集而不构建一堆圆形多边形?

附录:如果没有人对多半径问题有答案,那么单半径解决方案是什么?

更新

以下是我编写的一些示例,使用虚拟星型数据库,其中星星作为点存储在x-y网格上:

选择框中的所有点:

DECLARE @polygon geometry = geometry::STGeomFromText('POLYGON((' 
+ CAST(@MinX AS VARCHAR(10)) + ' '  + CAST(@MinY AS VARCHAR(10)) + ',' 
+ CAST(@MaxX AS VARCHAR(10)) + ' '  + CAST(@MinY AS VARCHAR(10)) + ', ' 
+ CAST(@MaxX AS VARCHAR(10)) + ' '  + CAST(@MaxY AS VARCHAR(10)) + ',' 
+ CAST(@MinX AS VARCHAR(10)) + ' '  + CAST(@MaxY AS VARCHAR(10)) + ',' 
+ CAST(@MinX AS VARCHAR(10)) + ' '  + CAST(@MinY AS VARCHAR(10)) + '))', 0);

SELECT  [Star].[Name]           AS [StarName],
        [Star].[StarTypeId]     AS [StarTypeId],        
FROM    [Star]
WHERE   @polygon.STContains([Star].[Location]) = 1

使用它作为模式,你可以做各种有趣的事情,比如 定义多个多边形:

WHERE   @polygon1.STContains([Star].[Location]) = 1
OR @polygon2.STContains([Star].[Location]) = 1
OR @polygon3.STContains([Star].[Location]) = 1

或检查距离:

WHERE [Star].[Location].STDistance(@polygon1) < @SomeDistance 

示例插入声明

INSERT [Star]
(
    [Name],
    [StarTypeId],
    [Location],
)
VALUES
(
    @GameId,
    @Name,
    @StarTypeId,
    GEOMETRY::Point(@LocationX, @LocationY, 0),
)

2 个答案:

答案 0 :(得分:12)

这是一个令人难以置信的迟到的答案,但也许我可以解释一下解决方案。您引用的“设置”编号是空间参考标识符或SRID。对于纬度/经度计算,您应该考虑将其设置为4326,这将确保将仪表用作测量单位。您还应该考虑切换到SqlGeography而不是SqlGeometry,但我们现在将继续使用SqlGeometry。要批量设置SRID,您可以按如下方式更新表:

UPDATE [YourTable] SET [SpatialColumn] = GEOMETRY.STPointFromText([SpatialColumn].STAsText(), 4326);

对于单个半径,您需要创建一个半径作为空间对象。例如:

DECLARE @radiusInMeters FLOAT = 1000; -- Set to a number in meters
DECLARE @radius GEOMETRY = GEOMETRY::Point(@x, @y, 4326).STBuffer(@radiusInMeters);

STBuffer()获取空间点并从中创建一个圆(现在是多边形类型)。然后,您可以按如下方式查询数据集:

SELECT * FROM [YourTable] WHERE [SpatialColumn].STIntersects(@radius);

以上内容现在将使用您在查询计划中的[SpatialColumn]上创建的任何空间索引。

还有一个更简单的选项可以使用(并且仍然使用空间索引)。 STDistance方法允许您执行以下操作:

DECLARE @radius GEOMETRY = GEOMETRY::Point(@x, @y, 4326);
DECLARE @distance FLOAT = 1000; -- A distance in metres   
SELECT * FROM [YourTable] WHERE [SpatialColumn].STDistance(@radius) <= @distance;

最后,使用一组半径。你有几个选择。第一个是依次为每个半径运行上面的内容,但我会考虑以下内容将其作为一个:

DECLARE #radiiCollection TABLE
(
    [RadiusInMetres] FLOAT,
    [Radius] GEOMETRY
)

INSERT INTO #radiiCollection ([RadiusInMetres], [Radius]) VALUES (1000, GEOMETRY::Point(@xValue, @yValue, 4326).STBuffer(1000));
-- Repeat for other radii

SELECT
    X.[Id],
    MIN(R.[RadiusInMetres]) AS [WithinRadiusDistance]
FROM
    [YourTable] X
    JOIN
    #radiiCollection RC ON RC.[Radius].STIntersects(X.[SpatialColumn])
GROUP BY
    X.[IdColumn],
    R.[RadiusInMetres]

DROP TABLE @radiiCollection;

上面的最终结果尚未经过测试,但我99%肯定只是在那里进行少量调整是可能的。在选择中取最小半径距离的理想是,如果多个半径来自单个位置,如果一个点在第一个半径内,它将自然地在所有其他半径内。因此,您将复制记录,但通过分组然后选择最小值,您只得到一个(并且最接近)。

希望它有所帮助,尽管你问了这个问题4周后。对不起,我没有早点看到它,如果只有一个空间标签可以提问!!!!

答案 1 :(得分:-1)

当然,这是可能的。个别where子句应该是这样的:

DIM @Center AS Location
-- Initialize the location here, you probably know better how to do that than I.
Dim @Radius AS Decimal(10, 2)
SELECT * from pointTable WHERE sqrt(square(@Center.STX-Location.STX)+square(@Center.STX-Location.STX)) > @Radius 

然后,您可以将一堆半径和xy点堆积到表变量中,如下所示:

Dim @MyCircleTable AS Table(Geometry Circle) 
INSERT INTO @MyCircleTable (.........)

注意:我没有把它通过编译器,但这是一个工作解决方案的基础。

其他选项似乎在这里: http://technet.microsoft.com/en-us/library/bb933904.aspx

这里有一个看似有效的语法演示: http://social.msdn.microsoft.com/Forums/sqlserver/en-US/6e1d7af4-ecc2-4d82-b069-f2517c3276c2/slow-spatial-predicates-stcontains-stintersects-stwithin-?forum=sqlspatial

第二篇文章暗示语法:

SELECT Distinct pointTable.* from pointTable pt, circletable crcs
WHERE crcs.geom.STContains(b.Location) = 1