]> granicus.if.org Git - postgis/commitdiff
Refactored ST_MapAlgebraFct to cache userfunction lookup, and accept NULL input and...
authorDavid Zwarg <dzwarg@azavea.com>
Fri, 18 Nov 2011 20:15:00 +0000 (20:15 +0000)
committerDavid Zwarg <dzwarg@azavea.com>
Fri, 18 Nov 2011 20:15:00 +0000 (20:15 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@8182 b70326c6-7e19-0410-871a-916f4a2858ee

raster/rt_pg/rt_pg.c
raster/test/regress/Makefile.in
raster/test/regress/create_rt_mapalgebra_test.sql
raster/test/regress/create_rt_mapalgebrafctngb_test.sql [new file with mode: 0644]
raster/test/regress/create_rt_mapalgebrafctngb_test_expected [new file with mode: 0644]
raster/test/regress/rt_mapalgebrafct.sql
raster/test/regress/rt_mapalgebrafct_expected
raster/test/regress/rt_mapalgebrafctngb.sql

index ded778e1fc439896c24b3bad5c1db5ae7205e6d4..22ba772c9fe28bad9a0d65f6c7a3658c9e2339f1 100644 (file)
@@ -2962,7 +2962,8 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
     rt_pixtype newpixeltype;
     int ret = -1;
     Oid oid;
-    Datum extraargs;
+    FmgrInfo cbinfo;
+    FunctionCallInfoData cbdata;
     Datum tmpnewval;
     char * strFromText = NULL;
 
@@ -3010,6 +3011,9 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
     if ( NULL == newrast ) {
         elog(ERROR, "RASTER_mapAlgebraFct: Could not create a new raster. "
             "Returning NULL");
+
+        rt_raster_destroy(raster);
+
         PG_RETURN_NULL();
     }
 
@@ -3136,6 +3140,10 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
     
     if (newpixeltype == PT_END) {
         elog(ERROR, "RASTER_mapAlgebraFct: Invalid pixeltype. Returning NULL");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
         PG_RETURN_NULL();
     }    
     
@@ -3145,12 +3153,73 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
     /* Get the name of the callback user function for raster values */
     if (PG_ARGISNULL(3)) {
         elog(ERROR, "RASTER_mapAlgebraFct: Required function is missing. Returning NULL");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
         PG_RETURN_NULL();
     }
 
     oid = PG_GETARG_OID(3);
+    if (oid == InvalidOid) {
+        elog(ERROR, "RASTER_mapAlgebraFct: Got invalid function object id. Returning NULL");
 
-    POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Got function oid: %d", oid);
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
+        PG_RETURN_NULL();
+    }
+
+    fmgr_info(oid, &cbinfo);
+
+    /* function cannot return set */
+    if (cbinfo.fn_retset) {
+        elog(ERROR, "RASTER_mapAlgebraFct: Function provided must return double precision not resultset");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
+        PG_RETURN_NULL();
+    }
+    /* function should have correct # of args */
+    else if (cbinfo.fn_nargs != 2) {
+        elog(ERROR, "RASTER_mapAlgebraFct: Function does not have two input parameters");
+
+        rt_raster_destroy(raster);
+        rt_raster_destroy(newrast);
+
+        PG_RETURN_NULL();
+    }
+
+    if (func_volatile(oid) == 'v') {
+        elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
+    }
+
+    /* prep function call data */
+#if POSTGIS_PGSQL_VERSION <= 90
+    InitFunctionCallInfoData(cbdata, &cbinfo, 2, InvalidOid, NULL);
+#else
+    InitFunctionCallInfoData(cbdata, &cbinfo, 2, InvalidOid, NULL, NULL);
+#endif
+    memset(cbdata.argnull, FALSE, 2);
+    
+    /* check that the function isn't strict if the args are null. */
+    if (PG_ARGISNULL(4)) {
+        if (cbinfo.fn_strict) {
+            elog(ERROR, "RASTER_mapAlgebraFct: Strict callback functions cannot have null parameters");
+
+            rt_raster_destroy(raster);
+            rt_raster_destroy(newrast);
+
+            PG_RETURN_NULL();
+        }
+
+        cbdata.arg[1] = (Datum)NULL;
+        cbdata.argnull[1] = TRUE;
+    }
+    else {
+        cbdata.arg[1] = PG_GETARG_DATUM(4);
+    }
 
     /**
      * Optimization: If the raster is only filled with nodata values return
@@ -3212,13 +3281,10 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
 
         PG_RETURN_POINTER(pgraster);      
     }
-
     
     POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Main computing loop (%d x %d)",
             width, height);
 
-    extraargs = PG_GETARG_DATUM(4);
-
     for (x = 0; x < width; x++) {
         for(y = 0; y < height; y++) {
             ret = rt_band_get_pixel(band, x, y, &r);
@@ -3227,13 +3293,31 @@ Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
              * We compute a value only for the withdata value pixel since the
              * nodata value has already been set by the first optimization
              **/
-            if (ret != -1 && FLT_NEQ(r, newnodatavalue)) {
+            if (ret != -1) {
+                if (FLT_EQ(r, newnodatavalue)) {
+                    if (cbinfo.fn_strict) {
+                        POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Strict callbacks cannot accept NULL arguments, skipping NODATA cell.");
+                        continue;
+                    }
+                    cbdata.argnull[0] = TRUE;
+                    cbdata.arg[0] = (Datum)NULL;
+                }
+                else {
+                    cbdata.argnull[0] = FALSE;
+                    cbdata.arg[0] = Float8GetDatum(r);
+                }
+
                 POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: (%dx%d), r = %f",
                     x, y, r);
                    
-                /* convert r to a datum for OidFunctionCall2 */
-                tmpnewval = OidFunctionCall2(oid,Float8GetDatum(r),extraargs);
-                newval = DatumGetFloat8(tmpnewval);
+                tmpnewval = FunctionCallInvoke(&cbdata);
+
+                if (cbdata.isnull) {
+                    newval = newnodatavalue;
+                }
+                else {
+                    newval = DatumGetFloat8(tmpnewval);
+                }
 
                 POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: new value = %f", 
                     newval);
index 9aa2f15c61854f4bd191a20ec22cddf26e0404b3..ebbd41906c4049b8b9e838344da366c3d2e75341 100644 (file)
@@ -90,6 +90,7 @@ TEST_UTILITY = \
        rt_mapalgebrafct.sql \
        rt_mapalgebraexpr_2raster.sql \
        rt_mapalgebrafct_2raster.sql \
+    create_rt_mapalgebrafctngb_test.sql \
     rt_mapalgebrafctngb.sql \
        rt_reclass.sql \
        rt_resample.sql \
index e7660937a5a745c10497f99e766b395300e8391e..416b7ebd83b64c5c7f8591b484c6a8f5fbd69b66 100644 (file)
@@ -20,7 +20,7 @@ CREATE OR REPLACE FUNCTION raster_plus_twenty(pixel FLOAT, VARIADIC args TEXT[])
         RETURN pixel + 20;
     END;
     $$ 
-    LANGUAGE 'plpgsql';
+    LANGUAGE 'plpgsql' IMMUTABLE;
 
 CREATE OR REPLACE FUNCTION raster_plus_arg1(pixel FLOAT, VARIADIC args TEXT[])
     RETURNS FLOAT AS 
@@ -34,7 +34,7 @@ CREATE OR REPLACE FUNCTION raster_plus_arg1(pixel FLOAT, VARIADIC args TEXT[])
         RETURN pixel + x;
     END;
     $$ 
-    LANGUAGE 'plpgsql';
+    LANGUAGE 'plpgsql' IMMUTABLE;
 
 CREATE OR REPLACE FUNCTION raster_polynomial(pixel FLOAT, VARIADIC args TEXT[])
     RETURNS FLOAT AS
@@ -52,4 +52,13 @@ CREATE OR REPLACE FUNCTION raster_polynomial(pixel FLOAT, VARIADIC args TEXT[])
         RETURN m * pixel + b;
     END;
     $$
-    LANGUAGE 'plpgsql';
+    LANGUAGE 'plpgsql' IMMUTABLE;
+
+ CREATE OR REPLACE FUNCTION raster_nullage(pixel FLOAT, VARIADIC args TEXT[])
+    RETURNS FLOAT AS
+    $$
+    BEGIN
+        RETURN NULL;
+    END;
+    $$
+    LANGUAGE 'plpgsql' IMMUTABLE;
diff --git a/raster/test/regress/create_rt_mapalgebrafctngb_test.sql b/raster/test/regress/create_rt_mapalgebrafctngb_test.sql
new file mode 100644 (file)
index 0000000..f77c208
--- /dev/null
@@ -0,0 +1,45 @@
+--
+-- A simple 'callback' user function that sums up all the values in a neighborhood.
+--
+CREATE OR REPLACE FUNCTION ST_Sum(matrix float[][], nodatamode text, variadic args text[])
+    RETURNS float AS
+    $$
+    DECLARE
+        _matrix float[][];
+        x1 integer;
+        x2 integer;
+        y1 integer;
+        y2 integer;
+        sum float;
+    BEGIN
+        _matrix := matrix;
+        sum := 0;
+        FOR x in array_lower(matrix, 1)..array_upper(matrix, 1) LOOP
+            FOR y in array_lower(matrix, 2)..array_upper(matrix, 2) LOOP
+                IF _matrix[x][y] IS NULL THEN
+                    IF nodatamode = 'ignore' THEN
+                        _matrix[x][y] := 0;
+                    ELSE
+                        _matrix[x][y] := nodatamode::float;
+                    END IF;
+                END IF;
+                sum := sum + _matrix[x][y];
+            END LOOP;
+        END LOOP;
+        RETURN sum;
+    END;
+    $$
+    LANGUAGE 'plpgsql';
+
+--
+--Test rasters
+--
+CREATE OR REPLACE FUNCTION ST_TestRasterNgb(h integer, w integer, val float8) 
+    RETURNS raster AS 
+    $$
+    DECLARE
+    BEGIN
+        RETURN ST_AddBand(ST_MakeEmptyRaster(h, w, 0, 0, 1, 1, 0, 0, -1), '32BF', val, -1);
+    END;
+    $$
+    LANGUAGE 'plpgsql';
diff --git a/raster/test/regress/create_rt_mapalgebrafctngb_test_expected b/raster/test/regress/create_rt_mapalgebrafctngb_test_expected
new file mode 100644 (file)
index 0000000..e69de29
index 063726986db589800b157638a688c17f3051febb..b14daedc023fa85b26fb0e0377470349b0d2381a 100644 (file)
@@ -27,3 +27,6 @@ SELECT ST_Value(rast, 1, 1), ST_Value(ST_MapAlgebraFct(rast, 1, '2BUI', 'raster_
 SELECT ST_Value(rast, 1, 1) + 13, ST_Value(ST_MapAlgebraFct(rast, 1, NULL, 'raster_plus_arg1(float, text[])'::regprocedure, '13'), 1, 1) FROM ST_TestRaster(0, 0, 200) AS rast;
 
 SELECT ST_Value(rast, 1, 1) * 21 + 14, ST_Value(ST_MapAlgebraFct(rast, 1, NULL, 'raster_polynomial(float, text[])'::regprocedure, '21', '14'), 1, 1) FROM ST_TestRaster(0, 0, 300) AS rast;
+
+-- Test null return from a user function = NODATA cell value
+SELECT ST_Value(rast, 1, 1), ST_Value(ST_MapAlgebraFct(rast, 1, NULL, 'raster_nullage(float, text[])'::regprocedure), 1, 1) FROM ST_TestRaster(0, 0, 100) AS rast;
index 8b7ed149cc106b3ca5ba772973b284f4a40707eb..d703c0ef82ddf5ac2d46d2eee015409220c5ee5b 100644 (file)
@@ -12,6 +12,106 @@ NOTICE:  Raster does not have the required band. Returning a raster without a ba
 t
 |19
 |19
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
+NOTICE:  Pixel value is null.
 |
 |19
 100|
@@ -19,3 +119,4 @@ t
 101|
 213|213
 6314|6314
+100|
index 0848cd8c9cc4c88e6faf8f5ebdd00729d63fe1b4..7258e95dc9129a74be108c5817dd2b4f173f8db0 100644 (file)
@@ -1,49 +1,3 @@
---
--- A simple 'callback' user function that sums up all the values in a neighborhood.
---
-CREATE OR REPLACE FUNCTION ST_Sum(matrix float[][], nodatamode text, variadic args text[])
-    RETURNS float AS
-    $$
-    DECLARE
-        _matrix float[][];
-        x1 integer;
-        x2 integer;
-        y1 integer;
-        y2 integer;
-        sum float;
-    BEGIN
-        _matrix := matrix;
-        sum := 0;
-        FOR x in array_lower(matrix, 1)..array_upper(matrix, 1) LOOP
-            FOR y in array_lower(matrix, 2)..array_upper(matrix, 2) LOOP
-                IF _matrix[x][y] IS NULL THEN
-                    IF nodatamode = 'ignore' THEN
-                        _matrix[x][y] := 0;
-                    ELSE
-                        _matrix[x][y] := nodatamode::float;
-                    END IF;
-                END IF;
-                sum := sum + _matrix[x][y];
-            END LOOP;
-        END LOOP;
-        RETURN sum;
-    END;
-    $$
-    LANGUAGE 'plpgsql';
-
---
---Test rasters
---
-CREATE OR REPLACE FUNCTION ST_TestRasterNgb(h integer, w integer, val float8) 
-    RETURNS raster AS 
-    $$
-    DECLARE
-    BEGIN
-        RETURN ST_AddBand(ST_MakeEmptyRaster(h, w, 0, 0, 1, 1, 0, 0, -1), '32BF', val, -1);
-    END;
-    $$
-    LANGUAGE 'plpgsql';
-
 -- Tests
 -- Test NULL Raster. Should be true.
 SELECT ST_MapAlgebraFctNgb(NULL, 1, NULL, 1, 1, 'ST_Sum(float[][], text, text[])'::regprocedure, 'NULL', NULL) IS NULL FROM ST_TestRasterNgb(0, 0, -1) rast;