sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo puedo manejar las consultas de superposición de polígonos de MySQL?

violín SQL

Crear tabla con columna poligonal

Tenga en cuenta que para usar índices espaciales, no puede usar InnoDB. Puede usar la geometría sin índices espaciales, pero el rendimiento se degrada como de costumbre.

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;

Obtenga 3 cuadrados y un triángulo insertado

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));

Seleccione todo lo que se cruza con un cuadrado pequeño en la esquina inferior izquierda (cuadrado morado #1)

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

Seleccione todo lo que se cruza con el triángulo desde la esquina inferior izquierda hasta la esquina inferior derecha y la esquina superior derecha) (cuadrados #1 y #2 y triángulo #4.)

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

Selecciona todo en cuadrado que esté fuera de nuestra imagen (nada)

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

Editar n.º 1:

Releí la pregunta y creo que tienes las relaciones espaciales un poco confundidas. Si lo que quieres es encontrar todo lo que se ajuste completamente dentro de un cuadrado (polígono), entonces necesitas usar Containers/ST_Contains. Consulte funciones espaciales en la documentación de MySQL para averiguar qué función hace el trabajo por usted. Tenga en cuenta la siguiente diferencia entre las funciones ST/MBR:

Selecciona todo lo que está completamente dentro de un cuadrado (#0 desde abajo) (cuadrados #1, #2, triángulo #4)

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

Selecciona todo lo que está completamente dentro de un cuadrado (#0 desde abajo) y no comparte bordes (cuadrado #2, triángulo #4)

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

Edición n.º 2:

Muy buena adición de @StephanB (SQL fiddle )

Seleccione cualquier objeto superpuesto

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
;

(solo tenga en cuenta que debe eliminar AND s1.id < s2.id si está trabajando con CONTAINS , como CONTAINS(a,b) <> CONTAINS(b,a) while Intersects(a,b) = Intersects(b,a) )

En la siguiente imagen (lista no exhaustiva):

  • 2 intersecta #6.

  • 6 intersecta #2

  • 0 se cruza con #1, #2, #3, #4, #5

  • 1 se cruza con #0, #5

  • 0 contiene #1, #3, #4 y #5 (#1, #3, #4 y #5 están dentro de #0)

  • 1 contiene #5 (#5 está dentro de #1)

  • 0 st_contiene #3, #4 y #5

  • 1 st_contiene #5

Edición n.° 3:Buscar por distancia/Trabajar en (con) círculos

MySQL no admite directamente el círculo como geometría, pero puede usar la función espacial Buffer(geometry,distance) para trabajar a su alrededor. Qué Buffer() hace, está creando un búfer de dicha distancia alrededor de la geometría. Si comienza con el punto de geometría, el búfer es de hecho un círculo.

Puede ver qué hace realmente el búfer simplemente llamando:

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

(el resultado es bastante largo, así que no lo publicaré aquí) En realidad crea un polígono que representa el búfer; en este caso (y mi MariaDB) el resultado es un polígono de 126 puntos, que se aproxima a un círculo. Con tal polígono puedes trabajar como lo harías con cualquier otro polígono. Por lo tanto, no debería haber una penalización de rendimiento.

Entonces, si desea seleccionar todos los polígonos que caen en un círculo puede enjuagar y repetir el ejemplo anterior (esto encontrará solo el cuadrado #3)

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

Seleccione todos los polígonos que se cruzan con un círculo

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

Cuando trabaje con formas diferentes a los rectángulos, debe usar el ST_* funciones Funciones sin ST_ utilice un rectángulo delimitador. Entonces, el ejemplo anterior selecciona el triángulo #4 aunque no esté en el círculo.

Como Buffer() crea polígonos bastante grandes, definitivamente habrá una penalización de rendimiento por usar ST_Distance() método. Lamentablemente no puedo cuantificarlo. Tendrás que hacer un benchmarking.

Otra forma de encontrar objetos por distancia es usando ST_Distance() función.

Seleccione todos los elementos de la tabla y calcule su distancia desde el punto PUNTO(6 15)

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

Puedes usar ST_Distance en WHERE cláusula también.

Seleccione todos los elementos cuya distancia al PUNTO(0 0) sea menor o igual a 10 (selecciona #1, #2 y #3)

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

Aunque la distancia se calcula desde el punto más cercano al punto más cercano. Haciéndolo similar al ST_Intersect . Entonces, el ejemplo anterior seleccionará el n. ° 2 aunque no encaje completamente dentro del círculo.

Y sí, el segundo argumento (0) para GeomFromText(text,srid) , no juega ningún papel, puede ignorarlo con seguridad. Lo tomé de una muestra y se quedó en mi respuesta. Lo he dejado fuera en mis ediciones posteriores.

por cierto. phpMyAdmin el soporte para la extensión espacial no es impecable, pero ayuda bastante ver lo que hay en su base de datos. Me ayudó con estas imágenes que adjunto.