sql >> Database >  >> RDS >> Mysql

Hoe kan ik omgaan met MySQL-polygoonoverlappende query's?

SQL-viool

Maak tabel met polygoonkolom

Houd er rekening mee dat u InnoDB niet kunt gebruiken om ruimtelijke indexen te gebruiken. U kunt de geometrie gebruiken zonder ruimtelijke indexen, maar de prestaties nemen zoals gewoonlijk af.

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;

Krijg 3 vierkanten en een driehoek ingevoegd

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

Selecteer alles wat het kleine vierkantje in de linker benedenhoek doorsnijdt (paars vierkant #1)

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

Selecteer alles wat de driehoek kruist, variërend van de linkerbenedenhoek tot de rechterbenedenhoek tot de rechterbovenhoek) (vierkanten #1 en #2 en driehoek #4.)

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

Selecteert alles in het vierkant dat buiten onze afbeelding valt (niets)

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

Bewerk #1:

Ik herlees de vraag en ik denk dat je de ruimtelijke relaties een beetje verward hebt. Als je alles wilt vinden dat volledig in een vierkant (polygoon) past, dan moet je Bevat/ST_Contains gebruiken. Zie ruimtelijke functies in MySQL-documentatie om erachter te komen welke functie het werk voor u doet. Let op het volgende verschil tussen ST/MBR-functies:

Selecteert alles wat volledig binnen een vierkant is (#0 van onder) (vierkanten #1, #2, driehoek #4)

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

Selecteert alles dat volledig binnen een vierkant is (#0 van onderaf) en deelt geen randen (vierkant #2, driehoek #4)

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

Bewerken #2:

Zeer mooie toevoeging van @StephanB (SQL fiddle )

Selecteer overlappende objecten

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
;

(houd er rekening mee dat u de AND s1.id < s2.id moet verwijderen als u werkt met CONTAINS , als CONTAINS(a,b) <> CONTAINS(b,a) while Intersects(a,b) = Intersects(b,a) )

In de volgende afbeelding (niet-limitatieve lijst):

  • 2 snijdt #6.

  • 6 snijdt #2

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

  • 1 snijdt #0, #5

  • 0 bevat #1, #3, #4 en #5 (#1, #3, #4 en #5 vallen binnen #0)

  • 1 bevat #5 (#5 ligt binnen #1)

  • 0 st_contains #3, #4 en #5

  • 1 st_contains #5

Bewerken #3:Zoeken op afstand/Werken in (met) cirkels

MySQL ondersteunt cirkel niet rechtstreeks als geometrie, maar u kunt de ruimtelijke functie Buffer(geometry,distance) gebruiken er omheen te werken. Wat Buffer() doet, is het creëren van een buffer van genoemde afstand rond geometrie. Als je begint met een geometriepunt, is de buffer inderdaad een cirkel.

U kunt zien wat buffer daadwerkelijk doet door gewoon te bellen:

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

(het resultaat is vrij lang, dus ik zal het hier niet posten) Het creëert in feite een polygoon die de buffer vertegenwoordigt - in dit geval (en mijn MariaDB) is het resultaat een polygoon van 126 punten, wat een cirkel benadert. Met zo'n veelhoek kun je werken zoals je met elke andere veelhoek zou werken. Er zou dus geen prestatiestraf moeten zijn.

Dus als u alle polygonen wilt selecteren die in een cirkel vallen je kunt het vorige voorbeeld spoelen en herhalen (dit vindt alleen het vierkant #3)

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

Selecteer alle polygonen die elkaar kruisen met een cirkel

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

Als u met andere vormen dan rechthoeken werkt, moet u de ST_* . gebruiken functies. Functies zonder ST_ gebruik een begrenzende rechthoek. Dus het vorige voorbeeld selecteert de driehoek #4, ook al staat deze niet in de cirkel.

Als Buffer() vrij grote polygonen maakt, zal er zeker wat prestatieverlies zijn bij het gebruik van de ST_Distance() methode. Ik kan het helaas niet kwantificeren. Je zult wat benchmarking moeten doen.

Een andere manier om objecten op afstand te vinden is met behulp van de ST_Distance() functie.

Selecteer alle elementen uit de tabel en bereken hun afstand vanaf punt POINT(6 15)

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

U kunt ST_Distance . gebruiken in WHERE clausule ook.

Selecteer alle elementen waarvan de afstand vanaf POINT(0 0) kleiner of gelijk is aan 10 (selecteert #1, #2 en #3)

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

Hoewel de afstand wordt berekend van het dichtstbijzijnde punt tot het dichtstbijzijnde punt. Het vergelijkbaar maken met de ST_Intersect . Dus in het bovenstaande voorbeeld wordt #2 geselecteerd, ook al past het niet helemaal binnen de cirkel.

En ja, het tweede argument (0) voor GeomFromText(text,srid) , geen enkele rol speelt, kunt u deze gerust negeren. Ik heb het uit een voorbeeld gehaald en het is een beetje blijven hangen in mijn antwoord. Ik heb het weggelaten in mijn latere bewerkingen.

btw. phpMyAdmin ondersteuning voor ruimtelijke uitbreiding is niet vlekkeloos, maar het helpt nogal wat om te zien wat er in uw database staat. Heeft me geholpen met deze afbeeldingen die ik heb bijgevoegd.




  1. Minimale logboekregistratie met INSERT...SELECT in lege geclusterde tabellen

  2. Dynamisch kolommen maken sql

  3. Hoe om te gaan met daglichtbesparing in de Oracle-database?

  4. Tabellen weergeven in de huidige database met PostgreSQL