sql >> Databáze >  >> RDS >> Mysql

Jak mohu zpracovat dotazy na překrytí polygonů MySQL?

SQL housle

Vytvořit tabulku s polygonovým sloupcem

Vezměte prosím na vědomí, že pro použití prostorových indexů nemůžete použít InnoDB. Můžete použít geometrii bez prostorových indexů, ale výkon se jako obvykle snižuje.

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;

Vložte 3 čtverce a trojúhelník

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

Vyberte vše, co protíná malý čtverec v levém dolním rohu (fialový čtverec #1)

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

Vyberte vše, co protíná trojúhelník od levého dolního přes pravé dolní rohy až po pravý horní roh) (čtverce #1 a #2 a trojúhelník #4.)

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

Vybere vše ve čtverci, co je mimo náš obrázek (nic)

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

Úprava č. 1:

Znovu jsem si přečetl otázku a myslím, že máte trochu zmatené prostorové vztahy. Pokud chcete najít vše, co se celé vejde do čtverce (polygonu), musíte použít Contains/ST_Contains. Viz prostorové funkce v dokumentaci MySQL abyste zjistili, která funkce dělá práci za vás. Všimněte si prosím následujícího rozdílu mezi funkcemi ST/MBR:

Vybere vše, co je zcela uvnitř čtverce (#0 zdola) (čtverce #1, #2, trojúhelník #4)

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

Vybere vše, co je zcela uvnitř čtverce (#0 zdola) a nesdílí žádné hrany (čtverec č. 2, trojúhelník č. 4)

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

Úprava č. 2:

Velmi pěkný doplněk od @StephanB (SQL housle )

Vyberte všechny překrývající se objekty

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
;

(jen si všimněte, že byste měli odstranit AND s1.id < s2.id pokud pracujete s CONTAINS , jako CONTAINS(a,b) <> CONTAINS(b,a) zatímco Intersects(a,b) = Intersects(b,a) )

Na následujícím obrázku (neúplný seznam):

  • 2 protínají číslo 6.

  • 6 protíná #2

  • 0 protíná #1, #2, #3, #4, #5

  • 1 protíná #0, #5

  • 0 obsahuje #1, #3, #4 a #5 (#1, #3, #4 a #5 jsou v rámci #0)

  • 1 obsahuje #5 (#5 je v rámci #1)

  • 0 st_obsahuje #3, #4 a #5

  • 1 st_contains #5

Úprava č. 3:Vyhledávání podle vzdálenosti/Práce v (s) kruzích

MySQL přímo nepodporuje kruh jako geometrii, ale můžete použít prostorovou funkci Buffer(geometry,distance) obejít to. Co Buffer() vytváří vyrovnávací paměť uvedené vzdálenosti kolem geometrie. Pokud začnete s geometrickým bodem, vyrovnávací paměť je skutečně kruh.

Můžete vidět, co buffer skutečně dělá, když zavoláte:

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

(výsledek je poměrně dlouhý, takže ho zde nebudu zveřejňovat) Ve skutečnosti vytvoří polygon, který představuje vyrovnávací paměť - v tomto případě (a mé MariaDB) je výsledkem 126 bodový polygon, který aproximuje kruh. S takovým polygonem můžete pracovat jako s jakýmkoli jiným polygonem. Takže by neměl být žádný trest za výkon.

Pokud tedy chcete vybrat všechny polygony, které spadají do kruhu můžete opláchnout a zopakovat předchozí příklad (tím se najde pouze čtverec #3)

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

Vyberte všechny mnohoúhelníky, které se protínají s kruhem

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

Při práci s tvary odlišnými od obdélníků byste měli použít ST_* funkcí. Funguje bez ST_ použijte ohraničující obdélník. Předchozí příklad tedy vybírá trojúhelník #4, i když není v kruhu.

Jako Buffer() vytváří poměrně velké polygony, určitě dojde k určitému snížení výkonu oproti použití ST_Distance() metoda. Bohužel to neumím vyčíslit. Budete muset provést nějaký benchmarking.

Dalším způsobem hledání objektů podle vzdálenosti je použití ST_Distance() funkce.

Vyberte všechny prvky z tabulky a vypočítejte jejich vzdálenost od bodu POINT(6 15)

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

Můžete použít ST_Distance v WHERE klauzule také.

Vyberte všechny prvky, jejichž vzdálenost od POINT(0 0) je menší nebo rovna 10 (vybere #1, #2 a #3)

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

I když se vzdálenost počítá od nejbližšího bodu k nejbližšímu bodu. Je to podobné jako ST_Intersect . Výše uvedený příklad tedy vybere #2, i když se zcela nevejde do kruhu.

A ano, druhý argument (0) pro GeomFromText(text,srid) , nehraje žádnou roli, můžete jej klidně ignorovat. Zvedl jsem to z nějakého vzorku a trochu to uvízlo v mé odpovědi. Ve svých pozdějších úpravách jsem to vynechal.

Mimochodem. phpMyAdmin podpora pro prostorové rozšíření není bezchybná, ale docela pomáhá vidět, co je ve vaší databázi. Pomohl mi s těmito obrázky, které přikládám.




  1. Proč se v SQL nemůže NULL shodovat s NULL?

  2. Jak zkontrolovat, zda pole PostgreSQL obsahuje hodnotu

  3. MySQL Vyberte:WHERE (teď) =BETWEEN tablevalue AND tablevalue

  4. Chyba Android SQLite:číslo proměnné musí být mezi ?1 a ?999