Changes between Initial Version and Version 1 of PostGIS_Raster_SoC_Idea_2012/Distance_Analysis_Tools/code


Ignore:
Timestamp:
Aug 20, 2012, 7:57:42 AM (12 years ago)
Author:
qliu
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • PostGIS_Raster_SoC_Idea_2012/Distance_Analysis_Tools/code

    v1 v1  
     1{{{
     2#!html
     3<div style='background-color: #F4F4F4; border: 1px solid gray; width:400px;' >
     4<br>
     5<UL>
     6<LI><A href=http://trac.osgeo.org/postgis/wiki/PostGIS_Raster_SoC_Idea_2012/Distance_Analysis_Tools/code#EuclideanDistance>Euclidean Distance</A></LI>
     7<LI><A href=http://trac.osgeo.org/postgis/wiki/PostGIS_Raster_SoC_Idea_2012/Distance_Analysis_Tools/code#CostDistance>Cost Distance</A></LI>
     8</UL>
     9<br>
     10</div>
     11}}}
     12
     13== Euclidean Distance ==
     14
     15'''ST_EuclideanDistance()'''
     16
     17{{{
     18#!sql
     19--------------------------------------------------------------------
     20-- ST_EuclideanDistance - (for one raster) Return a raster generated from
     21--                        a set of raster specifications, which values are
     22--                                                the Euclidean distance from pixel to the points
     23--                        (for now only concerning point geometries)
     24--                        from the input source table of geometries
     25--                        (points for now, lines or polygons in the future).
     26-- Arguments
     27-- width,height,rastulx,rastuly,rastscalex,rastscaley,rastskewx,rastskewy
     28--  - Dimenssions and georeference of resulted raster.
     29-- rastsrid - SRID of the resulted raster
     30-- rastndv - Nodata value for resulted raster
     31-- pixeltype text - Pixeltype assigned to the resulting raster. By default
     32--                  to be the pixeltype of the reference raster if not specified.
     33-- sourceschema text - Database schema of the source geometry table.
     34-- sourcetable text - Source geometry table name.
     35-- sourcegeomcolumn text - Source geometry table geometry column name.
     36-- maxdist double precision - Maximum distance from pixel to the source
     37--                            when the source is too far from the pixel.
     38--                            Pixel that exceeds it will be assigned nodatavalue.
     39--------------------------------------------------------------------
     40DROP FUNCTION IF EXISTS ST_EuclideanDistance(width integer,
     41                                                                                         height integer,
     42                                                                                         rastulx float8,
     43                                                                                         rastuly float8,
     44                                                                                         rastscalex float8,
     45                                                                                         rastscaley float8,
     46                                                                                         rastskewx float8,
     47                                                                                         rastskewy float8,
     48                                                                                         rastsrid integer,
     49                                                                                         rastndv float8,
     50                                                                                         pixeltype text,
     51                                                                                         sourceschema text,
     52                                                                                         sourcetable text,
     53                                                                                         sourcegeomcolumn text,
     54                                                                                         double precision = -1);
     55CREATE OR REPLACE FUNCTION ST_EuclideanDistance(width integer,
     56                                                                                                height integer,
     57                                                                                                rastulx float8,
     58                                                                                                rastuly float8,
     59                                                                                                rastscalex float8,
     60                                                                                                rastscaley float8,
     61                                                                                                rastskewx float8,
     62                                                                                                rastskewy float8,
     63                                                                                                rastsrid integer,
     64                                                                                                rastndv float8,
     65                                                                                                pixeltype text,
     66                                                                                                sourceschema text,
     67                                                                                                sourcetable text,
     68                                                                                                sourcegeomcolumn text,
     69                                                                                                double precision = -1)
     70    RETURNS raster AS
     71    $$
     72    DECLARE
     73                maxdist ALIAS FOR $15;
     74        x integer;
     75        y integer;
     76                sourcex float8;
     77                sourcey float8;
     78                sourcexr integer;
     79                sourceyr integer;
     80                sourcesrid integer;
     81                newx float8;
     82                newy float8;
     83                exesql text;
     84        newnodatavalue float8;
     85                newinitialvalue float8;
     86        newrast raster;
     87        newval float8;
     88        newpixeltype text;
     89                -- Assuming reference raster has only one band
     90                band integer := 1;
     91                geom geometry;
     92    BEGIN
     93                -- Assuming source point table has one SRID for all geometries
     94                EXECUTE 'SELECT DISTINCT ST_SRID(' || sourcegeomcolumn || ') FROM ' || sourceschema || '.' || sourcetable || ';' INTO sourcesrid;
     95
     96        -- Create a new empty raster using provided georeference
     97        newrast := ST_MakeEmptyRaster(width, height, rastulx, rastuly, rastscalex,rastscaley, rastskewx, rastskewy, rastsrid);
     98
     99        -- Check if the new raster is empty (width = 0 OR height = 0)
     100        IF ST_IsEmpty(newrast) THEN
     101            RAISE NOTICE 'ST_EuclideanDistance: Raster is empty. Returning an empty raster';
     102            RETURN newrast;
     103        END IF;
     104       
     105        -- Set the new pixeltype
     106        newpixeltype := pixeltype;
     107                IF newpixeltype != '1BB' AND newpixeltype != '2BUI' AND newpixeltype != '4BUI' AND newpixeltype != '8BSI' AND newpixeltype != '8BUI' AND
     108           newpixeltype != '16BSI' AND newpixeltype != '16BUI' AND newpixeltype != '32BSI' AND newpixeltype != '32BUI' AND newpixeltype != '32BF' AND newpixeltype != '64BF' THEN
     109            RAISE EXCEPTION 'ST_EuclideanDistance: Invalid pixeltype "%". Aborting.', newpixeltype;
     110        END IF;
     111
     112        -- Check for notada value
     113        newnodatavalue := rastndv;
     114        IF newnodatavalue IS NULL OR newnodatavalue < ST_MinPossibleValue(newpixeltype) OR newnodatavalue > (-ST_MinPossibleValue(newpixeltype) - 1) THEN
     115            RAISE NOTICE 'ST_EuclideanDistance: Reference raster do not have a nodata value or is out of range for the new raster pixeltype, nodata value for new raster set to the min value possible';
     116            newnodatavalue := ST_MinPossibleValue(newpixeltype);
     117        END IF;
     118        -- We set the initial value of the resulting raster to nodata value.
     119        -- If nodatavalue is null then the raster will be initialise to ST_MinPossibleValue
     120        newinitialvalue := newnodatavalue;
     121       
     122        -- Create the raster receiving all the computed distance values. Initialize it to the new initial value.
     123        newrast := ST_AddBand(newrast, newpixeltype, newinitialvalue, newnodatavalue);
     124
     125                -- Set raster pixel value to zero if pixel is source point
     126                exesql := 'SELECT ' || sourcegeomcolumn || ' FROM ' || sourceschema || '.' || sourcetable || ';';
     127                FOR geom IN EXECUTE(exesql) LOOP
     128                        sourcex := ST_X(geom);
     129                        sourcey := ST_Y(geom);
     130                        sourcexr := ST_World2RasterCoordX(newrast, sourcex, sourcey);
     131                        sourceyr := ST_World2RasterCoordY(newrast, sourcex, sourcey);
     132                        -- If source point within raster range, then set pixel value to zero
     133                        IF sourcexr <= width AND sourceyr <= height THEN
     134                                newrast := ST_SetValue(newrast, band, sourcexr, sourceyr, 0);
     135                        END IF;
     136                END LOOP;
     137               
     138                -- Scan pixels in the raster set pixel value to the computed Eculidean distance
     139                -- to its nearest source point.
     140                -- There is place for optimization here for a more efficient scanning method
     141        FOR x IN 1..width LOOP
     142            FOR y IN 1..height LOOP
     143                                newx := ST_Raster2WorldCoordX(newrast, x, y);
     144                                newy := ST_Raster2WorldCoordY(newrast, x, y);
     145                                -- If pixel is not the source point then compute Eculidean distance
     146                                IF ST_Value(newrast, band, x, y) != 0 OR ST_Value(newrast, band, x, y) IS NULL THEN
     147                                        exesql := 'SELECT ST_Distance(ST_SetSRID(ST_MakePoint(' || newx || ',' || newy || '),' || sourcesrid || '), (SELECT ' ||  sourcegeomcolumn || ' FROM ' || sourceschema || '.' || sourcetable || ' ORDER BY ' || sourcegeomcolumn || ' <-> ST_SetSRID(ST_MakePoint(' || newx || ',' || newy || '),' || sourcesrid || ') LIMIT 1));';
     148                                        EXECUTE exesql INTO newval;
     149                                        -- If max distance specified
     150                                        IF maxdist != -1 THEN
     151                                                -- Any distance exceeds max distance is assigned nodata value
     152                                                IF newval > maxdist THEN
     153                                                        newval = newnodatavalue;
     154                                                END IF;
     155                                        END IF;
     156                                END IF;
     157                                newrast := ST_SetValue(newrast, band, x, y, newval);
     158            END LOOP;
     159        END LOOP;
     160
     161        RETURN newrast;
     162    END;
     163    $$
     164    LANGUAGE 'plpgsql';
     165       
     166-- test
     167-- CREATE TABLE test_eudist_2 AS (SELECT 1 AS rid,ST_EuclideanDistance(10, 10, 0, 0, 9, 9, 0, 0, 4326,-1,'32BF','public','test_geom','the_geom') AS rast FROM test_raster);
     168-- CREATE TABLE test_eudist_22 AS (SELECT 1 AS rid,ST_EuclideanDistance(10, 10, 0, 0, 9, 9, 0, 0, 4326,-1,'32BF','public','test_geom','the_geom',4954880) AS rast FROM test_raster);
     169
     170
     171--------------------------------------------------------------------
     172-- ST_EuclideanDistance - (for one raster) Return a raster generated
     173--                                                from a one band reference raster,
     174--                        which values are the Euclidean distance
     175--                        from pixel to the points
     176--                                                (for now only concerning point geometries)
     177--                        from the input source table of geometries
     178--                        (points for now, lines or polygons in the future).
     179-- Arguments
     180-- refrast raster -  Reference raster align with which Euclidean distance
     181--                                       raster will be created.
     182-- pixeltype text - Pixeltype assigned to the resulting raster. By default
     183--                  to be the pixeltype of the reference raster if not specified.
     184-- sourceschema text - Database schema of the source geometry table.
     185-- sourcetable text - Source geometry table name.
     186-- sourcegeomcolumn text - Source geometry table geometry column name.
     187-- maxdist double precision - Maximum distance from pixel to the source
     188--                            when the source is too far from the pixel.
     189--                            Pixel that exceeds it will be assigned nodatavalue.
     190--------------------------------------------------------------------
     191DROP FUNCTION IF EXISTS ST_EuclideanDistance(refrast raster,
     192                                                                                         pixeltype text,
     193                                                                                         sourceschema text,
     194                                                                                         sourcetable text,
     195                                                                                         sourcegeomcolumn text,
     196                                                                                         double precision = -1);
     197CREATE OR REPLACE FUNCTION ST_EuclideanDistance(refrast raster,
     198                                                                                                pixeltype text,
     199                                                                                                sourceschema text,
     200                                                                                                sourcetable text,
     201                                                                                                sourcegeomcolumn text,
     202                                                                                                double precision = -1)
     203    RETURNS raster AS
     204    $$
     205    DECLARE
     206                maxdist ALIAS FOR $6;
     207        width integer;
     208        height integer;
     209        x integer;
     210        y integer;
     211                sourcex float8;
     212                sourcey float8;
     213                sourcexr integer;
     214                sourceyr integer;
     215                sourcesrid integer;
     216                newsrid integer;
     217                newx float8;
     218                newy float8;
     219                exesql text;
     220        newnodatavalue float8;
     221                newinitialvalue float8;
     222        newrast raster;
     223        newval float8;
     224        newpixeltype text;
     225                -- Assuming reference raster has only one band
     226                band integer := 1;
     227                geom geometry;
     228    BEGIN
     229        -- Check if reference raster is NULL
     230        IF refrast IS NULL THEN
     231            RAISE NOTICE 'ST_EuclideanDistance: Reference raster is NULL. Returning NULL';
     232            RETURN NULL;
     233        END IF;
     234               
     235        width := ST_Width(refrast);
     236        height := ST_Height(refrast);
     237                newsrid := ST_SRID(refrast);
     238                -- Assuming source point table has one SRID for all geometries
     239                EXECUTE 'SELECT DISTINCT ST_SRID(' || sourcegeomcolumn || ') FROM ' || sourceschema || '.' || sourcetable || ';' INTO sourcesrid;
     240               
     241        -- Create a new empty raster having the same georeference as the reference raster
     242        newrast := ST_MakeEmptyRaster(width, height, ST_UpperLeftX(refrast), ST_UpperLeftY(refrast), ST_ScaleX(refrast), ST_ScaleY(refrast), ST_SkewX(refrast), ST_SkewY(refrast), newsrid);
     243               
     244        -- Check if the new raster is empty (width = 0 OR height = 0)
     245        IF ST_IsEmpty(newrast) THEN
     246            RAISE NOTICE 'ST_EuclideanDistance: Raster is empty. Returning an empty raster';
     247            RETURN newrast;
     248        END IF;
     249       
     250        -- Set the new pixeltype
     251        newpixeltype := pixeltype;
     252                -- If pixeltype not specified then use the pixeltype of the reference raster
     253        IF newpixeltype IS NULL THEN
     254            newpixeltype := ST_BandPixelType(refrast, band);
     255        ELSIF newpixeltype != '1BB' AND newpixeltype != '2BUI' AND newpixeltype != '4BUI' AND newpixeltype != '8BSI' AND newpixeltype != '8BUI' AND
     256               newpixeltype != '16BSI' AND newpixeltype != '16BUI' AND newpixeltype != '32BSI' AND newpixeltype != '32BUI' AND newpixeltype != '32BF' AND newpixeltype != '64BF' THEN
     257            RAISE EXCEPTION 'ST_EuclideanDistance: Invalid pixeltype "%". Aborting.', newpixeltype;
     258        END IF;
     259               
     260        -- Check for notada value
     261        newnodatavalue := ST_BandNodataValue(refrast, band);
     262        IF newnodatavalue IS NULL OR newnodatavalue < ST_MinPossibleValue(newpixeltype) OR newnodatavalue > (-ST_MinPossibleValue(newpixeltype) - 1) THEN
     263            RAISE NOTICE 'ST_EuclideanDistance: Reference raster do not have a nodata value or is out of range for the new raster pixeltype, nodata value for new raster set to the min value possible';
     264            newnodatavalue := ST_MinPossibleValue(newpixeltype);
     265        END IF;
     266        -- We set the initial value of the resulting raster to nodata value.
     267        -- If nodatavalue is null then the raster will be initialise to ST_MinPossibleValue
     268        newinitialvalue := newnodatavalue;
     269       
     270        -- Create the raster receiving all the computed distance values. Initialize it to the new initial value.
     271        newrast := ST_AddBand(newrast, newpixeltype, newinitialvalue, newnodatavalue);
     272               
     273                -- Set raster pixel value to zero if pixel is source point
     274                exesql := 'SELECT ' || sourcegeomcolumn || ' FROM ' || sourceschema || '.' || sourcetable || ';';
     275                FOR geom IN EXECUTE(exesql) LOOP
     276                        sourcex := ST_X(geom);
     277                        sourcey := ST_Y(geom);
     278                        sourcexr := ST_World2RasterCoordX(newrast, sourcex, sourcey);
     279                        sourceyr := ST_World2RasterCoordY(newrast, sourcex, sourcey);
     280                        -- If source point within raster range, then set pixel value to zero
     281                        IF sourcexr <= width AND sourceyr <= height THEN
     282                                newrast := ST_SetValue(newrast, band, sourcexr, sourceyr, 0);
     283                        END IF;
     284                END LOOP;
     285               
     286                -- Scan pixels in the raster set pixel value to the computed Eculidean distance
     287                -- to its nearest source point.
     288                -- There is place for optimization here for a more efficient scanning method
     289        FOR x IN 1..width LOOP
     290            FOR y IN 1..height LOOP
     291                                newx := ST_Raster2WorldCoordX(newrast, x, y);
     292                                newy := ST_Raster2WorldCoordY(newrast, x, y);
     293                                -- If pixel is not the source point then compute Eculidean distance
     294                                IF ST_Value(newrast, band, x, y) != 0 OR ST_Value(newrast, band, x, y) IS NULL THEN
     295                                        exesql := 'SELECT ST_Distance(ST_SetSRID(ST_MakePoint(' || newx || ',' || newy || '),' || sourcesrid || '), (SELECT ' ||  sourcegeomcolumn || ' FROM ' || sourceschema || '.' || sourcetable || ' ORDER BY ' || sourcegeomcolumn || ' <-> ST_SetSRID(ST_MakePoint(' || newx || ',' || newy || '),' || sourcesrid || ') LIMIT 1));';
     296                                        EXECUTE exesql INTO newval;
     297                                        -- If max distance specified
     298                                        IF maxdist != -1 THEN
     299                                                -- Any distance exceeds max distance is assigned nodata value
     300                                                IF newval > maxdist THEN
     301                                                        newval := newnodatavalue;
     302                                                END IF;
     303                                        END IF;
     304                                END IF;
     305                                newrast := ST_SetValue(newrast, band, x, y, newval);
     306            END LOOP;
     307        END LOOP;
     308
     309        RETURN newrast;
     310    END;
     311    $$
     312    LANGUAGE 'plpgsql';
     313
     314-- test
     315-- CREATE TABLE test_eudist AS (SELECT 1 AS rid,ST_EuclideanDistance(rast,NULL,'public','test_geom','the_geom') AS rast FROM test_raster);
     316-- CREATE TABLE test_eudist2 AS (SELECT 1 AS rid,ST_EuclideanDistance(rast,NULL,'public','test_geom','the_geom',4954880) AS rast FROM test_raster);
     317}}}
     318
     319
     320== Cost Distance ==
     321
     322'''ST_CostDistance()'''
     323
     324{{{
     325#!sql
     326--------------------------------------------------------------------
     327-- ST_CostDistance - Return a raster generated from a one band reference
     328--                                       raster a table and a one band cost raster, which values
     329--                                       are the the least accumulative cost of traversing
     330--                                       the cost surface from source points to each cell.
     331--                   (points for now, lines or polygons in the future).
     332-- Arguments
     333-- refrast raster - Reference raster align with which cost distance
     334--                                      raster will be created.
     335-- pixeltype text - Pixeltype assigned to the resulting raster. By default
     336--                  to be the pixeltype of the reference raster if not specified.
     337-- costrast raster - Cost raster in which each cell value represents the
     338--                                       cost of traversing this cell.
     339-- sourceschema text - Database schema of the source geometry table.
     340-- sourcetable text - Source geometry table name.
     341-- sourcegeomcolumn text - Source geometry table geometry column name.
     342-- maxdist double precision - Maximum distance from pixel to the source
     343--                            when the source is too far from the pixel.
     344--                            Pixel that exceeds it will be assigned nodatavalue.
     345--------------------------------------------------------------------
     346-- Create array type which stores the column, row, and cost value of each pixel in the iteration.
     347CREATE TYPE p_node AS (id integer,x integer,y integer,val float8);
     348
     349-- Function to get a set of elements from an array
     350DROP FUNCTION IF EXISTS explode_array(p_node[]);
     351CREATE OR REPLACE FUNCTION explode_array(p_node[])
     352        RETURNS SETOF p_node AS
     353        $$
     354                SELECT $1[i] FROM GENERATE_SERIES(1,ARRAY_UPPER($1,1)) AS i;
     355        $$
     356        LANGUAGE 'sql' IMMUTABLE;
     357
     358DROP FUNCTION IF EXISTS ST_CostDistance(refrast raster,
     359                                                                                         pixeltype text,
     360                                                                                         costrast raster,
     361                                                                                         sourceschema text,
     362                                                                                         sourcetable text,
     363                                                                                         sourcegeomcolumn text,
     364                                                                                         double precision = -1);
     365CREATE OR REPLACE FUNCTION ST_CostDistance(refrast raster,
     366                                                                                                pixeltype text,
     367                                                                                                costrast raster,
     368                                                                                                sourceschema text,
     369                                                                                                sourcetable text,
     370                                                                                                sourcegeomcolumn text,
     371                                                                                                double precision = -1)
     372    RETURNS raster AS
     373    $$
     374    DECLARE
     375                maxdist ALIAS FOR $7;
     376                newrast raster;
     377        newwidth integer;
     378        newheight integer;
     379                newsrid integer;
     380                newpixeltype text;
     381                newnodatavalue float8;
     382                newinitialvalue float8;
     383               
     384        x integer;
     385        y integer;
     386                sourcex float8;
     387                sourcey float8;
     388                sourcexr integer;
     389                sourceyr integer;
     390                sourcesrid integer;
     391                sourceboxgeom geometry;
     392               
     393                costarray p_node[] = '{}';
     394                node p_node;
     395                costval float8;
     396               
     397                -- Assuming reference raster has only one band
     398                band integer := 1;
     399               
     400                nx integer;
     401                ny integer;
     402                ncoordx float8;
     403                ncoordy float8;
     404                ncostval float8;
     405                nnewcostval float8;
     406                ncostrastx integer;
     407                ncostrasty integer;
     408                ncostrastval float8;
     409               
     410                arrayid integer := 0;
     411                tempnode p_node[];
     412                minval float8;
     413                minnodeid int;
     414                exesql text;
     415                geom geometry;
     416                flag boolean := False; 
     417    BEGIN
     418            -- Check if reference raster is NULL
     419        IF refrast IS NULL THEN
     420            RAISE NOTICE 'ST_CostDistance: Reference raster is NULL. Returning NULL';
     421            RETURN NULL;
     422        END IF;
     423               
     424        -- Check if cost raster is NULL
     425        IF costrast IS NULL THEN
     426            RAISE NOTICE 'ST_CostDistance: Cost raster is NULL. Returning NULL';
     427            RETURN NULL;
     428        END IF;
     429               
     430        newwidth := ST_Width(refrast);
     431        newheight := ST_Height(refrast);
     432                newsrid := ST_SRID(refrast);
     433               
     434                -- Assuming source point table has one SRID for all geometries
     435                EXECUTE 'SELECT DISTINCT ST_SRID(' || sourcegeomcolumn || ') FROM ' || sourceschema || '.' || sourcetable || ';' INTO sourcesrid;
     436               
     437        -- Create a new empty raster having the same georeference as the reference raster
     438        newrast := ST_MakeEmptyRaster(newwidth, newheight, ST_UpperLeftX(refrast), ST_UpperLeftY(refrast), ST_ScaleX(refrast), ST_ScaleY(refrast), ST_SkewX(refrast), ST_SkewY(refrast), newsrid);
     439               
     440        -- Check if the new raster is empty (width = 0 OR height = 0)
     441        IF ST_IsEmpty(newrast) THEN
     442            RAISE NOTICE 'ST_CostDistance: Raster is empty. Returning an empty raster';
     443            RETURN newrast;
     444        END IF;
     445       
     446        -- Set the new pixeltype
     447        newpixeltype := pixeltype;
     448                -- If pixeltype not specified then use the pixeltype of the reference raster
     449        IF newpixeltype IS NULL THEN
     450            newpixeltype := ST_BandPixelType(refrast, band);
     451        ELSIF newpixeltype != '1BB' AND newpixeltype != '2BUI' AND newpixeltype != '4BUI' AND newpixeltype != '8BSI' AND newpixeltype != '8BUI' AND
     452               newpixeltype != '16BSI' AND newpixeltype != '16BUI' AND newpixeltype != '32BSI' AND newpixeltype != '32BUI' AND newpixeltype != '32BF' AND newpixeltype != '64BF' THEN
     453            RAISE EXCEPTION 'ST_CostDistance: Invalid pixeltype "%". Aborting.', newpixeltype;
     454        END IF;
     455               
     456        -- Check for notada value
     457        newnodatavalue := ST_BandNodataValue(refrast, band);
     458        IF newnodatavalue IS NULL OR newnodatavalue < ST_MinPossibleValue(newpixeltype) OR newnodatavalue > (-ST_MinPossibleValue(newpixeltype) - 1) THEN
     459            RAISE NOTICE 'ST_CostDistance: Reference raster do not have a nodata value or is out of range for the new raster pixeltype, nodata value for new raster set to the min value possible';
     460            newnodatavalue := ST_MinPossibleValue(newpixeltype);
     461        END IF;
     462        -- We set the initial value of the resulting raster to nodata value.
     463        -- If nodatavalue is null then the raster will be initialise to ST_MinPossibleValue
     464        newinitialvalue := newnodatavalue;
     465       
     466        -- Create the raster receiving all the computed cost distance values. Initialize it to the new initial value.
     467        newrast := ST_AddBand(newrast, newpixeltype, newinitialvalue, newnodatavalue);
     468               
     469                -- Check if source points boundary intersects with extent of cost raster, if not, return new raster initiated with no data value.
     470                exesql := 'SELECT ST_CONVEXHULL(ST_COLLECT(' || sourcegeomcolumn || ')) FROM ' || sourceschema || '.' || sourcetable || ';';
     471                EXECUTE exesql INTO sourceboxgeom;
     472                SELECT ST_Intersects(costrast,sourceboxgeom) INTO flag;
     473                IF NOT flag THEN
     474                        RAISE NOTICE 'ST_CostDistance: Source points are out side of the cost raster. Returning raster with no data values.';
     475                        RETURN newrast;
     476                END IF;
     477               
     478                -- Set raster pixel value to nodatavalue or zero if pixel is source point;
     479                -- Initialize cost_array with source points that are within the extent of cost raster.
     480                exesql := 'SELECT ' || sourcegeomcolumn || ' FROM ' || sourceschema || '.' || sourcetable || ';';
     481                FOR geom IN EXECUTE(exesql) LOOP
     482                        sourcex := ST_X(geom);
     483                        sourcey := ST_Y(geom);
     484                        sourcexr := ST_World2RasterCoordX(newrast, sourcex, sourcey);
     485                        sourceyr := ST_World2RasterCoordY(newrast, sourcex, sourcey);
     486                        -- If source point within new raster extent
     487                        IF sourcexr <= width AND sourceyr <= height THEN
     488                                -- If source point within cost raster extent, set new raster pixel value to zero
     489                                -- since there is no accumulative cost to return to itself;
     490                                -- Add source point to cost array.
     491                                IF ST_Within(ST_SetSRID(ST_MakePoint(sourcex,sourcey),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     492                                        newrast := ST_SetValue(newrast, band, sourcexr, sourceyr, 0);
     493                                        arrayid := arrayid + 1;
     494                                        node.id := arrayid;
     495                                        node.x := sourcexr;
     496                                        node.y := sourceyr;
     497                                        node.val := costval;
     498                                        costarray := ARRAY_APPEND(costarray,node);
     499                                END IF;
     500                        END IF;
     501                END LOOP;
     502               
     503                -- Loop through the cost array, pop up node with least accumulative cost value;
     504                -- Calculate cost values for its neighbors (8 directions);
     505                flag := True;
     506                WHILE flag LOOP
     507                        -- End loop when cost values in the cost array are all NULL
     508                        SELECT ARRAY_AGG(cv) FROM (SELECT DISTINCT explode_array(costarray) AS cv) AS foo INTO tempnode;
     509                        IF ARRAY_LENGTH(tempnode,1) = 1 AND tempnode[1] = NULL THEN
     510                                flag := False;
     511                        ELSE
     512                                SELECT MIN(costarray[i].val) FROM GENERATE_SERIES(1,ARRAY_UPPER(costarray,1)) AS i INTO minval;
     513                                SELECT costarray[i].id FROM GENERATE_SERIES(1,ARRAY_UPPER(costarray,1)) AS i WHERE costarray[i].val = minval INTO minnodeid;
     514                                -- Pop up node with min cost value, set its val in the array to be NULL
     515                                x := costarray[minnodeid].x;
     516                                y := costarray[minnodeid].y;
     517                                costval := ST_Value(newrast,band,x,y);
     518                                costarray[minnodeid] := NULL;
     519                                -- Calculate cost distance for neighbors in perpendicular directions
     520                                -- neighbor(x,y+1)
     521                                nx := x;
     522                                ny := y+1;
     523                                IF ny <= newheight THEN
     524                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     525                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     526                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     527                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     528                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     529                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     530                                                nnewcostval := (costval + ncostrastval)/2;
     531                                                ncostval := ST_Value(newrast,band,nx,ny);
     532                                                IF ncostval <> newnodatavalue THEN
     533                                                        IF nnewcostval < ncostval THEN
     534                                                                ncostval := nnewcostval;
     535                                                                arrayid := arrayid + 1;
     536                                                                node.id := arrayid;
     537                                                                node.x := nx;
     538                                                                node.y := ny;
     539                                                                node.val := ncostval;
     540                                                                costarray := ARRAY_APPEND(costarray,node);
     541                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     542                                                        END IF;
     543                                                END IF;
     544                                        ELSE
     545                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     546                                        END IF;
     547                                END IF;
     548                               
     549                                -- neighbor(x,y-1)
     550                                nx := x;
     551                                ny := y-1;
     552                                IF ny >= 1 THEN
     553                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     554                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     555                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     556                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     557                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     558                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     559                                                nnewcostval := (costval + ncostrastval)/2;
     560                                                ncostval := ST_Value(newrast,band,nx,ny);
     561                                                IF ncostval <> newnodatavalue THEN
     562                                                        IF nnewcostval < ncostval THEN
     563                                                                -- If max distance specified
     564                                                                IF maxdist != -1 THEN
     565                                                                        -- Any distance exceeds max distance is assigned nodata value
     566                                                                        IF nnewcostval > maxdist THEN
     567                                                                                nnewcostval := newnodatavalue;
     568                                                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     569                                                                        END IF;
     570                                                                END IF;
     571                                                                if nnewcostval <> newnodatavalue THEN
     572                                                                        ncostval := nnewcostval;
     573                                                                        arrayid := arrayid + 1;
     574                                                                        node.id := arrayid;
     575                                                                        node.x := nx;
     576                                                                        node.y := ny;
     577                                                                        node.val := ncostval;
     578                                                                        costarray := ARRAY_APPEND(costarray,node);
     579                                                                        newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     580                                                                END IF;
     581                                                        END IF;
     582                                                END IF;
     583                                        ELSE
     584                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     585                                        END IF;
     586                                END IF;
     587                               
     588                                -- neighbor(x+1,y)
     589                                nx := x+1;
     590                                ny := y;
     591                                IF nx <= newwidth THEN
     592                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     593                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     594                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     595                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     596                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     597                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     598                                                nnewcostval := (costval + ncostrastval)/2;
     599                                                ncostval := ST_Value(newrast,band,nx,ny);
     600                                                IF ncostval <> newnodatavalue THEN
     601                                                        IF nnewcostval < ncostval THEN
     602                                                                ncostval := nnewcostval;
     603                                                                arrayid := arrayid + 1;
     604                                                                node.id := arrayid;
     605                                                                node.x := nx;
     606                                                                node.y := ny;
     607                                                                node.val := ncostval;
     608                                                                costarray := ARRAY_APPEND(costarray,node);
     609                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     610                                                        END IF;
     611                                                END IF;
     612                                        ELSE
     613                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     614                                        END IF;
     615                                END IF;
     616                               
     617                                -- neighbor(x-1,y)
     618                                nx := x-1;
     619                                ny := y;
     620                                IF nx >= 1 THEN
     621                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     622                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     623                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     624                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     625                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     626                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     627                                                nnewcostval := (costval + ncostrastval)/2;
     628                                                ncostval := ST_Value(newrast,band,nx,ny);
     629                                                IF ncostval <> newnodatavalue THEN
     630                                                        IF nnewcostval < ncostval THEN
     631                                                                ncostval := nnewcostval;
     632                                                                arrayid := arrayid + 1;
     633                                                                node.id := arrayid;
     634                                                                node.x := nx;
     635                                                                node.y := ny;
     636                                                                node.val := ncostval;
     637                                                                costarray := ARRAY_APPEND(costarray,node);
     638                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     639                                                        END IF;
     640                                                END IF;
     641                                        ELSE
     642                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     643                                        END IF;
     644                                END IF;
     645                               
     646                               
     647                                -- Calculate cost distance for neighbors in diagonal directions
     648                                -- neighbor(x-1,y+1)
     649                                nx := x-1;
     650                                ny := y+1;
     651                                IF nx >= 1 AND ny <= newheight THEN
     652                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     653                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     654                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     655                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     656                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     657                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     658                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     659                                                ncostval := ST_Value(newrast,band,nx,ny);
     660                                                IF ncostval <> newnodatavalue THEN
     661                                                        IF nnewcostval < ncostval THEN
     662                                                                ncostval := nnewcostval;
     663                                                                arrayid := arrayid + 1;
     664                                                                node.id := arrayid;
     665                                                                node.x := nx;
     666                                                                node.y := ny;
     667                                                                node.val := ncostval;
     668                                                                costarray := ARRAY_APPEND(costarray,node);
     669                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     670                                                        END IF;
     671                                                END IF;
     672                                        ELSE
     673                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     674                                        END IF;
     675                                END IF;
     676                               
     677                                -- neighbor(x-1,y-1)
     678                                nx := x-1;
     679                                ny := y-1;
     680                                IF nx >= 1 AND ny >= 1 THEN
     681                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     682                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     683                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     684                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     685                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     686                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     687                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     688                                                ncostval := ST_Value(newrast,band,nx,ny);
     689                                                IF ncostval <> newnodatavalue THEN
     690                                                        IF nnewcostval < ncostval THEN
     691                                                                ncostval := nnewcostval;
     692                                                                arrayid := arrayid + 1;
     693                                                                node.id := arrayid;
     694                                                                node.x := nx;
     695                                                                node.y := ny;
     696                                                                node.val := ncostval;
     697                                                                costarray := ARRAY_APPEND(costarray,node);
     698                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     699                                                        END IF;
     700                                                END IF;
     701                                        ELSE
     702                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     703                                        END IF;
     704                                END IF;
     705                               
     706                                -- neighbor(x+1,y+1)
     707                                nx := x+1;
     708                                ny := y+1;
     709                                IF nx <= newwidth AND ny <= newheight THEN
     710                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     711                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     712                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     713                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     714                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     715                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     716                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     717                                                ncostval := ST_Value(newrast,band,nx,ny);
     718                                                IF ncostval <> newnodatavalue THEN
     719                                                        IF nnewcostval < ncostval THEN
     720                                                                ncostval := nnewcostval;
     721                                                                arrayid := arrayid + 1;
     722                                                                node.id := arrayid;
     723                                                                node.x := nx;
     724                                                                node.y := ny;
     725                                                                node.val := ncostval;
     726                                                                costarray := ARRAY_APPEND(costarray,node);
     727                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     728                                                        END IF;
     729                                                END IF;
     730                                        ELSE
     731                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     732                                        END IF;
     733                                END IF;
     734                               
     735                                -- neighbor(x+1,y-1)
     736                                nx := x+1;
     737                                ny := y-1;
     738                                IF nx <= newwidth AND ny >= 1 THEN
     739                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     740                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     741                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     742                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     743                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     744                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     745                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     746                                                ncostval := ST_Value(newrast,band,nx,ny);
     747                                                IF ncostval <> newnodatavalue THEN
     748                                                        IF nnewcostval < ncostval THEN
     749                                                                ncostval := nnewcostval;
     750                                                                arrayid := arrayid + 1;
     751                                                                node.id := arrayid;
     752                                                                node.x := nx;
     753                                                                node.y := ny;
     754                                                                node.val := ncostval;
     755                                                                costarray := ARRAY_APPEND(costarray,node);
     756                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     757                                                        END IF;
     758                                                END IF;
     759                                        ELSE
     760                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     761                                        END IF;
     762                                END IF;
     763                               
     764                        END IF;
     765                END LOOP;
     766
     767        RETURN newrast;
     768    END;
     769    $$
     770    LANGUAGE 'plpgsql';
     771       
     772       
     773--------------------------------------------------------------------
     774-- ST_CostDistance - Return a raster generated from a set of raster specifications
     775--                                       raster a table and a one band cost raster, which values
     776--                                       are the the least accumulative cost of traversing
     777--                                       the cost surface from source points to each cell.
     778--                   (points for now, lines or polygons in the future).
     779-- Arguments
     780-- width,height,rastulx,rastuly,rastscalex,rastscaley,rastskewx,rastskewy
     781--  - Dimenssions and georeference of resulted raster.
     782-- rastsrid - SRID of the resulted raster
     783-- rastndv - Nodata value for resulted raster
     784-- pixeltype text - Pixeltype assigned to the resulting raster. By default
     785--                  to be the pixeltype of the reference raster if not specified.
     786-- costrast raster - Cost raster in which each cell value represents the
     787--                                       cost of traversing this cell.
     788-- sourceschema text - Database schema of the source geometry table.
     789-- sourcetable text - Source geometry table name.
     790-- sourcegeomcolumn text - Source geometry table geometry column name.
     791-- maxdist double precision - Maximum distance from pixel to the source
     792--                            when the source is too far from the pixel.
     793--                            Pixel that exceeds it will be assigned nodatavalue.
     794--------------------------------------------------------------------
     795DROP FUNCTION IF EXISTS ST_CostDistance(width integer,
     796                                                                                height integer,
     797                                                                                rastulx float8,
     798                                                                                rastuly float8,
     799                                                                                rastscalex float8,
     800                                                                                rastscaley float8,
     801                                                                                rastskewx float8,
     802                                                                                rastskewy float8,
     803                                                                                rastsrid integer,
     804                                                                                rastndv float8,
     805                                                                                pixeltype text,
     806                                                                                costrast raster,
     807                                                                                sourceschema text,
     808                                                                                sourcetable text,
     809                                                                                sourcegeomcolumn text,
     810                                                                                double precision = -1);
     811CREATE OR REPLACE FUNCTION ST_CostDistance(width integer,
     812                                                                                   height integer,
     813                                                                                   rastulx float8,
     814                                                                                   rastuly float8,
     815                                                                                   rastscalex float8,
     816                                                                                   rastscaley float8,
     817                                                                                   rastskewx float8,
     818                                                                                   rastskewy float8,
     819                                                                                   rastsrid integer,
     820                                                                                   rastndv float8,
     821                                                                                   pixeltype text,
     822                                                                                   costrast raster,
     823                                                                                   sourceschema text,
     824                                                                                   sourcetable text,
     825                                                                                   sourcegeomcolumn text,
     826                                                                                   double precision = -1)
     827    RETURNS raster AS
     828    $$
     829    DECLARE
     830                maxdist ALIAS FOR $7;
     831                newrast raster;
     832        newwidth integer;
     833        newheight integer;
     834                newsrid integer;
     835                newpixeltype text;
     836                newnodatavalue float8;
     837                newinitialvalue float8;
     838               
     839        x integer;
     840        y integer;
     841                sourcex float8;
     842                sourcey float8;
     843                sourcexr integer;
     844                sourceyr integer;
     845                sourcesrid integer;
     846                sourceboxgeom geometry;
     847               
     848                costarray p_node[] = '{}';
     849                node p_node;
     850                costval float8;
     851               
     852                -- Assuming reference raster has only one band
     853                band integer := 1;
     854               
     855                nx integer;
     856                ny integer;
     857                ncoordx float8;
     858                ncoordy float8;
     859                ncostval float8;
     860                nnewcostval float8;
     861                ncostrastx integer;
     862                ncostrasty integer;
     863                ncostrastval float8;
     864               
     865                arrayid integer := 0;
     866                tempnode p_node[];
     867                minval float8;
     868                minnodeid int;
     869                exesql text;
     870                geom geometry;
     871                flag boolean := False; 
     872    BEGIN
     873        -- Check if cost raster is NULL
     874        IF costrast IS NULL THEN
     875            RAISE NOTICE 'ST_CostDistance: Cost raster is NULL. Returning NULL';
     876            RETURN NULL;
     877        END IF;
     878               
     879        newwidth := width;
     880        newheight := height;
     881                newsrid := rastsrid;
     882               
     883                -- Assuming source point table has one SRID for all geometries
     884                EXECUTE 'SELECT DISTINCT ST_SRID(' || sourcegeomcolumn || ') FROM ' || sourceschema || '.' || sourcetable || ';' INTO sourcesrid;
     885               
     886        -- Create a new empty raster having the same georeference as the reference raster
     887        newrast := ST_MakeEmptyRaster(width, height, rastulx, rastuly, rastscalex,rastscaley, rastskewx, rastskewy, rastsrid);
     888               
     889        -- Check if the new raster is empty (width = 0 OR height = 0)
     890        IF ST_IsEmpty(newrast) THEN
     891            RAISE NOTICE 'ST_CostDistance: Raster is empty. Returning an empty raster';
     892            RETURN newrast;
     893        END IF;
     894       
     895        -- Set the new pixeltype
     896        newpixeltype := pixeltype;
     897                -- If pixeltype not specified then use the pixeltype of the reference raster
     898        IF newpixeltype IS NULL THEN
     899            newpixeltype := ST_BandPixelType(refrast, band);
     900        ELSIF newpixeltype != '1BB' AND newpixeltype != '2BUI' AND newpixeltype != '4BUI' AND newpixeltype != '8BSI' AND newpixeltype != '8BUI' AND
     901               newpixeltype != '16BSI' AND newpixeltype != '16BUI' AND newpixeltype != '32BSI' AND newpixeltype != '32BUI' AND newpixeltype != '32BF' AND newpixeltype != '64BF' THEN
     902            RAISE EXCEPTION 'ST_CostDistance: Invalid pixeltype "%". Aborting.', newpixeltype;
     903        END IF;
     904               
     905        -- Check for notada value
     906        newnodatavalue := ST_BandNodataValue(refrast, band);
     907        IF newnodatavalue IS NULL OR newnodatavalue < ST_MinPossibleValue(newpixeltype) OR newnodatavalue > (-ST_MinPossibleValue(newpixeltype) - 1) THEN
     908            RAISE NOTICE 'ST_CostDistance: Reference raster do not have a nodata value or is out of range for the new raster pixeltype, nodata value for new raster set to the min value possible';
     909            newnodatavalue := ST_MinPossibleValue(newpixeltype);
     910        END IF;
     911        -- We set the initial value of the resulting raster to nodata value.
     912        -- If nodatavalue is null then the raster will be initialise to ST_MinPossibleValue
     913        newinitialvalue := newnodatavalue;
     914       
     915        -- Create the raster receiving all the computed cost distance values. Initialize it to the new initial value.
     916        newrast := ST_AddBand(newrast, newpixeltype, newinitialvalue, newnodatavalue);
     917               
     918                -- Check if source points boundary intersects with extent of cost raster, if not, return new raster initiated with no data value.
     919                exesql := 'SELECT ST_CONVEXHULL(ST_COLLECT(' || sourcegeomcolumn || ')) FROM ' || sourceschema || '.' || sourcetable || ';';
     920                EXECUTE exesql INTO sourceboxgeom;
     921                SELECT ST_Intersects(costrast,sourceboxgeom) INTO flag;
     922                IF NOT flag THEN
     923                        RAISE NOTICE 'ST_CostDistance: Source points are out side of the cost raster. Returning raster with no data values.';
     924                        RETURN newrast;
     925                END IF;
     926               
     927                -- Set raster pixel value to nodatavalue or zero if pixel is source point;
     928                -- Initialize cost_array with source points that are within the extent of cost raster.
     929                exesql := 'SELECT ' || sourcegeomcolumn || ' FROM ' || sourceschema || '.' || sourcetable || ';';
     930                FOR geom IN EXECUTE(exesql) LOOP
     931                        sourcex := ST_X(geom);
     932                        sourcey := ST_Y(geom);
     933                        sourcexr := ST_World2RasterCoordX(newrast, sourcex, sourcey);
     934                        sourceyr := ST_World2RasterCoordY(newrast, sourcex, sourcey);
     935                        -- If source point within new raster extent
     936                        IF sourcexr <= width AND sourceyr <= height THEN
     937                                -- If source point within cost raster extent, set new raster pixel value to zero
     938                                -- since there is no accumulative cost to return to itself;
     939                                -- Add source point to cost array.
     940                                IF ST_Within(ST_SetSRID(ST_MakePoint(sourcex,sourcey),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     941                                        newrast := ST_SetValue(newrast, band, sourcexr, sourceyr, 0);
     942                                        arrayid := arrayid + 1;
     943                                        node.id := arrayid;
     944                                        node.x := sourcexr;
     945                                        node.y := sourceyr;
     946                                        node.val := costval;
     947                                        costarray := ARRAY_APPEND(costarray,node);
     948                                END IF;
     949                        END IF;
     950                END LOOP;
     951               
     952                -- Loop through the cost array, pop up node with least accumulative cost value;
     953                -- Calculate cost values for its neighbors (8 directions);
     954                flag := True;
     955                WHILE flag LOOP
     956                        -- End loop when cost values in the cost array are all NULL
     957                        SELECT ARRAY_AGG(cv) FROM (SELECT DISTINCT explode_array(costarray) AS cv) AS foo INTO tempnode;
     958                        IF ARRAY_LENGTH(tempnode,1) = 1 AND tempnode[1] = NULL THEN
     959                                flag := False;
     960                        ELSE
     961                                SELECT MIN(costarray[i].val) FROM GENERATE_SERIES(1,ARRAY_UPPER(costarray,1)) AS i INTO minval;
     962                                SELECT costarray[i].id FROM GENERATE_SERIES(1,ARRAY_UPPER(costarray,1)) AS i WHERE costarray[i].val = minval INTO minnodeid;
     963                                -- Pop up node with min cost value, set its val in the array to be NULL
     964                                x := costarray[minnodeid].x;
     965                                y := costarray[minnodeid].y;
     966                                costval := ST_Value(newrast,band,x,y);
     967                                costarray[minnodeid] := NULL;
     968                                -- Calculate cost distance for neighbors in perpendicular directions
     969                                -- neighbor(x,y+1)
     970                                nx := x;
     971                                ny := y+1;
     972                                IF ny <= newheight THEN
     973                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     974                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     975                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     976                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     977                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     978                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     979                                                nnewcostval := (costval + ncostrastval)/2;
     980                                                ncostval := ST_Value(newrast,band,nx,ny);
     981                                                IF ncostval <> newnodatavalue THEN
     982                                                        IF nnewcostval < ncostval THEN
     983                                                                ncostval := nnewcostval;
     984                                                                arrayid := arrayid + 1;
     985                                                                node.id := arrayid;
     986                                                                node.x := nx;
     987                                                                node.y := ny;
     988                                                                node.val := ncostval;
     989                                                                costarray := ARRAY_APPEND(costarray,node);
     990                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     991                                                        END IF;
     992                                                END IF;
     993                                        ELSE
     994                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     995                                        END IF;
     996                                END IF;
     997                               
     998                                -- neighbor(x,y-1)
     999                                nx := x;
     1000                                ny := y-1;
     1001                                IF ny >= 1 THEN
     1002                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     1003                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     1004                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     1005                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     1006                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     1007                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     1008                                                nnewcostval := (costval + ncostrastval)/2;
     1009                                                ncostval := ST_Value(newrast,band,nx,ny);
     1010                                                IF ncostval <> newnodatavalue THEN
     1011                                                        IF nnewcostval < ncostval THEN
     1012                                                                -- If max distance specified
     1013                                                                IF maxdist != -1 THEN
     1014                                                                        -- Any distance exceeds max distance is assigned nodata value
     1015                                                                        IF nnewcostval > maxdist THEN
     1016                                                                                nnewcostval := newnodatavalue;
     1017                                                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1018                                                                        END IF;
     1019                                                                END IF;
     1020                                                                if nnewcostval <> newnodatavalue THEN
     1021                                                                        ncostval := nnewcostval;
     1022                                                                        arrayid := arrayid + 1;
     1023                                                                        node.id := arrayid;
     1024                                                                        node.x := nx;
     1025                                                                        node.y := ny;
     1026                                                                        node.val := ncostval;
     1027                                                                        costarray := ARRAY_APPEND(costarray,node);
     1028                                                                        newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     1029                                                                END IF;
     1030                                                        END IF;
     1031                                                END IF;
     1032                                        ELSE
     1033                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1034                                        END IF;
     1035                                END IF;
     1036                               
     1037                                -- neighbor(x+1,y)
     1038                                nx := x+1;
     1039                                ny := y;
     1040                                IF nx <= newwidth THEN
     1041                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     1042                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     1043                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     1044                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     1045                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     1046                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     1047                                                nnewcostval := (costval + ncostrastval)/2;
     1048                                                ncostval := ST_Value(newrast,band,nx,ny);
     1049                                                IF ncostval <> newnodatavalue THEN
     1050                                                        IF nnewcostval < ncostval THEN
     1051                                                                ncostval := nnewcostval;
     1052                                                                arrayid := arrayid + 1;
     1053                                                                node.id := arrayid;
     1054                                                                node.x := nx;
     1055                                                                node.y := ny;
     1056                                                                node.val := ncostval;
     1057                                                                costarray := ARRAY_APPEND(costarray,node);
     1058                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     1059                                                        END IF;
     1060                                                END IF;
     1061                                        ELSE
     1062                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1063                                        END IF;
     1064                                END IF;
     1065                               
     1066                                -- neighbor(x-1,y)
     1067                                nx := x-1;
     1068                                ny := y;
     1069                                IF nx >= 1 THEN
     1070                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     1071                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     1072                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     1073                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     1074                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     1075                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     1076                                                nnewcostval := (costval + ncostrastval)/2;
     1077                                                ncostval := ST_Value(newrast,band,nx,ny);
     1078                                                IF ncostval <> newnodatavalue THEN
     1079                                                        IF nnewcostval < ncostval THEN
     1080                                                                ncostval := nnewcostval;
     1081                                                                arrayid := arrayid + 1;
     1082                                                                node.id := arrayid;
     1083                                                                node.x := nx;
     1084                                                                node.y := ny;
     1085                                                                node.val := ncostval;
     1086                                                                costarray := ARRAY_APPEND(costarray,node);
     1087                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     1088                                                        END IF;
     1089                                                END IF;
     1090                                        ELSE
     1091                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1092                                        END IF;
     1093                                END IF;
     1094                               
     1095                               
     1096                                -- Calculate cost distance for neighbors in diagonal directions
     1097                                -- neighbor(x-1,y+1)
     1098                                nx := x-1;
     1099                                ny := y+1;
     1100                                IF nx >= 1 AND ny <= newheight THEN
     1101                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     1102                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     1103                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     1104                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     1105                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     1106                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     1107                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     1108                                                ncostval := ST_Value(newrast,band,nx,ny);
     1109                                                IF ncostval <> newnodatavalue THEN
     1110                                                        IF nnewcostval < ncostval THEN
     1111                                                                ncostval := nnewcostval;
     1112                                                                arrayid := arrayid + 1;
     1113                                                                node.id := arrayid;
     1114                                                                node.x := nx;
     1115                                                                node.y := ny;
     1116                                                                node.val := ncostval;
     1117                                                                costarray := ARRAY_APPEND(costarray,node);
     1118                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     1119                                                        END IF;
     1120                                                END IF;
     1121                                        ELSE
     1122                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1123                                        END IF;
     1124                                END IF;
     1125                               
     1126                                -- neighbor(x-1,y-1)
     1127                                nx := x-1;
     1128                                ny := y-1;
     1129                                IF nx >= 1 AND ny >= 1 THEN
     1130                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     1131                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     1132                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     1133                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     1134                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     1135                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     1136                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     1137                                                ncostval := ST_Value(newrast,band,nx,ny);
     1138                                                IF ncostval <> newnodatavalue THEN
     1139                                                        IF nnewcostval < ncostval THEN
     1140                                                                ncostval := nnewcostval;
     1141                                                                arrayid := arrayid + 1;
     1142                                                                node.id := arrayid;
     1143                                                                node.x := nx;
     1144                                                                node.y := ny;
     1145                                                                node.val := ncostval;
     1146                                                                costarray := ARRAY_APPEND(costarray,node);
     1147                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     1148                                                        END IF;
     1149                                                END IF;
     1150                                        ELSE
     1151                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1152                                        END IF;
     1153                                END IF;
     1154                               
     1155                                -- neighbor(x+1,y+1)
     1156                                nx := x+1;
     1157                                ny := y+1;
     1158                                IF nx <= newwidth AND ny <= newheight THEN
     1159                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     1160                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     1161                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     1162                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     1163                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     1164                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     1165                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     1166                                                ncostval := ST_Value(newrast,band,nx,ny);
     1167                                                IF ncostval <> newnodatavalue THEN
     1168                                                        IF nnewcostval < ncostval THEN
     1169                                                                ncostval := nnewcostval;
     1170                                                                arrayid := arrayid + 1;
     1171                                                                node.id := arrayid;
     1172                                                                node.x := nx;
     1173                                                                node.y := ny;
     1174                                                                node.val := ncostval;
     1175                                                                costarray := ARRAY_APPEND(costarray,node);
     1176                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     1177                                                        END IF;
     1178                                                END IF;
     1179                                        ELSE
     1180                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1181                                        END IF;
     1182                                END IF;
     1183                               
     1184                                -- neighbor(x+1,y-1)
     1185                                nx := x+1;
     1186                                ny := y-1;
     1187                                IF nx <= newwidth AND ny >= 1 THEN
     1188                                        ncoordx := ST_Raster2WorldCoordX(newrast, x, y);
     1189                                        ncoordy := ST_Raster2WorldCoordY(newrast, x, y);
     1190                                        IF ST_Within(ST_SetSRID(ST_MakePoint(ncoordx,ncoordy),ST_SRID(costrast)),ST_Envelope(costrast)) THEN
     1191                                                ncostrastx := ST_World2RasterCoordX(costrast,ncoordx,ncoordy);
     1192                                                ncostrasty := ST_World2RasterCoordY(costrast,ncoordx,ncoordy);
     1193                                                ncostrastval := ST_Value(costrast,band,ncostrastx,ncostrasty);
     1194                                                nnewcostval := sqrt(2) * (costval + ncostrastval)/2;
     1195                                                ncostval := ST_Value(newrast,band,nx,ny);
     1196                                                IF ncostval <> newnodatavalue THEN
     1197                                                        IF nnewcostval < ncostval THEN
     1198                                                                ncostval := nnewcostval;
     1199                                                                arrayid := arrayid + 1;
     1200                                                                node.id := arrayid;
     1201                                                                node.x := nx;
     1202                                                                node.y := ny;
     1203                                                                node.val := ncostval;
     1204                                                                costarray := ARRAY_APPEND(costarray,node);
     1205                                                                newrast := ST_SetValue(newrast, band, nx, ny, ncostval);
     1206                                                        END IF;
     1207                                                END IF;
     1208                                        ELSE
     1209                                                newrast := ST_SetValue(newrast, band, nx, ny, newnodatavalue);
     1210                                        END IF;
     1211                                END IF;
     1212                               
     1213                        END IF;
     1214                END LOOP;
     1215
     1216        RETURN newrast;
     1217    END;
     1218    $$
     1219    LANGUAGE 'plpgsql';
     1220}}}