Геометрия SQL найти все точки в радиусе

Я свободно владею SQL, но новичок в использовании функций геометрии SQL. У меня есть, вероятно, очень основная проблема для решения, но я не нашел хороших ресурсов в интернете, которые объясняют, как использовать объекты геометрии. (Technet-паршивый способ узнать что-то новое...)

у меня есть коллекция 2D точек на декартовой плоскости, и я пытаюсь найти все точки, которые находятся в коллекции радиусов.

Я создал и заполнил таблицу с помощью синтаксиса например:

Update [Things] set [Location] = геометрия:: точка (@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

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

2 ответов


Это невероятно поздний ответ, но возможно, я смогу пролить свет на решение. Номер "set", на который вы ссылаетесь, является Индентификатором пространственной привязки или SRID. Для расчетов lat / long следует установить значение 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% уверен, что это просто там с небольшим количеством настроек, являющихся возможностью. Идеал принимать минимальное расстояние радиуса в отборе что если несколько радиусов происходят из одного места, если точка находится в пределах первого радиуса, она, естественно, будет во всех остальных. Поэтому вы будете дублировать запись, но, группируя и выбирая min, вы получаете только одну (и ближайшую).

надеюсь, что это поможет, хотя через 4 недели после того, как вы задали вопрос. Жаль, что я не видел этого раньше, если бы только был только один пространственный тег для вопросов!!!!


конечно, это возможно. Отдельное предложение 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