简体   繁体   English

SQL Geometry 查找半径内的所有点

[英]SQL Geometry find all points in a radius

I am fluent in SQL but new to using the SQL Geometry features.我精通 SQL,但刚开始使用 SQL Geometry 功能。 I have what is probably a very basic problem to solve, but I haven't found any good resources online that explain how to use geometry objects.我有一个可能是非常基本的问题需要解决,但我还没有在网上找到任何好的资源来解释如何使用几何对象。 (Technet is a lousy way to learn new things...) (Technet 是一种糟糕的学习新事物的方式......)

I have a collection of 2d points on a Cartesian plane, and I am trying to find all points that are within a collection of radii.我在笛卡尔平面上有一组 2d 点,我试图找到一组半径内的所有点。

I created and populated a table using syntax like:我使用以下语法创建并填充了一个表:

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

(@X,@Y are just the x and y values, 0 is an arbitrary number shared by all objects that allows set filtering if I understand correctly) (@X,@Y 只是 x 和 y 值,0 是所有对象共享的任意数字,如果我理解正确,则允许设置过滤)

Here is where I go off the rails...Do I try to construct some sort of polygon collection and query using that, or is there some simple way of checking for intersection of multiple radii without building a bunch of circular polygons?这是我偏离轨道的地方......我是否尝试使用它来构建某种多边形集合和查询,或者是否有一些简单的方法可以在不构建一堆圆形多边形的情况下检查多个半径的交集?

Addendum: If nobody has the answer to the multiple radii question, what is the single radius solution?附录:如果没有人回答多半径问题,单半径解决方案是什么?

UPDATE更新

Here are some examples I have worked up, using an imaginary star database where stars are stored on a xy grid as points:以下是我使用的虚构星数据库中的一些示例,其中星作为点存储在 xy 网格上:

Selects all points in a box:选择一个框中的所有点:

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

using this as a pattern, you can do all sorts of interesting things, such as defining multiple polygons:使用它作为一种模式,你可以做各种有趣的事情,比如定义多个多边形:

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

Or checking distance:或检查距离:

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

Sample insert statement示例插入语句

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

This is an incredibly late answer, but perhaps I can shed some light on a solution.这是一个令人难以置信的迟到答案,但也许我可以阐明一些解决方案。 The "set" number you refer to is a Spatial Reference Indentifier or SRID.您所指的“集合”编号是空间参考标识符或 SRID。 For lat/long calculations you should consider setting this to 4326, which will ensure metres are used as a unit of measurement.对于纬度/经度计算,您应该考虑将其设置为 4326,这将确保使用米作为测量单位。 You should also consider switching to SqlGeography rather than SqlGeometry, but we'll continue with SqlGeometry for now.您还应该考虑切换到 SqlGeography 而不是 SqlGeometry,但我们现在将继续使用 SqlGeometry。 To bulk set the SRID, you can update your table as follows:要批量设置 SRID,您可以按如下方式更新您的表:

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

For a single radius, you need to create a radii as a spatial object.对于单个半径,您需要创建一个半径作为空间对象。 For example:例如:

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

STBuffer() takes the spatial point and creates a circle (now a Polygon type) from it. STBuffer() 获取空间点并从中创建一个圆(现在是 Polygon 类型)。 You can then query your data set as follows:然后,您可以按如下方式查询您的数据集:

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

The above will now use any Spatial Index you have created on the [SpatialColumn] in its query plan.以上现在将使用您在其查询计划中的 [SpatialColumn] 上创建的任何空间索引。

There is also a simpler option which will work (and still use a spatial index).还有一个更简单的选项可以工作(并且仍然使用空间索引)。 The STDistance method allows you to do the following: 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;

Lastly, working with a collection of radii.最后,使用一组半径。 You have a few options.你有几个选择。 The first is to run the above for each radii in turn, but I would consider the following to do it as one:第一个是依次为每个半径运行上述内容,但我会考虑将以下内容作为一个:

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;

The final above has not been tested, but I'm 99% sure it's just about there with a small amount of tweaking being a possibility.上面的最后一个还没有经过测试,但我 99% 肯定它就在那里,有可能进行少量的调整。 The ideal of taking the min radius distance in the select is that if the multiple radii stem from a single location, if a point is within the first radius, it will naturally be within all of the others.在选择中取最小半径距离的理想情况是,如果多个半径源于单个位置,如果一个点在第一个半径内,它自然会在所有其他半径内。 You'll therefore duplicate the record, but by grouping and then selecting the min, you get only one (and the closest).因此,您将复制该记录,但通过分组然后选择最小值,您只会得到一个(也是最接近的)。

Hope it helps, albeit 4 weeks after you asked the question.希望它有所帮助,尽管在您提出问题后 4 周。 Sorry I didn't see it sooner, if only there was only one spatial tag for questions!!!!抱歉没早点看到,要是问题只有一个空间标签就好了!!!!

Sure, this is possible.当然,这是可能的。 The individual where clause should be something like:个人 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 

You can then pile a bunch of radii and xy points into a table variable that looks like like:然后,您可以将一堆半径和 xy 点堆积到一个表变量中,如下所示:

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

Note: I have not put this through a compiler, but this is the bare bones of a working solution.注意:我还没有通过编译器来解决这个问题,但这是一个工作解决方案的基本框架。

Other option looks to be here: http://technet.microsoft.com/en-us/library/bb933904.aspx其他选项看起来在这里: http : //technet.microsoft.com/en-us/library/bb933904.aspx

And there's a demo of seemingly working syntax here: http://social.msdn.microsoft.com/Forums/sqlserver/en-US/6e1d7af4-ecc2-4d82-b069-f2517c3276c2/slow-spatial-predicates-stcontains-stintersects-stwithin-?forum=sqlspatial这里有一个看似有效的语法演示: http : //social.msdn.microsoft.com/Forums/sqlserver/en-US/6e1d7af4-ecc2-4d82-b069-f2517c3276c2/slow-spatial-predicates-stcontains-stintersects- stwithin-?forum=sqlspatial

The second post implies the syntax:第二篇文章暗示了语法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM