Как я могу обрабатывать запросы перекрытия многоугольника MySQL?

Например, у меня есть таблица пользователей. Каждый пользователь представляет собой "квадрат" (или круг) на карте.

Я хочу найти пользователей, которые перекрывают другой квадрат на карте. Поддерживается ли это в MySQL 5.6? (Что относительно версий разработки MySQL?)

Обратите внимание, что я не ищу "найти пользователей с точкой внутри этого квадрата". Я ищу "найти пользователей, квадрат которых перекрывает этот квадрат (не обязательно содержать, если оба квадрата касаются - это хорошо)".

Это действительно помогло бы, если бы кто-то мог представить пример записей INSERTING, а затем QUERYING их, используя полигоны ST_INTERSECT.

Ответы

Ответ 1

скрипт SQL Polygon demonstration

Создать таблицу с многоугольным столбцом

Обратите внимание, что для использования пространственных индексов вы не можете использовать InnoDB. Вы можете использовать геометрию без пространственных индексов, но производительность ухудшается, как обычно.

CREATE TABLE IF NOT EXISTS `spatial` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `poly` geometry NOT NULL,
  UNIQUE KEY `id` (`id`),
  SPATIAL INDEX `poly` (`poly`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

Получить 3 квадрата и вставленный треугольник

INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((10 50,50 50,50 10,10 10,10 50))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((1 15,5 15,5 11,1 11,1 15))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((11 5,15 5,15 1,11 5))',0));

Выберите все, что пересекает небольшой квадрат в левом нижнем углу (фиолетовый квадрат № 1)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((0 0,2 0,2 2,0 2,0 0))', 0 )
        )
;

Выберите все, что пересекает треугольник, начиная от нижнего левого угла до нижнего правого угла до верхнего правого угла) (квадраты # 1 и # 2 и треугольник # 4.)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((0 0,50 50,50 0,0 0))', 0 )
        )
;

Выбирает все в квадрате, которое находится за пределами нашего изображения (Ничего)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((100 100,200 100,200 200,100 200,100 100))', 0 )
        )
;

Изменить # 1:

Я перечитал вопрос, и я думаю, что у вас пространственные отношения немного смущены. Если вы хотите найти все, что подходит ко всему внутри квадрата (многоугольник), тогда вам нужно использовать Contains/ST_Contains. Пожалуйста, смотрите пространственные функции в документации MySQL, чтобы узнать, какая функция выполняет эту работу для вас. Обратите внимание на следующее различие между функциями ST/MBR:

Выбирает все, что находится внутри квадрата (# 0 снизу) (квадраты # 1, # 2, треугольник # 4)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        Contains(
          GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
          `poly`
        )
;

Выбирает все, что находится внутри квадрата (# 0 снизу) и не имеет границ (квадрат № 2, треугольник № 4)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Contains(
          GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
          `poly`
        )
;

Изменить # 2:

Очень приятное дополнение от @StephanB (скрипт SQL)

Чтобы перечислить все пересечения, присоедините таблицу к себе и отфильтруйте обратное пересечение

Выбрать любые перекрывающиеся объекты

SELECT s1.id,AsText(s1.poly), s2.id, AsText(s2.poly)
FROM  `spatial` s1, `spatial` s2
    WHERE 
        ST_Intersects(s1.poly, s2.poly)
    AND s1.id < s2.id
;

(просто отметьте, что вы должны удалить AND s1.id < s2.id, если вы работаете с CONTAINS, как CONTAINS(a,b) <> CONTAINS(b,a) while Intersects(a,b) = Intersects(b,a))

На следующем рисунке (неисчерпывающий список):

  • 2 пересекает # 6.

  • 6 пересекает # 2

  • 0 пересекает # 1, # 2, # 3, # 4, # 5

  • 1 пересекает # 0, # 5

  • 0 содержит # 1, # 3, # 4 и # 5 (# 1, # 3, # 4 и # 5 находятся в пределах # 0)

  • 1 содержит # 5 (# 5 находится в пределах # 1)

  • 0 st_contains # 3, # 4 и # 5

  • 1 st_contains # 5

Spatial relations

Редактировать # 3: Поиск по расстоянию/Работа в (с) кругах

MySQL не поддерживает прямой круг как геометрию, но вы можете использовать пространственную функцию Buffer(geometry,distance), чтобы обойти ее. Что делает Buffer(), создает буфер указанного расстояния вокруг геометрии. Если вы начинаете с точки геометрии, буфер действительно представляет собой круг.

Вы можете увидеть, какой буфер фактически выполняет, просто позвонив:

SELECT ASTEXT(BUFFER(GEOMFROMTEXT('POINT(5 5)'),3))

(результат довольно длинный, поэтому я не буду публиковать его здесь). Он фактически создает многоугольник, который представляет буфер - в этом случае (и мой MariaDB) результатом является 126-точечный многоугольник, который аппроксимирует круг. С таким полигоном вы можете работать так же, как и с любым другим полигоном. Таким образом, не должно быть штрафа за производительность.

Итак, если вы хотите выбрать все полигоны, которые попадают в круг, вы можете полоскать и повторить предыдущий пример (это найдет только квадрат # 3)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Contains(
          Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
          `poly`
        )
;

Выберите все полигоны, которые пересекаются с кружком

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(
          Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
          `poly`
        )
;

При работе с фигурами, отличными от прямоугольников, вы должны использовать функции ST_*. Функции без ST_ используют ограничивающий прямоугольник. Таким образом, предыдущий пример выбирает треугольник # 4, хотя он не находится в круге.

Так как Buffer() создает довольно большие полигоны, определенная производительность может быть снижена с использованием метода ST_Distance(). К сожалению, я не могу ее количественно оценить. Вам нужно будет провести бенчмаркинг.

Adding circle to first example

Другой способ поиска объектов по расстоянию - использовать функцию ST_Distance().

Выберите все элементы из таблицы и вычислите их расстояние от точки POINT (6 15)

SELECT id, AsText(`poly`), 
    ST_Distance(poly, GeomFromText('POINT(6 15)')) 
    FROM `spatial`
;

Вы можете использовать ST_Distance в разделе WHERE.

Выберите все элементы, расстояние от которых POINT (0 0) меньше или равно 10 (выбирает # 1, # 2 и # 3)

SELECT id, AsText(`poly`), 
    ST_Distance(poly, GeomFromText('POINT(6 15)')) 
    FROM `spatial`
    WHERE ST_Distance(poly, GeomFromText('POINT(6 15)')) <= 10
;

Хотя расстояние вычисляется от ближайшей точки до ближайшей точки. Сделать его похожим на ST_Intersect. Таким образом, приведенный выше пример будет выбирать # 2, даже если он не полностью помещается внутри круга.

И да, второй аргумент (0) для GeomFromText(text,srid), не играет никакой роли, вы можете смело игнорировать его. Я выбрал его из какого-то образца, и он застрял в моем ответе. Я оставил это в моих последних изменениях.

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