From 46a76d3b39bb36c2b896e2a67dc88dac13be1fce Mon Sep 17 00:00:00 2001 From: Bborie Park Date: Fri, 10 May 2013 00:09:11 +0000 Subject: [PATCH] Addition of ST_ColorMap(raster). Ticket #2290 git-svn-id: http://svn.osgeo.org/postgis/trunk@11392 b70326c6-7e19-0410-871a-916f4a2858ee --- NEWS | 1 + doc/reference_raster.xml | 128 +++- raster/rt_core/rt_api.c | 712 ++++++++++++++++++++++- raster/rt_core/rt_api.h | 52 ++ raster/rt_pg/rt_pg.c | 477 ++++++++++++++- raster/rt_pg/rtpostgis.sql.in | 73 +++ raster/test/cunit/Makefile.in | 1 + raster/test/cunit/cu_mapalgebra.c | 305 ++++++++++ raster/test/cunit/cu_misc.c | 117 ++++ raster/test/cunit/cu_tester.c | 2 + raster/test/regress/Makefile.in | 3 +- raster/test/regress/rt_colormap.sql | 133 +++++ raster/test/regress/rt_colormap_expected | 36 ++ 13 files changed, 2012 insertions(+), 28 deletions(-) create mode 100644 raster/test/cunit/cu_misc.c create mode 100644 raster/test/regress/rt_colormap.sql create mode 100644 raster/test/regress/rt_colormap_expected diff --git a/NEWS b/NEWS index 6971e8fbf..4086c6ec7 100644 --- a/NEWS +++ b/NEWS @@ -86,6 +86,7 @@ PostGIS 2.1.0 - #2163, ST_TPI for raster (Nathaniel Clay) - #2164, ST_TRI for raster (Nathaniel Clay) - #2302, ST_Roughness for raster (Nathaniel Clay) + - #2290, ST_ColorMap(raster) to generate RGBA bands * Enhancements * diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index d5179842c..f4ba7c8d3 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -10763,10 +10763,136 @@ UPDATE wind See Also - , , , , , + + , + , + , + , + , + + + + + + ST_ColorMap + Creates a new raster of up to four 8BUI bands (grayscale, RGB, RGBA) from the source raster and specified band. + + + + + raster ST_ColorMap + raster rast + integer nband=1 + text colormap=grayscale + text method=INTERPOLATE + + + + + + raster ST_ColorMap + raster rast + text colormap + text method=INTERPOLATE + + + + + + Description + + + Apply a colormap to the band at nband of rast resulting a new raster comprised of up to four 8BUI bands. The number of 8BUI bands in the new raster is determined by the number of color components defined in colormap. + + + + colormap can be a keyword of a pre-defined colormap or a set of lines defining the value and the color components. + + + + Valid pre-defined colormap keyword: + + + + + + grayscale, greyscale for a one 8BUI band raster + + + + + pseudocolor for a four 8BUI (RGBA) band raster + + + + + + Users can pass a set of entries (one per line) to colormap to specify custom colormaps. Each entry generally consists of five values: the pixel value and corresponding Red, Green, Blue, Alpha components (color components between 0 and 255). Percent values can be used instead of pixel values where 0% and 100% are the minimum and maximum values found in the raster band. Values can be separated with commas (','), tabs, colons (':') and/or spaces. The pixel value can be set to nv, null or nodata for the NODATA value. An example is provided below. + + + +5 0 0 0 255 +4 100:50 55 255 +1 150,100 150 255 +0% 255 255 255 255 +nv 0 0 0 0 + + + + The syntax of colormap is similar to that of the color-relief mode of GDAL gdaldem. + + + + Valid keywords for method: + + + + + + INTERPOLATE to use linear interpolation to smoothly blend the colors between the given pixel values + + + + + EXACT to strictly match only those pixels values found in the colormap. Pixels whose value does not match a colormap entry will be set to 0 0 0 0 (RGBA) + + + + + NEAREST to use the colormap entry whose value is closest to the pixel value + + + + + + + A great reference for colormaps is ColorBrewer. + + + + Availability: 2.1.0 + + + + Examples + + +-- needs examples + + + + + See Also + + , + + + + + ST_Union diff --git a/raster/rt_core/rt_api.c b/raster/rt_core/rt_api.c index b8f09496a..3f8117692 100644 --- a/raster/rt_core/rt_api.c +++ b/raster/rt_core/rt_api.c @@ -576,6 +576,129 @@ rt_util_same_geotransform_matrix(double *gt1, double *gt2) { return TRUE; } +/* coordinates in RGB and HSV are floating point values between 0 and 1 */ +rt_errorstate +rt_util_rgb_to_hsv(double rgb[3], double hsv[3]) { + int i; + + double minc; + double maxc; + + double h = 0.; + double s = 0.; + double v = 0.; + + minc = rgb[0]; + maxc = rgb[0]; + + /* get min and max values from RGB */ + for (i = 1; i < 3; i++) { + if (rgb[i] > maxc) + maxc = rgb[i]; + if (rgb[i] < minc) + minc = rgb[i]; + } + v = maxc; + + if (maxc != minc) { + double diff = 0.; + double rc = 0.; + double gc = 0.; + double bc = 0.; + double junk = 0.; + + diff = maxc - minc; + s = diff / maxc; + rc = (maxc - rgb[0]) / diff; + gc = (maxc - rgb[1]) / diff; + bc = (maxc - rgb[2]) / diff; + + if (DBL_EQ(rgb[0], maxc)) + h = bc - gc; + else if (DBL_EQ(rgb[1], maxc)) + h = 2.0 + rc - bc; + else + h = 4.0 + gc - rc; + + h = modf((h / 6.0), &junk); + } + + hsv[0] = h; + hsv[1] = s; + hsv[2] = v; + + return ES_NONE; +} + +/* coordinates in RGB and HSV are floating point values between 0 and 1 */ +rt_errorstate +rt_util_hsv_to_rgb(double hsv[3], double rgb[3]) { + double r = 0; + double g = 0; + double b = 0; + double v = hsv[2]; + + if (DBL_EQ(hsv[1], 0.)) + r = g = b = v; + else { + double i; + double f; + double p; + double q; + double t; + + int a; + + i = floor(hsv[0] * 6.); + f = (hsv[0] * 6.0) - i; + p = v * (1. - hsv[1]); + q = v * (1. - hsv[1] * f); + t = v * (1. - hsv[1] * (1. - f)); + + a = (int) i; + switch (a) { + case 1: + r = q; + g = v; + b = p; + break; + case 2: + r = p; + g = v; + b = t; + break; + case 3: + r = p; + g = q; + b = v; + break; + case 4: + r = t; + g = p; + b = v; + break; + case 5: + r = v; + g = p; + b = q; + break; + case 0: + case 6: + default: + r = v; + g = t; + b = p; + break; + } + } + + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; + + return ES_NONE; +} + /*- rt_context -------------------------------------------------------*/ /* Functions definitions */ @@ -4942,6 +5065,8 @@ rt_band_reclass( assert(NULL != srcband); assert(NULL != exprset && exprcount > 0); + RASTER_DEBUGF(4, "exprcount = %d", exprcount); + RASTER_DEBUGF(4, "exprset @ %p", exprset); /* source nodata */ src_hasnodata = rt_band_get_hasnodata_flag(srcband); @@ -5161,6 +5286,7 @@ rt_band_reclass( /* no expression matched, do not continue */ if (!do_nv) continue; + RASTER_DEBUGF(3, "Using exprset[%d] unless NODATA", i); /* converting a value from one range to another range OldRange = (OldMax - OldMin) @@ -5168,7 +5294,7 @@ rt_band_reclass( NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin */ - /* nodata */ + /* NODATA */ if (hasnodata && isnodata) { nv = nodataval; } @@ -5217,7 +5343,7 @@ rt_band_reclass( break; } - RASTER_DEBUGF(3, "(%d, %d) ov: %f or: %f - %f nr: %f - %f nv: %f" + RASTER_DEBUGF(4, "(%d, %d) ov: %f or: %f - %f nr: %f - %f nv: %f" , x , y , ov @@ -14697,3 +14823,585 @@ rt_errorstate rt_raster_get_perimeter( return ES_NONE; } + +/****************************************************************************** +* rt_raster_colormap() +******************************************************************************/ + +typedef struct _rti_colormap_rgbhsv_t* _rti_colormap_rgbhsv; +typedef struct _rti_colormap_arg_t* _rti_colormap_arg; +struct _rti_colormap_rgbhsv_t { + double rgb[3]; + double hsv[3]; +}; + +struct _rti_colormap_arg_t { + rt_raster raster; + rt_band band; + + rt_colormap_entry nodataentry; + int hasnodata; + double nodataval; + + int nexpr; + rt_reclassexpr *expr; + + int npos; + uint16_t *pos; + + int nrgbhsv; + _rti_colormap_rgbhsv rgbhsv; + +}; + +static _rti_colormap_arg +_rti_colormap_arg_init(rt_raster raster) { + _rti_colormap_arg arg = NULL; + + arg = rtalloc(sizeof(struct _rti_colormap_arg_t)); + if (arg == NULL) { + rterror("_rti_colormap_arg_init: Unable to allocate memory for _rti_color_arg"); + return NULL; + } + + arg->band = NULL; + arg->nodataentry = NULL; + arg->hasnodata = 0; + arg->nodataval = 0; + + if (raster == NULL) + arg->raster = NULL; + /* raster provided */ + else { + arg->raster = rt_raster_clone(raster, 0); + if (arg->raster == NULL) { + rterror("_rti_colormap_arg_init: Unable to create output raster"); + return NULL; + } + } + + arg->nexpr = 0; + arg->expr = NULL; + + arg->npos = 0; + arg->pos = NULL; + + arg->nrgbhsv = 0; + arg->rgbhsv = NULL; + + return arg; +} + +static void +_rti_colormap_arg_destroy(_rti_colormap_arg arg) { + int i = 0; + + if (arg->raster != NULL) { + rt_band band = NULL; + + for (i = rt_raster_get_num_bands(arg->raster) - 1; i >= 0; i--) { + band = rt_raster_get_band(arg->raster, i); + if (band != NULL) + rt_band_destroy(band); + } + + rt_raster_destroy(arg->raster); + } + + if (arg->nexpr) { + for (i = 0; i < arg->nexpr; i++) { + if (arg->expr[i] != NULL) + rtdealloc(arg->expr[i]); + } + rtdealloc(arg->expr); + } + + if (arg->npos) + rtdealloc(arg->pos); + + if (arg->rgbhsv != NULL) + rtdealloc(arg->rgbhsv); + + rtdealloc(arg); + arg = NULL; +} + +/** + * Returns a new raster with up to four 8BUI bands (RGBA) from + * applying a colormap to the user-specified band of the + * input raster. + * + * @param raster: input raster + * @param nband: 0-based index of the band to process with colormap + * @param colormap: rt_colormap object of colormap to apply to band + * + * @return new raster or NULL on error + */ +rt_raster rt_raster_colormap( + rt_raster raster, int nband, + rt_colormap colormap +) { + _rti_colormap_arg arg = NULL; + rt_raster rtnraster = NULL; + rt_band band = NULL; + int i = 0; + int j = 0; + int k = 0; + + assert(colormap != NULL); + + /* empty raster */ + if (rt_raster_is_empty(raster)) + return NULL; + + /* no colormap entries */ + if (colormap->nentry < 1) { + rterror("rt_raster_colormap: colormap must have at least one entry"); + return NULL; + } + + /* nband is valid */ + if (!rt_raster_has_band(raster, nband)) { + rterror("rt_raster_colormap: raster has no band at index %d", nband); + return NULL; + } + + band = rt_raster_get_band(raster, nband); + if (band == NULL) { + rterror("rt_raster_colormap: Unable to get band at index %d", nband); + return NULL; + } + + /* init internal variables */ + arg = _rti_colormap_arg_init(raster); + if (arg == NULL) { + rterror("rt_raster_colormap: Unable to initialize internal variables"); + return NULL; + } + + /* handle NODATA */ + if (rt_band_get_hasnodata_flag(band)) { + arg->hasnodata = 1; + rt_band_get_nodata(band, &(arg->nodataval)); + } + + /* # of colors */ + if (colormap->ncolor < 1) { + rterror("rt_raster_colormap: At least one color must be provided"); + _rti_colormap_arg_destroy(arg); + return NULL; + } + else if (colormap->ncolor > 4) { + rtinfo("More than four colors indicated. Using only the first four colors"); + colormap->ncolor = 4; + } + + /* find non-NODATA entries */ + arg->npos = 0; + arg->pos = rtalloc(sizeof(uint16_t) * colormap->nentry); + if (arg->pos == NULL) { + rterror("rt_raster_colormap: Unable to allocate memory for valid entries"); + _rti_colormap_arg_destroy(arg); + return NULL; + } + for (i = 0, j = 0; i < colormap->nentry; i++) { + /* special handling for NODATA entries */ + if (colormap->entry[i].isnodata) { + /* first NODATA entry found, use it */ + if (arg->nodataentry == NULL) + arg->nodataentry = &(colormap->entry[i]); + else + rtwarn("More than one colormap entry found for NODATA value. Only using first NOTDATA entry"); + + continue; + } + + (arg->npos)++; + arg->pos[j++] = i; + } + + /* INTERPOLATE and only one non-NODATA entry */ + if (colormap->method == CM_INTERPOLATE && arg->npos < 2) { + rtinfo("Method INTERPOLATE requires at least two non-NODATA colormap entries. Using NEAREST instead"); + colormap->method = CM_NEAREST; + } + + /* NODATA entry but band has no NODATA value */ + if (!arg->hasnodata && arg->nodataentry != NULL) { + rtinfo("Band at index %d has no NODATA value. Ignoring NODATA entry", nband); + arg->nodataentry = NULL; + } + + /* allocate expr */ + arg->nexpr = arg->npos; + + /* INTERPOLATE needs one less than the number of entries */ + if (colormap->method == CM_INTERPOLATE) + arg->nexpr -= 1; + /* EXACT requires a no matching expression */ + else if (colormap->method == CM_EXACT) + arg->nexpr += 1; + + /* NODATA entry exists, add expression */ + if (arg->nodataentry != NULL) + arg->nexpr += 1; + arg->expr = rtalloc(sizeof(rt_reclassexpr) * arg->nexpr); + if (arg->expr == NULL) { + rterror("rt_raster_colormap: Unable to allocate memory for reclass expressions"); + _rti_colormap_arg_destroy(arg); + return NULL; + } + RASTER_DEBUGF(4, "nexpr = %d", arg->nexpr); + RASTER_DEBUGF(4, "expr @ %p", arg->expr); + + for (i = 0; i < arg->nexpr; i++) { + arg->expr[i] = rtalloc(sizeof(struct rt_reclassexpr_t)); + if (arg->expr[i] == NULL) { + rterror("rt_raster_colormap: Unable to allocate memory for reclass expression"); + _rti_colormap_arg_destroy(arg); + return NULL; + } + } + + /* rgb to hsv */ + if (colormap->method == CM_INTERPOLATE && colormap->ncolor > 2) { + arg->nrgbhsv = arg->npos; + if (arg->nodataentry != NULL) + arg->nrgbhsv += 1; + + /* allocate space */ + arg->rgbhsv = rtalloc(sizeof(struct _rti_colormap_rgbhsv_t) * arg->nrgbhsv); + if (arg->rgbhsv == NULL) { + rterror("rt_raster_colormap: Unable to allocate memory for RGB to HSV conversion"); + _rti_colormap_arg_destroy(arg); + return NULL; + } + memset(arg->rgbhsv, 0, sizeof(struct _rti_colormap_rgbhsv_t) * arg->nrgbhsv); + + for (i = 0; i < arg->nrgbhsv; i++) { + /* convert colormap's 0 - 255 to 0 - 1 */ + for (j = 0; j < 3; j++) { + if (i < arg->npos) + arg->rgbhsv[i].rgb[j] = ((double) colormap->entry[arg->pos[i]].color[j]) / 255.; + else + arg->rgbhsv[i].rgb[j] = ((double) arg->nodataentry->color[j]) / 255.; + } + + /* convert to hsv */ + rt_util_rgb_to_hsv(arg->rgbhsv[i].rgb, arg->rgbhsv[i].hsv); + } + } + + /* reclassify bands */ + /* by # of colors */ + for (i = 0; i < colormap->ncolor; i++) { + k = 0; + + /* handle NODATA entry first */ + if (arg->nodataentry != NULL) { + arg->expr[k]->src.min = arg->nodataentry->value; + arg->expr[k]->src.max = arg->nodataentry->value; + arg->expr[k]->src.inc_min = 1; + arg->expr[k]->src.inc_max = 1; + arg->expr[k]->src.exc_min = 0; + arg->expr[k]->src.exc_max = 0; + + /* use HSV */ + if (arg->nrgbhsv && i < 3) { + arg->expr[k]->dst.min = arg->rgbhsv[arg->nrgbhsv - 1].hsv[i]; + arg->expr[k]->dst.max = arg->rgbhsv[arg->nrgbhsv - 1].hsv[i]; + } + /* use RGB */ + else { + arg->expr[k]->dst.min = arg->nodataentry->color[i]; + arg->expr[k]->dst.max = arg->nodataentry->color[i]; + } + + arg->expr[k]->dst.inc_min = 1; + arg->expr[k]->dst.inc_max = 1; + arg->expr[k]->dst.exc_min = 0; + arg->expr[k]->dst.exc_max = 0; + + RASTER_DEBUGF(4, "NODATA expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)", + k, + arg->expr[k]->src.min, + arg->expr[k]->src.max, + arg->expr[k]->src.inc_min, + arg->expr[k]->src.inc_max, + arg->expr[k]->src.exc_min, + arg->expr[k]->src.exc_max + ); + RASTER_DEBUGF(4, "NODATA expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)", + k, + arg->expr[k]->dst.min, + arg->expr[k]->dst.max, + arg->expr[k]->dst.inc_min, + arg->expr[k]->dst.inc_max, + arg->expr[k]->dst.exc_min, + arg->expr[k]->dst.exc_max + ); + + k++; + } + + /* by non-NODATA entry */ + for (j = 0; j < arg->npos; j++) { + if (colormap->method == CM_INTERPOLATE) { + if (j == arg->npos - 1) + continue; + + arg->expr[k]->src.min = colormap->entry[arg->pos[j + 1]].value; + arg->expr[k]->src.inc_min = 1; + arg->expr[k]->src.exc_min = 0; + + arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value; + arg->expr[k]->src.inc_max = 1; + arg->expr[k]->src.exc_max = 0; + + /* use HSV */ + if (arg->nrgbhsv && i < 3) { + arg->expr[k]->dst.min = arg->rgbhsv[j + 1].hsv[i]; + arg->expr[k]->dst.max = arg->rgbhsv[j].hsv[i]; + } + /* use RGB */ + else { + arg->expr[k]->dst.min = colormap->entry[arg->pos[j + 1]].color[i]; + arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i]; + } + + arg->expr[k]->dst.inc_min = 1; + arg->expr[k]->dst.exc_min = 0; + + arg->expr[k]->dst.inc_max = 1; + arg->expr[k]->dst.exc_max = 0; + } + else if (colormap->method == CM_NEAREST) { + + /* NOT last entry */ + if (j != arg->npos - 1) { + arg->expr[k]->src.min = ((colormap->entry[arg->pos[j]].value - colormap->entry[arg->pos[j + 1]].value) / 2.) + colormap->entry[arg->pos[j + 1]].value; + arg->expr[k]->src.inc_min = 0; + arg->expr[k]->src.exc_min = 0; + } + /* last entry */ + else { + arg->expr[k]->src.min = colormap->entry[arg->pos[j]].value; + arg->expr[k]->src.inc_min = 1; + arg->expr[k]->src.exc_min = 1; + } + + /* NOT first entry */ + if (j > 0) { + arg->expr[k]->src.max = arg->expr[k - 1]->src.min; + arg->expr[k]->src.inc_max = 1; + arg->expr[k]->src.exc_max = 0; + } + /* first entry */ + else { + arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value; + arg->expr[k]->src.inc_max = 1; + arg->expr[k]->src.exc_max = 1; + } + + arg->expr[k]->dst.min = colormap->entry[arg->pos[j]].color[i]; + arg->expr[k]->dst.inc_min = 1; + arg->expr[k]->dst.exc_min = 0; + + arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i]; + arg->expr[k]->dst.inc_max = 1; + arg->expr[k]->dst.exc_max = 0; + } + else if (colormap->method == CM_EXACT) { + arg->expr[k]->src.min = colormap->entry[arg->pos[j]].value; + arg->expr[k]->src.inc_min = 1; + arg->expr[k]->src.exc_min = 0; + + arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value; + arg->expr[k]->src.inc_max = 1; + arg->expr[k]->src.exc_max = 0; + + arg->expr[k]->dst.min = colormap->entry[arg->pos[j]].color[i]; + arg->expr[k]->dst.inc_min = 1; + arg->expr[k]->dst.exc_min = 0; + + arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i]; + arg->expr[k]->dst.inc_max = 1; + arg->expr[k]->dst.exc_max = 0; + } + + RASTER_DEBUGF(4, "expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)", + k, + arg->expr[k]->src.min, + arg->expr[k]->src.max, + arg->expr[k]->src.inc_min, + arg->expr[k]->src.inc_max, + arg->expr[k]->src.exc_min, + arg->expr[k]->src.exc_max + ); + + RASTER_DEBUGF(4, "expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)", + k, + arg->expr[k]->dst.min, + arg->expr[k]->dst.max, + arg->expr[k]->dst.inc_min, + arg->expr[k]->dst.inc_max, + arg->expr[k]->dst.exc_min, + arg->expr[k]->dst.exc_max + ); + + k++; + } + + /* EXACT has one last expression for catching all uncaught values */ + if (colormap->method == CM_EXACT) { + arg->expr[k]->src.min = 0; + arg->expr[k]->src.inc_min = 1; + arg->expr[k]->src.exc_min = 1; + + arg->expr[k]->src.max = 0; + arg->expr[k]->src.inc_max = 1; + arg->expr[k]->src.exc_max = 1; + + arg->expr[k]->dst.min = 0; + arg->expr[k]->dst.inc_min = 1; + arg->expr[k]->dst.exc_min = 0; + + arg->expr[k]->dst.max = 0; + arg->expr[k]->dst.inc_max = 1; + arg->expr[k]->dst.exc_max = 0; + + RASTER_DEBUGF(4, "expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)", + k, + arg->expr[k]->src.min, + arg->expr[k]->src.max, + arg->expr[k]->src.inc_min, + arg->expr[k]->src.inc_max, + arg->expr[k]->src.exc_min, + arg->expr[k]->src.exc_max + ); + + RASTER_DEBUGF(4, "expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)", + k, + arg->expr[k]->dst.min, + arg->expr[k]->dst.max, + arg->expr[k]->dst.inc_min, + arg->expr[k]->dst.inc_max, + arg->expr[k]->dst.exc_min, + arg->expr[k]->dst.exc_max + ); + + k++; + } + + /* call rt_band_reclass */ + if (arg->nrgbhsv) + arg->band = rt_band_reclass(band, PT_32BF, 0, 0, arg->expr, arg->nexpr); + else + arg->band = rt_band_reclass(band, PT_8BUI, 0, 0, arg->expr, arg->nexpr); + if (arg->band == NULL) { + rterror("rt_raster_colormap: Unable to reclassify band"); + _rti_colormap_arg_destroy(arg); + return NULL; + } + + /* add reclassified band to raster */ + if (rt_raster_add_band(arg->raster, arg->band, rt_raster_get_num_bands(arg->raster)) < 0) { + rterror("rt_raster_colormap: Unable to add reclassified band to output raster"); + _rti_colormap_arg_destroy(arg); + return NULL; + } + } + + /* convert HSV back to RGB 0 - 255 */ + if (arg->nrgbhsv) { + int width = rt_raster_get_width(arg->raster); + int height = rt_raster_get_height(arg->raster); + rt_band _band = NULL; + rt_band band[6]; + struct _rti_colormap_rgbhsv_t hsvrgb; + + /* get band objects */ + for (i = 0; i < 6; i++) { + /* existing band */ + if (i < 3) + band[i] = rt_raster_get_band(arg->raster, i); + /* new band */ + else { + void *mem =rtalloc(rt_pixtype_size(PT_8BUI) * width * height); + if (mem == NULL) { + rterror("rt_raster_colormap: Unable to allocate memory for new band"); + for (j = 3; j < i; j++) + rt_band_destroy(band[j]); + _rti_colormap_arg_destroy(arg); + return NULL; + } + memset(mem, 0, rt_pixtype_size(PT_8BUI) * width * height); + + band[i] = rt_band_new_inline( + width, height, + PT_8BUI, + 0, 0, + mem + ); + if (band[i] == NULL) { + rterror("rt_raster_colormap: Unable to create new band"); + rtdealloc(mem); + for (j = 3; j < i; j++) + rt_band_destroy(band[j]); + _rti_colormap_arg_destroy(arg); + return NULL; + } + rt_band_set_ownsdata_flag(band[i], 1); + } + } + + /* process each pixel */ + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + /* get HSV components */ + for (k = 0; k < 3; k++) { + if (rt_band_get_pixel(band[k], j, i, &(hsvrgb.hsv[k]), NULL) != ES_NONE) { + rterror("rt_raster_colormap: Unable to process HSV values from bands"); + for (i = 3; i < 6; i++) + rt_band_destroy(band[j]); + _rti_colormap_arg_destroy(arg); + return NULL; + } + } + + /* convert HSV to RGB 0 - 1 */ + rt_util_hsv_to_rgb(hsvrgb.hsv, hsvrgb.rgb); + + /* convert RGB 0 - 1 to 0 - 255 and burn */ + for (k = 0; k < 3; k++) { + if (rt_band_set_pixel(band[k + 3], j, i, ROUND(hsvrgb.rgb[k] * 255., 0), NULL) != ES_NONE) { + rterror("rt_raster_colormap: Unable to set RGB values to bands"); + for (i = 3; i < 6; i++) + rt_band_destroy(band[j]); + _rti_colormap_arg_destroy(arg); + return NULL; + } + } + } + } + + /* replace bands */ + for (i = 0; i < 3; i++) { + _band = rt_raster_replace_band(arg->raster, band[i + 3], i); + if (_band == NULL) { + rterror("rt_raster_colormap: Unable to replace HSV band with RGB band"); + for (j = i + 3; j < 6; j++) + rt_band_destroy(band[j]); + _rti_colormap_arg_destroy(arg); + return NULL; + } + rt_band_destroy(_band); + } + } + + rtnraster = arg->raster; + arg->raster = NULL; + _rti_colormap_arg_destroy(arg); + + return rtnraster; +} diff --git a/raster/rt_core/rt_api.h b/raster/rt_core/rt_api.h index 59b729517..1e937e18f 100644 --- a/raster/rt_core/rt_api.h +++ b/raster/rt_core/rt_api.h @@ -145,6 +145,9 @@ typedef struct rt_reclassexpr_t* rt_reclassexpr; typedef struct rt_iterator_t* rt_iterator; typedef struct rt_iterator_arg_t* rt_iterator_arg; +typedef struct rt_colormap_entry_t* rt_colormap_entry; +typedef struct rt_colormap_t* rt_colormap; + /* envelope information */ typedef struct { double MinX; @@ -1955,6 +1958,22 @@ rt_raster_iterator( rt_raster *rtnraster ); +/** + * Returns a new raster with up to four 8BUI bands (RGBA) from + * applying a colormap to the user-specified band of the + * input raster. + * + * @param raster: input raster + * @param nband: 0-based index of the band to process with colormap + * @param colormap: rt_colormap object of colormap to apply to band + * + * @return new raster or NULL on error + */ +rt_raster rt_raster_colormap( + rt_raster raster, int nband, + rt_colormap colormap +); + /*- utilities -------------------------------------------------------*/ /* @@ -2122,6 +2141,20 @@ rt_util_same_geotransform_matrix( double *gt2 ); +/* coordinates in RGB and HSV are floating point values between 0 and 1 */ +rt_errorstate +rt_util_rgb_to_hsv( + double rgb[3], + double hsv[3] +); + +/* coordinates in RGB and HSV are floating point values between 0 and 1 */ +rt_errorstate +rt_util_hsv_to_rgb( + double hsv[3], + double rgb[3] +); + /* helper macros for consistent floating point equality checks */ @@ -2362,4 +2395,23 @@ struct rt_gdaldriver_t { char *create_options; }; +/* raster colormap entry */ +struct rt_colormap_entry_t { + int isnodata; + double value; + uint8_t color[4]; /* RGBA */ +}; + +struct rt_colormap_t { + enum { + CM_INTERPOLATE, + CM_EXACT, + CM_NEAREST + } method; + + int ncolor; + uint16_t nentry; + rt_colormap_entry entry; +}; + #endif /* RT_API_H_INCLUDED */ diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index ed353a66f..fea5ee928 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -4,7 +4,7 @@ * WKTRaster - Raster Types for PostGIS * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage * - * Copyright (C) 2011-2012 Regents of the University of California + * Copyright (C) 2011-2013 Regents of the University of California * * Copyright (C) 2010-2011 Jorge Arevalo * Copyright (C) 2010-2011 David Zwarg @@ -311,6 +311,9 @@ Datum RASTER_valueCountCoverage(PG_FUNCTION_ARGS); /* reclassify specified bands of a raster */ Datum RASTER_reclass(PG_FUNCTION_ARGS); +/* apply colormap to specified band of a raster */ +Datum RASTER_colorMap(PG_FUNCTION_ARGS); + /* convert GDAL raster to raster */ Datum RASTER_fromGDALRaster(PG_FUNCTION_ARGS); @@ -388,9 +391,6 @@ Datum RASTER_union_finalfn(PG_FUNCTION_ARGS); /* raster clip */ Datum RASTER_clip(PG_FUNCTION_ARGS); -/* raster perimeter */ -Datum RASTER_perimeter(PG_FUNCTION_ARGS); - /* string replacement function taken from * http://ubuntuforums.org/showthread.php?s=aa6f015109fd7e4c7e30d2fd8b717497&t=141670&page=3 */ @@ -585,6 +585,7 @@ rtpg_trim(const char *input) { char *rtn; char *ptr; uint32_t offset = 0; + int inputlen = 0; if (!input) return NULL; @@ -592,21 +593,24 @@ rtpg_trim(const char *input) { return (char *) input; /* trim left */ - while (isspace(*input)) + while (isspace(*input) && *input != '\0') input++; /* trim right */ - ptr = ((char *) input) + strlen(input); - while (isspace(*--ptr)) - offset++; + inputlen = strlen(input); + if (inputlen) { + ptr = ((char *) input) + inputlen; + while (isspace(*--ptr)) + offset++; + } - rtn = palloc(sizeof(char) * (strlen(input) - offset + 1)); + rtn = palloc(sizeof(char) * (inputlen - offset + 1)); if (rtn == NULL) { fprintf(stderr, "Not enough memory\n"); return NULL; } - strncpy(rtn, input, strlen(input) - offset); - rtn[strlen(input) - offset] = '\0'; + strncpy(rtn, input, inputlen - offset); + rtn[inputlen - offset] = '\0'; return rtn; } @@ -10741,9 +10745,444 @@ Datum RASTER_reclass(PG_FUNCTION_ARGS) { PG_RETURN_POINTER(pgrtn); } -/** - * Returns raster from GDAL raster; - */ +/* ---------------------------------------------------------------- */ +/* apply colormap to specified band of a raster */ +/* ---------------------------------------------------------------- */ + +typedef struct rtpg_colormap_arg_t *rtpg_colormap_arg; +struct rtpg_colormap_arg_t { + rt_raster raster; + int nband; /* 1-based */ + rt_band band; + rt_bandstats bandstats; + + rt_colormap colormap; + int nodataentry; + + char **entry; + int nentry; + char **element; + int nelement; +}; + +static rtpg_colormap_arg +rtpg_colormap_arg_init() { + rtpg_colormap_arg arg = NULL; + + arg = palloc(sizeof(struct rtpg_colormap_arg_t)); + if (arg == NULL) { + elog(ERROR, "rtpg_colormap_arg: Unable to allocate memory for function arguments"); + return NULL; + } + + arg->raster = NULL; + arg->nband = 1; + arg->band = NULL; + arg->bandstats = NULL; + + arg->colormap = palloc(sizeof(struct rt_colormap_t)); + if (arg->colormap == NULL) { + elog(ERROR, "rtpg_colormap_arg: Unable to allocate memory for function arguments"); + return NULL; + } + arg->colormap->nentry = 0; + arg->colormap->entry = NULL; + arg->colormap->ncolor = 4; /* assume RGBA */ + arg->colormap->method = CM_INTERPOLATE; + arg->nodataentry = -1; + + arg->entry = NULL; + arg->nentry = 0; + arg->element = NULL; + arg->nelement = 0; + + return arg; +} + +static void +rtpg_colormap_arg_destroy(rtpg_colormap_arg arg) { + int i = 0; + if (arg->raster != NULL) + rt_raster_destroy(arg->raster); + + if (arg->bandstats != NULL) + pfree(arg->bandstats); + + if (arg->colormap != NULL) { + if (arg->colormap->entry != NULL) + pfree(arg->colormap->entry); + pfree(arg->colormap); + } + + if (arg->nentry) { + for (i = 0; i < arg->nentry; i++) { + if (arg->entry[i] != NULL) + pfree(arg->entry[i]); + } + pfree(arg->entry); + } + + if (arg->nelement) { + for (i = 0; i < arg->nelement; i++) + pfree(arg->element[i]); + pfree(arg->element); + } + + pfree(arg); + arg = NULL; +} + +PG_FUNCTION_INFO_V1(RASTER_colorMap); +Datum RASTER_colorMap(PG_FUNCTION_ARGS) +{ + rt_pgraster *pgraster = NULL; + rtpg_colormap_arg arg = NULL; + char *junk = NULL; + rt_raster raster = NULL; + + POSTGIS_RT_DEBUG(3, "RASTER_colorMap: Starting"); + + /* pgraster is NULL, return NULL */ + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + /* init arg */ + arg = rtpg_colormap_arg_init(); + if (arg == NULL) { + elog(ERROR, "RASTER_colorMap: Unable to initialize argument structure"); + PG_RETURN_NULL(); + } + + /* raster (0) */ + pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + /* raster object */ + arg->raster = rt_raster_deserialize(pgraster, FALSE); + if (!arg->raster) { + elog(ERROR, "RASTER_colorMap: Could not deserialize raster"); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + /* nband (1) */ + if (!PG_ARGISNULL(1)) + arg->nband = PG_GETARG_INT32(1); + POSTGIS_RT_DEBUGF(4, "nband = %d", arg->nband); + + /* check that band works */ + if (!rt_raster_has_band(arg->raster, arg->nband - 1)) { + elog(NOTICE, "Raster does not have band at index %d. Returning empty raster", arg->nband); + + raster = rt_raster_clone(arg->raster, 0); + if (raster == NULL) { + elog(ERROR, "RASTER_colorMap: Unable to create empty raster"); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + + pgraster = rt_raster_serialize(raster); + rt_raster_destroy(raster); + if (pgraster == NULL) + PG_RETURN_NULL(); + + SET_VARSIZE(pgraster, ((rt_pgraster*) pgraster)->size); + PG_RETURN_POINTER(pgraster); + } + + /* get band */ + arg->band = rt_raster_get_band(arg->raster, arg->nband - 1); + if (arg->band == NULL) { + elog(ERROR, "RASTER_colorMap: Unable to get band at index %d", arg->nband); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + /* method (3) */ + if (!PG_ARGISNULL(3)) { + char *method = NULL; + char *tmp = text_to_cstring(PG_GETARG_TEXT_P(3)); + POSTGIS_RT_DEBUGF(4, "raw method = %s", tmp); + + method = rtpg_trim(tmp); + pfree(tmp); + method = rtpg_strtoupper(method); + + if (strcmp(method, "INTERPOLATE") == 0) + arg->colormap->method = CM_INTERPOLATE; + else if (strcmp(method, "EXACT") == 0) + arg->colormap->method = CM_EXACT; + else if (strcmp(method, "NEAREST") == 0) + arg->colormap->method = CM_NEAREST; + else { + elog(NOTICE, "Unknown value provided for method. Defaulting to INTERPOLATE"); + arg->colormap->method = CM_INTERPOLATE; + } + } + /* default to INTERPOLATE */ + else + arg->colormap->method = CM_INTERPOLATE; + POSTGIS_RT_DEBUGF(4, "method = %d", arg->colormap->method); + + /* colormap (2) */ + if (PG_ARGISNULL(2)) { + elog(ERROR, "RASTER_colorMap: Value must be provided for colormap"); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + else { + char *tmp = NULL; + char *colormap = text_to_cstring(PG_GETARG_TEXT_P(2)); + char *_entry; + char *_element; + int i = 0; + int j = 0; + + POSTGIS_RT_DEBUGF(4, "colormap = %s", colormap); + + /* empty string */ + if (!strlen(colormap)) { + elog(ERROR, "RASTER_colorMap: Value must be provided for colormap"); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + arg->entry = rtpg_strsplit(colormap, "\n", &(arg->nentry)); + pfree(colormap); + if (arg->nentry < 1) { + elog(ERROR, "RASTER_colorMap: Unable to process the value provided for colormap"); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + /* allocate the max # of colormap entries */ + arg->colormap->entry = palloc(sizeof(struct rt_colormap_entry_t) * arg->nentry); + if (arg->colormap->entry == NULL) { + elog(ERROR, "RASTER_colorMap: Unable to allocate memory for colormap entries"); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + memset(arg->colormap->entry, 0, sizeof(struct rt_colormap_entry_t) * arg->nentry); + + /* each entry */ + for (i = 0; i < arg->nentry; i++) { + /* substitute space for other delimiters */ + tmp = rtpg_strreplace(arg->entry[i], ":", " ", NULL); + _entry = rtpg_strreplace(tmp, ",", " ", NULL); + pfree(tmp); + tmp = rtpg_strreplace(_entry, "\t", " ", NULL); + pfree(_entry); + _entry = rtpg_trim(tmp); + pfree(tmp); + + POSTGIS_RT_DEBUGF(4, "Processing entry[%d] = %s", i, arg->entry[i]); + POSTGIS_RT_DEBUGF(4, "Cleaned entry[%d] = %s", i, _entry); + + /* empty entry, continue */ + if (!strlen(_entry)) { + pfree(_entry); + continue; + } + + arg->element = rtpg_strsplit(_entry, " ", &(arg->nelement)); + pfree(_entry); + if (arg->nelement < 2) { + elog(ERROR, "RASTER_colorMap: Unable to process colormap entry %d", i + 1); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + else if (arg->nelement > 5) { + elog(NOTICE, "More than five elements in colormap entry %d. Using at most five elements", i + 1); + arg->nelement = 5; + } + + /* smallest # of colors */ + if ((arg->nelement - 1) < arg->colormap->ncolor) + arg->colormap->ncolor = arg->nelement - 1; + + /* each element of entry */ + for (j = 0; j < arg->nelement; j++) { + + _element = rtpg_trim(arg->element[j]); + _element = rtpg_strtoupper(_element); + POSTGIS_RT_DEBUGF(4, "Processing entry[%d][%d] = %s", i, j, arg->element[j]); + POSTGIS_RT_DEBUGF(4, "Cleaned entry[%d][%d] = %s", i, j, _element); + + /* first element is ALWAYS a band value, percentage OR "nv" string */ + if (j == 0) { + char *percent = NULL; + + /* NODATA */ + if ( + strcmp(_element, "NV") == 0 || + strcmp(_element, "NULL") == 0 || + strcmp(_element, "NODATA") == 0 + ) { + POSTGIS_RT_DEBUG(4, "Processing NODATA string"); + + if (arg->nodataentry > -1) { + elog(NOTICE, "More than one NODATA entry found. Using only the first one"); + } + else { + arg->colormap->entry[arg->colormap->nentry].isnodata = 1; + /* no need to set value as value comes from band's NODATA */ + arg->colormap->entry[arg->colormap->nentry].value = 0; + } + } + /* percent value */ + else if ((percent = strchr(_element, '%')) != NULL) { + double value; + POSTGIS_RT_DEBUG(4, "Processing percent string"); + + /* get the band stats */ + if (arg->bandstats == NULL) { + POSTGIS_RT_DEBUG(4, "Getting band stats"); + + arg->bandstats = rt_band_get_summary_stats(arg->band, 1, 1, 0, NULL, NULL, NULL); + if (arg->bandstats == NULL) { + elog(ERROR, "RASTER_colorMap: Unable to get band's summary stats to process percentages"); + pfree(_element); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + } + + /* get the string before the percent char */ + tmp = palloc(sizeof(char) * (percent - _element + 1)); + if (tmp == NULL) { + elog(ERROR, "RASTER_colorMap: Unable to allocate memory for value of percentage"); + pfree(_element); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + memcpy(tmp, _element, percent - _element); + tmp[percent - _element] = '\0'; + POSTGIS_RT_DEBUGF(4, "Percent value = %s", tmp); + + /* get percentage value */ + errno = 0; + value = strtod(tmp, NULL); + pfree(tmp); + if (errno != 0 || _element == junk) { + elog(ERROR, "RASTER_colorMap: Unable to process percent string to value"); + pfree(_element); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + /* check percentage */ + if (value < 0.) { + elog(NOTICE, "Percentage values cannot be less than zero. Defaulting to zero"); + value = 0.; + } + else if (value > 100.) { + elog(NOTICE, "Percentage values cannot be greater than 100. Defaulting to 100"); + value = 100.; + } + + /* get the true pixel value */ + /* TODO: should the percentage be quantile based? */ + arg->colormap->entry[arg->colormap->nentry].value = ((value / 100.) * (arg->bandstats->max - arg->bandstats->min)) + arg->bandstats->min; + } + /* straight value */ + else { + errno = 0; + arg->colormap->entry[arg->colormap->nentry].value = strtod(_element, &junk); + if (errno != 0 || _element == junk) { + elog(ERROR, "RASTER_colorMap: Unable to process string to value"); + pfree(_element); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + } + + } + /* RGB values (0 - 255) */ + else { + int value = 0; + + errno = 0; + value = (int) strtod(_element, &junk); + if (errno != 0 || _element == junk) { + elog(ERROR, "RASTER_colorMap: Unable to process string to value"); + pfree(_element); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + if (value > 255) { + elog(NOTICE, "RGBA value cannot be greater than 255. Defaulting to 255"); + value = 255; + } + else if (value < 0) { + elog(NOTICE, "RGBA value cannot be less than zero. Defaulting to zero"); + value = 0; + } + arg->colormap->entry[arg->colormap->nentry].color[j - 1] = value; + } + + pfree(_element); + } + + POSTGIS_RT_DEBUGF(4, "colormap->entry[%d] (isnodata, value, R, G, B, A) = (%d, %f, %d, %d, %d, %d)", + arg->colormap->nentry, + arg->colormap->entry[arg->colormap->nentry].isnodata, + arg->colormap->entry[arg->colormap->nentry].value, + arg->colormap->entry[arg->colormap->nentry].color[0], + arg->colormap->entry[arg->colormap->nentry].color[1], + arg->colormap->entry[arg->colormap->nentry].color[2], + arg->colormap->entry[arg->colormap->nentry].color[3] + ); + + arg->colormap->nentry++; + } + + POSTGIS_RT_DEBUGF(4, "colormap->nentry = %d", arg->colormap->nentry); + POSTGIS_RT_DEBUGF(4, "colormap->ncolor = %d", arg->colormap->ncolor); + } + + /* call colormap */ + raster = rt_raster_colormap(arg->raster, arg->nband - 1, arg->colormap); + if (raster == NULL) { + elog(ERROR, "RASTER_colorMap: Unable to create new raster with applied colormap"); + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + rtpg_colormap_arg_destroy(arg); + PG_FREE_IF_COPY(pgraster, 0); + pgraster = rt_raster_serialize(raster); + rt_raster_destroy(raster); + + POSTGIS_RT_DEBUG(3, "RASTER_colorMap: Done"); + + if (pgraster == NULL) + PG_RETURN_NULL(); + + SET_VARSIZE(pgraster, ((rt_pgraster*) pgraster)->size); + PG_RETURN_POINTER(pgraster); +} + +/* ---------------------------------------------------------------- */ +/* Returns raster from GDAL raster */ +/* ---------------------------------------------------------------- */ PG_FUNCTION_INFO_V1(RASTER_fromGDALRaster); Datum RASTER_fromGDALRaster(PG_FUNCTION_ARGS) { @@ -18692,16 +19131,6 @@ Datum RASTER_clip(PG_FUNCTION_ARGS) PG_RETURN_POINTER(pgrtn); } -/* ---------------------------------------------------------------- */ -/* Find perimeter of raster */ -/* ---------------------------------------------------------------- */ - -PG_FUNCTION_INFO_V1(RASTER_perimeter); -Datum RASTER_perimeter(PG_FUNCTION_ARGS) -{ - PG_RETURN_NULL(); -} - /* ---------------------------------------------------------------- */ /* Memory allocation / error reporting hooks */ /* TODO: reuse the ones in libpgcommon ? */ diff --git a/raster/rt_pg/rtpostgis.sql.in b/raster/rt_pg/rtpostgis.sql.in index ba55bfd3b..c53a656e9 100644 --- a/raster/rt_pg/rtpostgis.sql.in +++ b/raster/rt_pg/rtpostgis.sql.in @@ -1583,6 +1583,79 @@ CREATE OR REPLACE FUNCTION st_reclass(rast raster, reclassexpr text, pixeltype t AS $$ SELECT st_reclass($1, ROW(1, $2, $3, NULL)) $$ LANGUAGE 'sql' IMMUTABLE STRICT; +----------------------------------------------------------------------- +-- ST_ColorMap +----------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION _st_colormap( + rast raster, nband int, + colormap text, + method text DEFAULT 'INTERPOLATE' +) + RETURNS raster + AS 'MODULE_PATHNAME', 'RASTER_colorMap' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION st_colormap( + rast raster, nband int DEFAULT 1, + colormap text DEFAULT 'grayscale', + method text DEFAULT 'INTERPOLATE' +) + RETURNS raster + AS $$ + DECLARE + _ismap boolean; + _colormap text; + _element text[]; + BEGIN + _ismap := TRUE; + + -- clean colormap to see what it is + _colormap := split_part(colormap, E'\n', 1); + _colormap := regexp_replace(_colormap, E':+', ' ', 'g'); + _colormap := regexp_replace(_colormap, E',+', ' ', 'g'); + _colormap := regexp_replace(_colormap, E'\\t+', ' ', 'g'); + _colormap := regexp_replace(_colormap, E' +', ' ', 'g'); + _element := regexp_split_to_array(_colormap, ' '); + + -- treat as colormap + IF (array_length(_element, 1) > 1) THEN + _colormap := colormap; + -- treat as keyword + ELSE + method := 'INTERPOLATE'; + CASE lower(trim(both from _colormap)) + WHEN 'grayscale', 'greyscale' THEN + _colormap = ' + 100% 0 + 0% 254 + nv 255 + '; + WHEN 'pseudocolor' THEN + _colormap = ' + 100% 255 0 0 255 + 50% 0 255 0 255 + 0% 0 0 255 255 + nv 0 0 0 0 + '; + ELSE + RAISE EXCEPTION 'Unknown colormap keyword: %', colormap; + END CASE; + END IF; + + RETURN _st_colormap($1, $2, _colormap, $4); + END; + $$ LANGUAGE 'plpgsql' IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION st_colormap( + rast raster, + colormap text, + method text DEFAULT 'INTERPOLATE' +) + RETURNS RASTER + AS $$ SELECT ST_ColorMap($1, 1, $2, $3) $$ + LANGUAGE 'sql' IMMUTABLE STRICT; + ----------------------------------------------------------------------- -- ST_FromGDALRaster ----------------------------------------------------------------------- diff --git a/raster/test/cunit/Makefile.in b/raster/test/cunit/Makefile.in index 01905a04d..9770199f6 100644 --- a/raster/test/cunit/Makefile.in +++ b/raster/test/cunit/Makefile.in @@ -60,6 +60,7 @@ OBJS= \ cu_gdal.o \ cu_spatial_relationship.o \ cu_mapalgebra.o \ + cu_misc.o \ cu_tester.o # If we couldn't find the cunit library then display a helpful message diff --git a/raster/test/cunit/cu_mapalgebra.c b/raster/test/cunit/cu_mapalgebra.c index 2e9d9954e..0cb2f06d4 100644 --- a/raster/test/cunit/cu_mapalgebra.c +++ b/raster/test/cunit/cu_mapalgebra.c @@ -871,10 +871,315 @@ static void test_band_reclass() { rt_band_destroy(newband); } +static void test_raster_colormap() { + rt_raster raster; + rt_raster rtn; + rt_band band; + int x; + int y; + rt_colormap colormap = NULL; + double value; + int nodata; + + raster = rt_raster_new(9, 9); + CU_ASSERT(raster != NULL); /* or we're out of virtual memory */ + band = cu_add_band(raster, PT_8BUI, 0, 0); + CU_ASSERT(band != NULL); + rt_band_set_nodata(band, 0, NULL); + + for (y = 0; y < 9; y++) { + for (x = 0; x < 9; x++) { + rt_band_set_pixel(band, x, y, x, NULL); + } + } + + colormap = (rt_colormap) rtalloc(sizeof(struct rt_colormap_t)); + CU_ASSERT(colormap != NULL); + colormap->nentry = 3; + colormap->entry = (rt_colormap_entry) rtalloc(sizeof(struct rt_colormap_entry_t) * colormap->nentry); + CU_ASSERT(colormap->entry != NULL); + + colormap->entry[0].isnodata = 0; + colormap->entry[0].value = 8; + colormap->entry[0].color[0] = 255; + colormap->entry[0].color[1] = 255; + colormap->entry[0].color[2] = 255; + colormap->entry[0].color[3] = 255; + + colormap->entry[1].isnodata = 0; + colormap->entry[1].value = 3; + colormap->entry[1].color[0] = 127; + colormap->entry[1].color[1] = 127; + colormap->entry[1].color[2] = 127; + colormap->entry[1].color[3] = 255; + + colormap->entry[2].isnodata = 0; + colormap->entry[2].value = 0; + colormap->entry[2].color[0] = 0; + colormap->entry[2].color[1] = 0; + colormap->entry[2].color[2] = 0; + colormap->entry[2].color[3] = 255; + + /* 2 colors, 3 entries, INTERPOLATE */ + colormap->ncolor = 2; + colormap->method = CM_INTERPOLATE; + + rtn = rt_raster_colormap( + raster, 0, + colormap + ); + CU_ASSERT(rtn != NULL); + CU_ASSERT_EQUAL(rt_raster_get_num_bands(rtn), colormap->ncolor); + + band = rt_raster_get_band(rtn, 0); + CU_ASSERT(band != NULL); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 0, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 0, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 3, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 8, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 255, DBL_EPSILON); + + cu_free_raster(rtn); + + /* 4 colors, 3 entries, INTERPOLATE */ + colormap->ncolor = 4; + + rtn = rt_raster_colormap( + raster, 0, + colormap + ); + CU_ASSERT(rtn != NULL); + CU_ASSERT_EQUAL(rt_raster_get_num_bands(rtn), colormap->ncolor); + cu_free_raster(rtn); + + /* 4 colors, 3 entries, EXACT */ + colormap->method = CM_EXACT; + + rtn = rt_raster_colormap( + raster, 0, + colormap + ); + CU_ASSERT(rtn != NULL); + CU_ASSERT_EQUAL(rt_raster_get_num_bands(rtn), colormap->ncolor); + + band = rt_raster_get_band(rtn, 0); + CU_ASSERT(band != NULL); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 0, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 0, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 3, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 8, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 255, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 1, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 0, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 7, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 0, DBL_EPSILON); + + cu_free_raster(rtn); + + /* 4 colors, 3 entries, NEAREST */ + colormap->method = CM_NEAREST; + + rtn = rt_raster_colormap( + raster, 0, + colormap + ); + CU_ASSERT(rtn != NULL); + CU_ASSERT_EQUAL(rt_raster_get_num_bands(rtn), colormap->ncolor); + + band = rt_raster_get_band(rtn, 0); + CU_ASSERT(band != NULL); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 0, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 0, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 3, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 8, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 255, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 1, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 0, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 2, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 4, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 7, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 255, DBL_EPSILON); + + cu_free_raster(rtn); + + /* 4 colors, 2 entries, NEAREST */ + colormap->nentry = 2; + colormap->method = CM_NEAREST; + + rtn = rt_raster_colormap( + raster, 0, + colormap + ); + CU_ASSERT(rtn != NULL); + CU_ASSERT_EQUAL(rt_raster_get_num_bands(rtn), colormap->ncolor); + + band = rt_raster_get_band(rtn, 0); + CU_ASSERT(band != NULL); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 0, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 3, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 8, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 255, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 1, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 2, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 4, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 127, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 7, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 255, DBL_EPSILON); + + cu_free_raster(rtn); + + rtdealloc(colormap->entry); + rtdealloc(colormap); + + cu_free_raster(raster); + + /* new set of tests */ + raster = rt_raster_new(10, 10); + CU_ASSERT(raster != NULL); /* or we're out of virtual memory */ + band = cu_add_band(raster, PT_8BUI, 0, 0); + CU_ASSERT(band != NULL); + rt_band_set_nodata(band, 0, NULL); + + for (y = 0; y < 10; y++) { + for (x = 0; x < 10; x++) { + rt_band_set_pixel(band, x, y, (x * y) + x, NULL); + } + } + + colormap = (rt_colormap) rtalloc(sizeof(struct rt_colormap_t)); + CU_ASSERT(colormap != NULL); + colormap->nentry = 10; + colormap->entry = (rt_colormap_entry) rtalloc(sizeof(struct rt_colormap_entry_t) * colormap->nentry); + CU_ASSERT(colormap->entry != NULL); + + colormap->entry[0].isnodata = 0; + colormap->entry[0].value = 90; + colormap->entry[0].color[0] = 255; + colormap->entry[0].color[1] = 255; + colormap->entry[0].color[2] = 255; + colormap->entry[0].color[3] = 255; + + colormap->entry[1].isnodata = 0; + colormap->entry[1].value = 80; + colormap->entry[1].color[0] = 255; + colormap->entry[1].color[1] = 227; + colormap->entry[1].color[2] = 227; + colormap->entry[1].color[3] = 255; + + colormap->entry[2].isnodata = 0; + colormap->entry[2].value = 70; + colormap->entry[2].color[0] = 255; + colormap->entry[2].color[1] = 198; + colormap->entry[2].color[2] = 198; + colormap->entry[2].color[3] = 255; + + colormap->entry[3].isnodata = 0; + colormap->entry[3].value = 60; + colormap->entry[3].color[0] = 255; + colormap->entry[3].color[1] = 170; + colormap->entry[3].color[2] = 170; + colormap->entry[3].color[3] = 255; + + colormap->entry[4].isnodata = 0; + colormap->entry[4].value = 50; + colormap->entry[4].color[0] = 255; + colormap->entry[4].color[1] = 142; + colormap->entry[4].color[2] = 142; + colormap->entry[4].color[3] = 255; + + colormap->entry[5].isnodata = 0; + colormap->entry[5].value = 40; + colormap->entry[5].color[0] = 255; + colormap->entry[5].color[1] = 113; + colormap->entry[5].color[2] = 113; + colormap->entry[5].color[3] = 255; + + colormap->entry[6].isnodata = 0; + colormap->entry[6].value = 30; + colormap->entry[6].color[0] = 255; + colormap->entry[6].color[1] = 85; + colormap->entry[6].color[2] = 85; + colormap->entry[6].color[3] = 255; + + colormap->entry[7].isnodata = 0; + colormap->entry[7].value = 20; + colormap->entry[7].color[0] = 255; + colormap->entry[7].color[1] = 57; + colormap->entry[7].color[2] = 57; + colormap->entry[7].color[3] = 255; + + colormap->entry[8].isnodata = 0; + colormap->entry[8].value = 10; + colormap->entry[8].color[0] = 255; + colormap->entry[8].color[1] = 28; + colormap->entry[8].color[2] = 28; + colormap->entry[8].color[3] = 255; + + colormap->entry[9].isnodata = 0; + colormap->entry[9].value = 0; + colormap->entry[9].color[0] = 255; + colormap->entry[9].color[1] = 0; + colormap->entry[9].color[2] = 0; + colormap->entry[9].color[3] = 255; + + /* 2 colors, 3 entries, INTERPOLATE */ + colormap->ncolor = 4; + colormap->method = CM_INTERPOLATE; + + rtn = rt_raster_colormap( + raster, 0, + colormap + ); + CU_ASSERT(rtn != NULL); + CU_ASSERT_EQUAL(rt_raster_get_num_bands(rtn), colormap->ncolor); + + band = rt_raster_get_band(rtn, 2); + CU_ASSERT(band != NULL); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 0, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 0, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 5, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 14, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 6, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 17, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 9, 0, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 25, DBL_EPSILON); + + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 2, 4, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 28, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 3, 4, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 43, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 4, 4, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 57, DBL_EPSILON); + + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 6, 9, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 170, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 7, 9, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 198, DBL_EPSILON); + CU_ASSERT_EQUAL(rt_band_get_pixel(band, 8, 9, &value, &nodata), ES_NONE); + CU_ASSERT_DOUBLE_EQUAL(value, 227, DBL_EPSILON); + + cu_free_raster(rtn); + + rtdealloc(colormap->entry); + rtdealloc(colormap); + + cu_free_raster(raster); +} + /* register tests */ CU_TestInfo mapalgebra_tests[] = { PG_TEST(test_raster_iterator), PG_TEST(test_band_reclass), + PG_TEST(test_raster_colormap), CU_TEST_INFO_NULL }; CU_SuiteInfo mapalgebra_suite = {"mapalgebra", NULL, NULL, mapalgebra_tests}; diff --git a/raster/test/cunit/cu_misc.c b/raster/test/cunit/cu_misc.c new file mode 100644 index 000000000..c38f7b0e2 --- /dev/null +++ b/raster/test/cunit/cu_misc.c @@ -0,0 +1,117 @@ +/* + * PostGIS Raster - Raster Types for PostGIS + * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage + * + * Copyright (C) 2013 Regents of the University of California + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "CUnit/Basic.h" +#include "cu_tester.h" + +static void test_rgb_to_hsv() { + double rgb[3] = {0, 0, 0}; + double hsv[3] = {0, 0, 0}; + + rt_util_rgb_to_hsv(rgb, hsv); + CU_ASSERT_DOUBLE_EQUAL(hsv[0], 0, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[1], 0, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[2], 0, DBL_EPSILON); + + rgb[0] = 0; + rgb[1] = 0; + rgb[2] = 1; + rt_util_rgb_to_hsv(rgb, hsv); + CU_ASSERT_DOUBLE_EQUAL(hsv[0], 2/3., DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[1], 1, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[2], 1, DBL_EPSILON); + + rgb[0] = 0; + rgb[1] = 0.25; + rgb[2] = 0.5; + rt_util_rgb_to_hsv(rgb, hsv); + CU_ASSERT_DOUBLE_EQUAL(hsv[0], 7/12., DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[1], 1, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[2], 0.5, DBL_EPSILON); + + rgb[0] = 0.5; + rgb[1] = 1; + rgb[2] = 0.5; + rt_util_rgb_to_hsv(rgb, hsv); + CU_ASSERT_DOUBLE_EQUAL(hsv[0], 1/3., DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[1], 0.5, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[2], 1, DBL_EPSILON); + + rgb[0] = 0.2; + rgb[1] = 0.4; + rgb[2] = 0.4; + rt_util_rgb_to_hsv(rgb, hsv); + CU_ASSERT_DOUBLE_EQUAL(hsv[0], 0.5, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[1], 0.5, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(hsv[2], 0.4, DBL_EPSILON); +} + +static void test_hsv_to_rgb() { + double hsv[3] = {0, 0, 0}; + double rgb[3] = {0, 0, 0}; + + rt_util_hsv_to_rgb(hsv, rgb); + CU_ASSERT_DOUBLE_EQUAL(rgb[0], 0, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[1], 0, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[2], 0, DBL_EPSILON); + + hsv[0] = 2/3.; + hsv[1] = 1; + hsv[2] = 1; + rt_util_hsv_to_rgb(hsv, rgb); + CU_ASSERT_DOUBLE_EQUAL(rgb[0], 0., DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[1], 0, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[2], 1, DBL_EPSILON); + + hsv[0] = 7/12.; + hsv[1] = 1; + hsv[2] = 0.5; + rt_util_hsv_to_rgb(hsv, rgb); + CU_ASSERT_DOUBLE_EQUAL(rgb[0], 0, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[1], 0.25, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[2], 0.5, DBL_EPSILON); + + hsv[0] = 1/3.; + hsv[1] = 0.5; + hsv[2] = 1; + rt_util_hsv_to_rgb(hsv, rgb); + CU_ASSERT_DOUBLE_EQUAL(rgb[0], 0.5, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[1], 1, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[2], 0.5, DBL_EPSILON); + + hsv[0] = 0.5; + hsv[1] = 0.5; + hsv[2] = 0.4; + rt_util_hsv_to_rgb(hsv, rgb); + CU_ASSERT_DOUBLE_EQUAL(rgb[0], 0.2, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[1], 0.4, DBL_EPSILON); + CU_ASSERT_DOUBLE_EQUAL(rgb[2], 0.4, DBL_EPSILON); +} + +/* register tests */ +CU_TestInfo misc_tests[] = { + PG_TEST(test_rgb_to_hsv), + PG_TEST(test_hsv_to_rgb), + CU_TEST_INFO_NULL +}; +CU_SuiteInfo misc_suite = {"misc", NULL, NULL, misc_tests}; diff --git a/raster/test/cunit/cu_tester.c b/raster/test/cunit/cu_tester.c index ab1190262..31f8eb615 100644 --- a/raster/test/cunit/cu_tester.c +++ b/raster/test/cunit/cu_tester.c @@ -31,6 +31,7 @@ extern CU_SuiteInfo band_stats_suite; extern CU_SuiteInfo band_misc_suite; extern CU_SuiteInfo mapalgebra_suite; extern CU_SuiteInfo spatial_relationship_suite; +extern CU_SuiteInfo misc_suite; /* ** The main() function for setting up and running the tests. @@ -53,6 +54,7 @@ int main(int argc, char *argv[]) band_misc_suite, spatial_relationship_suite, mapalgebra_suite, + misc_suite, CU_SUITE_INFO_NULL }; diff --git a/raster/test/regress/Makefile.in b/raster/test/regress/Makefile.in index e6513b7a0..c2314099a 100644 --- a/raster/test/regress/Makefile.in +++ b/raster/test/regress/Makefile.in @@ -108,7 +108,8 @@ TEST_MAPALGEBRA = \ rt_invdistweight4ma \ rt_4ma \ rt_setvalues_geomval \ - rt_elevation_functions + rt_elevation_functions \ + rt_colormap TEST_SREL = \ rt_gist_relationships \ diff --git a/raster/test/regress/rt_colormap.sql b/raster/test/regress/rt_colormap.sql new file mode 100644 index 000000000..2a5841ef6 --- /dev/null +++ b/raster/test/regress/rt_colormap.sql @@ -0,0 +1,133 @@ +DROP TABLE IF EXISTS raster_colormap_out; +CREATE TABLE raster_colormap_out ( + testid integer, + rid integer, + rast raster +); +DROP TABLE IF EXISTS raster_colormap_in; +CREATE TABLE raster_colormap_in ( + rid integer, + rast raster +); + +INSERT INTO raster_colormap_in +SELECT + 1 AS rid, + ST_SetValues( + ST_AddBand( + ST_MakeEmptyRaster(10, 10, 0, 0, 1, -1, 0, 0, 0), + 1, '8BUI', 0, 0 + ), + 1, 1, 1, ARRAY[ + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], + [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], + [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], + [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], + [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], + [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], + [90, 91, 92, 93, 94, 95, 96, 97, 98, 99] + ]::double precision[] + ) AS rast +UNION ALL +SELECT + 2 AS rid, + ST_SetValues( + ST_AddBand( + ST_MakeEmptyRaster(10, 10, 0, 0, 1, -1, 0, 0, 0), + 1, '8BUI', 0, 0 + ), + 1, 1, 1, ARRAY[ + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + ]::double precision[] + ) AS rast +; + +INSERT INTO raster_colormap_out +SELECT + 1 AS testid, + rid, + _ST_ColorMap( + rast, 1, + ' +100% 255:255 255 256 + + 0% 0 0 0 255 + nv 0 0 0 0 +' + ) AS rast +FROM raster_colormap_in +UNION ALL +SELECT + 2 AS testid, + rid, + _ST_ColorMap( + rast, 1, + ' +100% 255:255 255 256 + 75% 127 255 255 255 + 50% 127, 0,127 255 + 25% 0 127 0 255 + 0% 0 0 0 255 + nv 0 0 0 0 +' + ) AS rast +FROM raster_colormap_in +UNION ALL +SELECT + 3 AS testid, + rid, + _ST_ColorMap( + rast, 1, + ' + 0% 1 127 0 255 +' + ) AS rast +FROM raster_colormap_in +UNION ALL +SELECT + 4 AS testid, + rid, + _ST_ColorMap( + rast, 1, + ' +9 0 0 255 +5 0 255 0 +4 0 255 0 +0 255 0 0 +' + ) AS rast +FROM raster_colormap_in +WHERE rid = 2 +UNION ALL +SELECT + 5 AS testid, + rid, + ST_ColorMap( + rast, 1, + 'grayscale' + ) AS rast +FROM raster_colormap_in +WHERE rid = 2 +; + +SELECT + testid + rid, + (ST_DumpValues(rast)).* +FROM raster_colormap_out +ORDER BY 1, 2; + +DROP TABLE IF EXISTS raster_colormap_in; +DROP TABLE IF EXISTS raster_colormap_out; diff --git a/raster/test/regress/rt_colormap_expected b/raster/test/regress/rt_colormap_expected new file mode 100644 index 000000000..447c1f9a6 --- /dev/null +++ b/raster/test/regress/rt_colormap_expected @@ -0,0 +1,36 @@ +NOTICE: table "raster_colormap_out" does not exist, skipping +NOTICE: table "raster_colormap_in" does not exist, skipping +NOTICE: RGBA value cannot be greater than 255. Defaulting to 255 +NOTICE: RGBA value cannot be greater than 255. Defaulting to 255 +NOTICE: RGBA value cannot be greater than 255. Defaulting to 255 +NOTICE: RGBA value cannot be greater than 255. Defaulting to 255 +NOTICE: Method INTERPOLATE requires at least two non-NODATA colormap entries. Using NEAREST instead +NOTICE: Method INTERPOLATE requires at least two non-NODATA colormap entries. Using NEAREST instead +1|1|{{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255}} +1|1|{{0,0,3,5,8,10,13,16,18,21},{23,26,29,31,34,36,39,42,44,47},{49,52,55,57,60,62,65,68,70,73},{75,78,81,83,86,88,91,94,96,99},{101,104,107,109,112,114,117,120,122,125},{128,130,133,135,138,141,143,146,148,151},{154,156,159,161,164,167,169,172,174,177},{180,182,185,187,190,193,195,198,200,203},{206,208,211,213,216,219,221,224,226,229},{232,234,237,239,242,245,247,250,252,255}} +1|2|{{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255}} +1|2|{{0,0,3,5,8,10,13,16,18,21},{23,26,29,31,34,36,39,42,44,47},{49,52,55,57,60,62,65,68,70,73},{75,78,81,83,86,88,91,94,96,99},{101,104,107,109,112,114,117,120,122,125},{128,130,133,135,138,141,143,146,148,151},{154,156,159,161,164,167,169,172,174,177},{180,182,185,187,190,193,195,198,200,203},{206,208,211,213,216,219,221,224,226,229},{232,234,237,239,242,245,247,250,252,255}} +1|3|{{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255},{0,0,32,64,96,128,159,191,223,255}} +1|3|{{0,0,3,5,8,10,13,16,18,21},{23,26,29,31,34,36,39,42,44,47},{49,52,55,57,60,62,65,68,70,73},{75,78,81,83,86,88,91,94,96,99},{101,104,107,109,112,114,117,120,122,125},{128,130,133,135,138,141,143,146,148,151},{154,156,159,161,164,167,169,172,174,177},{180,182,185,187,190,193,195,198,200,203},{206,208,211,213,216,219,221,224,226,229},{232,234,237,239,242,245,247,250,252,255}} +1|4|{{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255}} +1|4|{{0,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255}} +2|1|{{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255},{0,0,63,0,127,127,191,127,223,255}} +2|1|{{0,0,5,10,16,21,26,31,36,41},{47,52,57,62,65,67,67,66,64,61},{56,50,42,33,21,8,8,23,39,54},{70,86,101,117,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,132,137,143,148,153,158,164,169,174},{179,184,190,177,159,141,124,106,89,87},{94,101,108,116,123,130,135,140,145,151},{156,161,166,175,189,203,215,226,235,243},{250,255,255,255,255,255,255,255,255,255}} +2|2|{{0,0,5,10,14,18,23,27,32,37},{42,48,54,62,67,73,78,83,88,93},{98,104,109,114,119,124,127,127,127,127},{127,127,127,127,122,106,91,75,60,44},{29,13,124,109,93,78,62,47,31,16},{127,24,49,74,101,128,156,43,71,99},{127,155,184,195,200,205,211,216,221,226},{231,237,242,247,252,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,254,250,247,245,244,245,247,250,255}} +2|2|{{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255},{0,0,64,127,64,127,191,255,255,255}} +2|3|{{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255},{0,0,32,0,0,0,48,255,191,255}} +2|3|{{0,0,5,10,14,17,21,23,26,28},{30,31,31,32,32,31,30,29,27,25},{22,19,16,12,7,3,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,3,6,9,12,16,19,23,27,32},{36,41,46,52,57,63,68,75,81,102},{131,159,187,215,242,247,233,220,208,197},{188,180,173,171,177,182,187,192,198,203},{208,213,218,224,229,234,239,245,250,255}} +2|4|{{0,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255}} +2|4|{{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255},{0,255,255,255,255,255,255,255,255,255}} +3|1|{{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1}} +3|1|{{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1,1,1}} +3|2|{{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127}} +3|2|{{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127},{127,127,127,127,127,127,127,127,127,127}} +3|3|{{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0}} +3|3|{{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0}} +3|4|{{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255}} +3|4|{{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255},{255,255,255,255,255,255,255,255,255,255}} +4|1|{{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0},{255,255,255,128,0,0,0,0,0,0}} +4|2|{{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0},{0,128,255,255,255,255,255,255,128,0}} +4|3|{{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255},{0,0,0,0,0,0,127,255,255,255}} +5|1|{{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0},{255,254,222,191,159,127,95,64,32,0}} -- 2.40.0