I am working in a Postgresql database with PostGIS.
I need to automatically populate a PostGIS column with the intersection of another column's geometry and a landmass polygon. The current trigger function is not populating the column, returning NULL.
I expect: having the column "croppedtolandmass" filled with a single geometry of the intersection of the geometry column and my landmasses05deg grid.
I am getting: only NULLs in "croppedtolandmass".
No warning is raised.
All of the columns exists.
The table I want to update is defined like this:
CREATE TABLE IF NOT EXISTS public."MOBI_site"
(
"verbatimSiteID" text COLLATE pg_catalog."default" NOT NULL,
"datasetID" integer NOT NULL,
"footprintSRS" text COLLATE pg_catalog."default" NOT NULL,
"verbatimFootprintSRS" text COLLATE pg_catalog."default" NOT NULL,
geometry geometry,
croppedtolandmass geometry, --This is what I want to estimate on every update
CONSTRAINT "MOBI_site_pkey" PRIMARY KEY ("verbatimSiteID", "datasetID"),
CONSTRAINT "MOBI_site_MOBI_dataset_FK" FOREIGN KEY ("datasetID")
REFERENCES public."MOBI_dataset" ("datasetID") MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
)
My function and trigger code the following:
CREATE OR REPLACE FUNCTION update_cropped_geometry()
RETURNS TRIGGER AS $$
BEGIN
NEW.croppedtolandmass := ST_Intersection(
NEW.geometry::geography,
(SELECT ST_Union(geom) FROM wrld_grids.landmass05deg
WHERE ST_Intersects(ST_Envelope(NEW.geometry), ST_Envelope(geom))
AND ST_Intersects(NEW.geometry, geom)
)::geography
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_update_cropped_geometry
BEFORE UPDATE ON public."MOBI_site"
FOR EACH ROW
EXECUTE FUNCTION update_cropped_geometry();
I will appreciate any hint to discover what am I doing wrong.
I am working in a Postgresql database with PostGIS.
I need to automatically populate a PostGIS column with the intersection of another column's geometry and a landmass polygon. The current trigger function is not populating the column, returning NULL.
I expect: having the column "croppedtolandmass" filled with a single geometry of the intersection of the geometry column and my landmasses05deg grid.
I am getting: only NULLs in "croppedtolandmass".
No warning is raised.
All of the columns exists.
The table I want to update is defined like this:
CREATE TABLE IF NOT EXISTS public."MOBI_site"
(
"verbatimSiteID" text COLLATE pg_catalog."default" NOT NULL,
"datasetID" integer NOT NULL,
"footprintSRS" text COLLATE pg_catalog."default" NOT NULL,
"verbatimFootprintSRS" text COLLATE pg_catalog."default" NOT NULL,
geometry geometry,
croppedtolandmass geometry, --This is what I want to estimate on every update
CONSTRAINT "MOBI_site_pkey" PRIMARY KEY ("verbatimSiteID", "datasetID"),
CONSTRAINT "MOBI_site_MOBI_dataset_FK" FOREIGN KEY ("datasetID")
REFERENCES public."MOBI_dataset" ("datasetID") MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
)
My function and trigger code the following:
CREATE OR REPLACE FUNCTION update_cropped_geometry()
RETURNS TRIGGER AS $$
BEGIN
NEW.croppedtolandmass := ST_Intersection(
NEW.geometry::geography,
(SELECT ST_Union(geom) FROM wrld_grids.landmass05deg
WHERE ST_Intersects(ST_Envelope(NEW.geometry), ST_Envelope(geom))
AND ST_Intersects(NEW.geometry, geom)
)::geography
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_update_cropped_geometry
BEFORE UPDATE ON public."MOBI_site"
FOR EACH ROW
EXECUTE FUNCTION update_cropped_geometry();
I will appreciate any hint to discover what am I doing wrong.
Share Improve this question asked 2 days ago Gabriel Ortega-SolisGabriel Ortega-Solis 211 bronze badge New contributor Gabriel Ortega-Solis is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 7 | Show 2 more comments1 Answer
Reset to default 2Except a missing cast back to geometry
, there's nothing inherently wrong about the code you showed so I'd guess it's something about your data.
If I make my landmass05deg
small and scarce, most of my MOBI_site
geometries can't find anything to intersect with, so the update
will set croppedtolandmass
to null
. An intersection with nothing, is nothing.
To clarify: the trigger must be before update
or insert
in order to fire when you populate the table via insert
, merge..then insert
or copy
.
A plain before update
obviously only fires on update
, merge..then update
and insert..on conflict do update
.
If the trigger was defined after you pulled in your data, you need to run an unqualified update to make it fire for all existing rows. It might be a good idea to compare counts before and after, to see if it really changed nothing, or just very little.
demo at db<>fiddle
select count(*)
, count(*)filter(where croppedtolandmass is null) as nulls
from public."MOBI_site";
count | nulls |
---|---|
10000 | 10000 |
explain analyze verbose update public."MOBI_site" set croppedtolandmass=null;
The trigger intercepts it and overwrites that value everywhere (as long as your landmasses actually intersect your geometries):
QUERY PLAN |
---|
Update on public."MOBI_site" (cost=0.00..1327.99 rows=0 width=0) (actual time=1029.321..1029.321 rows=0 loops=1) |
-> Seq Scan on public."MOBI_site" (cost=0.00..1327.99 rows=30599 width=38) (actual time=0.171..8.603 rows=10000 loops=1) |
Output: NULL::geometry, ctid |
Planning Time: 0.057 ms |
Trigger trg_update_cropped_geometry: time=648.111 calls=10000 |
Execution Time: 1029.361 ms |
select count(*)
, count(*)filter(where croppedtolandmass is null) as nulls
from public."MOBI_site";
count | nulls |
---|---|
10000 | 9491 |
Side-note: the envelope intersection is redundant. Intersection of base geometries directly implies intersection of their envelopes On top of that, the ST_Intersects()
function is inlineable so you can run your query through EXPLAIN VERBOSE
and see there's a &&
operation on bboxes in it already:
explain analyze verbose
update public."MOBI_site" as t
set croppedtolandmass=ST_Intersection(
t.geometry::geography,
(SELECT ST_Union(geom) FROM wrld_grids.landmass05deg
WHERE ST_Intersects(ST_Envelope(t.geometry), ST_Envelope(geom))
AND ST_Intersects(t.geometry, geom)
)::geography
)::geometry;
QUERY PLAN |
---|
Update on public."MOBI_site" t (cost=0.00..779800.91 rows=0 width=0) (actual time=1201.927..1201.928 rows=0 loops=1) |
-> Seq Scan on public."MOBI_site" t (cost=0.00..779800.91 rows=9315 width=38) (actual time=0.080..274.986 rows=10000 loops=1) |
Output: (st_intersection((t.geometry)::geography, ((SubPlan 1))::geography))::geometry, t.ctid |
SubPlan 1 |
-> Aggregate (cost=83.40..83.41 rows=1 width=32) (actual time=0.007..0.007 rows=1 loops=10000) |
Output: st_union(landmass05deg.geom) |
-> Index Scan using landmass05deg_geom_idx on wrld_grids.landmass05deg (cost=0.13..58.40 rows=1 width=184) (actual time=0.005..0.005 rows=0 loops=10000) |
Output: landmass05deg.geom |
Index Cond: (landmass05deg.geom && t.geometry) |
Filter: (st_intersects(t.geometry, landmass05deg.geom) AND st_intersects(st_envelope(t.geometry), st_envelope(landmass05deg.geom))) |
Rows Removed by Filter: 0 |
Trigger trg_update_cropped_geometry: time=597.742 calls=10000 |
explain analyze verbose
update public."MOBI_site" as t
set croppedtolandmass=ST_Intersection(
t.geometry::geography,
(SELECT ST_Union(geom) FROM wrld_grids.landmass05deg
WHERE ST_Intersects(t.geometry, geom)
)::geography
)::geometry;
QUERY PLAN |
---|
Update on public."MOBI_site" t (cost=0.00..1089194.32 rows=0 width=0) (actual time=1245.480..1245.481 rows=0 loops=1) |
-> Seq Scan on public."MOBI_site" t (cost=0.00..1089194.32 rows=18630 width=38) (actual time=0.771..283.106 rows=10000 loops=1) |
Output: (st_intersection((t.geometry)::geography, ((SubPlan 1))::geography))::geometry, t.ctid |
SubPlan 1 |
-> Aggregate (cost=58.15..58.16 rows=1 width=32) (actual time=0.007..0.007 rows=1 loops=10000) |
Output: st_union(landmass05deg.geom) |
-> Index Scan using landmass05deg_geom_idx on wrld_grids.landmass05deg (cost=0.13..33.15 rows=1 width=184) (actual time=0.005..0.005 rows=0 loops=10000) |
Output: landmass05deg.geom |
Index Cond: (landmass05deg.geom && t.geometry) |
Filter: st_intersects(t.geometry, landmass05deg.geom) |
Rows Removed by Filter: 0 |
Trigger trg_update_cropped_geometry: time=604.371 calls=10000 |
geometry(geometry,4326)
) – JGH Commented 2 days agoST_Intersects()
function is inlineable so you can run your query throughexplain verbose
and see there's a&&
operation on bboxes in it already. Except a missing cast back togeometry
, there's nothing inherently wrong about the code you showed so I'd guess it's something about your data. – Zegarek Commented 2 days agoMOBI_site
geometries can't find anything to intersect with, so the update will setcroppedtolandmass
tonull
. An intersection with nothing, is nothing: demo. – Zegarek Commented 2 days ago