Mengubah geometri tabel Buffer secara dinamis di PostGIS menggunakan SQL biasa

8

Saya memiliki dua tabel di PostGIS, satu titik dan satu tabel point_buffer. Tabel titik memiliki bidang buffer_distance, dengan nilai default katakan 200. Sekarang saya ingin mengubah geometri tabel buffer setiap kali saya mengubah nilai buffer_distance di tabel titik saya. Saya bisa melakukan ini untuk satu baris di tabel point_buffer saya menggunakan yang berikut:

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

Tetapi setiap kali saya mencoba mengubah seluruh tabel point_buffer (menjatuhkan klausa WHERE di sub-kueri saya), saya menerima pesan kesalahan:

'ERROR: lebih dari satu baris dikembalikan oleh subquery yang digunakan sebagai ekspresi'.

Pertanyaan saya adalah, dapatkah saya mengubah seluruh tabel point_buffer sekaligus? Saya tahu satu opsi menggunakan for lopp, dengan batas atas menjadi nilai hitungan tabel titik saya dan menambah nilai point.gid. Tetapi saya ingin melakukan ini dalam SQL sederhana .

rayray
sumber

Jawaban:

5

Itu akan berhasil jika Anda ingin mengubah semua buffer menjadi 500:

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

Mungkin tampilan akan lebih masuk akal, itu benar-benar dinamis, tidak perlu pembaruan:

CREATE VIEW buffers
AS SELECT gid,
          ST_Buffer(the_geom,buffer_distance)
   FROM point;
underdark
sumber
Terima kasih. Saya tahu saya kehilangan sesuatu yang sangat konyol. Namun perubahan kecil ada di solusi Anda. Klausa WHERE harus WHERE point_buffer.gid = point.gid . Ya, tampilan akan lebih masuk akal. Saya juga berpikir ke arah itu.
thelastray
6

Anda dapat menggunakan tampilan, tetapi Anda juga dapat menggunakan pemicu untuk secara otomatis memperbarui tabel buffer Anda ketika Anda memodifikasi tabel titik asli. Ini sangat berguna jika Anda tidak ingin membuat ulang buffer setiap kali Anda melihat tabel Anda, karena perhitungan buffer adalah tugas intensif CPU.

Berikut ini adalah contoh kode lengkap yang mengimplementasikannya: tabel titik dan tabel point_buffer yang diperbarui secara otomatis berdasarkan modifikasi tabel titik.

Anda dapat mengujinya dengan QGIS: buka kedua tabel, masukkan mode edit pada tabel titik. Memindahkan titik atau mengubah nilai buffer_distance, dan setiap kali Anda menyimpan, lapisan buffer akan diperbarui.

Nikmati :)

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
sumber