PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
rt_warp.c
Go to the documentation of this file.
1/*
2 *
3 * WKTRaster - Raster Types for PostGIS
4 * http://trac.osgeo.org/postgis/wiki/WKTRaster
5 *
6 * Copyright (C) 2013 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#include "../../postgis_config.h"
32
33#include "librtcore.h"
34#include "librtcore_internal.h"
35
36/******************************************************************************
37* rt_raster_gdal_warp()
38******************************************************************************/
39
42
43 struct {
44 GDALDriverH drv;
45 GDALDatasetH ds;
46 char *srs;
48 } src, dst;
49
50 GDALWarpOptions *wopts;
51
52 struct {
53 struct {
54 char **item;
55 int len;
57
58 struct {
59 void *transform;
60 void *imgproj;
61 void *approx;
62 } arg;
63
64 GDALTransformerFunc func;
66
67};
68
69static _rti_warp_arg
71 _rti_warp_arg arg = NULL;
72
73 arg = rtalloc(sizeof(struct _rti_warp_arg_t));
74 if (arg == NULL) {
75 rterror("_rti_warp_arg_init: Could not allocate memory for _rti_warp_arg");
76 return NULL;
77 }
78
79 arg->src.drv = NULL;
80 arg->src.destroy_drv = 0;
81 arg->src.ds = NULL;
82 arg->src.srs = NULL;
83
84 arg->dst.drv = NULL;
85 arg->dst.destroy_drv = 0;
86 arg->dst.ds = NULL;
87 arg->dst.srs = NULL;
88
89 arg->wopts = NULL;
90
91 arg->transform.option.item = NULL;
92 arg->transform.option.len = 0;
93
94 arg->transform.arg.transform = NULL;
95 arg->transform.arg.imgproj = NULL;
96 arg->transform.arg.approx = NULL;
97
98 arg->transform.func = NULL;
99
100 return arg;
101}
102
103static void
105 int i = 0;
106
107 if (arg->dst.ds != NULL)
108 GDALClose(arg->dst.ds);
109 if (arg->dst.srs != NULL)
110 CPLFree(arg->dst.srs);
111
112 if (arg->dst.drv != NULL && arg->dst.destroy_drv) {
113 GDALDeregisterDriver(arg->dst.drv);
114 GDALDestroyDriver(arg->dst.drv);
115 }
116
117 if (arg->src.ds != NULL)
118 GDALClose(arg->src.ds);
119 if (arg->src.srs != NULL)
120 CPLFree(arg->src.srs);
121
122 if (arg->src.drv != NULL && arg->src.destroy_drv) {
123 GDALDeregisterDriver(arg->src.drv);
124 GDALDestroyDriver(arg->src.drv);
125 }
126
127 if (arg->transform.func == GDALApproxTransform) {
128 if (arg->transform.arg.imgproj != NULL)
129 GDALDestroyGenImgProjTransformer(arg->transform.arg.imgproj);
130 }
131
132 if (arg->wopts != NULL)
133 GDALDestroyWarpOptions(arg->wopts);
134
135 if (arg->transform.option.len > 0 && arg->transform.option.item != NULL) {
136 for (i = 0; i < arg->transform.option.len; i++) {
137 if (arg->transform.option.item[i] != NULL)
138 rtdealloc(arg->transform.option.item[i]);
139 }
140 rtdealloc(arg->transform.option.item);
141 }
142
143 rtdealloc(arg);
144 arg = NULL;
145}
146
178 rt_raster raster,
179 const char *src_srs, const char *dst_srs,
180 double *scale_x, double *scale_y,
181 int *width, int *height,
182 double *ul_xw, double *ul_yw,
183 double *grid_xw, double *grid_yw,
184 double *skew_x, double *skew_y,
185 GDALResampleAlg resample_alg, double max_err
186) {
187 CPLErr cplerr;
188 char *dst_options[] = {"SUBCLASS=VRTWarpedDataset", NULL};
189 _rti_warp_arg arg = NULL;
190
191 GDALRasterBandH band;
192 rt_band rtband = NULL;
193 rt_pixtype pt = PT_END;
194 GDALDataType gdal_pt = GDT_Unknown;
195 double nodata = 0.0;
196
197 double _gt[6] = {0};
198 double dst_extent[4];
199 rt_envelope extent;
200
201 int _dim[2] = {0};
202 double _skew[2] = {0};
203 double _scale[2] = {0};
204 int ul_user = 0;
205
206 rt_raster rast = NULL;
207 int i = 0;
208 int numBands = 0;
209
210 /* flag indicating that the spatial info is being substituted */
211 int subspatial = 0;
212
213 RASTER_DEBUG(3, "starting");
214
215 assert(NULL != raster);
216
217 /* internal variables */
218 arg = _rti_warp_arg_init();
219 if (arg == NULL) {
220 rterror("rt_raster_gdal_warp: Could not initialize internal variables");
221 return NULL;
222 }
223
224 /*
225 max_err must be gte zero
226
227 the value 0.125 is the default used in gdalwarp.cpp on line 283
228 */
229 if (max_err < 0.) max_err = 0.125;
230 RASTER_DEBUGF(4, "max_err = %f", max_err);
231
232 /* handle srs */
233 if (src_srs != NULL) {
234 /* reprojection taking place */
235 if (dst_srs != NULL && strcmp(src_srs, dst_srs) != 0) {
236 RASTER_DEBUG(4, "Warp operation does include a reprojection");
237 arg->src.srs = rt_util_gdal_convert_sr(src_srs, 0);
238 arg->dst.srs = rt_util_gdal_convert_sr(dst_srs, 0);
239
240 if (arg->src.srs == NULL || arg->dst.srs == NULL) {
241 rterror("rt_raster_gdal_warp: Could not convert srs values to GDAL accepted format");
243 return NULL;
244 }
245 }
246 /* no reprojection, a stub just for clarity */
247 else {
248 RASTER_DEBUG(4, "Warp operation does NOT include reprojection");
249 }
250 }
251 else if (dst_srs != NULL) {
252 /* dst_srs provided but not src_srs */
253 rterror("rt_raster_gdal_warp: SRS required for input raster if SRS provided for warped raster");
255 return NULL;
256 }
257
258 /* load raster into a GDAL MEM dataset */
259 arg->src.ds = rt_raster_to_gdal_mem(raster, arg->src.srs, NULL, NULL, 0, &(arg->src.drv), &(arg->src.destroy_drv));
260 if (NULL == arg->src.ds) {
261 rterror("rt_raster_gdal_warp: Could not convert raster to GDAL MEM format");
263 return NULL;
264 }
265 RASTER_DEBUG(3, "raster loaded into GDAL MEM dataset");
266
267 /* special case when src_srs and dst_srs is NULL and raster's geotransform matrix is default */
268 if (
269 src_srs == NULL && dst_srs == NULL &&
271 ) {
272 double gt[6];
273
274#if POSTGIS_DEBUG_LEVEL > 3
275 GDALGetGeoTransform(arg->src.ds, gt);
276 RASTER_DEBUGF(3, "GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
277 gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
278#endif
279
280 /* default geotransform */
282 RASTER_DEBUGF(3, "raster geotransform: %f, %f, %f, %f, %f, %f",
283 gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
284
285 /* substitute spatial info (lack of) with a real one EPSG:32731 (WGS84/UTM zone 31s) */
286 if (FLT_EQ(gt[0], 0.0) && FLT_EQ(gt[3], 0.0) && FLT_EQ(gt[1], 1.0) && FLT_EQ(gt[5], -1.0) &&
287 FLT_EQ(gt[2], 0.0) && FLT_EQ(gt[4], 0.0))
288 {
289 double ngt[6] = {166021.4431, 0.1, 0, 10000000.0000, 0, -0.1};
290
291 rtwarn("Raster has default geotransform. Adjusting metadata for use of GDAL Warp API");
292
293 subspatial = 1;
294
295 GDALSetGeoTransform(arg->src.ds, ngt);
296 GDALFlushCache(arg->src.ds);
297
298 /* EPSG:32731 */
299 arg->src.srs = rt_util_gdal_convert_sr("EPSG:32731", 0);
300 arg->dst.srs = rt_util_gdal_convert_sr("EPSG:32731", 0);
301
302#if POSTGIS_DEBUG_LEVEL > 3
303 GDALGetGeoTransform(arg->src.ds, gt);
304 RASTER_DEBUGF(3, "GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
305 gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
306#endif
307 }
308 }
309
310 /* set transform options */
311 if (arg->src.srs != NULL || arg->dst.srs != NULL) {
312 arg->transform.option.len = 2;
313 arg->transform.option.item = rtalloc(sizeof(char *) * (arg->transform.option.len + 1));
314 if (NULL == arg->transform.option.item) {
315 rterror("rt_raster_gdal_warp: Could not allocation memory for transform options");
317 return NULL;
318 }
319 memset(arg->transform.option.item, 0, sizeof(char *) * (arg->transform.option.len + 1));
320
321 for (i = 0; i < arg->transform.option.len; i++) {
322 const char *srs = i ? arg->dst.srs : arg->src.srs;
323 const char *lbl = i ? "DST_SRS=" : "SRC_SRS=";
324 size_t sz = sizeof(char) * (strlen(lbl) + 1);
325 if ( srs ) sz += strlen(srs);
326 arg->transform.option.item[i] = (char *) rtalloc(sz);
327 if (NULL == arg->transform.option.item[i]) {
328 rterror("rt_raster_gdal_warp: Could not allocation memory for transform options");
330 return NULL;
331 }
332 sprintf(arg->transform.option.item[i], "%s%s", lbl, srs ? srs : "");
333 RASTER_DEBUGF(4, "arg->transform.option.item[%d] = %s", i, arg->transform.option.item[i]);
334 }
335 }
336 else
337 arg->transform.option.len = 0;
338
339 /* transformation object for building dst dataset */
340 arg->transform.arg.transform = GDALCreateGenImgProjTransformer2(arg->src.ds, NULL, arg->transform.option.item);
341 if (NULL == arg->transform.arg.transform) {
342 rterror("rt_raster_gdal_warp: Could not create GDAL transformation object for output dataset creation");
344 return NULL;
345 }
346
347 /* get approximate output georeferenced bounds and resolution */
348 cplerr = GDALSuggestedWarpOutput2(
349 arg->src.ds, GDALGenImgProjTransform,
350 arg->transform.arg.transform, _gt, &(_dim[0]), &(_dim[1]), dst_extent, 0);
351 if (cplerr != CE_None) {
352 rterror("rt_raster_gdal_warp: Could not get GDAL suggested warp output for output dataset creation");
354 return NULL;
355 }
356 GDALDestroyGenImgProjTransformer(arg->transform.arg.transform);
357 arg->transform.arg.transform = NULL;
358
359 /*
360 don't use suggested dimensions as use of suggested scales
361 on suggested extent will result in suggested dimensions
362 */
363 _dim[0] = 0;
364 _dim[1] = 0;
365
366 RASTER_DEBUGF(3, "Suggested geotransform: %f, %f, %f, %f, %f, %f",
367 _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
368
369 /* store extent in easier-to-use object */
370 extent.MinX = dst_extent[0];
371 extent.MinY = dst_extent[1];
372 extent.MaxX = dst_extent[2];
373 extent.MaxY = dst_extent[3];
374
375 extent.UpperLeftX = dst_extent[0];
376 extent.UpperLeftY = dst_extent[3];
377
378 RASTER_DEBUGF(3, "Suggested extent: %f, %f, %f, %f",
379 extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
380
381 /* scale and width/height are mutually exclusive */
382 if (
383 ((NULL != scale_x) || (NULL != scale_y)) &&
384 ((NULL != width) || (NULL != height))
385 ) {
386 rterror("rt_raster_gdal_warp: Scale X/Y and width/height are mutually exclusive. Only provide one");
388 return NULL;
389 }
390
391 /* user-defined width */
392 if (NULL != width) {
393 _dim[0] = abs(*width);
394 _scale[0] = fabs((extent.MaxX - extent.MinX) / ((double) _dim[0]));
395 }
396 /* user-defined height */
397 if (NULL != height) {
398 _dim[1] = abs(*height);
399 _scale[1] = fabs((extent.MaxY - extent.MinY) / ((double) _dim[1]));
400 }
401
402 /* user-defined scale */
403 if (
404 ((NULL != scale_x) && (FLT_NEQ(*scale_x, 0.0))) &&
405 ((NULL != scale_y) && (FLT_NEQ(*scale_y, 0.0)))
406 ) {
407 _scale[0] = fabs(*scale_x);
408 _scale[1] = fabs(*scale_y);
409
410 /* special override since we changed the original GT scales */
411 if (subspatial) {
412 /*
413 _scale[0] *= 10;
414 _scale[1] *= 10;
415 */
416 _scale[0] /= 10;
417 _scale[1] /= 10;
418 }
419 }
420 else if (
421 ((NULL != scale_x) && (NULL == scale_y)) ||
422 ((NULL == scale_x) && (NULL != scale_y))
423 ) {
424 rterror("rt_raster_gdal_warp: Both X and Y scale values must be provided for scale");
426 return NULL;
427 }
428
429 /* scale not defined, use suggested */
430 if (FLT_EQ(_scale[0], 0.0) && FLT_EQ(_scale[1], 0.0))
431 {
432 _scale[0] = fabs(_gt[1]);
433 _scale[1] = fabs(_gt[5]);
434 }
435
436 RASTER_DEBUGF(4, "Using scale: %f x %f", _scale[0], -1 * _scale[1]);
437
438 /* user-defined skew */
439 if (NULL != skew_x) {
440 _skew[0] = *skew_x;
441
442 /*
443 negative scale-x affects skew
444 for now, force skew to be in left-right, top-down orientation
445 */
446 if (
447 NULL != scale_x &&
448 *scale_x < 0.
449 ) {
450 _skew[0] *= -1;
451 }
452 }
453 if (NULL != skew_y) {
454 _skew[1] = *skew_y;
455
456 /*
457 positive scale-y affects skew
458 for now, force skew to be in left-right, top-down orientation
459 */
460 if (
461 NULL != scale_y &&
462 *scale_y > 0.
463 ) {
464 _skew[1] *= -1;
465 }
466 }
467
468 RASTER_DEBUGF(4, "Using skew: %f x %f", _skew[0], _skew[1]);
469
470 /* reprocess extent if skewed */
471 if (FLT_NEQ(_skew[0], 0.0) || FLT_NEQ(_skew[1], 0.0))
472 {
473 rt_raster skewedrast;
474
475 RASTER_DEBUG(3, "Computing skewed extent's envelope");
476
478 extent,
479 _skew,
480 _scale,
481 0.01
482 );
483 if (skewedrast == NULL) {
484 rterror("rt_raster_gdal_warp: Could not compute skewed raster");
486 return NULL;
487 }
488
489 if (_dim[0] == 0)
490 _dim[0] = skewedrast->width;
491 if (_dim[1] == 0)
492 _dim[1] = skewedrast->height;
493
494 extent.UpperLeftX = skewedrast->ipX;
495 extent.UpperLeftY = skewedrast->ipY;
496
497 rt_raster_destroy(skewedrast);
498 }
499
500 /* dimensions not defined, compute */
501 if (!_dim[0])
502 _dim[0] = (int) fmax((fabs(extent.MaxX - extent.MinX) + (_scale[0] / 2.)) / _scale[0], 1);
503 if (!_dim[1])
504 _dim[1] = (int) fmax((fabs(extent.MaxY - extent.MinY) + (_scale[1] / 2.)) / _scale[1], 1);
505
506 /* temporary raster */
507 rast = rt_raster_new(_dim[0], _dim[1]);
508 if (rast == NULL) {
509 rterror("rt_raster_gdal_warp: Out of memory allocating temporary raster");
511 return NULL;
512 }
513
514 /* set raster's spatial attributes */
515 rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
516 rt_raster_set_scale(rast, _scale[0], -1 * _scale[1]);
517 rt_raster_set_skews(rast, _skew[0], _skew[1]);
518
520 RASTER_DEBUGF(3, "Temp raster's geotransform: %f, %f, %f, %f, %f, %f",
521 _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
522 RASTER_DEBUGF(3, "Temp raster's dimensions (width x height): %d x %d",
523 _dim[0], _dim[1]);
524
525 /* user-defined upper-left corner */
526 if (
527 NULL != ul_xw &&
528 NULL != ul_yw
529 ) {
530 ul_user = 1;
531
532 RASTER_DEBUGF(4, "Using user-specified upper-left corner: %f, %f", *ul_xw, *ul_yw);
533
534 /* set upper-left corner */
535 rt_raster_set_offsets(rast, *ul_xw, *ul_yw);
536 extent.UpperLeftX = *ul_xw;
537 extent.UpperLeftY = *ul_yw;
538 }
539 else if (
540 ((NULL != ul_xw) && (NULL == ul_yw)) ||
541 ((NULL == ul_xw) && (NULL != ul_yw))
542 ) {
543 rterror("rt_raster_gdal_warp: Both X and Y upper-left corner values must be provided");
544 rt_raster_destroy(rast);
546 return NULL;
547 }
548
549 /* alignment only considered if upper-left corner not provided */
550 if (
551 !ul_user && (
552 (NULL != grid_xw) || (NULL != grid_yw)
553 )
554 ) {
555
556 if (
557 ((NULL != grid_xw) && (NULL == grid_yw)) ||
558 ((NULL == grid_xw) && (NULL != grid_yw))
559 ) {
560 rterror("rt_raster_gdal_warp: Both X and Y alignment values must be provided");
561 rt_raster_destroy(rast);
563 return NULL;
564 }
565
566 RASTER_DEBUGF(4, "Aligning extent to user-specified grid: %f, %f", *grid_xw, *grid_yw);
567
568 do {
569 double _r[2] = {0};
570 double _w[2] = {0};
571
572 /* raster is already aligned */
573 if (FLT_EQ(*grid_xw, extent.UpperLeftX) && FLT_EQ(*grid_yw, extent.UpperLeftY)) {
574 RASTER_DEBUG(3, "Skipping raster alignment as it is already aligned to grid");
575 break;
576 }
577
578 extent.UpperLeftX = rast->ipX;
579 extent.UpperLeftY = rast->ipY;
580 rt_raster_set_offsets(rast, *grid_xw, *grid_yw);
581
582 /* process upper-left corner */
584 rast,
585 extent.UpperLeftX, extent.UpperLeftY,
586 &(_r[0]), &(_r[1]),
587 NULL
588 ) != ES_NONE) {
589 rterror("rt_raster_gdal_warp: Could not compute raster pixel for spatial coordinates");
590 rt_raster_destroy(rast);
592 return NULL;
593 }
594
596 rast,
597 _r[0], _r[1],
598 &(_w[0]), &(_w[1]),
599 NULL
600 ) != ES_NONE) {
601 rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
602
603 rt_raster_destroy(rast);
605 return NULL;
606 }
607
608 /* shift occurred */
609 if (FLT_NEQ(_w[0], extent.UpperLeftX)) {
610 if (NULL == width)
611 rast->width++;
612 else if (NULL == scale_x) {
613 double _c[2] = {0};
614
615 rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
616
617 /* get upper-right corner */
619 rast,
620 rast->width, 0,
621 &(_c[0]), &(_c[1]),
622 NULL
623 ) != ES_NONE) {
624 rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
625 rt_raster_destroy(rast);
627 return NULL;
628 }
629
630 rast->scaleX = fabs((_c[0] - _w[0]) / ((double) rast->width));
631 }
632 }
633 if (FLT_NEQ(_w[1], extent.UpperLeftY)) {
634 if (NULL == height)
635 rast->height++;
636 else if (NULL == scale_y) {
637 double _c[2] = {0};
638
639 rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
640
641 /* get upper-right corner */
643 rast,
644 0, rast->height,
645 &(_c[0]), &(_c[1]),
646 NULL
647 ) != ES_NONE) {
648 rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
649
650 rt_raster_destroy(rast);
652 return NULL;
653 }
654
655 rast->scaleY = -1 * fabs((_c[1] - _w[1]) / ((double) rast->height));
656 }
657 }
658
659 rt_raster_set_offsets(rast, _w[0], _w[1]);
660 RASTER_DEBUGF(4, "aligned offsets: %f x %f", _w[0], _w[1]);
661 }
662 while (0);
663 }
664
665 /*
666 after this point, rt_envelope extent is no longer used
667 */
668
669 /* get key attributes from rast */
670 _dim[0] = rast->width;
671 _dim[1] = rast->height;
673
674 /* scale-x is negative or scale-y is positive */
675 if ((
676 (NULL != scale_x) && (*scale_x < 0.)
677 ) || (
678 (NULL != scale_y) && (*scale_y > 0)
679 )) {
680 double _w[2] = {0};
681
682 /* negative scale-x */
683 if (
684 (NULL != scale_x) &&
685 (*scale_x < 0.)
686 ) {
688 rast,
689 rast->width, 0,
690 &(_w[0]), &(_w[1]),
691 NULL
692 ) != ES_NONE) {
693 rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
694 rt_raster_destroy(rast);
696 return NULL;
697 }
698
699 _gt[0] = _w[0];
700 _gt[1] = *scale_x;
701
702 /* check for skew */
703 if (NULL != skew_x && FLT_NEQ(*skew_x, 0.0))
704 _gt[2] = *skew_x;
705 }
706 /* positive scale-y */
707 if (
708 (NULL != scale_y) &&
709 (*scale_y > 0)
710 ) {
712 rast,
713 0, rast->height,
714 &(_w[0]), &(_w[1]),
715 NULL
716 ) != ES_NONE) {
717 rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
718 rt_raster_destroy(rast);
720 return NULL;
721 }
722
723 _gt[3] = _w[1];
724 _gt[5] = *scale_y;
725
726 /* check for skew */
727 if (NULL != skew_y && FLT_NEQ(*skew_y, 0.0))
728 _gt[4] = *skew_y;
729 }
730 }
731
732 rt_raster_destroy(rast);
733 rast = NULL;
734
735 RASTER_DEBUGF(3, "Applied geotransform: %f, %f, %f, %f, %f, %f",
736 _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
737 RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d",
738 _dim[0], _dim[1]);
739
740 if ( _dim[0] == 0 || _dim[1] == 0 ) {
741 rterror("rt_raster_gdal_warp: The width (%d) or height (%d) of the warped raster is zero", _dim[0], _dim[1]);
743 return NULL;
744 }
745
746 /* load VRT driver */
747 if (!rt_util_gdal_driver_registered("VRT")) {
748 RASTER_DEBUG(3, "Registering VRT driver");
749 GDALRegister_VRT();
750 arg->dst.destroy_drv = 1;
751 }
752 arg->dst.drv = GDALGetDriverByName("VRT");
753 if (NULL == arg->dst.drv) {
754 rterror("rt_raster_gdal_warp: Could not load the output GDAL VRT driver");
756 return NULL;
757 }
758
759 /* create dst dataset */
760 arg->dst.ds = GDALCreate(arg->dst.drv, "", _dim[0], _dim[1], 0, GDT_Byte, dst_options);
761 if (NULL == arg->dst.ds) {
762 rterror("rt_raster_gdal_warp: Could not create GDAL VRT dataset");
764 return NULL;
765 }
766
767 /* set dst srs */
768 if (arg->dst.srs != NULL) {
769 cplerr = GDALSetProjection(arg->dst.ds, arg->dst.srs);
770 if (cplerr != CE_None) {
771 rterror("rt_raster_gdal_warp: Could not set projection");
773 return NULL;
774 }
775 RASTER_DEBUGF(3, "Applied SRS: %s", GDALGetProjectionRef(arg->dst.ds));
776 }
777
778 /* set dst geotransform */
779 cplerr = GDALSetGeoTransform(arg->dst.ds, _gt);
780 if (cplerr != CE_None) {
781 rterror("rt_raster_gdal_warp: Could not set geotransform");
783 return NULL;
784 }
785
786 /* add bands to dst dataset */
787 numBands = rt_raster_get_num_bands(raster);
788 for (i = 0; i < numBands; i++) {
789 rtband = rt_raster_get_band(raster, i);
790 if (NULL == rtband) {
791 rterror("rt_raster_gdal_warp: Could not get band %d for adding to VRT dataset", i);
793 return NULL;
794 }
795
796 pt = rt_band_get_pixtype(rtband);
798 if (gdal_pt == GDT_Unknown)
799 rtwarn("rt_raster_gdal_warp: Unknown pixel type for band %d", i);
800
801 cplerr = GDALAddBand(arg->dst.ds, gdal_pt, NULL);
802 if (cplerr != CE_None) {
803 rterror("rt_raster_gdal_warp: Could not add band to VRT dataset");
805 return NULL;
806 }
807
808 /* get band to write data to */
809 band = NULL;
810 band = GDALGetRasterBand(arg->dst.ds, i + 1);
811 if (NULL == band) {
812 rterror("rt_raster_gdal_warp: Could not get GDAL band for additional processing");
814 return NULL;
815 }
816
817 /* set nodata */
818 if (rt_band_get_hasnodata_flag(rtband) != FALSE) {
819 rt_band_get_nodata(rtband, &nodata);
820 if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
821 rtwarn("rt_raster_gdal_warp: Could not set nodata value for band %d", i);
822 RASTER_DEBUGF(3, "nodata value set to %f", GDALGetRasterNoDataValue(band, NULL));
823 }
824 }
825
826 /* create transformation object */
827 arg->transform.arg.transform = arg->transform.arg.imgproj = GDALCreateGenImgProjTransformer2(
828 arg->src.ds, arg->dst.ds,
829 arg->transform.option.item
830 );
831 if (NULL == arg->transform.arg.transform) {
832 rterror("rt_raster_gdal_warp: Could not create GDAL transformation object");
834 return NULL;
835 }
836 arg->transform.func = GDALGenImgProjTransform;
837
838 /* use approximate transformation object */
839 if (max_err > 0.0) {
840 arg->transform.arg.transform = arg->transform.arg.approx = GDALCreateApproxTransformer(
841 GDALGenImgProjTransform,
842 arg->transform.arg.imgproj, max_err
843 );
844 if (NULL == arg->transform.arg.transform) {
845 rterror("rt_raster_gdal_warp: Could not create GDAL approximate transformation object");
847 return NULL;
848 }
849
850 arg->transform.func = GDALApproxTransform;
851 }
852
853 /* warp options */
854 arg->wopts = GDALCreateWarpOptions();
855 if (NULL == arg->wopts) {
856 rterror("rt_raster_gdal_warp: Could not create GDAL warp options object");
858 return NULL;
859 }
860
861 /* set options */
862 arg->wopts->eResampleAlg = resample_alg;
863 arg->wopts->hSrcDS = arg->src.ds;
864 arg->wopts->hDstDS = arg->dst.ds;
865 arg->wopts->pfnTransformer = arg->transform.func;
866 arg->wopts->pTransformerArg = arg->transform.arg.transform;
867 arg->wopts->papszWarpOptions = (char **) CPLMalloc(sizeof(char *) * 2);
868
869 arg->wopts->papszWarpOptions[0] = (char *) CPLMalloc(sizeof(char) * (strlen("INIT_DEST=NO_DATA") + 1));
870 strcpy(arg->wopts->papszWarpOptions[0], "INIT_DEST=NO_DATA");
871 arg->wopts->papszWarpOptions[1] = NULL;
872
873 /* set band mapping */
874 arg->wopts->nBandCount = numBands;
875 arg->wopts->panSrcBands = (int *) CPLMalloc(sizeof(int) * arg->wopts->nBandCount);
876 arg->wopts->panDstBands = (int *) CPLMalloc(sizeof(int) * arg->wopts->nBandCount);
877 for (i = 0; i < arg->wopts->nBandCount; i++)
878 arg->wopts->panDstBands[i] = arg->wopts->panSrcBands[i] = i + 1;
879
880 /*
881 * https://trac.osgeo.org/postgis/ticket/5881
882 * In order to call GDALWarp with BAND_INIT=NO_DATA we need to ensure
883 * that the src and dst rasters have nodata values and they are
884 * matched up nicely. This block used by tested with the hasnodata
885 * check on all src raster bands, but now we just do it every time
886 * because that makes sense (any warped raster is likely to have
887 * empty corners on output, and those corners need to be filled with
888 * some kind of NODATA value).
889 */
890 {
891 RASTER_DEBUG(3, "Setting nodata mapping");
892 arg->wopts->padfSrcNoDataReal = (double *) CPLMalloc(numBands * sizeof(double));
893 arg->wopts->padfDstNoDataReal = (double *) CPLMalloc(numBands * sizeof(double));
894 arg->wopts->padfSrcNoDataImag = (double *) CPLMalloc(numBands * sizeof(double));
895 arg->wopts->padfDstNoDataImag = (double *) CPLMalloc(numBands * sizeof(double));
896 if (
897 NULL == arg->wopts->padfSrcNoDataReal ||
898 NULL == arg->wopts->padfDstNoDataReal ||
899 NULL == arg->wopts->padfSrcNoDataImag ||
900 NULL == arg->wopts->padfDstNoDataImag
901 ) {
902 rterror("rt_raster_gdal_warp: Out of memory allocating nodata mapping");
904 return NULL;
905 }
906 for (i = 0; i < numBands; i++) {
907 band = rt_raster_get_band(raster, i);
908 if (!band) {
909 rterror("rt_raster_gdal_warp: Could not process bands for nodata values");
911 return NULL;
912 }
913
914 if (!rt_band_get_hasnodata_flag(band)) {
915 /*
916 based on line 1004 of gdalwarp.cpp
917 the problem is that there is a chance that this number is a legitimate value
918 */
919 arg->wopts->padfSrcNoDataReal[i] = -123456.789;
920 }
921 else {
922 rt_band_get_nodata(band, &(arg->wopts->padfSrcNoDataReal[i]));
923 }
924
925 arg->wopts->padfDstNoDataReal[i] = arg->wopts->padfSrcNoDataReal[i];
926 arg->wopts->padfDstNoDataImag[i] = arg->wopts->padfSrcNoDataImag[i] = 0.0;
927 RASTER_DEBUGF(4, "Mapped nodata value for band %d: %f (%f) => %f (%f)",
928 i,
929 arg->wopts->padfSrcNoDataReal[i], arg->wopts->padfSrcNoDataImag[i],
930 arg->wopts->padfDstNoDataReal[i], arg->wopts->padfDstNoDataImag[i]
931 );
932 }
933 }
934
935 /* warp raster */
936 RASTER_DEBUG(3, "Warping raster");
937 cplerr = GDALInitializeWarpedVRT(arg->dst.ds, arg->wopts);
938 if (cplerr != CE_None) {
939 rterror("rt_raster_gdal_warp: Could not warp raster");
941 return NULL;
942 }
943 /*
944 GDALSetDescription(arg->dst.ds, "/tmp/warped.vrt");
945 */
946 GDALFlushCache(arg->dst.ds);
947 RASTER_DEBUG(3, "Raster warped");
948
949 /* convert gdal dataset to raster */
950 RASTER_DEBUG(3, "Converting GDAL dataset to raster");
952
954
955 if (NULL == rast) {
956 rterror("rt_raster_gdal_warp: Could not warp raster");
957 return NULL;
958 }
959
960 /* substitute spatial, reset back to default */
961 if (subspatial) {
962 double gt[6] = {0, 1, 0, 0, 0, -1};
963 /* See http://trac.osgeo.org/postgis/ticket/2911 */
964 /* We should probably also tweak rotation here */
965 /* NOTE: the times 10 is because it was divided by 10 in a section above,
966 * I'm not sure the above division was needed */
967 gt[1] = _scale[0] * 10;
968 gt[5] = -1 * _scale[1] * 10;
969
972 }
973
974 RASTER_DEBUG(3, "done");
975
976 return rast;
977}
978
#define FALSE
Definition dbfopen.c:72
#define SRID_UNKNOWN
Unknown SRID value.
Definition liblwgeom.h:215
void rterror(const char *fmt,...) __attribute__((format(printf
Wrappers used for reporting errors and info.
void * rtalloc(size_t size)
Wrappers used for managing memory.
Definition rt_context.c:191
#define FLT_NEQ(x, y)
Definition librtcore.h:2435
#define RASTER_DEBUG(level, msg)
Definition librtcore.h:304
rt_errorstate rt_raster_cell_to_geopoint(rt_raster raster, double xr, double yr, double *xw, double *yw, double *gt)
Convert an xr, yr raster point to an xw, yw point on map.
Definition rt_raster.c:637
int32_t rt_raster_get_srid(rt_raster raster)
Get raster's SRID.
Definition rt_raster.c:360
#define RASTER_DEBUGF(level, msg,...)
Definition librtcore.h:308
void rt_raster_set_geotransform_matrix(rt_raster raster, double *gt)
Set raster's geotransform using 6-element array.
Definition rt_raster.c:609
void void void rtwarn(const char *fmt,...) __attribute__((format(printf
void rt_raster_set_scale(rt_raster raster, double scaleX, double scaleY)
Set scale in projection units.
Definition rt_raster.c:141
int rt_band_get_hasnodata_flag(rt_band band)
Get hasnodata flag value.
Definition rt_band.c:833
rt_errorstate rt_raster_geopoint_to_cell(rt_raster raster, double xw, double yw, double *xr, double *yr, double *igt)
Convert an xw, yw map point to a xr, yr raster point.
Definition rt_raster.c:686
void rt_raster_destroy(rt_raster raster)
Release memory associated to a raster.
Definition rt_raster.c:86
rt_pixtype
Definition librtcore.h:188
@ PT_END
Definition librtcore.h:201
void rt_raster_set_skews(rt_raster raster, double skewX, double skewY)
Set skews about the X and Y axis.
Definition rt_raster.c:172
rt_raster rt_raster_new(uint32_t width, uint32_t height)
Construct a raster with given dimensions.
Definition rt_raster.c:52
char * rt_util_gdal_convert_sr(const char *srs, int proj4)
Definition rt_util.c:322
GDALDataType rt_util_pixtype_to_gdal_datatype(rt_pixtype pt)
Convert rt_pixtype to GDALDataType.
Definition rt_util.c:213
rt_raster rt_raster_compute_skewed_raster(rt_envelope extent, double *skew, double *scale, double tolerance)
Definition rt_raster.c:869
#define FLT_EQ(x, y)
Definition librtcore.h:2436
rt_raster rt_raster_from_gdal_dataset(GDALDatasetH ds)
Return a raster from a GDAL dataset.
Definition rt_raster.c:2175
@ ES_NONE
Definition librtcore.h:182
uint16_t rt_raster_get_num_bands(rt_raster raster)
Definition rt_raster.c:376
int rt_util_gdal_driver_registered(const char *drv)
Definition rt_util.c:463
void rt_raster_set_srid(rt_raster raster, int32_t srid)
Set raster's SRID.
Definition rt_raster.c:367
rt_errorstate rt_band_get_nodata(rt_band band, double *nodata)
Get NODATA value.
Definition rt_band.c:2067
rt_pixtype rt_band_get_pixtype(rt_band band)
Return pixeltype of this band.
Definition rt_band.c:790
void rtdealloc(void *mem)
Definition rt_context.c:206
GDALDatasetH rt_raster_to_gdal_mem(rt_raster raster, const char *srs, uint32_t *bandNums, int *excludeNodataValues, int count, GDALDriverH *rtn_drv, int *destroy_rtn_drv)
Return GDAL dataset using GDAL MEM driver from raster.
Definition rt_raster.c:1813
void rt_raster_get_geotransform_matrix(rt_raster raster, double *gt)
Get 6-element array of raster geotransform matrix.
Definition rt_raster.c:588
void rt_raster_set_offsets(rt_raster raster, double x, double y)
Set insertion points in projection units.
Definition rt_raster.c:203
rt_band rt_raster_get_band(rt_raster raster, int bandNum)
Return Nth band, or NULL if unavailable.
Definition rt_raster.c:385
This library is the generic raster handling section of PostGIS.
static void _rti_warp_arg_destroy(_rti_warp_arg arg)
Definition rt_warp.c:104
struct _rti_warp_arg_t * _rti_warp_arg
Definition rt_warp.c:40
static _rti_warp_arg _rti_warp_arg_init()
Definition rt_warp.c:70
rt_raster rt_raster_gdal_warp(rt_raster raster, const char *src_srs, const char *dst_srs, double *scale_x, double *scale_y, int *width, int *height, double *ul_xw, double *ul_yw, double *grid_xw, double *grid_yw, double *skew_x, double *skew_y, GDALResampleAlg resample_alg, double max_err)
Return a warped raster using GDAL Warp API.
Definition rt_warp.c:177
struct _rti_warp_arg_t::@20 src
void * imgproj
Definition rt_warp.c:60
char ** item
Definition rt_warp.c:54
GDALDriverH drv
Definition rt_warp.c:44
struct _rti_warp_arg_t::@20 dst
void * approx
Definition rt_warp.c:61
struct _rti_warp_arg_t::@21::@22 option
GDALDatasetH ds
Definition rt_warp.c:45
struct _rti_warp_arg_t::@21::@23 arg
GDALTransformerFunc func
Definition rt_warp.c:64
void * transform
Definition rt_warp.c:59
GDALWarpOptions * wopts
Definition rt_warp.c:50
double MinX
Definition librtcore.h:167
double UpperLeftY
Definition librtcore.h:173
double UpperLeftX
Definition librtcore.h:172
double MaxX
Definition librtcore.h:168
double MinY
Definition librtcore.h:169
double MaxY
Definition librtcore.h:170
uint16_t width
Definition librtcore.h:2503
uint16_t height
Definition librtcore.h:2504