= Map Algebra = '''NOTE: This document describes features that are still in development, and may not be available in the source tree.''' == ST_MapAlgebra Variants == The specification for PostGIS Raster includes a definition for the function ST_MapAlgebra. This function has many variants, but the main differences between the variants are that one set of variants take an expression, and the other set of variants take a user function. '''Expression-accepting''' variants are very user-friendly, in that any SQL expression can be evaluated on a cell-by-cell or neighbor-by-neighbor basis. This ease of use comes at a cost of performance, as the expanded and evaluated expression is slower than creating a user function. '''User function''' variants require a bit more involvement, since users must create a pl/pgsql function first. This user function is evaluated on a cell-by-cell or neighbor-by-neighbor basis. The expression-accepting variants are: {{{ #!sql ST_MapAlgebra(rast raster, band integer, pixeltype text, expression text, nodatavalueexpr text); ST_MapAlgebra(rast raster, band integer, expression text); ST_MapAlgebra(rast raster, pixeltype text, expression text); ST_MapAlgebra(rast raster, expression text); ST_MapAlgebra(rast raster, band integer, expression text, nodatavalueexpr text); ST_MapAlgebra(rast raster, pixeltype text, expression text, nodatavalueexpr text); }}} The user function variants are: {{{ #!sql ST_MapAlgebra(rast raster, band integer, pixeltype text, userfunction regprocedure, variadic args text[]); ST_MapAlgebra(rast raster, band integer, userfunction regprocedure); ST_MapAlgebra(rast raster, pixeltype text, userfunction regprocedure); ST_MapAlgebra(rast raster, userfunction regprocedure); ST_MapAlgebra(rast raster, band integer, userfunction regprocedure, variadic args text[]); ST_MapAlgebra(rast raster, pixeltype text, userfunction regprocedure, variadic args text[]); }}} === Performance === The performance differences between the two variants are described below: || || Expression (full debug) || User Function (full debug) || Hardcoded (full debug) || Expression || User Function || Hardcoded || ||SQL|| 27.314 s || 26.511 s || 22.932 s || 90.0 ms || 61.0 ms || 35.0 ms || || C || 1.557 s || 1.786 s || 1.338 s || 50.0 ms || 45.0 ms || 34.0 ms || The first test was performed with full debugging and logging turned on. This showed little difference between the user function and expression variants. The second test was performed in psql with debugging turned off, and times taken from the psql query timer. The datasets for these tests were a set of 25 rasters, each 10x10, 1 band, 16BUI. The tests showed an improvement in performance when using a user function, but the performance enhancements were reduced when testing the C versions of the functions versus the SQL prototype versions. Another test on a set of tiles with 1,000,000 total cells shows a 20x performance improvement (95 s for the expression vs. 4 s for the user function). This type of performance improvement was to be expected, moving away from the expression evaluation technique. === Future Research === One thing the expression evaluation technique may excel at is caching query results, and improving value calculation over rasters with mostly uniform data. It is not known if an expression result is cached by the SPI_ interface at the C level, in which case, a raster with uniform values would approach 100% cache hits with this configuration. More investigation is required to determine if this is a viable path to pursue. == User Functions == The functions that can be used as an user function must accept a float as the first parameter, and a variadic text array as the second parameter. This function can return any data type that can fit inside of the resulting raster. The following examples are user functions for the single-raster version of ST_MapAlgebra: {{{ #!sql CREATE OR REPLACE FUNCTION addconstant(cell FLOAT, VARIADIC args TEXT[]) RETURNS FLOAT AS $$ BEGIN return cell + 1; END; $$ LANGUAGE 'plpgsql'; }}} This user function is pretty brain-dead. A more interesting user function would be: {{{ #!sql CREATE OR REPLACE FUNCTION polynomial(x FLOAT, VARIADIC args TEXT[]) RETURNS FLOAT AS $$ DECLARE m FLOAT; b FLOAT; BEGIN m := args[1]::FLOAT; b := args[2]::FLOAT; return m * x + b; END; $$ LANGUAGE 'plpgsql'; }}}