From 1bb6dcb742da15c8cc2c24ca715912b4f1972d40 Mon Sep 17 00:00:00 2001 From: Bborie Park Date: Fri, 10 Jun 2011 15:19:03 +0000 Subject: [PATCH] Rewrote ST_BandMetaData to use a C function instead of sequential calls for the metadata of a raster's band in plpgsql. Also added regression tests for ST_MetaData and ST_BandMetaData due to C functions. Associated ticket #1012 git-svn-id: http://svn.osgeo.org/postgis/trunk@7357 b70326c6-7e19-0410-871a-916f4a2858ee --- raster/rt_pg/rt_pg.c | 172 ++++++++++++++++++- raster/rt_pg/rtpostgis.sql.in.c | 41 ++--- raster/test/regress/Makefile.in | 2 + raster/test/regress/rt_bandmetadata.sql | 47 +++++ raster/test/regress/rt_bandmetadata_expected | 4 + raster/test/regress/rt_metadata.sql | 6 + raster/test/regress/rt_metadata_expected | 1 + 7 files changed, 242 insertions(+), 31 deletions(-) create mode 100644 raster/test/regress/rt_bandmetadata.sql create mode 100644 raster/test/regress/rt_bandmetadata_expected create mode 100644 raster/test/regress/rt_metadata.sql create mode 100644 raster/test/regress/rt_metadata_expected diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index b9ef3a943..571028424 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -225,6 +225,9 @@ Datum RASTER_transform(PG_FUNCTION_ARGS); /* get raster's meta data */ Datum RASTER_metadata(PG_FUNCTION_ARGS); +/* get raster band's meta data */ +Datum RASTER_bandmetadata(PG_FUNCTION_ARGS); + /* Replace function taken from * http://ubuntuforums.org/showthread.php?s=aa6f015109fd7e4c7e30d2fd8b717497&t=141670&page=3 */ @@ -4955,7 +4958,8 @@ Datum RASTER_transform(PG_FUNCTION_ARGS) * Get raster's meta data */ PG_FUNCTION_INFO_V1(RASTER_metadata); -Datum RASTER_metadata(PG_FUNCTION_ARGS) { +Datum RASTER_metadata(PG_FUNCTION_ARGS) +{ rt_pgraster *pgraster = NULL; rt_raster raster = NULL; @@ -5127,6 +5131,172 @@ Datum RASTER_metadata(PG_FUNCTION_ARGS) { PG_RETURN_DATUM(result); } +/** + * Get raster band's 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; + + TupleDesc tupdesc; + AttInMetadata *attinmeta; + + uint32_t numBands; + uint32_t bandindex = 1; + const char *pixtypename = NULL; + bool hasnodatavalue = FALSE; + double nodatavalue; + const char *bandpath = NULL; + bool isoutdb = FALSE; + + int i = 0; + char **values = NULL; + int values_length = 5; + HeapTuple tuple; + Datum result; + + POSTGIS_RT_DEBUG(3, "RASTER_bandmetadata: Starting"); + + /* pgraster is null, return null */ + if (PG_ARGISNULL(0)) PG_RETURN_NULL(); + pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + /* raster */ + /* TODO: can be optimized to only detoast the header! */ + raster = rt_raster_deserialize(pgraster); + if (!raster) { + elog(ERROR, "RASTER_transform: Could not deserialize raster"); + PG_RETURN_NULL(); + } + + /* 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(); + } + + /* 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"); + rt_raster_destroy(raster); + PG_RETURN_NULL(); + } + } + + 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(); + } + + /* pixeltype */ + pixtypename = rt_pixtype_name(rt_band_get_pixtype(band)); + + /* hasnodatavalue */ + if (rt_band_get_hasnodata_flag(band)) hasnodatavalue = TRUE; + + /* nodatavalue */ + nodatavalue = rt_band_get_nodata(band); + + /* path */ + bandpath = rt_band_get_ext_path(band); + + /* isoutdb */ + isoutdb = bandpath ? TRUE : FALSE; + + rt_band_destroy(band); + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + + /* 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" + ) + )); + } + + /* + * generate attribute metadata needed later to produce tuples from raw + * C strings + */ + attinmeta = TupleDescGetAttInMetadata(tupdesc); + + /* + * Prepare a values array for building the returned tuple. + * This should be an array of C strings which will + * be processed later by the type input functions. + */ + values = (char **) palloc(values_length * sizeof(char *)); + + values[0] = (char *) palloc(sizeof(char) * (strlen(pixtypename) + 1)); + values[1] = (char *) palloc(sizeof(char) * (MAX_INT_CHARLEN + 1)); + values[2] = (char *) palloc(sizeof(char) * (MAX_DBL_CHARLEN + 1)); + values[3] = (char *) palloc(sizeof(char) * (MAX_INT_CHARLEN + 1)); + if (bandpath) + values[4] = (char *) palloc(sizeof(char) * (strlen(bandpath) + 1)); + else + values[4] = NULL; + + snprintf( + values[0], + sizeof(char) * (strlen(pixtypename) + 1), + "%s", + pixtypename + ); + snprintf( + values[1], + sizeof(char) * (MAX_INT_CHARLEN + 1), + "%d", + hasnodatavalue + ); + snprintf( + values[2], + sizeof(char) * (MAX_DBL_CHARLEN + 1), + "%f", + nodatavalue + ); + snprintf( + values[3], + sizeof(char) * (MAX_INT_CHARLEN + 1), + "%d", + isoutdb + ); + if (bandpath) { + snprintf( + values[4], + sizeof(char) * (strlen(bandpath) + 1), + "%s", + bandpath + ); + } + + /* build a tuple */ + tuple = BuildTupleFromCStrings(attinmeta, values); + + /* make the tuple into a datum */ + result = HeapTupleGetDatum(tuple); + + /* clean up */ + for (i = 0; i < values_length; i++) { + if (NULL != values[i]) pfree(values[i]); + } + pfree(values); + + PG_RETURN_DATUM(result); +} + /* ---------------------------------------------------------------- */ /* Memory allocation / error reporting hooks */ /* ---------------------------------------------------------------- */ diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index 39c05a951..ff735094b 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -1837,36 +1837,17 @@ CREATE OR REPLACE FUNCTION st_bandpixeltype(raster) AS $$ SELECT st_bandpixeltype($1, 1) $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION st_bandmetadata(rast raster, - band int, - OUT pixeltype text, - OUT hasnodatavalue boolean, - OUT nodatavalue float4, - OUT isoutdb boolean, - OUT path text) - AS $$ - SELECT st_bandpixeltype($1, $2), - st_bandnodatavalue($1, $2) IS NOT NULL, - st_bandnodatavalue($1, $2), - st_bandpath($1, $2) IS NOT NULL, - st_bandpath($1, $2) - $$ - LANGUAGE SQL IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION st_bandmetadata(rast raster, - OUT pixeltype text, - OUT hasnodatavalue boolean, - OUT nodatavalue float4, - OUT isoutdb boolean, - OUT path text) - AS $$ - SELECT st_bandpixeltype($1, 1), - st_bandnodatavalue($1, 1) IS NOT NULL, - st_bandnodatavalue($1, 1), - st_bandpath($1, 1) IS NOT NULL, - st_bandpath($1, 1) - $$ - LANGUAGE SQL IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION st_bandmetadata( + rast raster, + band int DEFAULT 1, + OUT pixeltype text, + OUT hasnodatavalue boolean, + OUT nodatavalue float4, + OUT isoutdb boolean, + OUT path text +) + AS 'MODULE_PATHNAME','RASTER_bandmetadata' + LANGUAGE 'C' IMMUTABLE STRICT; ----------------------------------------------------------------------- -- Raster Pixel Accessors diff --git a/raster/test/regress/Makefile.in b/raster/test/regress/Makefile.in index b99f8a108..187a27048 100644 --- a/raster/test/regress/Makefile.in +++ b/raster/test/regress/Makefile.in @@ -61,6 +61,7 @@ TEST_PROPS = \ create_rt_empty_raster_test.sql \ rt_isempty.sql \ rt_hasnoband.sql \ + rt_metadata.sql \ $(NULL) TEST_BANDPROPS = \ @@ -77,6 +78,7 @@ TEST_BANDPROPS = \ rt_quantile.sql \ rt_valuecount.sql \ rt_valuepercent.sql \ + rt_bandmetadata.sql \ $(NULL) TEST_PIXEL = \ diff --git a/raster/test/regress/rt_bandmetadata.sql b/raster/test/regress/rt_bandmetadata.sql new file mode 100644 index 000000000..222de30cb --- /dev/null +++ b/raster/test/regress/rt_bandmetadata.sql @@ -0,0 +1,47 @@ +SELECT * 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 + ) +); +SELECT * 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 +); +SELECT * 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 +); diff --git a/raster/test/regress/rt_bandmetadata_expected b/raster/test/regress/rt_bandmetadata_expected new file mode 100644 index 000000000..0aa43866d --- /dev/null +++ b/raster/test/regress/rt_bandmetadata_expected @@ -0,0 +1,4 @@ +64BF|t|0|f| +64BF|t|0|f| +NOTICE: Invalid band index (must use 1-based). Returning NULL +|||| diff --git a/raster/test/regress/rt_metadata.sql b/raster/test/regress/rt_metadata.sql new file mode 100644 index 000000000..62e885e4f --- /dev/null +++ b/raster/test/regress/rt_metadata.sql @@ -0,0 +1,6 @@ +SELECT * FROM ST_MetaData( + ST_AddBand( + ST_MakeEmptyRaster(10, 10, 10, 10, 2, 2, 0, 0,-1) + , 1, '64BF', 0, 0 + ) +); diff --git a/raster/test/regress/rt_metadata_expected b/raster/test/regress/rt_metadata_expected new file mode 100644 index 000000000..a51807bc7 --- /dev/null +++ b/raster/test/regress/rt_metadata_expected @@ -0,0 +1 @@ +10|10|10|10|2|2|0|0|-1|1 -- 2.50.1