Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment puis-je gérer les requêtes de chevauchement de polygones MySQL ?

violon SQL

Créer un tableau avec une colonne de polygone

Veuillez noter que pour utiliser des index spatiaux, vous ne pouvez pas utiliser InnoDB. Vous pouvez utiliser la géométrie sans index spatiaux, mais les performances se dégradent comme d'habitude.

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;

Insérez 3 carrés et un triangle

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

Sélectionnez tout ce qui croise le petit carré dans le coin inférieur gauche (carré violet #1)

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

Sélectionnez tout ce qui croise le triangle allant du coin inférieur gauche au coin inférieur droit au coin supérieur droit) (carrés #1 et #2 et triangle #4.)

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

Sélectionne tout dans le carré qui est en dehors de notre image (rien)

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

Modifier #1 :

J'ai relu la question et je pense que tu as les relations spatiales un peu confuses. Si vous voulez trouver tout ce qui tient entièrement dans un carré (polygone), vous devez utiliser Contient/ST_Contains. Veuillez consulter les fonctions spatiales dans la documentation MySQL pour savoir quelle fonction fait le travail pour vous.Veuillez noter la différence suivante entre les fonctions ST/MBR :

Sélectionne tout ce qui est complètement à l'intérieur d'un carré (#0 à partir du bas) (carrés #1, #2, triangle #4)

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

Sélectionne tout ce qui est complètement à l'intérieur d'un carré (#0 à partir du bas) et ne partage aucun bord (carré #2, triangle #4)

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

Modifier #2 :

Très bel ajout de @StephanB (violon SQL )

Sélectionnez tous les objets qui se chevauchent

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
;

(notez simplement que vous devez supprimer le AND s1.id < s2.id si vous travaillez avec CONTAINS , comme CONTAINS(a,b) <> CONTAINS(b,a) tandis que Intersects(a,b) = Intersects(b,a) )

Dans l'image suivante (liste non exhaustive) :

  • 2 croise #6.

  • 6 intersecte #2

  • 0 croise #1, #2, #3, #4, #5

  • 1 coupe #0, #5

  • 0 contient #1, #3, #4 et #5 (#1, #3, #4 et #5 sont dans #0)

  • 1 contient #5 (#5 est dans #1)

  • 0 st_contains #3, #4 et #5

  • 1 er_contient #5

Modification 3 : Rechercher par distance/Travailler en (avec) cercles

MySQL ne prend pas directement en charge le cercle en tant que géométrie, mais vous pouvez utiliser la fonction spatiale Buffer(geometry,distance) pour travailler autour d'elle. Quel Buffer() fait, crée un tampon de ladite distance autour de la géométrie. Si vous commencez par un point de géométrie, le tampon est bien un cercle.

Vous pouvez voir ce que tampon fait réellement en appelant simplement :

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

(le résultat est assez long, donc je ne le posterai pas ici) Il crée en fait un polygone qui représente le tampon - dans ce cas (et mon MariaDB) le résultat est un polygone de 126 points, qui se rapproche d'un cercle. Avec un tel polygone, vous pouvez travailler comme vous le feriez avec n'importe quel autre polygone. Il ne devrait donc pas y avoir de pénalité de performance.

Donc, si vous voulez sélectionner tous les polygones qui tombent dans un cercle vous pouvez rincer et répéter l'exemple précédent (cela trouvera juste le carré #3)

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

Sélectionner tous les polygones qui se croisent avec un cercle

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

Lorsque vous travaillez avec des formes différentes des rectangles, vous devez utiliser le ST_* les fonctions. Fonctionne sans ST_ utiliser un rectangle de délimitation. Ainsi, l'exemple précédent sélectionne le triangle #4 même s'il n'est pas dans le cercle.

Comme Buffer() crée des polygones assez grands, il y aura certainement une pénalité de performance sur l'utilisation de la ST_Distance() méthode. Malheureusement, je ne peux pas le quantifier. Il va falloir faire un benchmark.

Une autre façon de trouver des objets par distance consiste à utiliser le ST_Distance() une fonction.

Sélectionnez tous les éléments du tableau et calculez leur distance à partir du point POINT(6 15)

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

Vous pouvez utiliser ST_Distance dans WHERE clause également.

Sélectionnez tous les éléments dont la distance de POINT(0 0) est inférieure ou égale à 10 (sélectionne #1, #2 et #3)

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

Bien que la distance soit calculée du point le plus proche au point le plus proche. Le rendre similaire à ST_Intersect . Ainsi, l'exemple ci-dessus sélectionnera #2 même s'il ne rentre pas entièrement dans le cercle.

Et oui, le deuxième argument (0) pour GeomFromText(text,srid) , ne joue aucun rôle, vous pouvez l'ignorer en toute sécurité. Je l'ai récupéré à partir d'un échantillon et il est resté coincé dans ma réponse. Je l'ai omis dans mes modifications ultérieures.

d'ailleurs. phpMyAdmin la prise en charge de l'extension spatiale n'est pas sans faille, mais cela aide un peu à voir ce qu'il y a dans votre base de données. M'a aidé avec ces images que j'ai jointes.