From: Bborie Park Date: Wed, 1 Aug 2012 23:16:41 +0000 (+0000) Subject: Rewrote ST_AddBand(raster, ...) array version in C. Ticket is #1363 X-Git-Tag: 2.1.0beta2~722 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2d9fbc9d1acc1b39cc6d7d4065cbe03fca9bca20;p=postgis Rewrote ST_AddBand(raster, ...) array version in C. Ticket is #1363 git-svn-id: http://svn.osgeo.org/postgis/trunk@10150 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/NEWS b/NEWS index ed2ecb78a..6c8cf83b1 100644 --- 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 diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index 540a39019..3ff1a23d9 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -934,6 +934,7 @@ WHERE short_name = 'GTiff') As g; raster torast raster[] fromrasts integer fromband=1 + integer torastindex=at_end diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index dabb75134..1c064ba36 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -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"); diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index 1e9d3fb3f..e3c5622bd 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -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 diff --git a/raster/rt_pg/rtpostgis_drop.sql.in.c b/raster/rt_pg/rtpostgis_drop.sql.in.c index 72a29e825..16ebca816 100644 --- a/raster/rt_pg/rtpostgis_drop.sql.in.c +++ b/raster/rt_pg/rtpostgis_drop.sql.in.c @@ -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); diff --git a/raster/test/regress/Makefile.in b/raster/test/regress/Makefile.in index 083fe7ee5..ffedfc484 100644 --- a/raster/test/regress/Makefile.in +++ b/raster/test/regress/Makefile.in @@ -39,7 +39,7 @@ TEST_METADATA = \ TEST_IO = \ rt_io -TEST_FUNC = \ +TEST_BASIC_FUNC = \ rt_bytea \ box3d \ rt_addband \ diff --git a/raster/test/regress/rt_addband.sql b/raster/test/regress/rt_addband.sql index c0c758697..569305df2 100644 --- a/raster/test/regress/rt_addband.sql +++ b/raster/test/regress/rt_addband.sql @@ -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; diff --git a/raster/test/regress/rt_addband_expected b/raster/test/regress/rt_addband_expected index bace39378..801ce9557 100644 --- a/raster/test/regress/rt_addband_expected +++ b/raster/test/regress/rt_addband_expected @@ -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