- #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 *
<refsection>
<title>See Also</title>
- <para><xref linkend="RT_ST_AddBand" />, <xref linkend="RT_ST_Band" />, <xref linkend="RT_ST_BandPixelType" />, <xref linkend="RT_ST_MakeEmptyRaster" />, <xref linkend="reclassarg" />, <xref linkend="RT_ST_Value" /></para>
+ <para>
+ <xref linkend="RT_ST_AddBand" />,
+ <xref linkend="RT_ST_Band" />,
+ <xref linkend="RT_ST_BandPixelType" />,
+ <xref linkend="RT_ST_MakeEmptyRaster" />,
+ <xref linkend="reclassarg" />,
+ <xref linkend="RT_ST_Value" />
+ </para>
</refsection>
</refentry>
+
+ <refentry id="RT_ST_ColorMap">
+ <refnamediv>
+ <refname>ST_ColorMap</refname>
+ <refpurpose>Creates a new raster of up to four 8BUI bands (grayscale, RGB, RGBA) from the source raster and specified band.</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>raster <function>ST_ColorMap</function></funcdef>
+ <paramdef><type>raster </type> <parameter>rast</parameter></paramdef>
+ <paramdef choice="opt"><type>integer </type> <parameter>nband=1</parameter></paramdef>
+ <paramdef choice="opt"><type>text </type> <parameter>colormap=grayscale</parameter></paramdef>
+ <paramdef choice="opt"><type>text </type> <parameter>method=INTERPOLATE</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>raster <function>ST_ColorMap</function></funcdef>
+ <paramdef><type>raster </type> <parameter>rast</parameter></paramdef>
+ <paramdef><type>text </type> <parameter>colormap</parameter></paramdef>
+ <paramdef choice="opt"><type>text </type> <parameter>method=INTERPOLATE</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+
+ <para>
+ Apply a <varname>colormap</varname> to the band at <varname>nband</varname> of <varname>rast</varname> 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 <varname>colormap</varname>.
+ </para>
+
+ <para>
+ <varname>colormap</varname> can be a keyword of a pre-defined colormap or a set of lines defining the value and the color components.
+ </para>
+
+ <para>
+ Valid pre-defined <varname>colormap</varname> keyword:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <varname>grayscale</varname>, <varname>greyscale</varname> for a one 8BUI band raster
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <varname>pseudocolor</varname> for a four 8BUI (RGBA) band raster
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ Users can pass a set of entries (one per line) to <varname>colormap</varname> 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 <emphasis>nv</emphasis>, <emphasis>null</emphasis> or <emphasis>nodata</emphasis> for the NODATA value. An example is provided below.
+ </para>
+
+ <programlisting>
+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
+ </programlisting>
+
+ <para>
+ The syntax of <varname>colormap</varname> is similar to that of the color-relief mode of GDAL <ulink url="http://www.gdal.org/gdaldem.html#gdaldem_color_relief">gdaldem</ulink>.
+ </para>
+
+ <para>
+ Valid keywords for <varname>method</varname>:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <varname>INTERPOLATE</varname> to use linear interpolation to smoothly blend the colors between the given pixel values
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <varname>EXACT</varname> 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)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <varname>NEAREST</varname> to use the colormap entry whose value is closest to the pixel value
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <note>
+ <para>
+ A great reference for colormaps is <ulink url="http://www.colorbrewer2.org">ColorBrewer</ulink>.
+ </para>
+ </note>
+
+ <para>Availability: 2.1.0 </para>
+ </refsection>
+
+ <refsection>
+ <title>Examples</title>
+ <para></para>
+ <programlisting>
+-- needs examples
+ </programlisting>
+ </refsection>
+
+ <refsection>
+ <title>See Also</title>
+ <para>
+ <xref linkend="RT_ST_MapAlgebra" />,
+ <xref linkend="RT_ST_Reclass" />
+ </para>
+ </refsection>
+ </refentry>
+
<refentry id="RT_ST_Union">
<refnamediv>
<refname>ST_Union</refname>
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 */
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);
/* 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)
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
*/
- /* nodata */
+ /* NODATA */
if (hasnodata && isnodata) {
nv = nodataval;
}
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
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;
+}
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;
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 -------------------------------------------------------*/
/*
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
*/
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 */
* 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
* <bkpark@ucdavis.edu>
* Copyright (C) 2010-2011 Jorge Arevalo <jorge.arevalo@deimos-space.com>
* Copyright (C) 2010-2011 David Zwarg <dzwarg@azavea.com>
/* 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);
/* 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
*/
char *rtn;
char *ptr;
uint32_t offset = 0;
+ int inputlen = 0;
if (!input)
return NULL;
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;
}
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)
{
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 ? */
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
-----------------------------------------------------------------------
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
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};
--- /dev/null
+/*
+ * PostGIS Raster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * Copyright (C) 2013 Regents of the University of California
+ * <bkpark@ucdavis.edu>
+ *
+ * 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};
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.
band_misc_suite,
spatial_relationship_suite,
mapalgebra_suite,
+ misc_suite,
CU_SUITE_INFO_NULL
};
rt_invdistweight4ma \
rt_4ma \
rt_setvalues_geomval \
- rt_elevation_functions
+ rt_elevation_functions \
+ rt_colormap
TEST_SREL = \
rt_gist_relationships \
--- /dev/null
+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;
--- /dev/null
+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}}