SQL Geometry находит все точки в радиусе

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

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

Я создал и заполнил таблицу, используя синтаксис вроде:

Обновить [Вещи] установить [Местоположение] = геометрия::Точка(@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
(
    @Name,
    @StarTypeId,
    GEOMETRY::Point(@LocationX, @LocationY, 0),
)

person Roger Hill    schedule 10.02.2014    source источник


Ответы (2)


Это невероятно поздний ответ, но, возможно, я смогу пролить свет на решение. Номер «набора», на который вы ссылаетесь, является идентификатором пространственной ссылки или 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 недели после того, как вы задали вопрос. Извините, что не увидел раньше, если бы только был один пространственный тег для вопросов!!!!

person Jon Bellamy    schedule 09.03.2014
comment
Интересное второе решение, мне нужно немного поиграть с ним и подумать. Два незначительных момента: я почти уверен, что если вы не работаете с криволинейной поверхностью, вам не следует использовать ГЕОГРАФИИ 4326, которая не является евклидовой поверхностью. Это проблема плоского пространства, поэтому использование GEOMETRY, вероятно, является правильным выбором. Во-вторых, в вашем примере вы предлагаете использовать временную таблицу sql, когда было бы намного лучше (быстрее) использовать переменную таблицы. Временные таблицы записываются на диск, табличные переменные находятся в памяти. - person Roger Hill; 10.03.2014
comment
@MadTigger Я не могу не согласиться с использованием табличных переменных, но, если честно, я не тратил время на оптимизацию, больше на теорию, которая поможет вам на вашем пути - учитывая, что через 4 недели вы, возможно, уже решили Это! :-) Простите предположение о 4326, опять же, если вы работаете над проблемой плоского пространства, вы совершенно правы, что не используете 4326 и используете геометрию. Я просто так привык к тому, что люди используют геометрию как отговорку от реальных земных проблем, когда они должны использовать географию, а ваш вопрос не был ясен по этому поводу. Привычка к предположениям — это то, от чего я должен избавиться. - person Jon Bellamy; 11.03.2014

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

И здесь есть демонстрация, казалось бы, работающего синтаксиса: -stcontains-stintersects-stwithin-?forum=sqlspatial" rel="nofollow">http://social.msdn.microsoft.com/Forums/sqlserver/en-US/6e1d7af4-ecc2-4d82-b069-f2517c3276c2/slow- пространственные-предикаты-stcontains-stintersects-stwithin-?forum=sqlspatial

Второй пост подразумевает синтаксис:

SELECT Distinct pointTable.* from pointTable pt, circletable crcs
WHERE crcs.geom.STContains(b.Location) = 1
person Dylan Brams    schedule 11.02.2014
comment
Вот как бы вы это сделали, если бы собирались использовать базовые типы данных SQL. Однако я спрашиваю об использовании объектов геометрии SQL именно потому, что они используют деревья квадрантов или другое пространственное индексирование. Ваше решение полностью развалится, если вы захотите выполнить поиск в базе данных с несколькими сотнями тысяч точек, где объект, использующий пространственное разбиение, не будет иметь никаких проблем. С вашим решением все становится еще хуже, когда я добавляю дополнительные радиусы, так как мне придется проверять расстояние до каждой точки в базе данных для каждого радиуса, на который я собирался смотреть. Спасибо за попытку, однако - person Roger Hill; 12.02.2014
comment
Сервер не волшебный, вам все равно придется делать огромное количество вычислений. Вам понадобится хранилище данных или вычисляемое поле, если это огромные таблицы и важны загрузка/время безотказной работы сервера. Тем не менее, решение обновлено, чтобы содержать возможности SQL-Native. - person Dylan Brams; 12.02.2014
comment
Это неправильно. Смысл пространственного индексирования (BSP-деревья, квадродеревья и т. д.) состоит в том, чтобы избежать необходимости выполнять всю математику, быстро сокращая пространство поиска до релевантных данных. Пространственная индексация при правильном использовании очень и очень быстра. При этом ссылки, которые вы нашли, были очень полезными. Я собираюсь написать и опубликовать краткий обзор того, как это сделать для будущих читателей. Спасибо за помощь. - person Roger Hill; 14.02.2014