Dynamisches Ändern der Puffertabellengeometrie in PostGIS mithilfe von einfachem SQL

8

Ich habe zwei Tabellen in PostGIS, eine point- und eine point_buffer-Tabelle. Die Punkttabelle hat ein Feld buffer_distance mit einem Standardwert von beispielsweise 200. Jetzt möchte ich die Geometrie der Puffertabelle ändern, wenn ich den Wert buffer_distance in meiner Punkttabelle ändere. Ich kann dies für eine einzelne Zeile in meiner point_buffer-Tabelle folgendermaßen tun:

UPDATE point_buffer
SET the_geom = (SELECT ST_Buffer(the_geom,500) FROM point WHERE gid = 1)
FROM point
WHERE point.gid = point_buffer.gid

Aber wenn ich versuche, die gesamte point_buffer-Tabelle zu ändern (WHERE-Klausel in meiner Unterabfrage löschen), erhalte ich die folgende Fehlermeldung:

'FEHLER: Mehr als eine Zeile, die von einer als Ausdruck verwendeten Unterabfrage zurückgegeben wird'.

Meine Frage ist, kann ich die gesamte point_buffer-Tabelle auf einmal ändern? Ich weiß, dass eine Option die Verwendung von for lopp ist, wobei die Obergrenze der Zählwert meiner Punktetabelle ist und der point.gid-Wert erhöht wird. Aber ich möchte dies in einfachem SQL tun .

der letzte Strahl
quelle

Antworten:

5

Das sollte funktionieren, wenn Sie alle Puffer auf 500 ändern möchten:

UPDATE point_buffer
SET the_geom = (SELECT ST_Buffer(point.the_geom,500)
                FROM point
                WHERE point.gid = point_buffer.gid);

Vielleicht wäre eine Ansicht sinnvoller, das wäre wirklich dynamisch, keine Updates erforderlich:

CREATE VIEW buffers
AS SELECT gid,
          ST_Buffer(the_geom,buffer_distance)
   FROM point;
Unterdunkel
quelle
Vielen Dank. Ich wusste, dass mir etwas sehr Dummes fehlte. Es gibt jedoch eine kleine Änderung in Ihrer Lösung. Die WHERE-Klausel sollte WHERE point_buffer.gid = point.gid sein . Ja, eine Ansicht wäre wirklich sinnvoller. Ich habe auch in diese Richtung gedacht.
letzte Strahl
6

Sie können eine Ansicht verwenden, aber Sie können auch Trigger verwenden, um Ihre Puffertabelle automatisch zu aktualisieren, wenn Sie die ursprüngliche Punktetabelle ändern. Dies ist sehr nützlich, wenn Sie die Puffer nicht jedes Mal neu generieren möchten, wenn Sie Ihre Tabelle anzeigen, da die Pufferberechnung eine CPU-intensive Aufgabe ist.

Hier ist ein vollständiges Codebeispiel, das es implementiert: eine Punkttabelle und eine point_buffer-Tabelle, die basierend auf den Änderungen der Punkttabelle automatisch aktualisiert werden.

Sie können es mit QGIS testen: Öffnen Sie beide Tabellen und rufen Sie den Bearbeitungsmodus für die Punktetabelle auf. Verschieben Sie einen Punkt oder ändern Sie den Wert für buffer_distance. Jedes Mal, wenn Sie speichern, wird die Pufferschicht aktualisiert.

genießen :)

drop table if exists point;
create table point (
    gid serial primary key
    , point_name varchar
    , buffer_distance double precision
    , the_geom geometry
);

drop table if exists point_buffer;
create table point_buffer (
    gid serial primary key
    , point_gid integer
    , the_geom geometry
);

select populate_geometry_columns();

insert into 
    point (point_name, buffer_distance, the_geom) 
select
    'point ' || n::varchar as point_name
    , random() * 100 + min_buf as buffer_distance
    , st_setsrid(st_point(random() * 10000 + x0, random() * 10000 + y0), 2154) as the_geom
from
        generate_series(1, 1000) as n
        , (values (10)) as foox(x0)
        , (values (10)) as fooy(y0)
        , (values (10)) as buf(min_buf);

-- insert values into point_buffer
insert into
    point_buffer (point_gid, the_geom)
select
    gid as point_gid
    , st_buffer(the_geom, buffer_distance)
from
    point;

-- update all point_buffer
update
    point_buffer as pb
set
    the_geom = st_buffer(p.the_geom, p.buffer_distance)
from
    point as p
where
    p.gid = pb.point_gid;

-- add trigger to automate insert / delete / update
create or replace function update_point_buffer() returns trigger as
$$
begin
    -- delete
    IF (TG_OP = 'DELETE') THEN
        delete from point_buffer as pb where point_gid = OLD.gid;
        return OLD;
    -- insert
    ELSIF (TG_OP = 'INSERT') THEN
        insert into
            point_buffer (point_gid, the_geom)
        select
            NEW.gid as point_gid
            , st_buffer(NEW.the_geom, NEW.buffer_distance);
        return NEW;
    -- update
    else
        update
            point_buffer as pb
        set
            the_geom = st_buffer(NEW.the_geom, NEW.buffer_distance)
        where
            pb.gid = NEW.gid;
        return NEW;
    END IF;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_point_point_buffer ON point;
CREATE TRIGGER trg_point_point_buffer AFTER DELETE OR INSERT OR UPDATE ON point
    FOR EACH ROW EXECUTE PROCEDURE update_point_buffer();

/* use it */

-- insert
insert into 
    point (point_name, buffer_distance, the_geom)
select
    'added point to test trigger' as point_name
    , random() * 100 + min_buf as buffer_distance
    , st_setsrid(st_point(random() * 10000 + x0, random() * 10000 + y0), 2154) as the_geom
from
        (values (10)) as foox(x0)
        , (values (10)) as fooy(y0)
        , (values (10)) as buf(min_buf);

select
    st_astext(pb.the_geom)
    , *
from 
    point_buffer as pb
join
    point as p
on
    p.gid = pb.point_gid
where
    p.point_name = 'added point to test trigger';

-- update
update 
    point as p
set
    the_geom = st_setsrid(st_point(0, 0), 2154)
    , buffer_distance = 1
where
    p.point_name = 'added point to test trigger';

-- check point_buffer
select
    st_astext(pb.the_geom)
    , *
from 
    point_buffer as pb
join
    point as p
on
    p.gid = pb.point_gid
where
    p.point_name = 'added point to test trigger';

-- delete
delete from
    point as p
where
    p.point_name = 'added point to test trigger';

-- check point_buffer
select
    *
from
    point_buffer as pb
where
    point_gid = 1001;
Vincent
quelle