1 /*
2  *
3  * WKTRaster - Raster Types for PostGIS
4  * http://trac.osgeo.org/postgis/wiki/WKTRaster
5  *
6  * Copyright (C) 2018 Bborie Park <dustymugs@gmail.com>
7  * Copyright (C) 2011-2013 Regents of the University of California
8  *   <bkpark@ucdavis.edu>
9  * Copyright (C) 2010-2011 Jorge Arevalo <jorge.arevalo@deimos-space.com>
10  * Copyright (C) 2010-2011 David Zwarg <dzwarg@azavea.com>
11  * Copyright (C) 2009-2011 Pierre Racine <pierre.racine@sbf.ulaval.ca>
12  * Copyright (C) 2009-2011 Mateusz Loskot <mateusz@loskot.net>
13  * Copyright (C) 2008-2009 Sandro Santilli <strk@kbt.io>
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software Foundation,
27  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28  *
29  */
30 
31 // For stat64()
32 #define _LARGEFILE64_SOURCE 1
33 
34 #include <stdio.h>
35 
36 #include "librtcore.h"
37 #include "librtcore_internal.h"
38 
39 #include "cpl_vsi.h"
40 #include "gdal_vrt.h"
41 
42 /**
43  * Create an in-db rt_band with no data
44  *
45  * @param width     : number of pixel columns
46  * @param height    : number of pixel rows
47  * @param pixtype   : pixel type for the band
48  * @param hasnodata : indicates if the band has nodata value
49  * @param nodataval : the nodata value, will be appropriately
50  *                    truncated to fit the pixtype size.
51  * @param data      : pointer to actual band data, required to
52  *                    be aligned accordingly to
53  *                    rt_pixtype_aligment(pixtype) and big enough
54  *                    to hold raster width*height values.
55  *                    Data will NOT be copied, ownership is left
56  *                    to caller which is responsible to keep it
57  *                    allocated for the whole lifetime of the returned
58  *                    rt_band.
59  *
60  * @return an rt_band, or 0 on failure
61  */
62 rt_band
rt_band_new_inline(uint16_t width,uint16_t height,rt_pixtype pixtype,uint32_t hasnodata,double nodataval,uint8_t * data)63 rt_band_new_inline(
64 	uint16_t width, uint16_t height,
65 	rt_pixtype pixtype,
66 	uint32_t hasnodata, double nodataval,
67 	uint8_t* data
68 ) {
69 	rt_band band = NULL;
70 
71 	assert(NULL != data);
72 
73 	band = rtalloc(sizeof(struct rt_band_t));
74 	if (band == NULL) {
75 		rterror("rt_band_new_inline: Out of memory allocating rt_band");
76 		return NULL;
77 	}
78 
79 	RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s", band, rt_pixtype_name(pixtype));
80 
81 	band->pixtype = pixtype;
82 	band->offline = 0;
83 	band->width = width;
84 	band->height = height;
85 	band->hasnodata = hasnodata ? 1 : 0;
86 	band->isnodata = FALSE; /* we don't know what is in data, so must be FALSE */
87 	band->nodataval = 0;
88 	band->data.mem = data;
89 	band->ownsdata = 0; /* we do NOT own this data!!! */
90 	band->raster = NULL;
91 
92 	RASTER_DEBUGF(3, "Created rt_band with dimensions %d x %d", band->width, band->height);
93 
94 	/* properly set nodataval as it may need to be constrained to the data type */
95 	if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
96 		rterror("rt_band_new_inline: Could not set NODATA value");
97 		rt_band_destroy(band);
98 		return NULL;
99 	}
100 
101 	return band;
102 }
103 
104 /**
105  * Create an out-db rt_band
106  *
107  * @param width     : number of pixel columns
108  * @param height    : number of pixel rows
109  * @param pixtype   : pixel type for the band
110  * @param hasnodata : indicates if the band has nodata value
111  * @param nodataval : the nodata value, will be appropriately
112  *                    truncated to fit the pixtype size.
113  * @param bandNum   : 0-based band number in the external file
114  *                    to associate this band with.
115  * @param path      : NULL-terminated path string pointing to the file
116  *                    containing band data. The string will NOT be
117  *                    copied, ownership is left to caller which is
118  *                    responsible to keep it allocated for the whole
119  *                    lifetime of the returned rt_band.
120  *
121  * @return an rt_band, or 0 on failure
122  */
123 rt_band
rt_band_new_offline(uint16_t width,uint16_t height,rt_pixtype pixtype,uint32_t hasnodata,double nodataval,uint8_t bandNum,const char * path)124 rt_band_new_offline(
125 	uint16_t width, uint16_t height,
126 	rt_pixtype pixtype,
127 	uint32_t hasnodata, double nodataval,
128 	uint8_t bandNum, const char* path
129 ) {
130 	rt_band band = NULL;
131 	int pathlen = 0;
132 
133 	assert(NULL != path);
134 
135 	band = rtalloc(sizeof(struct rt_band_t));
136 	if (band == NULL) {
137 		rterror("rt_band_new_offline: Out of memory allocating rt_band");
138 		return NULL;
139 	}
140 
141 	RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s",
142 		band, rt_pixtype_name(pixtype)
143 	);
144 
145 	band->pixtype = pixtype;
146 	band->offline = 1;
147 	band->width = width;
148 	band->height = height;
149 	band->hasnodata = hasnodata ? 1 : 0;
150 	band->nodataval = 0;
151 	band->isnodata = FALSE; /* we don't know if the offline band is NODATA */
152 	band->ownsdata = 0; /* offline, flag is useless as all offline data cache is owned internally */
153 	band->raster = NULL;
154 
155 	/* properly set nodataval as it may need to be constrained to the data type */
156 	if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
157 		rterror("rt_band_new_offline: Could not set NODATA value");
158 		rt_band_destroy(band);
159 		return NULL;
160 	}
161 
162 	band->data.offline.bandNum = bandNum;
163 
164 	/* memory for data.offline.path is managed internally */
165 	pathlen = strlen(path);
166 	band->data.offline.path = rtalloc(sizeof(char) * (pathlen + 1));
167 	if (band->data.offline.path == NULL) {
168 		rterror("rt_band_new_offline: Out of memory allocating offline path");
169 		rt_band_destroy(band);
170 		return NULL;
171 	}
172 	memcpy(band->data.offline.path, path, pathlen);
173 	band->data.offline.path[pathlen] = '\0';
174 
175 	band->data.offline.mem = NULL;
176 
177 	return band;
178 }
179 
180 /**
181  * Create an out-db rt_band from path
182  *
183  * @param width     : number of pixel columns
184  * @param height    : number of pixel rows
185  * @param hasnodata : indicates if the band has nodata value
186  * @param nodataval : the nodata value, will be appropriately
187  * @param bandNum   : 1-based band number in the external file
188  *                    to associate this band with.
189  * @param path      : NULL-terminated path string pointing to the file
190  *                    containing band data. The string will NOT be
191  *                    copied, ownership is left to caller which is
192  *                    responsible to keep it allocated for the whole
193  *                    lifetime of the returned rt_band.
194  * @param force     : if True, ignore all validation checks
195  *
196  * @return an rt_band, or 0 on failure
197  */
198 rt_band
rt_band_new_offline_from_path(uint16_t width,uint16_t height,int hasnodata,double nodataval,uint8_t bandNum,const char * path,int force)199 rt_band_new_offline_from_path(
200 	uint16_t width,
201 	uint16_t height,
202 	int hasnodata,
203 	double nodataval,
204 	uint8_t bandNum,
205 	const char* path,
206 	int force
207 ) {
208 	GDALDatasetH hdsSrc = NULL;
209 	int nband = 0;
210 	GDALRasterBandH hbandSrc = NULL;
211 
212 	GDALDataType gdpixtype;
213 	rt_pixtype pt = PT_END;
214 
215 	/* open outdb raster file */
216 	rt_util_gdal_register_all(0);
217 	hdsSrc = rt_util_gdal_open(path, GA_ReadOnly, 1);
218 	if (hdsSrc == NULL && !force) {
219 		rterror("rt_band_new_offline_from_path: Cannot open offline raster: %s", path);
220 		return NULL;
221 	}
222 
223 	nband = GDALGetRasterCount(hdsSrc);
224 	if (!nband && !force) {
225 		rterror("rt_band_new_offline_from_path: No bands found in offline raster: %s", path);
226 		GDALClose(hdsSrc);
227 		return NULL;
228 	}
229 	/* bandNum is 1-based */
230 	else if (bandNum > nband && !force) {
231 		rterror(
232 			"rt_band_new_offline_from_path: Specified band %d not found in offline raster: %s",
233 			bandNum,
234 			path
235 		);
236 		GDALClose(hdsSrc);
237 		return NULL;
238 	}
239 
240 	hbandSrc = GDALGetRasterBand(hdsSrc, bandNum);
241 	if (hbandSrc == NULL && !force) {
242 		rterror(
243 			"rt_band_new_offline_from_path: Cannot get band %d from GDAL dataset",
244 			bandNum
245 		);
246 		GDALClose(hdsSrc);
247 		return NULL;
248 	}
249 
250 	gdpixtype = GDALGetRasterDataType(hbandSrc);
251 	pt = rt_util_gdal_datatype_to_pixtype(gdpixtype);
252 	if (pt == PT_END && !force) {
253 		rterror(
254 			"rt_band_new_offline_from_path: Unsupported pixel type %s of band %d from GDAL dataset",
255 			GDALGetDataTypeName(gdpixtype),
256 			bandNum
257 		);
258 		GDALClose(hdsSrc);
259 		return NULL;
260 	}
261 
262 	/* use out-db band's nodata value if nodataval not already set */
263 	if (!hasnodata)
264 		nodataval = GDALGetRasterNoDataValue(hbandSrc, &hasnodata);
265 
266 	GDALClose(hdsSrc);
267 
268 	return rt_band_new_offline(
269 		width, height,
270 		pt,
271 		hasnodata, nodataval,
272 		bandNum - 1, path
273 	);
274 }
275 
276 /**
277  * Create a new band duplicated from source band.  Memory is allocated
278  * for band path (if band is offline) or band data (if band is online).
279  * The caller is responsible for freeing the memory when the returned
280  * rt_band is destroyed.
281  *
282  * @param : the band to copy
283  *
284  * @return an rt_band or NULL on failure
285  */
286 rt_band
rt_band_duplicate(rt_band band)287 rt_band_duplicate(rt_band band) {
288 	rt_band rtn = NULL;
289 
290 	assert(band != NULL);
291 
292 	/* offline */
293 	if (band->offline) {
294 		rtn = rt_band_new_offline(
295 			band->width, band->height,
296 			band->pixtype,
297 			band->hasnodata, band->nodataval,
298 			band->data.offline.bandNum, (const char *) band->data.offline.path
299 		);
300 	}
301 	/* online */
302 	else {
303 		uint8_t *data = NULL;
304 		data = rtalloc(rt_pixtype_size(band->pixtype) * band->width * band->height);
305 		if (data == NULL) {
306 			rterror("rt_band_duplicate: Out of memory allocating online band data");
307 			return NULL;
308 		}
309 		memcpy(data, band->data.mem, rt_pixtype_size(band->pixtype) * band->width * band->height);
310 
311 		rtn = rt_band_new_inline(
312 			band->width, band->height,
313 			band->pixtype,
314 			band->hasnodata, band->nodataval,
315 			data
316 		);
317 		rt_band_set_ownsdata_flag(rtn, 1); /* we DO own this data!!! */
318 	}
319 
320 	if (rtn == NULL) {
321 		rterror("rt_band_duplicate: Could not copy band");
322 		return NULL;
323 	}
324 
325 	return rtn;
326 }
327 
328 int
rt_band_is_offline(rt_band band)329 rt_band_is_offline(rt_band band) {
330   assert(NULL != band);
331 	return band->offline ? 1 : 0;
332 }
333 
334 /**
335  * Destroy a raster band
336  *
337  * @param band : the band to destroy
338  */
339 void
rt_band_destroy(rt_band band)340 rt_band_destroy(rt_band band) {
341 	if (band == NULL)
342 		return;
343 
344 	RASTER_DEBUGF(3, "Destroying rt_band @ %p", band);
345 
346 	/* offline band */
347 	if (band->offline) {
348 		/* memory cache */
349 		if (band->data.offline.mem != NULL)
350 			rtdealloc(band->data.offline.mem);
351 		/* offline file path */
352 		if (band->data.offline.path != NULL)
353 			rtdealloc(band->data.offline.path);
354 	}
355 	/* inline band and band owns the data */
356 	else if (band->data.mem != NULL && band->ownsdata)
357 		rtdealloc(band->data.mem);
358 
359 	rtdealloc(band);
360 }
361 
362 const char*
rt_band_get_ext_path(rt_band band)363 rt_band_get_ext_path(rt_band band) {
364 
365     assert(NULL != band);
366 
367 
368     if (!band->offline) {
369         RASTER_DEBUG(3, "rt_band_get_ext_path: Band is not offline");
370         return NULL;
371     }
372     return band->data.offline.path;
373 }
374 
375 rt_errorstate
rt_band_get_ext_band_num(rt_band band,uint8_t * bandnum)376 rt_band_get_ext_band_num(rt_band band, uint8_t *bandnum) {
377 	assert(NULL != band);
378 	assert(NULL != bandnum);
379 
380 	*bandnum = 0;
381 
382 	if (!band->offline) {
383 		RASTER_DEBUG(3, "rt_band_get_ext_band_num: Band is not offline");
384 		return ES_ERROR;
385 	}
386 
387 	*bandnum = band->data.offline.bandNum;
388 
389 	return ES_NONE;
390 }
391 
392 /**
393 	* Get pointer to raster band data
394 	*
395 	* @param band : the band who's data to get
396 	*
397 	* @return pointer to band data or NULL if error
398 	*/
399 void *
rt_band_get_data(rt_band band)400 rt_band_get_data(rt_band band) {
401 	assert(NULL != band);
402 
403 	if (band->offline) {
404 		if (band->data.offline.mem != NULL)
405 			return band->data.offline.mem;
406 
407 		if (rt_band_load_offline_data(band) != ES_NONE)
408 			return NULL;
409 		else
410 			return band->data.offline.mem;
411 	}
412 	else
413 		return band->data.mem;
414 }
415 
416 /* variable for PostgreSQL GUC: postgis.enable_outdb_rasters */
417 char enable_outdb_rasters = 1;
418 
419 /**
420 	* Load offline band's data.  Loaded data is internally owned
421 	* and should not be released by the caller.  Data will be
422 	* released when band is destroyed with rt_band_destroy().
423 	*
424 	* @param band : the band who's data to get
425 	*
426 	* @return ES_NONE if success, ES_ERROR if failure
427 	*/
428 rt_errorstate
rt_band_load_offline_data(rt_band band)429 rt_band_load_offline_data(rt_band band) {
430 	GDALDatasetH hdsSrc = NULL;
431 	int nband = 0;
432 	VRTDatasetH hdsDst = NULL;
433 	VRTSourcedRasterBandH hbandDst = NULL;
434 	double ogt[6] = {0};
435 	double offset[2] = {0};
436 
437 	rt_raster _rast = NULL;
438 	rt_band _band = NULL;
439 	int aligned = 0;
440 	int err = ES_NONE;
441 
442 	assert(band != NULL);
443 	assert(band->raster != NULL);
444 
445 	if (!band->offline) {
446 		rterror("rt_band_load_offline_data: Band is not offline");
447 		return ES_ERROR;
448 	}
449 	else if (!strlen(band->data.offline.path)) {
450 		rterror("rt_band_load_offline_data: Offline band does not a have a specified file");
451 		return ES_ERROR;
452 	}
453 
454 	/* offline_data is disabled */
455 	if (!enable_outdb_rasters) {
456 		rterror("rt_band_load_offline_data: Access to offline bands disabled");
457 		return ES_ERROR;
458 	}
459 
460 	rt_util_gdal_register_all(0);
461 	hdsSrc = rt_util_gdal_open(band->data.offline.path, GA_ReadOnly, 1);
462 	if (hdsSrc == NULL) {
463 		rterror("rt_band_load_offline_data: Cannot open offline raster: %s", band->data.offline.path);
464 		return ES_ERROR;
465 	}
466 
467 	/* # of bands */
468 	nband = GDALGetRasterCount(hdsSrc);
469 	if (!nband) {
470 		rterror("rt_band_load_offline_data: No bands found in offline raster: %s", band->data.offline.path);
471 		GDALClose(hdsSrc);
472 		return ES_ERROR;
473 	}
474 	/* bandNum is 0-based */
475 	else if (band->data.offline.bandNum + 1 > nband) {
476 		rterror("rt_band_load_offline_data: Specified band %d not found in offline raster: %s", band->data.offline.bandNum, band->data.offline.path);
477 		GDALClose(hdsSrc);
478 		return ES_ERROR;
479 	}
480 
481 	/* get offline raster's geotransform */
482 	if (GDALGetGeoTransform(hdsSrc, ogt) != CE_None) {
483 		RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
484 		ogt[0] = 0;
485 		ogt[1] = 1;
486 		ogt[2] = 0;
487 		ogt[3] = 0;
488 		ogt[4] = 0;
489 		ogt[5] = -1;
490 	}
491 	RASTER_DEBUGF(3, "Offline geotransform (%f, %f, %f, %f, %f, %f)",
492 		ogt[0], ogt[1], ogt[2], ogt[3], ogt[4], ogt[5]);
493 
494 	/* are rasters aligned? */
495 	_rast = rt_raster_new(1, 1);
496 	rt_raster_set_geotransform_matrix(_rast, ogt);
497 	rt_raster_set_srid(_rast, band->raster->srid);
498 	err = rt_raster_same_alignment(band->raster, _rast, &aligned, NULL);
499 	rt_raster_destroy(_rast);
500 
501 	if (err != ES_NONE) {
502 		rterror("rt_band_load_offline_data: Could not test alignment of in-db representation of out-db raster");
503 		GDALClose(hdsSrc);
504 		return ES_ERROR;
505 	}
506 	else if (!aligned) {
507 		rtwarn("The in-db representation of the out-db raster is not aligned. Band data may be incorrect");
508 	}
509 
510 	/* get offsets */
511 	rt_raster_geopoint_to_cell(
512 		band->raster,
513 		ogt[0], ogt[3],
514 		&(offset[0]), &(offset[1]),
515 		NULL
516 	);
517 
518 	RASTER_DEBUGF(4, "offsets: (%f, %f)", offset[0], offset[1]);
519 
520 	/* create VRT dataset */
521 	hdsDst = VRTCreate(band->width, band->height);
522 	GDALSetGeoTransform(hdsDst, ogt);
523 	/*
524 	GDALSetDescription(hdsDst, "/tmp/offline.vrt");
525 	*/
526 
527 	/* add band as simple sources */
528 	GDALAddBand(hdsDst, rt_util_pixtype_to_gdal_datatype(band->pixtype), NULL);
529 	hbandDst = (VRTSourcedRasterBandH) GDALGetRasterBand(hdsDst, 1);
530 
531 	if (band->hasnodata)
532 		GDALSetRasterNoDataValue(hbandDst, band->nodataval);
533 
534 	VRTAddSimpleSource(
535 		hbandDst, GDALGetRasterBand(hdsSrc, band->data.offline.bandNum + 1),
536 		fabs(offset[0]), fabs(offset[1]),
537 		band->width, band->height,
538 		0, 0,
539 		band->width, band->height,
540 		"near", VRT_NODATA_UNSET
541 	);
542 
543 	/* make sure VRT reflects all changes */
544 	VRTFlushCache(hdsDst);
545 
546 	/* convert VRT dataset to rt_raster */
547 	_rast = rt_raster_from_gdal_dataset(hdsDst);
548 
549 	GDALClose(hdsDst);
550 	GDALClose(hdsSrc);
551 	/*
552 	{
553 		FILE *fp;
554 		fp = fopen("/tmp/gdal_open_files.log", "w");
555 		GDALDumpOpenDatasets(fp);
556 		fclose(fp);
557 	}
558 	*/
559 
560 	if (_rast == NULL) {
561 		rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
562 		return ES_ERROR;
563 	}
564 
565 	_band = rt_raster_get_band(_rast, 0);
566 	if (_band == NULL) {
567 		rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
568 		rt_raster_destroy(_rast);
569 		return ES_ERROR;
570 	}
571 
572 	/* band->data.offline.mem not NULL, free first */
573 	if (band->data.offline.mem != NULL) {
574 		rtdealloc(band->data.offline.mem);
575 		band->data.offline.mem = NULL;
576 	}
577 
578 	band->data.offline.mem = _band->data.mem;
579 
580 	rtdealloc(_band); /* cannot use rt_band_destroy */
581 	rt_raster_destroy(_rast);
582 
583 	return ES_NONE;
584 }
585 
rt_band_get_file_size(rt_band band)586 uint64_t rt_band_get_file_size(rt_band band) {
587     VSIStatBufL sStat;
588 
589     assert(NULL != band);
590     if (!band->offline) {
591         rterror("rt_band_get_file_size: Band is not offline");
592         return 0;
593     }
594     /* offline_data is disabled */
595     if (!enable_outdb_rasters) {
596         rterror("rt_band_get_file_size: Access to offline bands disabled");
597         return 0;
598     }
599 
600     if( VSIStatL(band->data.offline.path, &sStat) != 0 ) {
601         rterror("rt_band_get_file_size: Cannot access file");
602         return 0;
603     }
604 
605     return sStat.st_size;
606 }
607 
rt_band_get_file_timestamp(rt_band band)608 uint64_t rt_band_get_file_timestamp(rt_band band) {
609     VSIStatBufL sStat;
610 
611     assert(NULL != band);
612     if (!band->offline) {
613         rterror("rt_band_get_file_timestamp: Band is not offline");
614         return 0;
615     }
616     /* offline_data is disabled */
617     if (!enable_outdb_rasters) {
618         rterror("rt_band_get_file_timestamp: Access to offline bands disabled");
619         return 0;
620     }
621 
622     if( VSIStatL(band->data.offline.path, &sStat) != 0 ) {
623         rterror("rt_band_get_file_timestamp: Cannot access file");
624         return 0;
625     }
626 
627     return sStat.st_mtime;
628 }
629 
630 rt_pixtype
rt_band_get_pixtype(rt_band band)631 rt_band_get_pixtype(rt_band band) {
632 
633     assert(NULL != band);
634 
635 
636     return band->pixtype;
637 }
638 
639 uint16_t
rt_band_get_width(rt_band band)640 rt_band_get_width(rt_band band) {
641 
642     assert(NULL != band);
643 
644 
645     return band->width;
646 }
647 
648 uint16_t
rt_band_get_height(rt_band band)649 rt_band_get_height(rt_band band) {
650 
651     assert(NULL != band);
652 
653 
654     return band->height;
655 }
656 
657 /* Get ownsdata flag */
658 int
rt_band_get_ownsdata_flag(rt_band band)659 rt_band_get_ownsdata_flag(rt_band band) {
660 	assert(NULL != band);
661 
662 	return band->ownsdata ? 1 : 0;
663 }
664 
665 /* set ownsdata flag */
666 void
rt_band_set_ownsdata_flag(rt_band band,int flag)667 rt_band_set_ownsdata_flag(rt_band band, int flag) {
668 	assert(NULL != band);
669 
670 	band->ownsdata = flag ? 1 : 0;
671 }
672 
673 int
rt_band_get_hasnodata_flag(rt_band band)674 rt_band_get_hasnodata_flag(rt_band band) {
675 	assert(NULL != band);
676 
677 	return band->hasnodata ? 1 : 0;
678 }
679 
680 void
rt_band_set_hasnodata_flag(rt_band band,int flag)681 rt_band_set_hasnodata_flag(rt_band band, int flag) {
682 
683     assert(NULL != band);
684 
685     band->hasnodata = (flag) ? 1 : 0;
686 
687 		/* isnodata depends on hasnodata */
688 		if (!band->hasnodata && band->isnodata) {
689 			RASTER_DEBUG(3, "Setting isnodata to FALSE as band no longer has NODATA");
690 			band->isnodata = 0;
691 		}
692 }
693 
694 rt_errorstate
rt_band_set_isnodata_flag(rt_band band,int flag)695 rt_band_set_isnodata_flag(rt_band band, int flag) {
696 	assert(NULL != band);
697 
698 	if (!band->hasnodata) {
699 		/* silently permit setting isnodata flag to FALSE */
700 		if (!flag)
701 			band->isnodata = 0;
702 		else {
703 			rterror("rt_band_set_isnodata_flag: Cannot set isnodata flag as band has no NODATA");
704 			return ES_ERROR;
705 		}
706 	}
707 	else
708 		band->isnodata = (flag) ? 1 : 0;
709 
710 	return ES_NONE;
711 }
712 
713 int
rt_band_get_isnodata_flag(rt_band band)714 rt_band_get_isnodata_flag(rt_band band) {
715 	assert(NULL != band);
716 
717 	if (band->hasnodata)
718 		return band->isnodata ? 1 : 0;
719 	else
720 		return 0;
721 }
722 
723 /**
724  * Set nodata value
725  *
726  * @param band : the band to set nodata value to
727  * @param val : the nodata value
728  * @param converted : if non-zero, value was truncated/clamped/coverted
729  *
730  * @return ES_NONE or ES_ERROR
731  */
732 rt_errorstate
rt_band_set_nodata(rt_band band,double val,int * converted)733 rt_band_set_nodata(rt_band band, double val, int *converted) {
734 	rt_pixtype pixtype = PT_END;
735 	int32_t checkvalint = 0;
736 	uint32_t checkvaluint = 0;
737 	float checkvalfloat = 0;
738 	double checkvaldouble = 0;
739 
740 	assert(NULL != band);
741 
742 	if (converted != NULL)
743 		*converted = 0;
744 
745 	pixtype = band->pixtype;
746 
747 	RASTER_DEBUGF(3, "rt_band_set_nodata: setting nodata value %g with band type %s", val, rt_pixtype_name(pixtype));
748 
749 	/* return -1 on out of range */
750 	switch (pixtype) {
751 		case PT_1BB: {
752 			band->nodataval = rt_util_clamp_to_1BB(val);
753 			checkvalint = band->nodataval;
754 			break;
755 		}
756 		case PT_2BUI: {
757 			band->nodataval = rt_util_clamp_to_2BUI(val);
758 			checkvalint = band->nodataval;
759 			break;
760 		}
761 		case PT_4BUI: {
762 			band->nodataval = rt_util_clamp_to_4BUI(val);
763 			checkvalint = band->nodataval;
764 			break;
765 		}
766 		case PT_8BSI: {
767 			band->nodataval = rt_util_clamp_to_8BSI(val);
768 			checkvalint = band->nodataval;
769 			break;
770 		}
771 		case PT_8BUI: {
772 			band->nodataval = rt_util_clamp_to_8BUI(val);
773 			checkvalint = band->nodataval;
774 			break;
775 		}
776 		case PT_16BSI: {
777 			band->nodataval = rt_util_clamp_to_16BSI(val);
778 			checkvalint = band->nodataval;
779 			break;
780 		}
781 		case PT_16BUI: {
782 			band->nodataval = rt_util_clamp_to_16BUI(val);
783 			checkvalint = band->nodataval;
784 			break;
785 		}
786 		case PT_32BSI: {
787 			band->nodataval = rt_util_clamp_to_32BSI(val);
788 			checkvalint = band->nodataval;
789 			break;
790 		}
791 		case PT_32BUI: {
792 			band->nodataval = rt_util_clamp_to_32BUI(val);
793 			checkvaluint = band->nodataval;
794 			break;
795 		}
796 		case PT_32BF: {
797 			band->nodataval = rt_util_clamp_to_32F(val);
798 			checkvalfloat = band->nodataval;
799 			break;
800 		}
801 		case PT_64BF: {
802 			band->nodataval = val;
803 			checkvaldouble = band->nodataval;
804 			break;
805 		}
806 		default: {
807 			rterror("rt_band_set_nodata: Unknown pixeltype %d", pixtype);
808 			band->hasnodata = 0;
809 			return ES_ERROR;
810 		}
811 	}
812 
813 	RASTER_DEBUGF(3, "rt_band_set_nodata: band->hasnodata = %d", band->hasnodata);
814 	RASTER_DEBUGF(3, "rt_band_set_nodata: band->nodataval = %f", band->nodataval);
815 	/* the nodata value was just set, so this band has NODATA */
816 	band->hasnodata = 1;
817 
818 	/* also set isnodata flag to false */
819 	band->isnodata = 0;
820 
821 	if (rt_util_dbl_trunc_warning(
822 		val,
823 		checkvalint, checkvaluint,
824 		checkvalfloat, checkvaldouble,
825 		pixtype
826 	) && converted != NULL) {
827 		*converted = 1;
828 	}
829 
830 	return ES_NONE;
831 }
832 
833 /**
834  * Set values of multiple pixels.  Unlike rt_band_set_pixel,
835  * values in vals are expected to be of the band's pixel type
836  * as this function uses memcpy.
837  *
838  * It is important to be careful when using this function as
839  * the number of values being set may exceed a pixel "row".
840  * Remember that the band values are stored in a stream (1-D array)
841  * regardless of what the raster's width and height might be.
842  * So, setting a number of values may cross multiple pixel "rows".
843  *
844  * @param band : the band to set value to
845  * @param x : X coordinate (0-based)
846  * @param y : Y coordinate (0-based)
847  * @param vals : the pixel values to apply
848  * @param len : # of elements in vals
849  *
850  * @return ES_NONE on success, ES_ERROR on error
851  */
852 rt_errorstate
rt_band_set_pixel_line(rt_band band,int x,int y,void * vals,uint32_t len)853 rt_band_set_pixel_line(
854 	rt_band band,
855 	int x, int y,
856 	void *vals, uint32_t len
857 ) {
858 	rt_pixtype pixtype = PT_END;
859 	int size = 0;
860 	uint8_t *data = NULL;
861 	uint32_t offset = 0;
862 
863 	assert(NULL != band);
864 	assert(vals != NULL && len > 0);
865 
866 	RASTER_DEBUGF(3, "length of values = %d", len);
867 
868 	if (band->offline) {
869 		rterror("rt_band_set_pixel_line not implemented yet for OFFDB bands");
870 		return ES_ERROR;
871 	}
872 
873 	pixtype = band->pixtype;
874 	size = rt_pixtype_size(pixtype);
875 
876 	if (
877 		x < 0 || x >= band->width ||
878 		y < 0 || y >= band->height
879 	) {
880 		rterror("rt_band_set_pixel_line: Coordinates out of range (%d, %d) vs (%d, %d)", x, y, band->width, band->height);
881 		return ES_ERROR;
882 	}
883 
884 	data = rt_band_get_data(band);
885 	offset = x + (y * band->width);
886 	RASTER_DEBUGF(4, "offset = %d", offset);
887 
888 	/* make sure len of values to copy don't exceed end of data */
889 	if (len > (band->width * band->height) - offset) {
890 		rterror("rt_band_set_pixel_line: Could not apply pixels as values length exceeds end of data");
891 		return ES_ERROR;
892 	}
893 
894 	switch (pixtype) {
895 		case PT_1BB:
896 		case PT_2BUI:
897 		case PT_4BUI:
898 		case PT_8BUI:
899 		case PT_8BSI: {
900 			uint8_t *ptr = data;
901 			ptr += offset;
902 			memcpy(ptr, vals, size * len);
903 			break;
904 		}
905 		case PT_16BUI: {
906 			uint16_t *ptr = (uint16_t *) data;
907 			ptr += offset;
908 			memcpy(ptr, vals, size * len);
909 			break;
910 		}
911 		case PT_16BSI: {
912 			int16_t *ptr = (int16_t *) data;
913 			ptr += offset;
914 			memcpy(ptr, vals, size * len);
915 			break;
916 		}
917 		case PT_32BUI: {
918 			uint32_t *ptr = (uint32_t *) data;
919 			ptr += offset;
920 			memcpy(ptr, vals, size * len);
921 			break;
922 		}
923 		case PT_32BSI: {
924 			int32_t *ptr = (int32_t *) data;
925 			ptr += offset;
926 			memcpy(ptr, vals, size * len);
927 			break;
928 		}
929 		case PT_32BF: {
930 			float *ptr = (float *) data;
931 			ptr += offset;
932 			memcpy(ptr, vals, size * len);
933 			break;
934 		}
935 		case PT_64BF: {
936 			double *ptr = (double *) data;
937 			ptr += offset;
938 			memcpy(ptr, vals, size * len);
939 			break;
940 		}
941 		default: {
942 			rterror("rt_band_set_pixel_line: Unknown pixeltype %d", pixtype);
943 			return ES_ERROR;
944 		}
945 	}
946 
947 #if POSTGIS_DEBUG_LEVEL > 0
948 	{
949 		double value;
950 		rt_band_get_pixel(band, x, y, &value, NULL);
951 		RASTER_DEBUGF(4, "pixel at (%d, %d) = %f", x, y, value);
952 	}
953 #endif
954 
955 	/* set band's isnodata flag to FALSE */
956 	if (rt_band_get_hasnodata_flag(band))
957 		rt_band_set_isnodata_flag(band, 0);
958 
959 	return ES_NONE;
960 }
961 
962 /**
963  * Set single pixel's value
964  *
965  * @param band : the band to set value to
966  * @param x : x ordinate (0-based)
967  * @param y : y ordinate (0-based)
968  * @param val : the pixel value
969  * @param converted : (optional) non-zero if value truncated/clamped/converted
970  *
971  * @return ES_NONE on success, ES_ERROR on error
972  */
973 rt_errorstate
rt_band_set_pixel(rt_band band,int x,int y,double val,int * converted)974 rt_band_set_pixel(
975 	rt_band band,
976 	int x, int y,
977 	double val,
978 	int *converted
979 ) {
980 	rt_pixtype pixtype = PT_END;
981 	uint8_t *data = NULL;
982 	uint32_t offset = 0;
983 
984 	int32_t checkvalint = 0;
985 	uint32_t checkvaluint = 0;
986 	float checkvalfloat = 0;
987 	double checkvaldouble = 0;
988 
989 	assert(NULL != band);
990 
991 	if (converted != NULL)
992 		*converted = 0;
993 
994 	if (band->offline) {
995 		rterror("rt_band_set_pixel not implemented yet for OFFDB bands");
996 		return ES_ERROR;
997 	}
998 
999 	pixtype = band->pixtype;
1000 
1001 	if (
1002 		x < 0 || x >= band->width ||
1003 		y < 0 || y >= band->height
1004 	) {
1005 		rterror("rt_band_set_pixel: Coordinates out of range");
1006 		return ES_ERROR;
1007 	}
1008 
1009 	/* check that clamped value isn't clamped NODATA */
1010 	if (band->hasnodata && pixtype != PT_64BF) {
1011 		double newval;
1012 		int corrected;
1013 
1014 		rt_band_corrected_clamped_value(band, val, &newval, &corrected);
1015 
1016 		if (corrected) {
1017 #if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
1018 			rtwarn("Value for pixel %d x %d has been corrected as clamped value becomes NODATA", x, y);
1019 #endif
1020 			val = newval;
1021 
1022 			if (converted != NULL)
1023 				*converted = 1;
1024 		}
1025 	}
1026 
1027 	data = rt_band_get_data(band);
1028 	offset = x + (y * band->width);
1029 
1030 	switch (pixtype) {
1031 		case PT_1BB: {
1032 			data[offset] = rt_util_clamp_to_1BB(val);
1033 			checkvalint = data[offset];
1034 			break;
1035 		}
1036 		case PT_2BUI: {
1037 			data[offset] = rt_util_clamp_to_2BUI(val);
1038 			checkvalint = data[offset];
1039 			break;
1040 		}
1041 		case PT_4BUI: {
1042 			data[offset] = rt_util_clamp_to_4BUI(val);
1043 			checkvalint = data[offset];
1044 			break;
1045 		}
1046 		case PT_8BSI: {
1047 			data[offset] = (uint8_t)rt_util_clamp_to_8BSI(val);
1048 			checkvalint = (int8_t) data[offset];
1049 			break;
1050 		}
1051 		case PT_8BUI: {
1052 			data[offset] = rt_util_clamp_to_8BUI(val);
1053 			checkvalint = data[offset];
1054 			break;
1055 		}
1056 		case PT_16BSI: {
1057 			int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
1058 			ptr[offset] = rt_util_clamp_to_16BSI(val);
1059 			checkvalint = (int16_t) ptr[offset];
1060 			break;
1061 		}
1062 		case PT_16BUI: {
1063 			uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
1064 			ptr[offset] = rt_util_clamp_to_16BUI(val);
1065 			checkvalint = ptr[offset];
1066 			break;
1067 		}
1068 		case PT_32BSI: {
1069 			int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
1070 			ptr[offset] = rt_util_clamp_to_32BSI(val);
1071 			checkvalint = (int32_t) ptr[offset];
1072 			break;
1073 		}
1074 		case PT_32BUI: {
1075 			uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
1076 			ptr[offset] = rt_util_clamp_to_32BUI(val);
1077 			checkvaluint = ptr[offset];
1078 			break;
1079 		}
1080 		case PT_32BF: {
1081 			float *ptr = (float*) data; /* we assume correct alignment */
1082 			ptr[offset] = rt_util_clamp_to_32F(val);
1083 			checkvalfloat = ptr[offset];
1084 			break;
1085 		}
1086 		case PT_64BF: {
1087 			double *ptr = (double*) data; /* we assume correct alignment */
1088 			ptr[offset] = val;
1089 			checkvaldouble = ptr[offset];
1090 			break;
1091 		}
1092 		default: {
1093 			rterror("rt_band_set_pixel: Unknown pixeltype %d", pixtype);
1094 			return ES_ERROR;
1095 		}
1096 	}
1097 
1098 	/* If the stored value is not NODATA, reset the isnodata flag */
1099 	if (!rt_band_clamped_value_is_nodata(band, val)) {
1100 		RASTER_DEBUG(3, "Band has a value that is not NODATA. Setting isnodata to FALSE");
1101 		band->isnodata = FALSE;
1102 	}
1103 
1104 	/* Overflow checking */
1105 	if (rt_util_dbl_trunc_warning(
1106 		val,
1107 		checkvalint, checkvaluint,
1108 		checkvalfloat, checkvaldouble,
1109 		pixtype
1110 	) && converted != NULL) {
1111 		*converted = 1;
1112 	}
1113 
1114 	return ES_NONE;
1115 }
1116 
1117 /**
1118  * Get values of multiple pixels.  Unlike rt_band_get_pixel,
1119  * values in vals are of the band's pixel type so cannot be
1120  * assumed to be double.  Function uses memcpy.
1121  *
1122  * It is important to be careful when using this function as
1123  * the number of values being fetched may exceed a pixel "row".
1124  * Remember that the band values are stored in a stream (1-D array)
1125  * regardless of what the raster's width and height might be.
1126  * So, getting a number of values may cross multiple pixel "rows".
1127  *
1128  * @param band : the band to get pixel value from
1129  * @param x : pixel column (0-based)
1130  * @param y : pixel row (0-based)
1131  * @param len : the number of pixels to get
1132  * @param **vals : the pixel values
1133  * @param *nvals : the number of pixel values being returned
1134  *
1135  * @return ES_NONE on success, ES_ERROR on error
1136  */
rt_band_get_pixel_line(rt_band band,int x,int y,uint16_t len,void ** vals,uint16_t * nvals)1137 rt_errorstate rt_band_get_pixel_line(
1138 	rt_band band,
1139 	int x, int y,
1140 	uint16_t len,
1141 	void **vals, uint16_t *nvals
1142 ) {
1143 	uint8_t *_vals = NULL;
1144 	int pixsize = 0;
1145 	uint8_t *data = NULL;
1146 	uint32_t offset = 0;
1147 	uint16_t _nvals = 0;
1148 	int maxlen = 0;
1149 	uint8_t *ptr = NULL;
1150 
1151 	assert(NULL != band);
1152 	assert(vals != NULL && nvals != NULL);
1153 
1154 	/* initialize to no values */
1155 	*nvals = 0;
1156 
1157 	if (
1158 		x < 0 || x >= band->width ||
1159 		y < 0 || y >= band->height
1160 	) {
1161 		rtwarn("Attempting to get pixel values with out of range raster coordinates: (%d, %d)", x, y);
1162 		return ES_ERROR;
1163 	}
1164 
1165 	if (len < 1)
1166 		return ES_NONE;
1167 
1168 	data = rt_band_get_data(band);
1169 	if (data == NULL) {
1170 		rterror("rt_band_get_pixel_line: Cannot get band data");
1171 		return ES_ERROR;
1172 	}
1173 
1174 	/* +1 for the nodata value */
1175 	offset = x + (y * band->width);
1176 	RASTER_DEBUGF(4, "offset = %d", offset);
1177 
1178 	pixsize = rt_pixtype_size(band->pixtype);
1179 	RASTER_DEBUGF(4, "pixsize = %d", pixsize);
1180 
1181 	/* cap _nvals so that it doesn't overflow */
1182 	_nvals = len;
1183 	maxlen = band->width * band->height;
1184 
1185 	if (((int) (offset + _nvals)) > maxlen) {
1186 		_nvals = maxlen - offset;
1187 		rtwarn("Limiting returning number values to %d", _nvals);
1188 	}
1189 	RASTER_DEBUGF(4, "_nvals = %d", _nvals);
1190 
1191 	ptr = data + (offset * pixsize);
1192 
1193 	_vals = rtalloc(_nvals * pixsize);
1194 	if (_vals == NULL) {
1195 		rterror("rt_band_get_pixel_line: Could not allocate memory for pixel values");
1196 		return ES_ERROR;
1197 	}
1198 
1199 	/* copy pixels */
1200 	memcpy(_vals, ptr, _nvals * pixsize);
1201 
1202 	*vals = _vals;
1203 	*nvals = _nvals;
1204 
1205 	return ES_NONE;
1206 }
1207 
1208 /**
1209  * Retrieve a point value from the raster using a world coordinate
1210  * and selected interpolation.
1211  *
1212  * @param band : the band to read for values
1213  * @param xr : x unrounded raster coordinate
1214  * @param yr : y unrounded raster coordinate
1215  * @param r_value : return pointer for point value
1216  * @param r_nodata : return pointer for if this is a nodata
1217  *
1218  * @return ES_ERROR on error, otherwise ES_NONE
1219  */
1220 rt_errorstate
rt_band_get_pixel_resample(rt_band band,double xr,double yr,rt_resample_type resample,double * r_value,int * r_nodata)1221 rt_band_get_pixel_resample(
1222 	rt_band band,
1223 	double xr, double yr,
1224 	rt_resample_type resample,
1225 	double *r_value, int *r_nodata
1226 )
1227 {
1228 	if (resample == RT_BILINEAR) {
1229 		return rt_band_get_pixel_bilinear(
1230 			band, xr, yr, r_value, r_nodata
1231 			);
1232 	}
1233 	else if (resample == RT_NEAREST) {
1234 		return rt_band_get_pixel(
1235 			band, floor(xr), floor(yr),
1236 			r_value, r_nodata
1237 			);
1238 	}
1239 	else {
1240 		rtwarn("Invalid resample type requested %d", resample);
1241 		return ES_ERROR;
1242 	}
1243 
1244 }
1245 
1246 /**
1247  * Retrieve a point value from the raster using a world coordinate
1248  * and bilinear interpolation.
1249  *
1250  * @param rast : the raster to read for values
1251  * @param bandnum : the band to read for the values
1252  * @param xw : x world coordinate in
1253  * @param yw : y world coordinate in
1254  * @param r_value : return pointer for point value
1255  * @param r_nodata : return pointer for if this is a nodata
1256  *
1257  * @return ES_ERROR on error, otherwise ES_NONE
1258  */
1259 rt_errorstate
rt_band_get_pixel_bilinear(rt_band band,double xr,double yr,double * r_value,int * r_nodata)1260 rt_band_get_pixel_bilinear(
1261 	rt_band band,
1262 	double xr, double yr,
1263 	double *r_value, int *r_nodata)
1264 {
1265 	rt_errorstate err;
1266 	double xcenter, ycenter;
1267 	double values[2][2];
1268 	double nodatavalue = 0.0;
1269 	int   nodatas[2][2];
1270 	int x[2][2];
1271 	int y[2][2];
1272 	int xcell, ycell;
1273 	int xdir, ydir;
1274 	int i, j;
1275 	uint16_t width, height;
1276 
1277 	/* Cell coordinates */
1278 	xcell = (int)floor(xr);
1279 	ycell = (int)floor(yr);
1280 	xcenter = xcell + 0.5;
1281 	ycenter = ycell + 0.5;
1282 
1283 	/* Raster geometry */
1284 	width = rt_band_get_width(band);
1285 	height = rt_band_get_height(band);
1286 
1287 	/* Reject out-of-range sample */
1288 	if(xcell < 0 || ycell < 0 || xcell >= width || ycell >= height) {
1289 		rtwarn("Attempting to get pixel value with out of range raster coordinates: (%d, %d)", xcell, ycell);
1290 		return ES_ERROR;
1291 	}
1292 
1293 	/* Quadrant of 2x2 grid the raster coordinate falls in */
1294 	xdir = xr < xcenter ? 1 : 0;
1295 	ydir = yr < ycenter ? 1 : 0;
1296 
1297 	err = rt_band_get_nodata(band, &nodatavalue);
1298 	if (err != ES_NONE) {
1299 		nodatavalue = 0.0;
1300 	}
1301 
1302 	/* Read the 2x2 values from the band */
1303 	for (i = 0; i < 2; i++) {
1304 		for (j = 0; j < 2; j++) {
1305 			double value = nodatavalue;
1306 			int nodata = 0;
1307 			int xij = xcell + (i - xdir);
1308 			int yij = ycell + (j - ydir);
1309 
1310 			if(xij < 0 || yij < 0 || xij >= width || yij >= height) {
1311 				nodata = 1;
1312 			}
1313 			else {
1314 				rt_errorstate err = rt_band_get_pixel(
1315 					band, xij, yij,
1316 					&value, &nodata
1317 					);
1318 				if (err != ES_NONE)
1319 					nodata = 1;
1320 			}
1321 			x[i][j] = xij;
1322 			y[i][j] = yij;
1323 			values[i][j] = value;
1324 			nodatas[i][j] = nodata;
1325 		}
1326 	}
1327 
1328 	/* Point falls in nodata cell, just return nodata */
1329 	if (nodatas[xdir][ydir]) {
1330 		*r_value = nodatavalue;
1331 		*r_nodata = 1;
1332 		return ES_NONE;
1333 	}
1334 
1335 	/* Normalize raster coordinate to the bottom left */
1336 	/* so we are working on a unit square */
1337 	xr = xr - (x[0][0] + 0.5);
1338 	yr = yr - (y[0][0] + 0.5);
1339 
1340 	/* Point is in cell with values, so we take nodata */
1341 	/* neighbors off the table by matching them to the */
1342 	/* most controlling cell */
1343 	for (i = 0; i < 2; i++) {
1344 		for (j = 0; j < 2; j++) {
1345 			if (nodatas[i][j])
1346 				values[i][j] = values[xdir][ydir];
1347 		}
1348 	}
1349 
1350 	/* Calculate bilinear value */
1351 	/* https://en.wikipedia.org/wiki/Bilinear_interpolation#Unit_square */
1352 	*r_nodata = 0;
1353 	*r_value = values[0][0] * (1-xr) * (1-yr) +
1354 	           values[1][0] * (1-yr) * xr +
1355 	           values[0][1] * (1-xr) * yr +
1356 	           values[1][1] * xr * yr;
1357 
1358 	return ES_NONE;
1359 }
1360 
1361 
1362 /**
1363  * Get pixel value. If band's isnodata flag is TRUE, value returned
1364  * will be the band's NODATA value
1365  *
1366  * @param band : the band to set nodata value to
1367  * @param x : x ordinate (0-based)
1368  * @param y : x ordinate (0-based)
1369  * @param *value : pixel value
1370  * @param *nodata : 0 if pixel is not NODATA
1371  *
1372  * @return 0 on success, -1 on error (value out of valid range).
1373  */
1374 rt_errorstate
rt_band_get_pixel(rt_band band,int x,int y,double * value,int * nodata)1375 rt_band_get_pixel(
1376 	rt_band band,
1377 	int x, int y,
1378 	double *value,
1379 	int *nodata
1380 ) {
1381 	rt_pixtype pixtype = PT_END;
1382 	uint8_t* data = NULL;
1383 	uint32_t offset = 0;
1384 
1385 	assert(NULL != band);
1386 	assert(NULL != value);
1387 
1388 	/* set nodata to 0 */
1389 	if (nodata != NULL)
1390 		*nodata = 0;
1391 
1392 	if (
1393 		x < 0 || x >= band->width ||
1394 		y < 0 || y >= band->height
1395 	) {
1396 		rtwarn("Attempting to get pixel value with out of range raster coordinates: (%d, %d)", x, y);
1397 		return ES_ERROR;
1398 	}
1399 
1400 	/* band is NODATA */
1401 	if (band->isnodata) {
1402 		RASTER_DEBUG(3, "Band's isnodata flag is TRUE. Returning NODATA value");
1403 		*value = band->nodataval;
1404 		if (nodata != NULL) *nodata = 1;
1405 		return ES_NONE;
1406 	}
1407 
1408 	data = rt_band_get_data(band);
1409 	if (data == NULL) {
1410 		rterror("rt_band_get_pixel: Cannot get band data");
1411 		return ES_ERROR;
1412 	}
1413 
1414 	/* +1 for the nodata value */
1415 	offset = x + (y * band->width);
1416 
1417 	pixtype = band->pixtype;
1418 
1419 	switch (pixtype) {
1420 		case PT_1BB:
1421 #ifdef OPTIMIZE_SPACE
1422 			{
1423 				int byteOffset = offset / 8;
1424 				int bitOffset = offset % 8;
1425 				data += byteOffset;
1426 
1427 				/* Bit to set is bitOffset into data */
1428 				*value = getBits(data, val, 1, bitOffset);
1429 				break;
1430 			}
1431 #endif
1432 		case PT_2BUI:
1433 #ifdef OPTIMIZE_SPACE
1434 			{
1435 				int byteOffset = offset / 4;
1436 				int bitOffset = offset % 4;
1437 				data += byteOffset;
1438 
1439 				/* Bits to set start at bitOffset into data */
1440 				*value = getBits(data, val, 2, bitOffset);
1441 				break;
1442 			}
1443 #endif
1444 		case PT_4BUI:
1445 #ifdef OPTIMIZE_SPACE
1446 			{
1447 				int byteOffset = offset / 2;
1448 				int bitOffset = offset % 2;
1449 				data += byteOffset;
1450 
1451 				/* Bits to set start at bitOffset into data */
1452 				*value = getBits(data, val, 2, bitOffset);
1453 				break;
1454 			}
1455 #endif
1456 		case PT_8BSI: {
1457 			int8_t val = (int8_t)data[offset];
1458 			*value = val;
1459 			break;
1460 		}
1461 		case PT_8BUI: {
1462 			uint8_t val = data[offset];
1463 			*value = val;
1464 			break;
1465 		}
1466 		case PT_16BSI: {
1467 			int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
1468 			*value = ptr[offset];
1469 			break;
1470 		}
1471 		case PT_16BUI: {
1472 			uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
1473 			*value = ptr[offset];
1474 			break;
1475 		}
1476 		case PT_32BSI: {
1477 			int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
1478 			*value = ptr[offset];
1479 			break;
1480 		}
1481 		case PT_32BUI: {
1482 			uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
1483 			*value = ptr[offset];
1484 			break;
1485 		}
1486 		case PT_32BF: {
1487 			float *ptr = (float*) data; /* we assume correct alignment */
1488 			*value = ptr[offset];
1489 			break;
1490 		}
1491 		case PT_64BF: {
1492 			double *ptr = (double*) data; /* we assume correct alignment */
1493 			*value = ptr[offset];
1494 			break;
1495 		}
1496 		default: {
1497 			rterror("rt_band_get_pixel: Unknown pixeltype %d", pixtype);
1498 			return ES_ERROR;
1499 		}
1500 	}
1501 
1502 	/* set NODATA flag */
1503 	if (band->hasnodata && nodata != NULL) {
1504 		if (rt_band_clamped_value_is_nodata(band, *value))
1505 			*nodata = 1;
1506 	}
1507 
1508 	return ES_NONE;
1509 }
1510 
1511 /**
1512  * Get nearest pixel(s) with value (not NODATA) to specified pixel
1513  *
1514  * @param band : the band to get nearest pixel(s) from
1515  * @param x : the column of the pixel (0-based)
1516  * @param y : the line of the pixel (0-based)
1517  * @param distancex : the number of pixels around the specified pixel
1518  * along the X axis
1519  * @param distancey : the number of pixels around the specified pixel
1520  * along the Y axis
1521  * @param exclude_nodata_value : if non-zero, ignore nodata values
1522  * to check for pixels with value
1523  * @param npixels : return set of rt_pixel object or NULL
1524  *
1525  * @return -1 on error, otherwise the number of rt_pixel objects
1526  * in npixels
1527  */
rt_band_get_nearest_pixel(rt_band band,int x,int y,uint16_t distancex,uint16_t distancey,int exclude_nodata_value,rt_pixel * npixels)1528 uint32_t rt_band_get_nearest_pixel(
1529 	rt_band band,
1530 	int x, int y,
1531 	uint16_t distancex, uint16_t distancey,
1532 	int exclude_nodata_value,
1533 	rt_pixel *npixels
1534 ) {
1535 	rt_pixel npixel = NULL;
1536 	int extent[4] = {0};
1537 	int max_extent[4] = {0};
1538 	int d0 = 0;
1539 	uint32_t distance[2] = {0};
1540 	uint32_t _d[2] = {0};
1541 	uint32_t i = 0;
1542 	uint32_t j = 0;
1543 	uint32_t k = 0;
1544 	int _max = 0;
1545 	int _x = 0;
1546 	int _y = 0;
1547 	int *_min = NULL;
1548 	double pixval = 0;
1549 	double minval = 0;
1550 	uint32_t count = 0;
1551 	int isnodata = 0;
1552 
1553 	int inextent = 0;
1554 
1555 	assert(NULL != band);
1556 	assert(NULL != npixels);
1557 
1558 	RASTER_DEBUG(3, "Starting");
1559 
1560 	/* process distance */
1561 	distance[0] = distancex;
1562 	distance[1] = distancey;
1563 
1564 	/* no distance, means get nearest pixels and return */
1565 	if (!distance[0] && !distance[1])
1566 		d0 = 1;
1567 
1568 	RASTER_DEBUGF(4, "Selected pixel: %d x %d", x, y);
1569 	RASTER_DEBUGF(4, "Distances: %d x %d", distance[0], distance[1]);
1570 
1571 	/* shortcuts if outside band extent */
1572 	if (
1573 		exclude_nodata_value && (
1574 			(x < 0 || x > band->width) ||
1575 			(y < 0 || y > band->height)
1576 		)
1577 	) {
1578 		/* no distances specified, jump to pixel close to extent */
1579 		if (d0) {
1580 			if (x < 0)
1581 				x = -1;
1582 			else if (x > band->width)
1583 				x = band->width;
1584 
1585 			if (y < 0)
1586 				y = -1;
1587 			else if (y > band->height)
1588 				y = band->height;
1589 
1590 			RASTER_DEBUGF(4, "Moved selected pixel: %d x %d", x, y);
1591 		}
1592 		/*
1593 			distances specified
1594 			if distances won't capture extent of band, return 0
1595 		*/
1596 		else if (
1597 			((x < 0 && (uint32_t) abs(x) > distance[0]) || (x - band->width >= (int)distance[0])) ||
1598 			((y < 0 && (uint32_t) abs(y) > distance[1]) || (y - band->height >= (int)distance[1]))
1599 		) {
1600 			RASTER_DEBUG(4, "No nearest pixels possible for provided pixel and distances");
1601 			return 0;
1602 		}
1603 	}
1604 
1605 	/* no NODATA, exclude is FALSE */
1606 	if (!band->hasnodata)
1607 		exclude_nodata_value = FALSE;
1608 	/* band is NODATA and excluding NODATA */
1609 	else if (exclude_nodata_value && band->isnodata) {
1610 		RASTER_DEBUG(4, "No nearest pixels possible as band is NODATA and excluding NODATA values");
1611 		return 0;
1612 	}
1613 
1614 	/* determine the maximum distance to prevent an infinite loop */
1615 	if (d0) {
1616 		int a, b;
1617 
1618 		/* X axis */
1619 		a = abs(x);
1620 		b = abs(x - band->width);
1621 
1622 		if (a > b)
1623 			distance[0] = a;
1624 		else
1625 			distance[0] = b;
1626 
1627 		/* Y axis */
1628 		a = abs(y);
1629 		b = abs(y - band->height);
1630 		if (a > b)
1631 			distance[1] = a;
1632 		else
1633 			distance[1] = b;
1634 
1635 		RASTER_DEBUGF(4, "Maximum distances: %d x %d", distance[0], distance[1]);
1636 	}
1637 
1638 	/* minimum possible value for pixel type */
1639 	minval = rt_pixtype_get_min_value(band->pixtype);
1640 	RASTER_DEBUGF(4, "pixtype: %s", rt_pixtype_name(band->pixtype));
1641 	RASTER_DEBUGF(4, "minval: %f", minval);
1642 
1643 	/* set variables */
1644 	count = 0;
1645 	*npixels = NULL;
1646 
1647 	/* maximum extent */
1648 	max_extent[0] = x - (int)distance[0]; /* min X */
1649 	max_extent[1] = y - (int)distance[1]; /* min Y */
1650 	max_extent[2] = x + (int)distance[0]; /* max X */
1651 	max_extent[3] = y + (int)distance[1]; /* max Y */
1652 	RASTER_DEBUGF(4, "Maximum Extent: (%d, %d, %d, %d)",
1653 		max_extent[0], max_extent[1], max_extent[2], max_extent[3]);
1654 
1655 	_d[0] = 0;
1656 	_d[1] = 0;
1657 	do {
1658 		_d[0]++;
1659 		_d[1]++;
1660 
1661 		extent[0] = x - (int)_d[0]; /* min x */
1662 		extent[1] = y - (int)_d[1]; /* min y */
1663 		extent[2] = x + (int)_d[0]; /* max x */
1664 		extent[3] = y + (int)_d[1]; /* max y */
1665 
1666 		RASTER_DEBUGF(4, "Processing distances: %d x %d", _d[0], _d[1]);
1667 		RASTER_DEBUGF(4, "Extent: (%d, %d, %d, %d)",
1668 			extent[0], extent[1], extent[2], extent[3]);
1669 
1670 		for (i = 0; i < 2; i++) {
1671 
1672 			/* by row */
1673 			if (i < 1)
1674 				_max = extent[2] - extent[0] + 1;
1675 			/* by column */
1676 			else
1677 				_max = extent[3] - extent[1] + 1;
1678 			_max = abs(_max);
1679 
1680 			for (j = 0; j < 2; j++) {
1681 				/* by row */
1682 				if (i < 1) {
1683 					_x = extent[0];
1684 					_min = &_x;
1685 
1686 					/* top row */
1687 					if (j < 1)
1688 						_y = extent[1];
1689 					/* bottom row */
1690 					else
1691 						_y = extent[3];
1692 				}
1693 				/* by column */
1694 				else {
1695 					_y = extent[1] + 1;
1696 					_min = &_y;
1697 
1698 					/* left column */
1699 					if (j < 1) {
1700 						_x = extent[0];
1701 						_max -= 2;
1702 					}
1703 					/* right column */
1704 					else
1705 						_x = extent[2];
1706 				}
1707 
1708 				RASTER_DEBUGF(4, "_min, _max: %d, %d", *_min, _max);
1709 				for (k = 0; k < (uint32_t) _max; k++) {
1710 					/* check that _x and _y are not outside max extent */
1711 					if (
1712 						_x < max_extent[0] || _x > max_extent[2] ||
1713 						_y < max_extent[1] || _y > max_extent[3]
1714 					) {
1715 						(*_min)++;
1716 						continue;
1717 					}
1718 
1719 					/* outside band extent, set to NODATA */
1720 					if (
1721 						(_x < 0 || _x >= band->width) ||
1722 						(_y < 0 || _y >= band->height)
1723 					) {
1724 						/* no NODATA, set to minimum possible value */
1725 						if (!band->hasnodata)
1726 							pixval = minval;
1727 						/* has NODATA, use NODATA */
1728 						else
1729 							pixval = band->nodataval;
1730 						RASTER_DEBUGF(4, "NODATA pixel outside band extent: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
1731 						inextent = 0;
1732 						isnodata = 1;
1733 					}
1734 					else {
1735 						if (rt_band_get_pixel(
1736 							band,
1737 							_x, _y,
1738 							&pixval,
1739 							&isnodata
1740 						) != ES_NONE) {
1741 							rterror("rt_band_get_nearest_pixel: Could not get pixel value");
1742 							if (count) rtdealloc(*npixels);
1743 							return -1;
1744 						}
1745 						RASTER_DEBUGF(4, "Pixel: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
1746 						inextent = 1;
1747 					}
1748 
1749 					/* use pixval? */
1750 					if (!exclude_nodata_value || (exclude_nodata_value && !isnodata)) {
1751 						/* add pixel to result set */
1752 						RASTER_DEBUGF(4, "Adding pixel to set of nearest pixels: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
1753 						count++;
1754 
1755 						if (*npixels == NULL)
1756 							*npixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
1757 						else
1758 							*npixels = (rt_pixel) rtrealloc(*npixels, sizeof(struct rt_pixel_t) * count);
1759 						if (*npixels == NULL) {
1760 							rterror("rt_band_get_nearest_pixel: Could not allocate memory for nearest pixel(s)");
1761 							return -1;
1762 						}
1763 
1764 						npixel = &((*npixels)[count - 1]);
1765 						npixel->x = _x;
1766 						npixel->y = _y;
1767 						npixel->value = pixval;
1768 
1769 						/* special case for when outside band extent */
1770 						if (!inextent && !band->hasnodata)
1771 							npixel->nodata = 1;
1772 						else
1773 							npixel->nodata = 0;
1774 					}
1775 
1776 					(*_min)++;
1777 				}
1778 			}
1779 		}
1780 
1781 		/* distance threshholds met */
1782 		if (_d[0] >= distance[0] && _d[1] >= distance[1])
1783 			break;
1784 		else if (d0 && count)
1785 			break;
1786 	}
1787 	while (1);
1788 
1789 	RASTER_DEBUGF(3, "Nearest pixels in return: %d", count);
1790 
1791 	return count;
1792 }
1793 
1794 
1795 
1796 /**
1797  * Search band for pixel(s) with search values
1798  *
1799  * @param band : the band to query for minimum and maximum pixel values
1800  * @param exclude_nodata_value : if non-zero, ignore nodata values
1801  * @param searchset : array of values to count
1802  * @param searchcount : the number of search values
1803  * @param pixels : pixels with the search value
1804  *
1805  * @return -1 on error, otherwise number of pixels
1806  */
1807 int
rt_band_get_pixel_of_value(rt_band band,int exclude_nodata_value,double * searchset,int searchcount,rt_pixel * pixels)1808 rt_band_get_pixel_of_value(
1809 	rt_band band, int exclude_nodata_value,
1810 	double *searchset, int searchcount,
1811 	rt_pixel *pixels
1812 ) {
1813 	int x;
1814 	int y;
1815 	int i;
1816 	double pixval;
1817 	int err;
1818 	int count = 0;
1819 	int isnodata = 0;
1820 	int isequal = 0;
1821 
1822 	rt_pixel pixel = NULL;
1823 
1824 	assert(NULL != band);
1825 	assert(NULL != pixels);
1826 	assert(NULL != searchset && searchcount > 0);
1827 
1828 	if (!band->hasnodata)
1829 		exclude_nodata_value = FALSE;
1830 	/* band is NODATA and exclude_nodata_value = TRUE, nothing to search */
1831 	else if (exclude_nodata_value && band->isnodata) {
1832 		RASTER_DEBUG(4, "Pixels cannot be searched as band is NODATA and excluding NODATA values");
1833 		return 0;
1834 	}
1835 
1836 	for (x = 0; x < band->width; x++) {
1837 		for (y = 0; y < band->height; y++) {
1838 			err = rt_band_get_pixel(band, x, y, &pixval, &isnodata);
1839 			if (err != ES_NONE) {
1840 				rterror("rt_band_get_pixel_of_value: Cannot get band pixel");
1841 				return -1;
1842 			}
1843 			else if (exclude_nodata_value && isnodata)
1844 				continue;
1845 
1846 			for (i = 0; i < searchcount; i++) {
1847 				if (rt_pixtype_compare_clamped_values(band->pixtype, searchset[i], pixval, &isequal) != ES_NONE) {
1848 					continue;
1849 				}
1850 
1851 				if (FLT_NEQ(pixval, searchset[i]) || !isequal)
1852 					continue;
1853 
1854 				/* match found */
1855 				count++;
1856 				if (*pixels == NULL)
1857 					*pixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
1858 				else
1859 					*pixels = (rt_pixel) rtrealloc(*pixels, sizeof(struct rt_pixel_t) * count);
1860 				if (*pixels == NULL) {
1861 					rterror("rt_band_get_pixel_of_value: Could not allocate memory for pixel(s)");
1862 					return -1;
1863 				}
1864 
1865 				pixel = &((*pixels)[count - 1]);
1866 				pixel->x = x;
1867 				pixel->y = y;
1868 				pixel->nodata = 0;
1869 				pixel->value = pixval;
1870 			}
1871 		}
1872 	}
1873 
1874 	return count;
1875 }
1876 
1877 /**
1878  * Get NODATA value
1879  *
1880  * @param band : the band whose NODATA value will be returned
1881  * @param nodata : the band's NODATA value
1882  *
1883  * @return ES_NONE or ES_ERROR
1884  */
1885 rt_errorstate
rt_band_get_nodata(rt_band band,double * nodata)1886 rt_band_get_nodata(rt_band band, double *nodata) {
1887 	assert(NULL != band);
1888 	assert(NULL != nodata);
1889 
1890 	*nodata = band->nodataval;
1891 
1892 	if (!band->hasnodata) {
1893 		rterror("rt_band_get_nodata: Band has no NODATA value");
1894 		return ES_ERROR;
1895 	}
1896 
1897 	return ES_NONE;
1898 }
1899 
1900 double
rt_band_get_min_value(rt_band band)1901 rt_band_get_min_value(rt_band band) {
1902 	assert(NULL != band);
1903 
1904 	return rt_pixtype_get_min_value(band->pixtype);
1905 }
1906 
1907 int
rt_band_check_is_nodata(rt_band band)1908 rt_band_check_is_nodata(rt_band band) {
1909 	int i, j, err;
1910 	double pxValue;
1911 	int isnodata = 0;
1912 
1913 	assert(NULL != band);
1914 	band->isnodata = FALSE;
1915 
1916 	/* Check if band has nodata value */
1917 	if (!band->hasnodata) {
1918 		RASTER_DEBUG(3, "Band has no NODATA value");
1919 		return FALSE;
1920 	}
1921 
1922 	pxValue = band->nodataval;
1923 
1924 	/* Check all pixels */
1925 	for (i = 0; i < band->width; i++) {
1926 		for (j = 0; j < band->height; j++) {
1927 			err = rt_band_get_pixel(band, i, j, &pxValue, &isnodata);
1928 			if (err != ES_NONE) {
1929 				rterror("rt_band_check_is_nodata: Cannot get band pixel");
1930 				return FALSE;
1931 			}
1932 			else if (!isnodata) {
1933 				band->isnodata = FALSE;
1934 				return FALSE;
1935 			}
1936 		}
1937 	}
1938 
1939 	band->isnodata = TRUE;
1940 	return TRUE;
1941 }
1942 
1943 /**
1944  * Compare clamped value to band's clamped NODATA value.
1945  *
1946  * @param band : the band whose NODATA value will be used for comparison
1947  * @param val : the value to compare to the NODATA value
1948  *
1949  * @return 2 if unclamped value is unclamped NODATA
1950  *         1 if clamped value is clamped NODATA
1951  *         0 if clamped value is NOT clamped NODATA
1952  */
1953 int
rt_band_clamped_value_is_nodata(rt_band band,double val)1954 rt_band_clamped_value_is_nodata(rt_band band, double val) {
1955 	int isequal = 0;
1956 
1957 	assert(NULL != band);
1958 
1959 	/* no NODATA, so never equal */
1960 	if (!band->hasnodata)
1961 		return 0;
1962 
1963 	/* value is exactly NODATA */
1964 	if (FLT_EQ(val, band->nodataval))
1965 		return 2;
1966 
1967 	/* ignore error from rt_pixtype_compare_clamped_values */
1968 	rt_pixtype_compare_clamped_values(
1969 		band->pixtype,
1970 		val, band->nodataval,
1971 		&isequal
1972 	);
1973 
1974 	return isequal ? 1 : 0;
1975 }
1976 
1977 /**
1978  * Correct value when clamped value is equal to clamped NODATA value.
1979  * Correction does NOT occur if unclamped value is exactly unclamped
1980  * NODATA value.
1981  *
1982  * @param band : the band whose NODATA value will be used for comparison
1983  * @param val : the value to compare to the NODATA value and correct
1984  * @param *newval : pointer to corrected value
1985  * @param *corrected : (optional) non-zero if val was corrected
1986  *
1987  * @return ES_NONE if success, ES_ERROR if error
1988  */
1989 rt_errorstate
rt_band_corrected_clamped_value(rt_band band,double val,double * newval,int * corrected)1990 rt_band_corrected_clamped_value(
1991 	rt_band band,
1992 	double val,
1993 	double *newval, int *corrected
1994 ) {
1995 	double minval = 0.;
1996 
1997 	assert(NULL != band);
1998 	assert(NULL != newval);
1999 
2000 	if (corrected != NULL)
2001 		*corrected = 0;
2002 
2003 	/* no need to correct if clamped values IS NOT clamped NODATA */
2004 	if (rt_band_clamped_value_is_nodata(band, val) != 1) {
2005 		*newval = val;
2006 		return ES_NONE;
2007 	}
2008 
2009 	minval = rt_pixtype_get_min_value(band->pixtype);
2010 	*newval = val;
2011 
2012 	switch (band->pixtype) {
2013 		case PT_1BB:
2014 			*newval = !band->nodataval;
2015 			break;
2016 		case PT_2BUI:
2017 			if (rt_util_clamp_to_2BUI(val) == rt_util_clamp_to_2BUI(minval))
2018 				(*newval)++;
2019 			else
2020 				(*newval)--;
2021 			break;
2022 		case PT_4BUI:
2023 			if (rt_util_clamp_to_4BUI(val) == rt_util_clamp_to_4BUI(minval))
2024 				(*newval)++;
2025 			else
2026 				(*newval)--;
2027 			break;
2028 		case PT_8BSI:
2029 			if (rt_util_clamp_to_8BSI(val) == rt_util_clamp_to_8BSI(minval))
2030 				(*newval)++;
2031 			else
2032 				(*newval)--;
2033 			break;
2034 		case PT_8BUI:
2035 			if (rt_util_clamp_to_8BUI(val) == rt_util_clamp_to_8BUI(minval))
2036 				(*newval)++;
2037 			else
2038 				(*newval)--;
2039 			break;
2040 		case PT_16BSI:
2041 			if (rt_util_clamp_to_16BSI(val) == rt_util_clamp_to_16BSI(minval))
2042 				(*newval)++;
2043 			else
2044 				(*newval)--;
2045 			break;
2046 		case PT_16BUI:
2047 			if (rt_util_clamp_to_16BUI(val) == rt_util_clamp_to_16BUI(minval))
2048 				(*newval)++;
2049 			else
2050 				(*newval)--;
2051 			break;
2052 		case PT_32BSI:
2053 			if (rt_util_clamp_to_32BSI(val) == rt_util_clamp_to_32BSI(minval))
2054 				(*newval)++;
2055 			else
2056 				(*newval)--;
2057 			break;
2058 		case PT_32BUI:
2059 			if (rt_util_clamp_to_32BUI(val) == rt_util_clamp_to_32BUI(minval))
2060 				(*newval)++;
2061 			else
2062 				(*newval)--;
2063 			break;
2064 		case PT_32BF:
2065 			if (FLT_EQ(rt_util_clamp_to_32F(val), rt_util_clamp_to_32F(minval)))
2066 				*newval += FLT_EPSILON;
2067 			else
2068 				*newval -= FLT_EPSILON;
2069 			break;
2070 		case PT_64BF:
2071 			break;
2072 		default:
2073 			rterror("rt_band_corrected_clamped_value: Unknown pixeltype %d", band->pixtype);
2074 			return ES_ERROR;
2075 	}
2076 
2077 	if (corrected != NULL)
2078 		*corrected = 1;
2079 
2080 	return ES_NONE;
2081 }
2082 
2083