예제 #1
0
static void test_raster_from_band() {
	uint32_t bandNums[] = {1,3};
	int lenBandNums = 2;
	rt_raster raster;
	rt_raster rast;
	rt_band band;
	uint32_t xmax = 100;
	uint32_t ymax = 100;
	uint32_t x;

	raster = rt_raster_new(xmax, ymax);
	CU_ASSERT(raster != NULL);

	for (x = 0; x < 5; x++) {
		band = cu_add_band(raster, PT_32BUI, 0, 0);
		CU_ASSERT(band != NULL);
		rt_band_set_nodata(band, 0, NULL);
	}

	rast = rt_raster_from_band(raster, bandNums, lenBandNums);
	CU_ASSERT(rast != NULL);

	CU_ASSERT(!rt_raster_is_empty(rast));
	CU_ASSERT(rt_raster_has_band(rast, 1));

	cu_free_raster(rast);
	cu_free_raster(raster);
}
예제 #2
0
static void test_raster_metadata() {
	rt_raster raster = NULL;

	/* create raster */
	raster = rt_raster_new(5, 5);
	CU_ASSERT(raster != NULL);

	/* # of bands */
	CU_ASSERT_EQUAL(rt_raster_get_num_bands(raster), 0);

	/* has bands */
	CU_ASSERT(!rt_raster_has_band(raster, 1));

	/* upper-left corner */
	rt_raster_set_offsets(raster, 30, -70);
	CU_ASSERT_DOUBLE_EQUAL(rt_raster_get_x_offset(raster), 30, DBL_EPSILON);
	CU_ASSERT_DOUBLE_EQUAL(rt_raster_get_y_offset(raster), -70, DBL_EPSILON);

	/* scale */
	rt_raster_set_scale(raster, 10, -10);
	CU_ASSERT_DOUBLE_EQUAL(rt_raster_get_x_scale(raster), 10, DBL_EPSILON);
	CU_ASSERT_DOUBLE_EQUAL(rt_raster_get_y_scale(raster), -10, DBL_EPSILON);

	/* skew */
	rt_raster_set_skews(raster, 0.0001, -0.05);
	CU_ASSERT_DOUBLE_EQUAL(rt_raster_get_x_skew(raster), 0.0001, DBL_EPSILON);
	CU_ASSERT_DOUBLE_EQUAL(rt_raster_get_y_skew(raster), -0.05, DBL_EPSILON);

	/* srid */
	rt_raster_set_srid(raster, 4326);
	CU_ASSERT_EQUAL(rt_raster_get_srid(raster), 4326);
	rt_raster_set_srid(raster, 4269);
	CU_ASSERT_EQUAL(rt_raster_get_srid(raster), 4269);

	cu_free_raster(raster);
}
예제 #3
0
파일: cu_gdal.c 프로젝트: ahinz/postgis
static void test_gdal_polygonize() {
	int i;
	rt_raster rt;
	int nPols = 0;
	rt_geomval gv = NULL;
	char *wkt = NULL;

	rt = fillRasterToPolygonize(1, -1.0);
	CU_ASSERT(rt_raster_has_band(rt, 0));

	nPols = 0;
	gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 1.8, FLT_EPSILON);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 2.0, 1.);
#endif

	wkt = lwgeom_to_text((const LWGEOM *) gv[0].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 1,3 2,2 2,2 3,1 3,1 6,2 6,2 7,3 7,3 8,5 8,5 6,3 6,3 3,4 3,5 3,5 1,3 1))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[1].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[1].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 3,3 6,6 6,6 3,3 3))");
	rtdealloc(wkt);

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[2].val, 2.8, FLT_EPSILON);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[2].val, 3.0, 1.);
#endif

	wkt = lwgeom_to_text((const LWGEOM *) gv[2].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((5 1,5 3,6 3,6 6,5 6,5 8,6 8,6 7,7 7,7 6,8 6,8 3,7 3,7 2,6 2,6 1,5 1))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[3].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[3].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((0 0,0 9,9 9,9 0,0 0),(6 7,6 8,3 8,3 7,2 7,2 6,1 6,1 3,2 3,2 2,3 2,3 1,6 1,6 2,7 2,7 3,8 3,8 6,7 6,7 7,6 7))");
	rtdealloc(wkt);

	for (i = 0; i < nPols; i++) lwgeom_free((LWGEOM *) gv[i].geom);
	rtdealloc(gv);
	cu_free_raster(rt);

	/* Second test: NODATA value = 1.8 */
#ifdef GDALFPOLYGONIZE
	rt = fillRasterToPolygonize(1, 1.8);
#else
	rt = fillRasterToPolygonize(1, 2.0);
#endif

	/* We can check rt_raster_has_band here too */
	CU_ASSERT(rt_raster_has_band(rt, 0));

	nPols = 0;
	gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);

	/*
	for (i = 0; i < nPols; i++) {
		wkt = lwgeom_to_text((const LWGEOM *) gv[i].geom);
		printf("(i, val, geom) = (%d, %f, %s)\n", i, gv[i].val, wkt);
		rtdealloc(wkt);
	}
	*/

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[1].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[1].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 3,3 6,6 6,6 3,3 3))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[2].val, 2.8, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[2].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((5 1,5 3,6 3,6 6,5 6,5 8,6 8,6 7,7 7,7 6,8 6,8 3,7 3,7 2,6 2,6 1,5 1))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[3].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[3].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((0 0,0 9,9 9,9 0,0 0),(6 7,6 8,3 8,3 7,2 7,2 6,1 6,1 3,2 3,2 2,3 2,3 1,6 1,6 2,7 2,7 3,8 3,8 6,7 6,7 7,6 7))");
	rtdealloc(wkt);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 0.0, 1.);
	wkt = lwgeom_to_text((const LWGEOM *) gv[0].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 3,3 6,6 6,6 3,3 3))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[1].val, 3.0, 1.);
	wkt = lwgeom_to_text((const LWGEOM *) gv[1].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((5 1,5 3,6 3,6 6,5 6,5 8,6 8,6 7,7 7,7 6,8 6,8 3,7 3,7 2,6 2,6 1,5 1))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[2].val, 0.0, 1.);
	wkt = lwgeom_to_text((const LWGEOM *) gv[2].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((0 0,0 9,9 9,9 0,0 0),(6 7,6 8,3 8,3 7,2 7,2 6,1 6,1 3,2 3,2 2,3 2,3 1,6 1,6 2,7 2,7 3,8 3,8 6,7 6,7 7,6 7))");
	rtdealloc(wkt);
#endif

	for (i = 0; i < nPols; i++) lwgeom_free((LWGEOM *) gv[i].geom);
	rtdealloc(gv);
	cu_free_raster(rt);

	/* Third test: NODATA value = 2.8 */
#ifdef GDALFPOLYGONIZE
	rt = fillRasterToPolygonize(1, 2.8);
#else	
	rt = fillRasterToPolygonize(1, 3.0);
#endif

	/* We can check rt_raster_has_band here too */
	CU_ASSERT(rt_raster_has_band(rt, 0));

	nPols = 0;
	gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);

	/*
	for (i = 0; i < nPols; i++) {
		wkt = lwgeom_to_text((const LWGEOM *) gv[i].geom);
		printf("(i, val, geom) = (%d, %f, %s)\n", i, gv[i].val, wkt);
		rtdealloc(wkt);
	}
	*/

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 1.8, FLT_EPSILON);

	CU_ASSERT_DOUBLE_EQUAL(gv[3].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[3].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((0 0,0 9,9 9,9 0,0 0),(6 7,6 8,3 8,3 7,2 7,2 6,1 6,1 3,2 3,2 2,3 2,3 1,6 1,6 2,7 2,7 3,8 3,8 6,7 6,7 7,6 7))");
	rtdealloc(wkt);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 2.0, 1.);

	CU_ASSERT_DOUBLE_EQUAL(gv[2].val, 0.0, 1.);
	wkt = lwgeom_to_text((const LWGEOM *) gv[2].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((0 0,0 9,9 9,9 0,0 0),(6 7,6 8,3 8,3 7,2 7,2 6,1 6,1 3,2 3,2 2,3 2,3 1,6 1,6 2,7 2,7 3,8 3,8 6,7 6,7 7,6 7))");
	rtdealloc(wkt);
#endif

	wkt = lwgeom_to_text((const LWGEOM *) gv[0].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 1,3 2,2 2,2 3,1 3,1 6,2 6,2 7,3 7,3 8,5 8,5 6,3 6,3 3,4 3,5 3,5 1,3 1))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[1].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[1].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 3,3 6,6 6,6 3,3 3))");
	rtdealloc(wkt);

	for (i = 0; i < nPols; i++) lwgeom_free((LWGEOM *) gv[i].geom);
	rtdealloc(gv);
	cu_free_raster(rt);

	/* Fourth test: NODATA value = 0 */
	rt = fillRasterToPolygonize(1, 0.0);
	/* We can check rt_raster_has_band here too */
	CU_ASSERT(rt_raster_has_band(rt, 0));

	nPols = 0;
	gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);
	
	/*
	for (i = 0; i < nPols; i++) {
		wkt = lwgeom_to_text((const LWGEOM *) gv[i].geom);
		printf("(i, val, geom) = (%d, %f, %s)\n", i, gv[i].val, wkt);
		rtdealloc(wkt);
	}
	*/

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 1.8, FLT_EPSILON);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 2.0, 1.);
#endif

	wkt = lwgeom_to_text((const LWGEOM *) gv[0].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 1,3 2,2 2,2 3,1 3,1 6,2 6,2 7,3 7,3 8,5 8,5 6,3 6,3 3,4 3,5 3,5 1,3 1))");
	rtdealloc(wkt);

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[1].val, 2.8, FLT_EPSILON);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[1].val, 3.0, 1.);
#endif

	wkt = lwgeom_to_text((const LWGEOM *) gv[1].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((5 1,5 3,6 3,6 6,5 6,5 8,6 8,6 7,7 7,7 6,8 6,8 3,7 3,7 2,6 2,6 1,5 1))");
	rtdealloc(wkt);

	for (i = 0; i < nPols; i++) lwgeom_free((LWGEOM *) gv[i].geom);
	rtdealloc(gv);
	cu_free_raster(rt);

	/* Last test: There is no NODATA value (all values are valid) */
	rt = fillRasterToPolygonize(0, 0.0);
	/* We can check rt_raster_has_band here too */
	CU_ASSERT(rt_raster_has_band(rt, 0));

	nPols = 0;
	gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);

	/*
	for (i = 0; i < nPols; i++) {
		wkt = lwgeom_to_text((const LWGEOM *) gv[i].geom);
		printf("(i, val, geom) = (%d, %f, %s)\n", i, gv[i].val, wkt);
		rtdealloc(wkt);
	}
	*/

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 1.8, FLT_EPSILON);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[0].val, 2.0, 1.);
#endif

	wkt = lwgeom_to_text((const LWGEOM *) gv[0].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 1,3 2,2 2,2 3,1 3,1 6,2 6,2 7,3 7,3 8,5 8,5 6,3 6,3 3,4 3,5 3,5 1,3 1))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[1].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[1].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((3 3,3 6,6 6,6 3,3 3))");
	rtdealloc(wkt);

#ifdef GDALFPOLYGONIZE
	CU_ASSERT_DOUBLE_EQUAL(gv[2].val, 2.8, FLT_EPSILON);
#else
	CU_ASSERT_DOUBLE_EQUAL(gv[2].val, 3.0, 1.);
#endif

	wkt = lwgeom_to_text((const LWGEOM *) gv[2].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((5 1,5 3,6 3,6 6,5 6,5 8,6 8,6 7,7 7,7 6,8 6,8 3,7 3,7 2,6 2,6 1,5 1))");
	rtdealloc(wkt);

	CU_ASSERT_DOUBLE_EQUAL(gv[3].val, 0.0, FLT_EPSILON);
	wkt = lwgeom_to_text((const LWGEOM *) gv[3].geom);
	CU_ASSERT_STRING_EQUAL(wkt, "POLYGON((0 0,0 9,9 9,9 0,0 0),(6 7,6 8,3 8,3 7,2 7,2 6,1 6,1 3,2 3,2 2,3 2,3 1,6 1,6 2,7 2,7 3,8 3,8 6,7 6,7 7,6 7))");
	rtdealloc(wkt);

	for (i = 0; i < nPols; i++) lwgeom_free((LWGEOM *) gv[i].geom);
	rtdealloc(gv);
	cu_free_raster(rt);
}
예제 #4
0
static int
_rti_iterator_arg_populate(
	_rti_iterator_arg _param,
	rt_iterator itrset, uint16_t itrcount,
	uint16_t distancex, uint16_t distancey,
	int *allnull, int *allempty
) {
	int i = 0;
	int hasband = 0;

	_param->count = itrcount;
	_param->distance.x = distancex;
	_param->distance.y = distancey;
	_param->dimension.columns = distancex * 2 + 1;
	_param->dimension.rows = distancey * 2 + 1;

	/* allocate memory for children */
	_param->raster = rtalloc(sizeof(rt_raster) * itrcount);
	_param->isempty = rtalloc(sizeof(int) * itrcount);
	_param->width = rtalloc(sizeof(int) * itrcount);
	_param->height = rtalloc(sizeof(int) * itrcount);

	_param->offset = rtalloc(sizeof(double *) * itrcount);

	_param->band.rtband = rtalloc(sizeof(rt_band) * itrcount);
	_param->band.hasnodata = rtalloc(sizeof(int) * itrcount);
	_param->band.isnodata = rtalloc(sizeof(int) * itrcount);
	_param->band.nodataval = rtalloc(sizeof(double) * itrcount);
	_param->band.minval = rtalloc(sizeof(double) * itrcount);

	if (
		_param->raster == NULL ||
		_param->isempty == NULL ||
		_param->width == NULL ||
		_param->height == NULL ||
		_param->offset == NULL ||
		_param->band.rtband == NULL ||
		_param->band.hasnodata == NULL ||
		_param->band.isnodata == NULL ||
		_param->band.nodataval == NULL ||
		_param->band.minval == NULL
	) {
		rterror("_rti_iterator_arg_populate: Could not allocate memory for children of _rti_iterator_arg");
		return 0;
	}

	*allnull = 0;
	*allempty = 0;

	/*
		check input rasters
			not empty, band # is valid
			copy raster pointers and set flags
	*/
	for (i = 0; i < itrcount; i++) {
		/* initialize elements */
		_param->raster[i] = NULL;
		_param->isempty[i] = 0;
		_param->width[i] = 0;
		_param->height[i] = 0;

		_param->offset[i] = NULL;

		_param->band.rtband[i] = NULL;
		_param->band.hasnodata[i] = 0;
		_param->band.isnodata[i] = 0;
		_param->band.nodataval[i] = 0;
		_param->band.minval[i] = 0;

		/* set isempty */
		if (itrset[i].raster == NULL) {
			_param->isempty[i] = 1;

			(*allnull)++;
			(*allempty)++;

			continue;
		}
		else if (rt_raster_is_empty(itrset[i].raster)) {
			_param->isempty[i] = 1;

			(*allempty)++;

			continue;
		}

		/* check band number */
		hasband = rt_raster_has_band(itrset[i].raster, itrset[i].nband);
		if (!hasband) {
			if (!itrset[i].nbnodata) {
				rterror("_rti_iterator_arg_populate: Band %d not found for raster %d", itrset[i].nband, i);
				return 0;
			}
			else {
				RASTER_DEBUGF(4, "Band %d not found for raster %d. Using NODATA", itrset[i].nband, i);
			}
		}

		_param->raster[i] = itrset[i].raster;
		if (hasband) {
			_param->band.rtband[i] = rt_raster_get_band(itrset[i].raster, itrset[i].nband);
			if (_param->band.rtband[i] == NULL) {
				rterror("_rti_iterator_arg_populate: Could not get band %d for raster %d", itrset[i].nband, i);
				return 0;
			}

			/* hasnodata */
			_param->band.hasnodata[i] = rt_band_get_hasnodata_flag(_param->band.rtband[i]);

			/* hasnodata = TRUE */
			if (_param->band.hasnodata[i]) {
				/* nodataval */
				rt_band_get_nodata(_param->band.rtband[i], &(_param->band.nodataval[i]));

				/* isnodata */
				_param->band.isnodata[i] = rt_band_get_isnodata_flag(_param->band.rtband[i]);
			}
			/* hasnodata = FALSE */
			else {
				/* minval */
				_param->band.minval[i] = rt_band_get_min_value(_param->band.rtband[i]);
			}
		}

		/* width, height */
		_param->width[i] = rt_raster_get_width(_param->raster[i]);
		_param->height[i] = rt_raster_get_height(_param->raster[i]);

		/* init offset */
		_param->offset[i] = rtalloc(sizeof(double) * 2);
		if (_param->offset[i] == NULL) {
			rterror("_rti_iterator_arg_populate: Could not allocate memory for offsets");
			return 0;
		}
	}

	return 1;
}
예제 #5
0
/**
 * 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: Could not get band at index %d", nband);
		return NULL;
	}

	/* init internal variables */
	arg = _rti_colormap_arg_init(raster);
	if (arg == NULL) {
		rterror("rt_raster_colormap: Could not 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: Could not 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: Could not 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: Could not allocate memory for reclass expression");
			_rti_colormap_arg_destroy(arg);
			return NULL;
		}
	}

	/* 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;

			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;

				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 */
		arg->band = rt_band_reclass(band, PT_8BUI, 0, 0, arg->expr, arg->nexpr);
		if (arg->band == NULL) {
			rterror("rt_raster_colormap: Could not 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: Could not add reclassified band to output raster");
			_rti_colormap_arg_destroy(arg);
			return NULL;
		}
	}

	rtnraster = arg->raster;
	arg->raster = NULL;
	_rti_colormap_arg_destroy(arg);

	return rtnraster;
}