Cambiar la geometría de la tabla de búfer dinámicamente en PostGIS usando SQL simple

8

Tengo dos tablas en PostGIS, una tabla point y una tabla point_buffer. La tabla de puntos tiene un campo buffer_distance, con un valor predeterminado que dice 200. Ahora quiero cambiar la geometría de la tabla de buffer cada vez que cambie el valor de buffer_distance en mi tabla de puntos. Puedo hacer esto para una sola fila en mi tabla point_buffer usando lo siguiente:

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

Pero cada vez que intento cambiar toda la tabla point_buffer (cláusula WHERE en mi subconsulta) recibo un mensaje de error:

'ERROR: más de una fila devuelta por una subconsulta utilizada como expresión'.

Mi pregunta es, ¿puedo cambiar toda la tabla point_buffer de una vez? Sé que una opción es usar un for lopp, con el límite superior como el valor de conteo de mi tabla de puntos e incrementando el valor de point.gid. Pero quiero hacer esto en SQL simple .

thelastray
fuente

Respuestas:

5

Eso debería funcionar si desea cambiar todos los búferes a 500:

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

Tal vez una vista tendría más sentido, eso sería realmente dinámico, no se necesitan actualizaciones:

CREATE VIEW buffers
AS SELECT gid,
          ST_Buffer(the_geom,buffer_distance)
   FROM point;
bajo oscuro
fuente
Gracias. Sabía que me faltaba algo muy tonto. Sin embargo, hay un pequeño cambio en su solución. La cláusula WHERE debe ser WHERE point_buffer.gid = point.gid . Sí, una vista realmente tendría más sentido. También estaba pensando en esa dirección.
thelastray
6

Puede usar una vista, pero también puede usar disparadores para actualizar automáticamente su tabla de búfer cuando modifica la tabla de puntos original. Esto es realmente útil si no desea regenerar los búferes cada vez que ve su tabla, ya que el cálculo del búfer es una tarea intensiva de la CPU.

Aquí hay un ejemplo de código completo que lo implementa: una tabla de puntos y una tabla point_buffer que se actualiza automáticamente en función de las modificaciones de la tabla de puntos.

Puede probarlo con QGIS: abra ambas tablas, ingrese al modo de edición en la tabla de puntos. Mueva un punto o cambie el valor de buffer_distance, y cada vez que guarde, la capa de búfer se actualizará.

disfruta :)

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
fuente