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