Alterando a geometria da tabela de buffer dinamicamente no PostGIS usando SQL simples

8

Eu tenho duas tabelas no PostGIS, uma tabela point e uma point_buffer. A tabela de pontos possui um campo buffer_distance, com um valor padrão como 200. Agora eu quero alterar a geometria da tabela de buffer sempre que alterar o valor de buffer_distance na minha tabela de pontos. Eu posso fazer isso para uma única linha na minha tabela point_buffer usando o seguinte:

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

Mas sempre que estou tentando alterar toda a tabela point_buffer (soltando a cláusula WHERE na minha subconsulta), estou recebendo uma mensagem de erro:

'ERRO: mais de uma linha retornada por uma subconsulta usada como expressão'.

Minha pergunta é: posso alterar a tabela point_buffer inteira de uma só vez? Eu sei que uma opção está usando a for lopp, com o limite superior sendo o valor count da minha tabela de pontos e incrementando o valor point.gid. Mas eu quero fazer isso em SQL simples .

bandeja
fonte

Respostas:

5

Isso deve funcionar se você quiser alterar todos os buffers para 500:

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

Talvez uma visão faça mais sentido, que seja realmente dinâmica, sem necessidade de atualizações:

CREATE VIEW buffers
AS SELECT gid,
          ST_Buffer(the_geom,buffer_distance)
   FROM point;
underdark
fonte
Obrigado. Eu sabia que estava faltando algo muito bobo. No entanto, existe uma pequena alteração na sua solução. A cláusula WHERE deve ser WHERE point_buffer.gid = point.gid . Sim, uma visão realmente faria mais sentido. Eu também estava pensando nessa direção.
Thelastray 6/04/12
6

Você pode usar uma exibição, mas também pode usar gatilhos para atualizar automaticamente sua tabela de buffer quando modificar a tabela de pontos original. Isso é realmente útil se você não deseja regenerar os buffers toda vez que visualiza sua tabela, pois o cálculo de buffers é uma tarefa que exige muita CPU.

Aqui está um exemplo de código completo que o implementa: uma tabela de pontos e uma tabela point_buffer que são atualizadas automaticamente com base nas modificações da tabela de pontos.

Você pode testá-lo com o QGIS: abra as duas tabelas, entre no modo de edição na tabela de pontos. Mova um ponto ou altere o valor de buffer_distance e toda vez que você salvar, a camada do buffer será atualizada.

aproveitar :)

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
fonte