2013-03-25 16 views
7

Próbuję wykonać rekursywne przecięcie wszystkich wielokątów w tabeli przestrzennej i uzyskać wynikowe (wielokropkowe) pololigony oraz informacje o każdym skrzyżowaniu dla każdego z nich.Posturcze rekurencyjne skrzyżowanie poligonów

Obraz (naprawdę nie w skali) to wytłumaczyć: Example

Powiedzmy istnieją A, B, C kwadratów w tabeli. Chciałbym mieć poligony A, B, C, A+B, A+C, B+C, A+B+C na wyjściu i muszę wiedzieć, że A+B jest przecięciem A i B i tak dalej.

Do tej pory mam zapytanie, które wykonuje przecięcia, ale nie "odcina" przeciętej części oryginalnych wielokątów. Na przykład:

Polygon A should be  A - (A+B) - (A+C) - (A+B+C) 
Polygon A+C should be A+C - (A+B+C) 

Obraz wyniku otrzymuję teraz na A i A+C wielokątów:

Current WRONG result

Oto skrypt testowy, korzystając z placów w obrazach jako dane. Patrząc na kolumnę area, jest jasne, że brakuje jakiejś rekurencyjnej wartości zmiennej ST_, po prostu nie mogę zrozumieć, jak to zrobić. Każdy pomysł jest mile widziany.

-- Create a test table 
CREATE TABLE test (
    name text PRIMARY KEY, 
    geom geometry(POLYGON) 
); 

-- Insert test data 
INSERT INTO test (name, geom) VALUES 
    ('A', ST_GeomFromText('POLYGON((1 2, 1 6, 5 6, 5 2, 1 2))')), 
    ('B', ST_GeomFromText('POLYGON((0 0, 0 4, 4 4, 4 0, 0 0))')), 
    ('C', ST_GeomFromText('POLYGON((2 0, 2 4, 6 4, 6 0, 2 0))')); 


-- Query  
WITH RECURSIVE 
source (rownum, geom, ret) AS (
    SELECT row_number() OVER (ORDER BY name ASC), ST_Multi(geom), ARRAY[name] FROM test 
), 
r (rownum, geom, ret, incroci) AS (
    SELECT rownum, geom, ret, 0 FROM source 
    UNION ALL 
    SELECT s.rownum, ST_CollectionExtract(ST_Intersection(s.geom, r.geom), 3), (r.ret || s.ret), (r.incroci + 1) 
     FROM source AS s INNER JOIN r ON s.rownum > r.rownum AND ST_Intersects(s.geom, r.geom) AND ST_Area(ST_Intersection(s.geom, r.geom)) > 0.5 
), 
result (geom, ret) AS (
    SELECT ST_Union(geom) AS geom, ret FROM r GROUP BY ret 
) 
SELECT geom, ST_Area(geom) AS area, ret FROM result ORDER BY ret 

funkcja Okno nie jest to bezwzględnie konieczne w tym konkretnym przykładzie, oczywiście, ale ten kod jest uproszczoną wersją mojego prawdziwego przypadku, który robi kilka rzeczy na boku.

Używam PostgreSQL 9.2 i postgis 2,0

Odpowiedz

4

ST_DIFFRENCE nie musi być rekurencyjne, masz już wszystkie wielokąty tak z każdym GeoM trzeba odjąć unię innych geoms które zawierają że ret ale nie jest równy temu. To działa, więc powinieneś zrobić to w ten sposób:

WITH RECURSIVE 
source (rownum, geom, ret) AS (
    SELECT row_number() OVER (ORDER BY name ASC), ST_Multi(geom), ARRAY[name] FROM test 
), 
r (rownum, geom, ret, incroci) AS (
    SELECT rownum, geom, ret, 0 FROM source 
    UNION ALL 
    SELECT s.rownum, ST_CollectionExtract(ST_Intersection(s.geom, r.geom), 3), (r.ret || s.ret), (r.incroci + 1) 
     FROM source AS s INNER JOIN r ON s.rownum > r.rownum AND ST_Intersects(s.geom, r.geom) AND ST_Area(ST_Intersection(s.geom, r.geom)) > 0.5 
), 
result (geom, ret) AS (
    SELECT ST_Difference(ST_Union(r.geom),q.geom) AS geom, r.ret FROM r JOIN (SELECT r.ret,ST_UNION(COALESCE(r2.geom,ST_GeomFromText('POLYGON EMPTY'))) as geom FROM r LEFT JOIN r AS r2 ON r.ret<@r2.ret AND r.ret!=r2.ret GROUP BY r.ret) AS q on r.ret=q.ret GROUP BY r.ret,q.geom 
) 
SELECT geom, ST_Area(geom) AS area, ret FROM result ORDER BY ret 
+0

Dziękuję bardzo, bardzo sprytnie, używając takiego połączenia! – Eggplant