SQL几何查找半径中的所有点

罗杰·希尔

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

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

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

更新[事物]设置[位置] = geometry :: Point(@X,@Y,0)

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

这是我脱离困境的地方...我是否尝试构建某种多边形集合并使用它进行查询,或者是否存在一些简单的方法来检查多个半径的交集而无需构建一堆圆形多边形?

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

更新

这是我使用虚构的恒星数据库处理的一些示例,其中恒星存储在xy网格上作为点:

选择一个框中的所有点:

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),
)
乔恩·贝拉米

这是一个令人难以置信的迟到的答案,但是也许我可以阐明一个解决方案。您引用的“设置”数字是空间参考标识符或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()获取空间点并从中创建一个圆(现在为Polygon类型)。然后,您可以查询数据集,如下所示:

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周后。抱歉,如果只有一个空间标签来提问,我不会很快看到它的!!!

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章