int *dims;
int *lbs;
+ uint32_t numBands;
uint32_t *bandNums;
uint32 idx = 0;
int n;
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);
}
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;
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 */
}
/**
- * 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);
+ }
}
/**
+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,
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,
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,
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
);