SQL геометрия найти все точки в радиусе
Я свободно владею SQL, но плохо знаком с использованием функций геометрии SQL. У меня есть, пожалуй, одна из основных проблем, которую нужно решить, но я не нашел в сети хороших ресурсов, объясняющих, как использовать геометрические объекты. (Technet - паршивый способ изучать новые вещи...)
У меня есть набор 2d точек на декартовой плоскости, и я пытаюсь найти все точки, которые находятся в пределах набора радиусов.
Я создал и заполнил таблицу, используя синтаксис, такой как:
Обновить [Вещи] установить [Местоположение] = геометрия:: Точка (@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),
)
2 ответа
Это невероятно поздний ответ, но, возможно, я смогу пролить свет на решение. Номер "set", на который вы ссылаетесь, является Идентификатором пространственной привязки или 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 недели после того, как вы задали вопрос. Извините, я не видел этого раньше, если бы был только один пространственный тег для вопросов!!!!
Конечно, это возможно. Индивидуальное выражение 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