Scal dowolne sąsiednie wielokąty

22

Chciałbym wykonać testy przylegania na warstwie działki (wielokątów) i scalić je, jeśli spełniają określone kryteria (mogą być wielkości). Na poniższym zdjęciu chciałbym scalić wielokąty 1,2,3 i 4, ale nie 5.

Mam dwa problemy:

  1. ST_TOUCHESzwraca PRAWDA, jeśli dotykają się tylko narożniki, a nie segment linii. Myślę, że potrzebuję ST_RELATE, aby sprawdzić segmenty linii wspólnej.
  2. Idealnie chciałbym scalić WSZYSTKIE sąsiadujące wielokąty w jeden, ale nie jestem pewien, jak skalować poza dwa - jak w, scalić 1,2,3 i 4 (i prawdopodobnie więcej na temat rzeczywistych danych) w jednej rundzie.

Struktura, którą mam teraz, opiera się na samodzielnym połączeniu ST_TOUCHES.

wprowadź opis zdjęcia tutaj

Dane dotyczące zabawek

CREATE TABLE testpoly AS 
SELECT 
1 AS id, ST_PolyFromText('POLYGON ((0 0, 10 0, 10 20, 00 20, 0 0 ))') AS geom UNION SELECT
2 AS id, ST_PolyFromText('POLYGON ((10 0, 20 0, 20 20, 10 20, 10 0 ))') AS geom UNION SELECT
3 AS id, ST_PolyFromText('POLYGON ((10 -20, 20 -20, 20 0, 10 0, 10 -20 ))') AS geom UNION SELECT
4 AS id, ST_PolyFromText('POLYGON ((20 -20, 30 -20, 30 0, 20 0, 20 -20 ))') AS geom  UNION SELECT 
5 AS id, ST_PolyFromText('POLYGON ((30 0, 40 0, 40 20, 30 20, 30 0 ))') AS geom ;

Wybór

SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND t1.geom && t2.geom 
) 
l2

Oto wynik:

+-----+---------+-------------------------------------------------------------------------------+
| gid | adj_gid | geo_combo                                                                     |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 2       | POLYGON((10 0,0 0,0 20,10 20,20 20,20 0,10 0))                                |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 3       | MULTIPOLYGON(((10 0,0 0,0 20,10 20,10 0)),((10 0,20 0,20 -20,10 -20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 1       | POLYGON((10 20,20 20,20 0,10 0,0 0,0 20,10 20))                               |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 3       | POLYGON((10 0,10 20,20 20,20 0,20 -20,10 -20,10 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 4       | MULTIPOLYGON(((20 0,10 0,10 20,20 20,20 0)),((20 0,30 0,30 -20,20 -20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 1       | MULTIPOLYGON(((10 0,20 0,20 -20,10 -20,10 0)),((10 0,0 0,0 20,10 20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 2       | POLYGON((20 0,20 -20,10 -20,10 0,10 20,20 20,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 4       | POLYGON((20 -20,10 -20,10 0,20 0,30 0,30 -20,20 -20))                         |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 2       | MULTIPOLYGON(((20 0,30 0,30 -20,20 -20,20 0)),((20 0,10 0,10 20,20 20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 3       | POLYGON((20 0,30 0,30 -20,20 -20,10 -20,10 0,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 5       | MULTIPOLYGON(((30 0,30 -20,20 -20,20 0,30 0)),((30 0,30 20,40 20,40 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 5   | 4       | MULTIPOLYGON(((30 0,30 20,40 20,40 0,30 0)),((30 0,30 -20,20 -20,20 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+

Zauważ, że wielokąt id = 3 dzieli punkt z id = 1 i dlatego jest zwracany jako wynik dodatni. Jeśli zmienię klauzulę WHERE na, ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');nie otrzymam żadnych zapisów.

  1. Po pierwsze , jak określić ST_Relate, aby upewnić się, że brane są pod uwagę tylko paczki dzielące segment linii.

  2. A potem, jak mam połączyć wielokąty 1,2,3,4 w jednej rundzie, zwijając wyniki z powyższego wezwania, jednocześnie uznając, że przyleganie 1 do 2 jest takie samo jak odwrotność?

Aktualizacja

Jeśli dodam to do whereklauzuli, oczywiście otrzymam tylko wielokąty, a nie wieloboki, eliminując w ten sposób fałszywe pozytywy dla moich celów - szlifowanie narożników zostanie zignorowane.

GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'

Chociaż nie jest to idealne (wolałbym używać sprawdzeń topologii ST_RELATEjako bardziej ogólnego rozwiązania), jest to krok naprzód. Pozostaje zatem kwestia odszyfrowania i połączenia ich. Być może, gdybym mógł wygenerować sekwencję dla dotykających się tylko wielokątów, mógłbym się z tym połączyć.

Aktualizacja II

Ten wydaje się działać do wyboru wielokątów dzielących linie (ale nie narożniki), a zatem jest bardziej ogólnym rozwiązaniem niż powyższy MULTIPOLYGONtest. Moja klauzula where wygląda teraz tak:

WHERE
              ST_Touches( t1.geom, t2.geom ) 
              AND t1.geom && t2.geom 

              -- 'overlap' relation
              AND ST_Relate(t1.geom, t2.geom)='FF2F11212') t2 

Teraz pozostaje tylko to, jak wykonać scalenie dla więcej niż tylko pary wielokątów, ale dla dowolnej liczby pasującej do kryteriów za jednym razem.

ako
źródło
2
Jestem pewien, że ST_Relate to właściwy sposób. Rozwiązałem podobny problem, sprawdzając, czy długość skrzyżowań była większa od zera, aby wykluczyć przecięcia jednopunktowe. Hack, ale działa.
John Powell,
Jeśli istnieje sposób na grupowanie przyległych wielokątów w tablice, możesz zmodyfikować ST_IntersectionArray[funkcję] [1] do pracy ze ST_Union [1]: gis.stackexchange.com/a/60295/36886
raphael
2
Jeśli chodzi o grupowanie ze sobą sąsiadujących wielokątów, możesz zmodyfikować algorytm tworzenia klastrów typu bottom-up, który napisałem tutaj ( gis.stackexchange.com/a/115715/36886 ), aby przetestować raczej sąsiedztwo, a nie spację, a następnie użyć ST_Union podczas grupowania na wynikowych klasterach
raphael
3
Istnieje również ST_ClusterIntersectimg, który może zrobić to, czego potrzebujesz. Potrzebujesz Postgis 2.2
John Powell,

Odpowiedzi:

3

Nie mogłem przestać myśleć, że twój przykład jest w rzeczywistości rastrem i chociaż wspomniałeś, że chciałbyś się połączyć w oparciu o „określone kryteria (może to być rozmiar)”, chciałbym dać mu szansę na konwersję rastra.

W twoim konkretnym przykładzie będzie to działać:

WITH rast AS (
  SELECT 
  ST_UNION(ST_AsRaster(geom,10, 20, '2BUI')) r
  FROM testpoly 
)
,p AS (
    SELECT (ST_DumpAsPolygons(r)).geom FROM rast
)
SELECT t.id,p.* 
FROM p
LEFT JOIN testpoly  t ON ST_Equals(p.geom, t.geom)

Dzieje się tak, ponieważ ponieważ twoje wielokąty są idealnie wyrównanymi komórkami, ładnie przekształcą się w raster (rozmiar komórki 10 x 20). Dumpaspolygons pomaga ci tutaj, łącząc wszystkie sąsiednie komórki w jedną i porównując z oryginalnymi wielokątami, będziesz nawet w stanie odzyskać id dla niepołączonych poli.

Po wyjaśnieniu tego jestem bardzo ciekawy, jak to się skaluje i jak duży jest twój zestaw danych: D

przechylenie
źródło
Sprytny pomysł. Jest to jednak zabawkowy przykład - moje rzeczywiste dane to warstwa paczki, która nie będzie dobrze odwzorowana na rastry.
ako
3

Oto przykład, jak to zrobić w stylu proceduralnym z wieloma przejściami pod maską.

CREATE TABLE joined_testpoly AS SELECT array[id] ids, geom FROM testpoly; 

Powinieneś być w stanie przenosić więcej kolumn i zastosować dodatkowe kryteria łączenia, modyfikując sposób działania LIMIT 1poniższego wyboru:

CREATE OR REPLACE FUNCTION reduce_joined_testpoly()
RETURNS void
AS $$
DECLARE
  joined_row joined_testpoly%ROWTYPE;
BEGIN
  LOOP
     SELECT array_cat(a.ids, b.ids), st_union(a.geom, b.geom)
         INTO joined_row 
     FROM joined_testpoly a INNER JOIN joined_testpoly b
           on a.ids != b.ids
              and ST_Touches(a.geom, b.geom) and a.geom && b.geom 
              and ST_Relate(a.geom, b.geom)='FF2F11212'
         LIMIT 1;
     IF NOT FOUND THEN
           EXIT;
     END IF;
     INSERT INTO joined_testpoly VALUES (joined_row.ids, joined_row.geom);
     DELETE FROM joined_testpoly
         WHERE joined_testpoly.ids <@ joined_row.ids 
           AND joined_testpoly.ids != joined_row.ids;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;

Uruchom rzecz:

SELECT reduce_joined_testpoly();

Właściwe związki, bez wieloboków:

SELECT ids, st_geometrytype(geom), st_area(geom), st_numgeometries(geom) 
FROM joined_testpoly;
    ids    | st_geometrytype | st_area | st_numgeometries 
-----------+-----------------+---------+------------------
 {5}       | ST_Polygon      |     200 |                1
 {1,2,3,4} | ST_Polygon      |     800 |                1
Eoghan
źródło
2

Oto kolejna (nie działająca) strategia dla odniesienia (której nie udało mi się wykluczyć przypadku pojedynczego punktu dotykowego). Powinno być szybsze niż moja inna odpowiedź, ponieważ zajmuje tylko „jedno przejście”.

SELECT st_numgeometries(g), (SELECT st_union(x.geom) FROM st_dump(g) x GROUP BY g)
FROM (
    SELECT unnest(st_clusterintersecting(geom)) g, id < 100 as other_arbitrary_grouping 
    FROM testpoly
    GROUP BY other_arbitrary_grouping) c;

(prosimy o poprawienie i opublikowanie innej odpowiedzi, jeśli ktokolwiek może uzyskać geometrię id = 5 we własnej grupie)

Aby odzyskać listę identyfikatorów itp., Musisz st_containsponownie dołączyć do tabeli testpoly, jak opisano szczegółowo w następującej odpowiedzi: /programming//a/37486732/6691, ale nie mogłem tego uruchomić dla wielokątów z jakiegoś powodu.

Eoghan
źródło
2

Oto szybki cios przy użyciu nieco zmodyfikowanego oryginalnego zapytania:

with gr as (SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND ST_Relate(t1.geom,t2.geom, '****1****')
      AND t1.geom && t2.geom 
) 
l2) select ST_AsText(st_union(gr.geo_combo)) from gr;

Odnośniki: https://postgis.net/docs/using_postgis_dbmanagement.html#DE-9IM

cm1
źródło