Making slices through postgis raster tables

56 Views Asked by At

I have an existing python script that i am trying to "translate" into postgres sql. The reason for this is that the server im using for this is not running python.

The objective is to slice through several postgis raster tabels intersecting a specific elevation value. Each raster is the bottom of a layer in a geological model. For a given elevation (e.g. 0 meter below sealevel) i want the cells where the elevation is between the bottom (current layer) and the top (the previous raster layer) displaying the layer id of the bottom layer.

My problem is that the sql is terrible and the time of execution is very long. I have 50 postgis raster tables and many elevations to loop through. In comparisson the python script, using numpy and rasterio, executed with half an hour for all elevations. This sql script uses the same time for a single elevation.

The figure below is my poor attempt to draw the objectiv. The left is displaying the input raster tables from sideview, as well as the elevation to slice them. The right displays the output raster from topview, displaying the layer id (e.g. layer 1 and 2) from the slice.

image description

-- THIS IS ONLY RUNNING ONCE
DROP TABLE IF EXISTS mstfohm.table_name_out;

CREATE TABLE mstfohm.table_name_out AS 
WITH 
tmp AS 
(
    SELECT st_union(rast) AS rast
    FROM mstfohm.table_name_template
)
SELECT  
    ST_MapAlgebra(
            rast, 
            rast, 
            '[rast]^0'
    ) AS rast 
FROM tmp
;

-- THIS PART IS REPEATED A LOT OF TIMES FOR DIFFERENT ELEVATIONS AND RASTER TABLES
-- layer 1 
DROP TABLE IF EXISTS temp_raster;

CREATE TEMP TABLE temp_raster AS 
    (
        SELECT *
        FROM mstfohm.table_name_out
    )
;

DROP TABLE IF EXISTS mstfohm.table_name_out;

CREATE TABLE mstfohm.table_name_out AS 
WITH 
top AS 
(
    SELECT st_union(rast) AS rast
    FROM mstfohm.table_name_in1
),
bot AS 
(
    SELECT st_union(rast) AS rast
    FROM mstfohm.table_name_in2
),
topbot AS 
(
    SELECT 
        ST_MapAlgebra(
            top.rast, 
            bot.rast, 
            'CASE WHEN [rast1] >= 0 AND [rast2] < 0 THEN [rast1]^0 * 1 ELSE [rast2]^0 END'
            ) AS rast 
    FROM top, bot
)
SELECT 
    ST_MapAlgebra(
        a.rast, 
        b.rast, 
        'CASE WHEN [rast2] = 1 THEN [rast1] ELSE [rast2] END'
        ) AS rast
FROM topbot a, temp_raster b
;

-- layer 2 
DROP TABLE IF EXISTS temp_raster;

CREATE TEMP TABLE temp_raster AS 
    (
        SELECT *
        FROM mstfohm.table_name_out
    )
;

DROP TABLE IF EXISTS mstfohm.table_name_out;

CREATE TABLE mstfohm.table_name_out AS 
WITH 
top AS 
(
    SELECT st_union(rast) AS rast
    FROM mstfohm.table_name_in2
),
bot AS 
(
    SELECT st_union(rast) AS rast
    FROM mstfohm.table_name_in3
),
topbot AS 
(
    SELECT 
        ST_MapAlgebra(
            top.rast, 
            bot.rast, 
            'CASE WHEN [rast1] >= 0 AND [rast2] < 0 THEN [rast1]^0 * 2 ELSE [rast2]^0 END'
            ) AS rast 
    FROM top, bot
)
SELECT 
    ST_MapAlgebra(
        a.rast, 
        b.rast, 
        'CASE WHEN [rast2] = 1 THEN [rast1] ELSE [rast2] END'
        ) AS rast
FROM topbot a, temp_raster b
;

As a comparison the python script was in essence the code snippit below where the rasterio numpy array "array_slice" is updated through looping through all layers for each elevation.

array_slice = np.where(
    np.isnan(array_slice),
    np.where(
        (elevation_value >= array_bot) & (elevation_value <= array_top) (array_top != arr_bot),
        layer_id,
        array_slice 
    ),
    array_slice 
)
0

There are 0 best solutions below