I have 1 year of daily climate data in a single raster table in a postgis-enabled postgres database. It has 365 bands (one for each day). How do I average all the bands to get a single annual average per pixel? I tried ST_Union, but it returns all the bands, or I'm not using it correctly:
select rid, st_union(rast, 'MEAN')
from climate_table
group by rid;
I figured out a workaround using ST_DumpAsPolygons, but it is very slow. Any suggestions appreciated. (Also, I can't believe "bands" is not yet a tag, and I don't have enough reputation to create it!)
According to the documentation he function ST_Union()
takes a set of rasters. You have a raster with many bands.
You can use ST_Union
but you have to transform your data into a set of rasters where each one contains 1 band first.
Here is a testcase:
-- Create a table and add a row with an empty raster:
CREATE TABLE r_src(rid integer primary key, rast raster);
INSERT INTO r_src values(1,ST_MakeEmptyRaster(4,4,0,0,1));
-- Add two band layers to this raster one with value 10 for each point,
-- the other with value 4 for each point.
UPDATE r_src
SET rast = ST_AddBand(rast,'4BUI'::text,10)
WHERE rid = 1;
UPDATE r_src
SET rast = ST_AddBand(rast,'4BUI'::text,4)
WHERE rid = 1;
-- check what we have
SELECT (rmd).width, (rmd).height, (rmd).numbands
FROM (SELECT ST_MetaData(rast) As rmd
FROM r_src WHERE rid = 1) AS foo;
-- 4| 4| 2 --ok
-- output rasterdata in hex for both bands:
SELECT ST_AsHexWKB(rast) As rastbin FROM r_src WHERE rid=1;
-- output rasterdata for the 2nd band (note the '...040404' point values
-- compared to '...0A0A0A' if you select the band at idx 1
SELECT ST_AsHexWKB(ST_Band(rast,2))FROM r_src WHERE rid = 1;
-- now step by step...
-- src is the single rast in our source table,
-- in extracted this is joined with a generated series
-- for each value in the series a new raster is returned
-- containing the band at that index:
WITH src AS (SELECT rast FROM r_src WHERE rid = 1),
extracted AS (SELECT ST_Band(src.rast,ser.idx)
FROM src
JOIN generate_series(1,2) AS ser(idx ) ON true)
SELECT * FROM extracted;
-- outputs 2 rows with one raster each, and each raster containing one band
-- now aggreagating these:
WITH src AS (SELECT rast FROM r_src WHERE rid = 1),
extracted AS (SELECT ST_Band(src.rast,ser.idx) AS r_day
FROM src
JOIN generate_series(1,2) AS ser(idx ) ON true)
SELECT ST_Union(r_day,1,'MEAN') FROM extracted;
-- outputs one row with the resulting raster holding one band
-- that band contains the 'MEAN' point values '...070707'
I checked this out with a dataset you linked to and imported it into postgres using raster2pgsql
. Due to its size, I decided to split it into tiles of 100x100 and got a table with 45 rows. Now to convert the whole raster I used the following statement:
CREATE TABLE averages AS
WITH extracted AS (SELECT src.rid , ST_Band(src.rast,ser.idx) AS r_day
FROM rastertest src
JOIN generate_series(1,365) AS ser(idx ) ON true)
SELECT rid, ST_Union(r_day,1,'MEAN') AS rast
FROM extracted
GROUP BY rid;
This creates a new table averages with the average values only.