Utwórz tabelę z kolumną wielokątną
Pamiętaj, że aby użyć indeksów przestrzennych, nie możesz użyć InnoDB. Możesz użyć geometrii bez indeksów przestrzennych, ale wydajność spada jak zwykle.
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;
Wstaw 3 kwadraty i trójkąt
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));
Zaznacz wszystko, co przecina mały kwadrat w lewym dolnym rogu (fioletowy kwadrat #1)
SELECT id,AsText(poly) FROM `spatial`
WHERE
ST_Intersects(`poly`,
GEOMFROMTEXT('POLYGON((0 0,2 0,2 2,0 2,0 0))', 0 )
)
;
Wybierz wszystko, co przecina trójkąt, od lewego dolnego do prawego dolnego narożnika do prawego górnego rogu) (kwadraty #1 i #2 oraz trójkąt #4.)
SELECT id,AsText(poly) FROM `spatial`
WHERE
ST_Intersects(`poly`,
GEOMFROMTEXT('POLYGON((0 0,50 50,50 0,0 0))', 0 )
)
;
Zaznacza wszystko w kwadracie, co znajduje się poza naszym obrazem (nic)
SELECT id,AsText(poly) FROM `spatial`
WHERE
ST_Intersects(`poly`,
GEOMFROMTEXT('POLYGON((100 100,200 100,200 200,100 200,100 100))', 0 )
)
;
Edytuj nr 1:
Ponownie przeczytałem pytanie i myślę, że masz trochę zamieszane relacje przestrzenne. Jeśli chcesz znaleźć wszystko, co mieści się w całości w kwadracie (wielokąt), musisz użyć opcji Contains/ST_Contains. Zobacz funkcje przestrzenne w dokumentacji MySQL aby dowiedzieć się, która funkcja spełnia Twoje zadanie. Zwróć uwagę na następującą różnicę między funkcjami ST/MBR:
Wybiera wszystko, co znajduje się w całości w kwadracie (nr 0 od dołu) (kwadraty #1, #2, trójkąt #4)
SELECT id,AsText(poly) FROM `spatial`
WHERE
Contains(
GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
`poly`
)
;
Zaznacza wszystko, co znajduje się w całości w kwadracie (nr 0 od dołu) i nie ma wspólnych krawędzi (kwadrat #2, trójkąt #4)
SELECT id,AsText(poly) FROM `spatial`
WHERE
ST_Contains(
GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
`poly`
)
;
Edytuj nr 2:
Bardzo fajny dodatek od @StephanB (skrzypce SQL )
Zaznacz dowolne nakładające się obiekty
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
;
(pamiętaj, że powinieneś usunąć AND s1.id < s2.id
jeśli pracujesz z CONTAINS
, jako CONTAINS(a,b) <> CONTAINS(b,a)
while Intersects(a,b) = Intersects(b,a)
)
Na poniższym obrazku (lista nie jest wyczerpująca):
-
2 przecięcia #6.
-
6 przecina #2
-
0 przecina #1, #2, #3, #4, #5
-
1 przecina #0, #5
-
0 zawiera #1, #3, #4 i #5 (#1, #3, #4 i #5 mieszczą się w #0)
-
1 zawiera #5 (#5 mieści się w #1)
-
0 st_zawiera #3, #4 i #5
-
1 st_zawiera #5
Edycja nr 3:Wyszukiwanie według odległości/Praca w kręgach (z)
MySQL nie obsługuje bezpośrednio okręgu jako geometrii, ale możesz użyć funkcji przestrzennej Buffer(geometry,distance)
obejść to. Co Buffer()
robi, tworzy bufor tej odległości wokół geometrii. Jeśli zaczniesz od punktu geometrycznego, bufor jest rzeczywiście okręgiem.
Możesz zobaczyć, co właściwie robi bufor, wywołując po prostu:
SELECT ASTEXT(BUFFER(GEOMFROMTEXT('POINT(5 5)'),3))
(wynik jest dość długi, więc nie będę go tutaj zamieszczać) W rzeczywistości tworzy wielokąt, który reprezentuje bufor - w tym przypadku (i mojej MariaDB) wynikiem jest wielokąt o długości 126 punktów, który przybliża okrąg. Z takim wielokątem możesz pracować tak, jak z każdym innym wielokątem. Więc nie powinno być żadnych spadków wydajności.
Więc jeśli chcesz zaznaczyć wszystkie wielokąty, które wpadają w okrąg możesz wypłukać i powtórzyć poprzedni przykład (zostanie wyświetlony tylko kwadrat #3)
SELECT id,AsText(poly) FROM `spatial`
WHERE
ST_Contains(
Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
`poly`
)
;
Zaznacz wszystkie wielokąty przecinające się z okręgiem
SELECT id,AsText(poly) FROM `spatial`
WHERE
ST_Intersects(
Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
`poly`
)
;
Pracując z kształtami innymi niż prostokąty, powinieneś użyć ST_*
Funkcje. Funkcje bez ST_
użyj prostokąta ograniczającego. Zatem w poprzednim przykładzie zaznaczono trójkąt #4, mimo że nie znajduje się on w okręgu.
Jako Buffer()
tworzy dość duże wielokąty, na pewno nastąpi spadek wydajności w porównaniu z użyciem ST_Distance()
metoda. Niestety nie potrafię tego określić ilościowo. Będziesz musiał przeprowadzić analizę porównawczą.
Innym sposobem znajdowania obiektów według odległości jest użycie ST_Distance()
funkcjonować.
Wybierz wszystkie elementy z tabeli i oblicz ich odległość od punktu POINT(6 15)
SELECT id, AsText(`poly`),
ST_Distance(poly, GeomFromText('POINT(6 15)'))
FROM `spatial`
;
Możesz użyć ST_Distance
w WHERE
również klauzulę.
Zaznacz wszystkie elementy, których odległość od POINT(0 0) jest mniejsza lub równa 10 (wybiera #1, #2 i #3)
SELECT id, AsText(`poly`),
ST_Distance(poly, GeomFromText('POINT(6 15)'))
FROM `spatial`
WHERE ST_Distance(poly, GeomFromText('POINT(6 15)')) <= 10
;
Chociaż odległość jest obliczana od najbliższego punktu do najbliższego punktu. Uczynienie go podobnym do ST_Intersect
. Tak więc powyższy przykład wybierze #2, mimo że nie mieści się w całości w okręgu.
I tak, drugi argument (0) dla GeomFromText(text,srid)
, nie odgrywa żadnej roli, można to spokojnie zignorować. Wziąłem to z jakiejś próbki i utknąłem w mojej odpowiedzi. Pominąłem to w moich późniejszych edycjach.
przy okazji phpMyAdmin obsługa rozszerzenia przestrzennego nie jest bezbłędna, ale pomaga całkiem sporo zobaczyć, co znajduje się w Twojej bazie danych. Pomógł mi z załączonymi obrazami.