]> granicus.if.org Git - postgis/commitdiff
Rewrote ST_AddBand(raster, ...) array version in C. Ticket is #1363
authorBborie Park <bkpark at ucdavis.edu>
Wed, 1 Aug 2012 23:16:41 +0000 (23:16 +0000)
committerBborie Park <bkpark at ucdavis.edu>
Wed, 1 Aug 2012 23:16:41 +0000 (23:16 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@10150 b70326c6-7e19-0410-871a-916f4a2858ee

NEWS
doc/reference_raster.xml
raster/rt_pg/rt_pg.c
raster/rt_pg/rtpostgis.sql.in.c
raster/rt_pg/rtpostgis_drop.sql.in.c
raster/test/regress/Makefile.in
raster/test/regress/rt_addband.sql
raster/test/regress/rt_addband_expected

diff --git a/NEWS b/NEWS
index ed2ecb78ade1d9cd84c03b5ba82e94966d101528..6c8cf83b1f67eb08d792dbc9b5113f3f2e83d76f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,7 @@ PostGIS 2.1.0
 
 * Enhancements *
   - #823, tiger geocoder: Make loader_generate_script download portion less greedy
+  - #1363, ST_AddBand(raster, ...) array version rewritten in C
   - #1661, Add aggregate variant of ST_SameAlignment
   - #1719, Add support for Point and GeometryCollection ST_MakeValid inputs
   - #1796, Big performance boost for distance calculations in geography
index 540a39019fcfd369db63fd1241006374423c9935..3ff1a23d9edca6659344e156f4bb0fe921dda46a 100644 (file)
@@ -934,6 +934,7 @@ WHERE short_name = 'GTiff') As g;
                                                <paramdef><type>raster </type> <parameter>torast</parameter></paramdef>
                                                <paramdef><type>raster[] </type> <parameter>fromrasts</parameter></paramdef>
                                                <paramdef choice='opt'><type>integer </type> <parameter>fromband=1</parameter></paramdef>
+                                               <paramdef choice='opt'><type>integer </type> <parameter>torastindex=at_end</parameter></paramdef>
                                        </funcprototype>
 
                                </funcsynopsis>
index dabb75134e3ca4456815954c1a9b491ae3316a27..1c064ba3661e62925b57c59fbf4947ded89b9cb7 100644 (file)
@@ -136,6 +136,21 @@ static char *rtpg_getSR(int srid);
  * TODO: In case of functions returning NULL, we should free the memory too.
  *****************************************************************************/
 
+/******************************************************************************
+ * Notes for use of PG_DETOAST_DATUM, PG_DETOAST_DATUM_SLICE
+ * and PG_DETOAST_DATUM_COPY
+ *
+ * When getting raster (not band) metadata, use PG_DETOAST_DATUM_SLICE()
+ *   PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0,
+ *     sizeof(struct rt_raster_serialized_t))
+ *
+ * When setting raster or band metadata, use PG_DETOAST_DATUM()
+ *   PG_DETOAST_DATUM(PG_GETARG_DATUM(0))
+ *
+ * When setting band pixel values, use PG_DETOAST_DATUM_COPY()
+ *
+ *****************************************************************************/
+
 /* Prototypes */
 
 /* Utility functions */
@@ -219,6 +234,7 @@ Datum RASTER_neighborhood(PG_FUNCTION_ARGS);
 Datum RASTER_makeEmpty(PG_FUNCTION_ARGS);
 Datum RASTER_addBand(PG_FUNCTION_ARGS);
 Datum RASTER_copyBand(PG_FUNCTION_ARGS);
+Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS);
 
 /* Raster analysis */
 Datum RASTER_mapAlgebraExpr(PG_FUNCTION_ARGS);
@@ -4166,6 +4182,236 @@ Datum RASTER_addBand(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(pgrtn);
 }
 
+/**
+ * Add bands from array of rasters to a destination raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_addBandRasterArray);
+Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS)
+{
+       rt_pgraster *pgraster = NULL;
+       rt_pgraster *pgsrc = NULL;
+       rt_pgraster *pgrtn = NULL;
+
+       rt_raster raster = NULL;
+       rt_raster src = NULL;
+
+       int srcnband = 1;
+       bool appendband = FALSE;
+       int dstnband = 1;
+       int srcnumbands = 0;
+       int dstnumbands = 0;
+
+       ArrayType *array;
+       Oid etype;
+       Datum *e;
+       bool *nulls;
+       int16 typlen;
+       bool typbyval;
+       char typalign;
+       int ndims = 1;
+       int *dims;
+       int *lbs;
+       int n = 0;
+
+       int rtn = 0;
+       int i = 0;
+
+       /* destination raster */
+       if (!PG_ARGISNULL(0)) {
+               pgraster = (rt_pgraster *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+
+               /* raster */
+               raster = rt_raster_deserialize(pgraster, FALSE);
+               if (!raster) {
+                       elog(ERROR, "RASTER_addBandRasterArray: Could not deserialize destination raster");
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       PG_RETURN_NULL();
+               }
+
+               POSTGIS_RT_DEBUG(4, "destination raster isn't NULL");
+       }
+
+       /* source rasters' band index, 1-based */
+       if (!PG_ARGISNULL(2))
+               srcnband = PG_GETARG_INT32(2);
+       if (srcnband < 1) {
+               elog(NOTICE, "Invalid band index for source rasters (must be 1-based).  Returning original raster");
+               if (raster != NULL) {
+                       rt_raster_destroy(raster);
+                       PG_RETURN_POINTER(pgraster);
+               }
+               else
+                       PG_RETURN_NULL();
+       }
+       POSTGIS_RT_DEBUGF(4, "srcnband = %d", srcnband);
+
+       /* destination raster's band index, 1-based */
+       if (!PG_ARGISNULL(3)) {
+               dstnband = PG_GETARG_INT32(3);
+               appendband = FALSE;
+
+               if (dstnband < 1) {
+                       elog(NOTICE, "Invalid band index for destination raster (must be 1-based).  Returning original raster");
+                       if (raster != NULL) {
+                               rt_raster_destroy(raster);
+                               PG_RETURN_POINTER(pgraster);
+                       }
+                       else
+                               PG_RETURN_NULL();
+               }
+       }
+       else
+               appendband = TRUE;
+
+       /* additional processing of dstnband */
+       if (raster != NULL) {
+               dstnumbands = rt_raster_get_num_bands(raster);
+
+               if (dstnumbands < 1) {
+                       appendband = TRUE;
+                       dstnband = 1;
+               }
+               else if (appendband)
+                       dstnband = dstnumbands + 1;
+               else if (dstnband > dstnumbands) {
+                       elog(NOTICE, "Band index provided for destination raster is greater than the number of bands in the raster.  Bands will be appended");
+                       appendband = TRUE;
+                       dstnband = dstnumbands + 1;
+               }
+       }
+       POSTGIS_RT_DEBUGF(4, "appendband = %d", appendband);
+       POSTGIS_RT_DEBUGF(4, "dstnband = %d", dstnband);
+
+       /* process set of source rasters */
+       POSTGIS_RT_DEBUG(3, "Processing array of source rasters");
+       array = PG_GETARG_ARRAYTYPE_P(1);
+       etype = ARR_ELEMTYPE(array);
+       get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+       ndims = ARR_NDIM(array);
+       dims = ARR_DIMS(array);
+       lbs = ARR_LBOUND(array);
+
+       deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+               &nulls, &n);
+
+       /* quick check that the source band number is valid for all rasters */
+       for (i = 0; i < n; i++) {
+               if (nulls[i]) continue;
+               src = NULL;
+
+               pgsrc = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(e[i], 0, sizeof(struct rt_raster_serialized_t));
+               src = rt_raster_deserialize(pgsrc, TRUE);
+               if (src == NULL) {
+                       elog(ERROR, "RASTER_addBandRasterArray: Could not deserialize source raster at index %d", i + 1);
+                       pfree(nulls);
+                       pfree(e);
+                       if (raster != NULL) {
+                               rt_raster_destroy(raster);
+                               PG_FREE_IF_COPY(pgraster, 0);
+                       }
+                       PG_RETURN_NULL();
+               }
+
+               srcnumbands = rt_raster_get_num_bands(src);
+               rt_raster_destroy(src);
+               POSTGIS_RT_DEBUGF(4, "source raster %d has %d bands", i + 1, srcnumbands);
+
+               /* band index isn't valid */
+               if (srcnband > srcnumbands) {
+                       elog(NOTICE, "Invalid band index for source raster at index %d.  Returning original raster", i + 1);
+                       pfree(nulls);
+                       pfree(e);
+                       if (raster != NULL) {
+                               rt_raster_destroy(raster);
+                               PG_RETURN_POINTER(pgraster);
+                       }
+                       else
+                               PG_RETURN_NULL();
+               }
+       }
+
+       /* decrement srcnband and dstnband by 1, now 0-based */
+       srcnband--;
+       dstnband--;
+       POSTGIS_RT_DEBUGF(4, "0-based nband (src, dst) = (%d, %d)", srcnband, dstnband);
+
+       /* still here, time to copy bands */
+       for (i = 0; i < n; i++) {
+               if (nulls[i]) continue;
+               src = NULL;
+
+               pgsrc = (rt_pgraster *) PG_DETOAST_DATUM(e[i]);
+               src = rt_raster_deserialize(pgsrc, FALSE);
+               if (src == NULL) {
+                       elog(ERROR, "RASTER_addBandRasterArray: Could not deserialize source raster at index %d", i + 1);
+                       pfree(nulls);
+                       pfree(e);
+                       if (raster != NULL)
+                               rt_raster_destroy(raster);
+                       if (pgraster != NULL)
+                               PG_FREE_IF_COPY(pgraster, 0);
+                       PG_RETURN_NULL();
+               }
+
+               /* destination raster is empty, new raster */
+               if (raster == NULL) {
+                       uint32_t srcnbands[1] = {srcnband};
+
+                       POSTGIS_RT_DEBUG(4, "empty destination raster, using rt_raster_from_band");
+
+                       raster = rt_raster_from_band(src, srcnbands, 1);
+                       rt_raster_destroy(src);
+                       if (raster == NULL) {
+                               elog(ERROR, "RASTER_addBandRasterArray: Could not create raster from source raster at index %d", i + 1);
+                               pfree(nulls);
+                               pfree(e);
+                               if (pgraster != NULL)
+                                       PG_FREE_IF_COPY(pgraster, 0);
+                               PG_RETURN_NULL();
+                       }
+               }
+               /* copy band */
+               else {
+                       rtn = rt_raster_copy_band(
+                               raster, src,
+                               srcnband, dstnband
+                       );
+                       rt_raster_destroy(src);
+
+                       if (rtn == -1 || rt_raster_get_num_bands(raster) == dstnumbands) {
+                               elog(NOTICE, "Could not add band from source raster at index %d to destination raster.  Returning original raster", i + 1);
+                               rt_raster_destroy(raster);
+                               pfree(nulls);
+                               pfree(e);
+                               if (pgraster != NULL) {
+                                       PG_FREE_IF_COPY(pgraster, 0);
+                                       PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+                               }
+                               else
+                                       PG_RETURN_NULL();
+                       }
+               }
+
+               dstnband++;
+               dstnumbands++;
+       }
+
+       if (raster != NULL) {
+               pgrtn = rt_raster_serialize(raster);
+               rt_raster_destroy(raster);
+               if (pgraster != NULL)
+                       PG_FREE_IF_COPY(pgraster, 0);
+               if (!pgrtn)
+                       PG_RETURN_NULL();
+
+               SET_VARSIZE(pgrtn, pgrtn->size);
+               PG_RETURN_POINTER(pgrtn);
+       }
+
+       PG_RETURN_NULL();
+}
+
 /**
  * Copy a band from one raster to another one at the given position.
  */
@@ -13170,9 +13416,9 @@ Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
     if (strcmp(strFromText, "VALUE") == 0)
         valuereplace = true;
     else if (strcmp(strFromText, "IGNORE") != 0 && strcmp(strFromText, "NULL") != 0) {
-        // if the text is not "IGNORE" or "NULL", it may be a numerical value
+        /* if the text is not "IGNORE" or "NULL", it may be a numerical value */
         if (sscanf(strFromText, "%d", &intReplace) <= 0 && sscanf(strFromText, "%f", &fltReplace) <= 0) {
-            // the value is NOT an integer NOR a floating point
+            /* the value is NOT an integer NOR a floating point */
             elog(NOTICE, "Neighborhood NODATA mode is not recognized. Must be one of 'value', 'ignore', "
                 "'NULL', or a numeric value. Returning new raster with the original band");
 
index 1e9d3fb3fd961fdc91dc69324cd6546efad6ecb6..e3c5622bd28fde7cde29ed9594eb2a1f88fb9289 100644 (file)
@@ -268,27 +268,14 @@ CREATE OR REPLACE FUNCTION st_addband(
        AS 'MODULE_PATHNAME', 'RASTER_copyBand'
        LANGUAGE 'c' IMMUTABLE; 
 
--- Variant that adds multiple raster bands in one array call --
--- If null is passed in for the torast, then array of rasts is accumulated.
-CREATE OR REPLACE FUNCTION st_addband(torast raster, fromrasts raster[], fromband integer DEFAULT 1)
-    RETURNS raster
-    AS $$
-       DECLARE var_result raster := torast;
-               var_num integer := array_upper(fromrasts,1);
-               var_i integer := 1; 
-       BEGIN 
-               IF torast IS NULL AND var_num > 0 THEN
-                       var_result := ST_Band(fromrasts[1],fromband); 
-                       var_i := 2;
-               END IF;
-               WHILE var_i <= var_num LOOP
-                       var_result := ST_AddBand(var_result, fromrasts[var_i], 1);
-                       var_i := var_i + 1;
-               END LOOP;
-               
-               RETURN var_result;
-       END;
-$$ LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION st_addband(
+       torast raster,
+       fromrasts raster[], fromband integer DEFAULT 1,
+       torastindex int DEFAULT NULL
+)
+       RETURNS raster
+       AS 'MODULE_PATHNAME', 'RASTER_addBandRasterArray'
+       LANGUAGE 'c' IMMUTABLE;
 
 -----------------------------------------------------------------------
 -- Constructor ST_Band
index 72a29e8252d49e345c3dac71676b39783bfd98e4..16ebca816100bcf15d8900ef4b46d25f114a2030 100644 (file)
@@ -430,3 +430,6 @@ DROP FUNCTION IF EXISTS st_contains(raster, integer, geometry);
 DROP FUNCTION IF EXISTS st_contains(geometry, raster, integer);
 DROP FUNCTION IF EXISTS _st_contains(raster, geometry, integer);
 DROP FUNCTION IF EXISTS _st_contains(geometry, raster, integer);
+
+-- function signature changed
+DROP FUNCTION IF EXISTS st_addband(raster, raster[], integer);
index 083fe7ee589b48c20298859e2a4040f0aa8b6062..ffedfc484e0ec23b3260cd02b57e4fc219a15c2b 100644 (file)
@@ -39,7 +39,7 @@ TEST_METADATA = \
 TEST_IO = \
        rt_io
 
-TEST_FUNC = \
+TEST_BASIC_FUNC = \
        rt_bytea \
        box3d \
        rt_addband \
index c0c7586974d394a88f92f737da58b2a69a9875e1..569305df25c1b9db3770ea09af2730dc3e4ea666 100644 (file)
@@ -127,5 +127,46 @@ FROM (
                ) AS rast
 ) foo;
 
+SELECT * FROM ST_BandMetadata(
+       ST_AddBand(
+               NULL::raster,
+               ARRAY[
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '4BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '8BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '16BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '32BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '64BF', 1, 0)
+               ]::raster[]
+       ), ARRAY[]::int[]
+);
+
+SELECT * FROM ST_BandMetadata(
+       ST_AddBand(
+               ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '2BUI', 1, 0),
+               ARRAY[
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '4BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '8BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '16BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '32BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '64BF', 1, 0)
+               ]::raster[]
+       ), ARRAY[]::int[]
+);
+
+SELECT * FROM ST_BandMetadata(
+       ST_AddBand(
+               ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '2BUI', 1, 0),
+               ARRAY[
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '4BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '8BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '16BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '32BUI', 1, 0),
+                       ST_AddBand(ST_MakeEmptyRaster(5, 5, 0, 0, 1, -1, 0, 0, 0), 1, '64BF', 1, 0)
+               ]::raster[],
+               1,
+               1
+       ), ARRAY[]::int[]
+);
+
 -- raster array version test
 SELECT (ST_DumpAsPolygons(newrast,3)).val As b3val FROM (SELECT ST_AddBand(NULL, array_agg(rast)) AS newrast FROM (SELECT ST_AsRaster(ST_Buffer(ST_Point(10,10), 34),200,200, '8BUI',i*30) As rast FROM generate_series(1,3) As i ) As foo ) As foofoo;
index bace39378d0db47c20af9d04e8fb704f1fda729d..801ce9557bb7db9620dde58ce96d66d726e0c36d 100644 (file)
@@ -90,4 +90,21 @@ NOTICE:  rt_raster_copy_band: Second raster has no band
 NOTICE:  RASTER_copyBand: Could not add band to raster. Returning original raster.
 1234.5678
 1234.567|255
+1|4BUI|0|f|
+2|8BUI|0|f|
+3|16BUI|0|f|
+4|32BUI|0|f|
+5|64BF|0|f|
+1|2BUI|0|f|
+2|4BUI|0|f|
+3|8BUI|0|f|
+4|16BUI|0|f|
+5|32BUI|0|f|
+6|64BF|0|f|
+1|4BUI|0|f|
+2|8BUI|0|f|
+3|16BUI|0|f|
+4|32BUI|0|f|
+5|64BF|0|f|
+6|2BUI|0|f|
 90