From: Bborie Park Date: Tue, 29 Nov 2011 23:25:49 +0000 (+0000) Subject: In preparation of turning the table raster_columns into a view, refactored ST_BandMet... X-Git-Tag: 2.0.0alpha1~606 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=28c81a8579585bdda733eb9693b902d6321bd1f6;p=postgis In preparation of turning the table raster_columns into a view, refactored ST_BandMetadata to have the bandnum parameter be variadic. Fleshed out regression tests for ST_BandMetadata. git-svn-id: http://svn.osgeo.org/postgis/trunk@8259 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index 6aaff2cce..da5712b30 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -3386,6 +3386,7 @@ Datum RASTER_band(PG_FUNCTION_ARGS) int *dims; int *lbs; + uint32_t numBands; uint32_t *bandNums; uint32 idx = 0; int n; @@ -3409,6 +3410,8 @@ Datum RASTER_band(PG_FUNCTION_ARGS) do { if (skip) break; + numBands = rt_raster_get_num_bands(raster); + array = PG_GETARG_ARRAYTYPE_P(1); etype = ARR_ELEMTYPE(array); get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign); @@ -3445,7 +3448,7 @@ Datum RASTER_band(PG_FUNCTION_ARGS) } POSTGIS_RT_DEBUGF(3, "band idx (before): %d", idx); - if (idx > pgraster->numBands || idx < 1) { + if (idx > numBands || idx < 1) { elog(NOTICE, "Invalid band index (must use 1-based). Returning original raster"); skip = TRUE; break; @@ -3958,7 +3961,7 @@ Datum RASTER_histogram(PG_FUNCTION_ARGS) raster = rt_raster_deserialize(pgraster, FALSE); if (!raster) { elog(ERROR, "RASTER_histogram: Could not deserialize raster"); - PG_RETURN_NULL(); + SRF_RETURN_DONE(funcctx); } /* band index is 1-based */ @@ -7660,133 +7663,248 @@ Datum RASTER_metadata(PG_FUNCTION_ARGS) } /** - * Get raster band's meta data + * Get raster bands' meta data */ PG_FUNCTION_INFO_V1(RASTER_bandmetadata); Datum RASTER_bandmetadata(PG_FUNCTION_ARGS) { - rt_pgraster *pgraster = NULL; - rt_raster raster = NULL; - rt_band band = NULL; + FuncCallContext *funcctx; + TupleDesc tupdesc; + int call_cntr; + int max_calls; - uint32_t numBands; - uint32_t bandindex = 1; - const char *tmp = NULL; - char *pixtypename = NULL; - bool hasnodatavalue = FALSE; - double nodatavalue; - char *bandpath = NULL; - bool isoutdb = FALSE; + struct bandmetadata { + uint32_t bandnum; + char *pixeltype; + bool hasnodata; + double nodataval; + bool isoutdb; + char *bandpath; + }; + struct bandmetadata *bmd = NULL; + struct bandmetadata *bmd2 = NULL; - TupleDesc tupdesc; bool *nulls = NULL; - int values_length = 5; + int values_length = 6; Datum values[values_length]; HeapTuple tuple; Datum result; - POSTGIS_RT_DEBUG(3, "RASTER_bandmetadata: Starting"); + if (SRF_IS_FIRSTCALL()) { + MemoryContext oldcontext; - /* pgraster is null, return null */ - if (PG_ARGISNULL(0)) PG_RETURN_NULL(); - pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + rt_pgraster *pgraster = NULL; + rt_raster raster = NULL; + rt_band band = NULL; - /* raster */ - raster = rt_raster_deserialize(pgraster, FALSE); - if (!raster) { - elog(ERROR, "RASTER_bandmetadata: Could not deserialize raster"); - PG_RETURN_NULL(); - } + ArrayType *array; + Oid etype; + Datum *e; + bool *nulls; + int16 typlen; + bool typbyval; + char typalign; + int ndims = 1; + int *dims; + int *lbs; + int i = 0; + int j = 0; + int n = 0; - /* numbands */ - numBands = rt_raster_get_num_bands(raster); - if (numBands < 1) { - elog(NOTICE, "Raster provided has no bands"); - rt_raster_destroy(raster); - PG_RETURN_NULL(); - } + uint32_t numBands; + uint32_t idx = 1; + uint32_t *bandNums = NULL; + const char *tmp = NULL; - /* band index */ - if (!PG_ARGISNULL(1)) { - bandindex = PG_GETARG_INT32(1); - if (bandindex < 1 || bandindex > numBands) { - elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL"); + POSTGIS_RT_DEBUG(3, "RASTER_bandmetadata: Starting"); + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* switch to memory context appropriate for multiple function calls */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* pgraster is null, return null */ + if (PG_ARGISNULL(0)) SRF_RETURN_DONE(funcctx); + pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + /* raster */ + raster = rt_raster_deserialize(pgraster, FALSE); + if (!raster) { + elog(ERROR, "RASTER_bandmetadata: Could not deserialize raster"); + SRF_RETURN_DONE(funcctx); + } + + /* numbands */ + numBands = rt_raster_get_num_bands(raster); + if (numBands < 1) { + elog(NOTICE, "Raster provided has no bands"); rt_raster_destroy(raster); - PG_RETURN_NULL(); + SRF_RETURN_DONE(funcctx); } - } - band = rt_raster_get_band(raster, bandindex - 1); - if (NULL == band) { - elog(NOTICE, "Could not get raster band at index %d", bandindex); - rt_raster_destroy(raster); - PG_RETURN_NULL(); - } + /* band index */ + array = PG_GETARG_ARRAYTYPE_P(1); + etype = ARR_ELEMTYPE(array); + get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign); - /* pixeltype */ - tmp = rt_pixtype_name(rt_band_get_pixtype(band)); - pixtypename = palloc(sizeof(char) * (strlen(tmp) + 1)); - strncpy(pixtypename, tmp, strlen(tmp) + 1); + switch (etype) { + case INT2OID: + case INT4OID: + break; + default: + elog(ERROR, "RASTER_bandmetadata: Invalid data type for band number(s)"); + rt_raster_destroy(raster); + SRF_RETURN_DONE(funcctx); + break; + } - /* hasnodatavalue */ - if (rt_band_get_hasnodata_flag(band)) hasnodatavalue = TRUE; + ndims = ARR_NDIM(array); + dims = ARR_DIMS(array); + lbs = ARR_LBOUND(array); - /* nodatavalue */ - if (hasnodatavalue) - nodatavalue = rt_band_get_nodata(band); - else - nodatavalue = 0; + deconstruct_array(array, etype, typlen, typbyval, typalign, &e, + &nulls, &n); - /* path */ - tmp = rt_band_get_ext_path(band); - if (tmp) { - bandpath = palloc(sizeof(char) * (strlen(tmp) + 1)); - strncpy(bandpath, tmp, strlen(tmp) + 1); - } + bandNums = palloc(sizeof(uint32_t) * n); + for (i = 0, j = 0; i < n; i++) { + if (nulls[i]) continue; - /* isoutdb */ - isoutdb = bandpath ? TRUE : FALSE; + switch (etype) { + case INT2OID: + idx = (uint32_t) DatumGetInt16(e[i]); + break; + case INT4OID: + idx = (uint32_t) DatumGetInt32(e[i]); + break; + } - rt_band_destroy(band); - rt_raster_destroy(raster); + POSTGIS_RT_DEBUGF(3, "band idx (before): %d", idx); + if (idx > numBands || idx < 1) { + elog(NOTICE, "Invalid band index: %d. Indices must be 1-based. Returning NULL", idx); + pfree(bandNums); + rt_raster_destroy(raster); + SRF_RETURN_DONE(funcctx); + } - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { - ereport(ERROR, ( - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg( - "function returning record called in context " - "that cannot accept type record" - ) - )); + bandNums[j] = idx; + POSTGIS_RT_DEBUGF(3, "bandNums[%d] = %d", j, bandNums[j]); + j++; + } + + if (j < n) + bandNums = repalloc(bandNums, sizeof(uint32_t) * j); + bmd = (struct bandmetadata *) palloc(sizeof(struct bandmetadata) * j); + + for (i = 0; i < j; i++) { + band = rt_raster_get_band(raster, bandNums[i] - 1); + if (NULL == band) { + elog(NOTICE, "Could not get raster band at index %d", bandNums[i]); + rt_raster_destroy(raster); + SRF_RETURN_DONE(funcctx); + } + + /* bandnum */ + bmd[i].bandnum = bandNums[i]; + + /* pixeltype */ + tmp = rt_pixtype_name(rt_band_get_pixtype(band)); + bmd[i].pixeltype = palloc(sizeof(char) * (strlen(tmp) + 1)); + strncpy(bmd[i].pixeltype, tmp, strlen(tmp) + 1); + + /* hasnodatavalue */ + if (rt_band_get_hasnodata_flag(band)) + bmd[i].hasnodata = TRUE; + else + bmd[i].hasnodata = FALSE; + + /* nodatavalue */ + if (bmd[i].hasnodata) + bmd[i].nodataval = rt_band_get_nodata(band); + else + bmd[i].nodataval = 0; + + /* path */ + tmp = rt_band_get_ext_path(band); + if (tmp) { + bmd[i].bandpath = palloc(sizeof(char) * (strlen(tmp) + 1)); + strncpy(bmd[i].bandpath, tmp, strlen(tmp) + 1); + } + else + bmd[i].bandpath = NULL; + + /* isoutdb */ + bmd[i].isoutdb = bmd[i].bandpath ? TRUE : FALSE; + + rt_band_destroy(band); + } + + rt_raster_destroy(raster); + + /* Store needed information */ + funcctx->user_fctx = bmd; + + /* total number of tuples to be returned */ + funcctx->max_calls = j; + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { + ereport(ERROR, ( + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg( + "function returning record called in context " + "that cannot accept type record" + ) + )); + } + + BlessTupleDesc(tupdesc); + funcctx->tuple_desc = tupdesc; + + MemoryContextSwitchTo(oldcontext); } - BlessTupleDesc(tupdesc); + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); - nulls = palloc(sizeof(bool) * values_length); - memset(nulls, FALSE, values_length); + call_cntr = funcctx->call_cntr; + max_calls = funcctx->max_calls; + tupdesc = funcctx->tuple_desc; + bmd2 = funcctx->user_fctx; - values[0] = CStringGetTextDatum(pixtypename); - values[1] = BoolGetDatum(hasnodatavalue); - values[2] = Float8GetDatum(nodatavalue); - values[3] = BoolGetDatum(isoutdb); - if (bandpath && strlen(bandpath)) - values[4] = CStringGetTextDatum(bandpath); - else - nulls[4] = TRUE; + /* do when there is more left to send */ + if (call_cntr < max_calls) { + nulls = palloc(sizeof(bool) * values_length); + memset(nulls, FALSE, values_length); - /* build a tuple */ - tuple = heap_form_tuple(tupdesc, values, nulls); + values[0] = UInt32GetDatum(bmd2[call_cntr].bandnum); + values[1] = CStringGetTextDatum(bmd2[call_cntr].pixeltype); + values[2] = BoolGetDatum(bmd2[call_cntr].hasnodata); + values[3] = Float8GetDatum(bmd2[call_cntr].nodataval); + values[4] = BoolGetDatum(bmd2[call_cntr].isoutdb); + if (bmd2[call_cntr].bandpath && strlen(bmd2[call_cntr].bandpath)) + values[5] = CStringGetTextDatum(bmd2[call_cntr].bandpath); + else + nulls[5] = TRUE; - /* make the tuple into a datum */ - result = HeapTupleGetDatum(tuple); + /* build a tuple */ + tuple = heap_form_tuple(tupdesc, values, nulls); - /* clean up */ - pfree(nulls); - pfree(pixtypename); - if (bandpath) pfree(bandpath); + /* make the tuple into a datum */ + result = HeapTupleGetDatum(tuple); - PG_RETURN_DATUM(result); + /* clean up */ + pfree(nulls); + pfree(bmd2[call_cntr].pixeltype); + if (bmd2[call_cntr].bandpath) pfree(bmd2[call_cntr].bandpath); + + SRF_RETURN_NEXT(funcctx, result); + } + /* do when there is no more left */ + else { + pfree(bmd2); + SRF_RETURN_DONE(funcctx); + } } /** diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index e5f61f7f1..c61f0ddbf 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -1837,6 +1837,23 @@ CREATE OR REPLACE FUNCTION st_bandpixeltype(rast raster, band integer DEFAULT 1) AS 'MODULE_PATHNAME','RASTER_getBandPixelTypeName' LANGUAGE 'C' IMMUTABLE STRICT; +CREATE TYPE bandmetadata AS ( + bandnum int, + pixeltype text, + hasnodata boolean, + nodatavalue double precision, + isoutdb boolean, + path text +); + +CREATE OR REPLACE FUNCTION st_bandmetadata( + rast raster, + VARIADIC band int[] +) + RETURNS SETOF bandmetadata + AS 'MODULE_PATHNAME','RASTER_bandmetadata' + LANGUAGE 'C' IMMUTABLE STRICT; + CREATE OR REPLACE FUNCTION st_bandmetadata( rast raster, band int DEFAULT 1, @@ -1846,8 +1863,8 @@ CREATE OR REPLACE FUNCTION st_bandmetadata( OUT isoutdb boolean, OUT path text ) - AS 'MODULE_PATHNAME','RASTER_bandmetadata' - LANGUAGE 'C' IMMUTABLE STRICT; + AS $$ SELECT pixeltype, hasnodata, nodatavalue, isoutdb, path FROM st_bandmetadata($1, VARIADIC ARRAY[$2]::int[]) LIMIT 1 $$ + LANGUAGE 'sql' IMMUTABLE STRICT; ----------------------------------------------------------------------- -- Raster Pixel Accessors diff --git a/raster/test/regress/rt_bandmetadata.sql b/raster/test/regress/rt_bandmetadata.sql index ef4ca754f..071f0a016 100644 --- a/raster/test/regress/rt_bandmetadata.sql +++ b/raster/test/regress/rt_bandmetadata.sql @@ -1,3 +1,38 @@ +CREATE OR REPLACE FUNCTION make_test_raster( + width integer DEFAULT 10, + height integer DEFAULT 10, + ul_x double precision DEFAULT 0, + ul_y double precision DEFAULT 0, + skew_x double precision DEFAULT 0, + skew_y double precision DEFAULT 0, + numbands integer DEFAULT 1, + autofill boolean DEFAULT FALSE +) + RETURNS raster + AS $$ + DECLARE + i int; + x int; + y int; + rast raster; + BEGIN + rast := ST_MakeEmptyRaster(width, height, ul_x, ul_y, 1, 1, skew_x, skew_y, 0); + FOR i IN 1..numbands LOOP + rast := ST_AddBand(rast, i, '8BUI', 0, i); + + IF autofill IS TRUE THEN + FOR x IN 1..width LOOP + FOR y IN 1..height LOOP + rast := ST_SetValue(rast, i, x, y, ((x - 1) * width) + y); + END LOOP; + END LOOP; + END IF; + END LOOP; + + RETURN rast; + END; + $$ LANGUAGE 'plpgsql'; + SELECT pixeltype, hasnodata, @@ -5,20 +40,9 @@ SELECT isoutdb, path FROM ST_BandMetaData( - ST_SetValue( - ST_SetValue( - ST_SetValue( - ST_AddBand( - ST_MakeEmptyRaster(10, 10, 10, 10, 2, 2, 0, 0,-1) - , 1, '64BF', 0, 0 - ) - , 1, 1, 1, -10 - ) - , 1, 5, 4, 0 - ) - , 1, 5, 5, 3.14159 - ) + make_test_raster(10, 10, 0, 0, 0, 0) ); + SELECT pixeltype, hasnodata, @@ -26,21 +50,10 @@ SELECT isoutdb, path FROM ST_BandMetaData( - ST_SetValue( - ST_SetValue( - ST_SetValue( - ST_AddBand( - ST_MakeEmptyRaster(10, 10, 10, 10, 2, 2, 0, 0,-1) - , 1, '64BF', 0, 0 - ) - , 1, 1, 1, -10 - ) - , 1, 5, 4, 0 - ) - , 1, 5, 5, 3.14159 - ), - 1 + make_test_raster(10, 10, 0, 0, 0, 0, 2), + 2 ); + SELECT pixeltype, hasnodata, @@ -48,18 +61,51 @@ SELECT isoutdb, path FROM ST_BandMetaData( - ST_SetValue( - ST_SetValue( - ST_SetValue( - ST_AddBand( - ST_MakeEmptyRaster(10, 10, 10, 10, 2, 2, 0, 0,-1) - , 1, '64BF', 0, 0 - ) - , 1, 1, 1, -10 - ) - , 1, 5, 4, 0 - ) - , 1, 5, 5, 3.14159 - ), - 2 + make_test_raster(10, 10, 0, 0, 0, 0, 3, TRUE), + 3 +); + +SELECT + pixeltype, + hasnodata, + round(nodatavalue::numeric, 3), + isoutdb, + path +FROM ST_BandMetaData( + make_test_raster(10, 10, 0, 0, 0, 0, 5, TRUE), + 4 +); + +SELECT + pixeltype, + hasnodata, + round(nodatavalue::numeric, 3), + isoutdb, + path +FROM ST_BandMetaData( + make_test_raster(10, 10, 0, 0, 0, 0, 5, TRUE), + 6 +); + +SELECT + bandnum + pixeltype, + hasnodata, + round(nodatavalue::numeric, 3), + isoutdb, + path +FROM ST_BandMetaData( + make_test_raster(10, 10, 0, 0, 0, 0, 5, TRUE), + 1,2,5 +); + +DROP FUNCTION IF EXISTS make_test_raster( + integer, + integer, + double precision, + double precision, + double precision, + double precision, + integer, + boolean ); diff --git a/raster/test/regress/rt_bandmetadata_expected b/raster/test/regress/rt_bandmetadata_expected index 4824dbc28..076218fc7 100644 --- a/raster/test/regress/rt_bandmetadata_expected +++ b/raster/test/regress/rt_bandmetadata_expected @@ -1,4 +1,9 @@ -64BF|t|0.000|f| -64BF|t|0.000|f| -NOTICE: Invalid band index (must use 1-based). Returning NULL +8BUI|t|1.000|f| +8BUI|t|2.000|f| +8BUI|t|3.000|f| +8BUI|t|4.000|f| +NOTICE: Invalid band index: 6. Indices must be 1-based. Returning NULL |||| +1|t|1.000|f| +2|t|2.000|f| +5|t|5.000|f|