Return a warped raster using GDAL Warp API.
187 {
188 CPLErr cplerr;
189 char *dst_options[] = {"SUBCLASS=VRTWarpedDataset", NULL};
191
192 int hasnodata = 0;
193
194 GDALRasterBandH
band;
197 GDALDataType gdal_pt = GDT_Unknown;
198 double nodata = 0.0;
199
200 double _gt[6] = {0};
201 double dst_extent[4];
203
204 int _dim[2] = {0};
205 double _skew[2] = {0};
206 double _scale[2] = {0};
207 int ul_user = 0;
208
210 int i = 0;
211 int numBands = 0;
212
213
214 int subspatial = 0;
215
217
218 assert(NULL != raster);
219
220
222 if (arg == NULL) {
223 rterror(
"rt_raster_gdal_warp: Could not initialize internal variables");
224 return NULL;
225 }
226
227
228
229
230
231
232 if (max_err < 0.) max_err = 0.125;
234
235
236 if (src_srs != NULL) {
237
238 if (dst_srs != NULL && strcmp(src_srs, dst_srs) != 0) {
239 RASTER_DEBUG(4,
"Warp operation does include a reprojection");
242
244 rterror(
"rt_raster_gdal_warp: Could not convert srs values to GDAL accepted format");
246 return NULL;
247 }
248 }
249
250 else {
251 RASTER_DEBUG(4,
"Warp operation does NOT include reprojection");
252 }
253 }
254 else if (dst_srs != NULL) {
255
256 rterror(
"rt_raster_gdal_warp: SRS required for input raster if SRS provided for warped raster");
258 return NULL;
259 }
260
261
263 if (NULL == arg->
src.
ds) {
264 rterror(
"rt_raster_gdal_warp: Could not convert raster to GDAL MEM format");
266 return NULL;
267 }
269
270
271 if (
272 src_srs == NULL && dst_srs == NULL &&
274 ) {
276
277#if POSTGIS_DEBUG_LEVEL > 3
278 GDALGetGeoTransform(arg->
src.
ds, gt);
279 RASTER_DEBUGF(3,
"GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
280 gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
281#endif
282
283
285 RASTER_DEBUGF(3,
"raster geotransform: %f, %f, %f, %f, %f, %f",
286 gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
287
288
291 {
292 double ngt[6] = {166021.4431, 0.1, 0, 10000000.0000, 0, -0.1};
293
294 rtwarn(
"Raster has default geotransform. Adjusting metadata for use of GDAL Warp API");
295
296 subspatial = 1;
297
298 GDALSetGeoTransform(arg->
src.
ds, ngt);
299 GDALFlushCache(arg->
src.
ds);
300
301
304
305#if POSTGIS_DEBUG_LEVEL > 3
306 GDALGetGeoTransform(arg->
src.
ds, gt);
307 RASTER_DEBUGF(3,
"GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
308 gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
309#endif
310 }
311 }
312
313
317 if (NULL == arg->
transform.option.item) {
318 rterror(
"rt_raster_gdal_warp: Could not allocation memory for transform options");
320 return NULL;
321 }
322 memset(arg->
transform.option.item, 0,
sizeof(
char *) * (arg->
transform.option.len + 1));
323
324 for (i = 0; i < arg->
transform.option.len; i++) {
326 const char *lbl = i ? "DST_SRS=" : "SRC_SRS=";
327 size_t sz = sizeof(char) * (strlen(lbl) + 1);
328 if ( srs ) sz += strlen(srs);
330 if (NULL == arg->
transform.option.item[i]) {
331 rterror(
"rt_raster_gdal_warp: Could not allocation memory for transform options");
333 return NULL;
334 }
335 sprintf(arg->
transform.option.item[i],
"%s%s", lbl, srs ? srs :
"");
337 }
338 }
339 else
341
342
343 arg->
transform.arg.transform = GDALCreateGenImgProjTransformer2(arg->
src.
ds, NULL, arg->
transform.option.item);
344 if (NULL == arg->
transform.arg.transform) {
345 rterror(
"rt_raster_gdal_warp: Could not create GDAL transformation object for output dataset creation");
347 return NULL;
348 }
349
350
351 cplerr = GDALSuggestedWarpOutput2(
352 arg->
src.
ds, GDALGenImgProjTransform,
353 arg->
transform.arg.transform, _gt, &(_dim[0]), &(_dim[1]), dst_extent, 0);
354 if (cplerr != CE_None) {
355 rterror(
"rt_raster_gdal_warp: Could not get GDAL suggested warp output for output dataset creation");
357 return NULL;
358 }
359 GDALDestroyGenImgProjTransformer(arg->
transform.arg.transform);
361
362
363
364
365
366 _dim[0] = 0;
367 _dim[1] = 0;
368
369 RASTER_DEBUGF(3,
"Suggested geotransform: %f, %f, %f, %f, %f, %f",
370 _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
371
372
373 extent.
MinX = dst_extent[0];
374 extent.
MinY = dst_extent[1];
375 extent.
MaxX = dst_extent[2];
376 extent.
MaxY = dst_extent[3];
377
380
383
384
385 if (
386 ((NULL != scale_x) || (NULL != scale_y)) &&
387 ((NULL != width) || (NULL != height))
388 ) {
389 rterror(
"rt_raster_gdal_warp: Scale X/Y and width/height are mutually exclusive. Only provide one");
391 return NULL;
392 }
393
394
395 if (NULL != width) {
396 _dim[0] = abs(*width);
397 _scale[0] = fabs((extent.
MaxX - extent.
MinX) / ((
double) _dim[0]));
398 }
399
400 if (NULL != height) {
401 _dim[1] = abs(*height);
402 _scale[1] = fabs((extent.
MaxY - extent.
MinY) / ((
double) _dim[1]));
403 }
404
405
406 if (
407 ((NULL != scale_x) && (
FLT_NEQ(*scale_x, 0.0))) &&
408 ((NULL != scale_y) && (
FLT_NEQ(*scale_y, 0.0)))
409 ) {
410 _scale[0] = fabs(*scale_x);
411 _scale[1] = fabs(*scale_y);
412
413
414 if (subspatial) {
415
416
417
418
419 _scale[0] /= 10;
420 _scale[1] /= 10;
421 }
422 }
423 else if (
424 ((NULL != scale_x) && (NULL == scale_y)) ||
425 ((NULL == scale_x) && (NULL != scale_y))
426 ) {
427 rterror(
"rt_raster_gdal_warp: Both X and Y scale values must be provided for scale");
429 return NULL;
430 }
431
432
434 {
435 _scale[0] = fabs(_gt[1]);
436 _scale[1] = fabs(_gt[5]);
437 }
438
439 RASTER_DEBUGF(4,
"Using scale: %f x %f", _scale[0], -1 * _scale[1]);
440
441
442 if (NULL != skew_x) {
443 _skew[0] = *skew_x;
444
445
446
447
448
449 if (
450 NULL != scale_x &&
451 *scale_x < 0.
452 ) {
453 _skew[0] *= -1;
454 }
455 }
456 if (NULL != skew_y) {
457 _skew[1] = *skew_y;
458
459
460
461
462
463 if (
464 NULL != scale_y &&
465 *scale_y > 0.
466 ) {
467 _skew[1] *= -1;
468 }
469 }
470
472
473
475 {
477
479
481 extent,
482 _skew,
483 _scale,
484 0.01
485 );
486 if (skewedrast == NULL) {
487 rterror(
"rt_raster_gdal_warp: Could not compute skewed raster");
489 return NULL;
490 }
491
492 if (_dim[0] == 0)
493 _dim[0] = skewedrast->
width;
494 if (_dim[1] == 0)
495 _dim[1] = skewedrast->
height;
496
499
501 }
502
503
504 if (!_dim[0])
505 _dim[0] = (int) fmax((fabs(extent.
MaxX - extent.
MinX) + (_scale[0] / 2.)) / _scale[0], 1);
506 if (!_dim[1])
507 _dim[1] = (int) fmax((fabs(extent.
MaxY - extent.
MinY) + (_scale[1] / 2.)) / _scale[1], 1);
508
509
511 if (rast == NULL) {
512 rterror(
"rt_raster_gdal_warp: Out of memory allocating temporary raster");
514 return NULL;
515 }
516
517
521
523 RASTER_DEBUGF(3,
"Temp raster's geotransform: %f, %f, %f, %f, %f, %f",
524 _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
525 RASTER_DEBUGF(3,
"Temp raster's dimensions (width x height): %d x %d",
526 _dim[0], _dim[1]);
527
528
529 if (
530 NULL != ul_xw &&
531 NULL != ul_yw
532 ) {
533 ul_user = 1;
534
535 RASTER_DEBUGF(4,
"Using user-specified upper-left corner: %f, %f", *ul_xw, *ul_yw);
536
537
541 }
542 else if (
543 ((NULL != ul_xw) && (NULL == ul_yw)) ||
544 ((NULL == ul_xw) && (NULL != ul_yw))
545 ) {
546 rterror(
"rt_raster_gdal_warp: Both X and Y upper-left corner values must be provided");
549 return NULL;
550 }
551
552
553 if (
554 !ul_user && (
555 (NULL != grid_xw) || (NULL != grid_yw)
556 )
557 ) {
558
559 if (
560 ((NULL != grid_xw) && (NULL == grid_yw)) ||
561 ((NULL == grid_xw) && (NULL != grid_yw))
562 ) {
563 rterror(
"rt_raster_gdal_warp: Both X and Y alignment values must be provided");
566 return NULL;
567 }
568
569 RASTER_DEBUGF(4,
"Aligning extent to user-specified grid: %f, %f", *grid_xw, *grid_yw);
570
571 do {
572 double _r[2] = {0};
573 double _w[2] = {0};
574
575
577 RASTER_DEBUG(3,
"Skipping raster alignment as it is already aligned to grid");
578 break;
579 }
580
584
585
587 rast,
589 &(_r[0]), &(_r[1]),
590 NULL
592 rterror(
"rt_raster_gdal_warp: Could not compute raster pixel for spatial coordinates");
595 return NULL;
596 }
597
599 rast,
600 _r[0], _r[1],
601 &(_w[0]), &(_w[1]),
602 NULL
604 rterror(
"rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
605
608 return NULL;
609 }
610
611
613 if (NULL == width)
615 else if (NULL == scale_x) {
616 double _c[2] = {0};
617
619
620
622 rast,
624 &(_c[0]), &(_c[1]),
625 NULL
627 rterror(
"rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
630 return NULL;
631 }
632
633 rast->scaleX = fabs((_c[0] - _w[0]) / ((
double)
rast->width));
634 }
635 }
637 if (NULL == height)
639 else if (NULL == scale_y) {
640 double _c[2] = {0};
641
643
644
646 rast,
648 &(_c[0]), &(_c[1]),
649 NULL
651 rterror(
"rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
652
655 return NULL;
656 }
657
658 rast->scaleY = -1 * fabs((_c[1] - _w[1]) / ((
double)
rast->height));
659 }
660 }
661
664 }
665 while (0);
666 }
667
668
669
670
671
672
673 _dim[0] =
rast->width;
674 _dim[1] =
rast->height;
676
677
678 if ((
679 (NULL != scale_x) && (*scale_x < 0.)
680 ) || (
681 (NULL != scale_y) && (*scale_y > 0)
682 )) {
683 double _w[2] = {0};
684
685
686 if (
687 (NULL != scale_x) &&
688 (*scale_x < 0.)
689 ) {
691 rast,
693 &(_w[0]), &(_w[1]),
694 NULL
696 rterror(
"rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
699 return NULL;
700 }
701
702 _gt[0] = _w[0];
703 _gt[1] = *scale_x;
704
705
706 if (NULL != skew_x &&
FLT_NEQ(*skew_x, 0.0))
707 _gt[2] = *skew_x;
708 }
709
710 if (
711 (NULL != scale_y) &&
712 (*scale_y > 0)
713 ) {
715 rast,
717 &(_w[0]), &(_w[1]),
718 NULL
720 rterror(
"rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
723 return NULL;
724 }
725
726 _gt[3] = _w[1];
727 _gt[5] = *scale_y;
728
729
730 if (NULL != skew_y &&
FLT_NEQ(*skew_y, 0.0))
731 _gt[4] = *skew_y;
732 }
733 }
734
737
738 RASTER_DEBUGF(3,
"Applied geotransform: %f, %f, %f, %f, %f, %f",
739 _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
740 RASTER_DEBUGF(3,
"Raster dimensions (width x height): %d x %d",
741 _dim[0], _dim[1]);
742
743 if ( _dim[0] == 0 || _dim[1] == 0 ) {
744 rterror(
"rt_raster_gdal_warp: The width (%d) or height (%d) of the warped raster is zero", _dim[0], _dim[1]);
746 return NULL;
747 }
748
749
752 GDALRegister_VRT();
754 }
755 arg->
dst.
drv = GDALGetDriverByName(
"VRT");
756 if (NULL == arg->
dst.
drv) {
757 rterror(
"rt_raster_gdal_warp: Could not load the output GDAL VRT driver");
759 return NULL;
760 }
761
762
763 arg->
dst.
ds = GDALCreate(arg->
dst.
drv,
"", _dim[0], _dim[1], 0, GDT_Byte, dst_options);
764 if (NULL == arg->
dst.
ds) {
765 rterror(
"rt_raster_gdal_warp: Could not create GDAL VRT dataset");
767 return NULL;
768 }
769
770
771 if (arg->
dst.
srs != NULL) {
772 cplerr = GDALSetProjection(arg->
dst.
ds, arg->
dst.
srs);
773 if (cplerr != CE_None) {
774 rterror(
"rt_raster_gdal_warp: Could not set projection");
776 return NULL;
777 }
779 }
780
781
782 cplerr = GDALSetGeoTransform(arg->
dst.
ds, _gt);
783 if (cplerr != CE_None) {
784 rterror(
"rt_raster_gdal_warp: Could not set geotransform");
786 return NULL;
787 }
788
789
791 for (i = 0; i < numBands; i++) {
793 if (NULL == rtband) {
794 rterror(
"rt_raster_gdal_warp: Could not get band %d for adding to VRT dataset", i);
796 return NULL;
797 }
798
801 if (gdal_pt == GDT_Unknown)
802 rtwarn(
"rt_raster_gdal_warp: Unknown pixel type for band %d", i);
803
804 cplerr = GDALAddBand(arg->
dst.
ds, gdal_pt, NULL);
805 if (cplerr != CE_None) {
806 rterror(
"rt_raster_gdal_warp: Could not add band to VRT dataset");
808 return NULL;
809 }
810
811
813 band = GDALGetRasterBand(arg->
dst.
ds, i + 1);
814 if (NULL == band) {
815 rterror(
"rt_raster_gdal_warp: Could not get GDAL band for additional processing");
817 return NULL;
818 }
819
820
822 hasnodata = 1;
824 if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
825 rtwarn(
"rt_raster_gdal_warp: Could not set nodata value for band %d", i);
826 RASTER_DEBUGF(3,
"nodata value set to %f", GDALGetRasterNoDataValue(band, NULL));
827 }
828 }
829
830
831 arg->
transform.arg.transform = arg->
transform.arg.imgproj = GDALCreateGenImgProjTransformer2(
834 );
835 if (NULL == arg->
transform.arg.transform) {
836 rterror(
"rt_raster_gdal_warp: Could not create GDAL transformation object");
838 return NULL;
839 }
840 arg->
transform.func = GDALGenImgProjTransform;
841
842
843 if (max_err > 0.0) {
844 arg->
transform.arg.transform = arg->
transform.arg.approx = GDALCreateApproxTransformer(
845 GDALGenImgProjTransform,
847 );
848 if (NULL == arg->
transform.arg.transform) {
849 rterror(
"rt_raster_gdal_warp: Could not create GDAL approximate transformation object");
851 return NULL;
852 }
853
854 arg->
transform.func = GDALApproxTransform;
855 }
856
857
858 arg->
wopts = GDALCreateWarpOptions();
859 if (NULL == arg->
wopts) {
860 rterror(
"rt_raster_gdal_warp: Could not create GDAL warp options object");
862 return NULL;
863 }
864
865
866 arg->
wopts->eResampleAlg = resample_alg;
871 arg->
wopts->papszWarpOptions = (
char **) CPLMalloc(
sizeof(
char *) * 2);
872 arg->
wopts->papszWarpOptions[0] = (
char *) CPLMalloc(
sizeof(
char) * (strlen(
"INIT_DEST=NO_DATA") + 1));
873 strcpy(arg->
wopts->papszWarpOptions[0],
"INIT_DEST=NO_DATA");
874 arg->
wopts->papszWarpOptions[1] = NULL;
875
876
877 arg->
wopts->nBandCount = numBands;
878 arg->
wopts->panSrcBands = (
int *) CPLMalloc(
sizeof(
int) * arg->
wopts->nBandCount);
879 arg->
wopts->panDstBands = (
int *) CPLMalloc(
sizeof(
int) * arg->
wopts->nBandCount);
880 for (i = 0; i < arg->
wopts->nBandCount; i++)
881 arg->
wopts->panDstBands[i] = arg->
wopts->panSrcBands[i] = i + 1;
882
883
884 if (hasnodata) {
886 arg->
wopts->padfSrcNoDataReal = (
double *) CPLMalloc(numBands *
sizeof(
double));
887 arg->
wopts->padfDstNoDataReal = (
double *) CPLMalloc(numBands *
sizeof(
double));
888 arg->
wopts->padfSrcNoDataImag = (
double *) CPLMalloc(numBands *
sizeof(
double));
889 arg->
wopts->padfDstNoDataImag = (
double *) CPLMalloc(numBands *
sizeof(
double));
890 if (
891 NULL == arg->
wopts->padfSrcNoDataReal ||
892 NULL == arg->
wopts->padfDstNoDataReal ||
893 NULL == arg->
wopts->padfSrcNoDataImag ||
894 NULL == arg->
wopts->padfDstNoDataImag
895 ) {
896 rterror(
"rt_raster_gdal_warp: Out of memory allocating nodata mapping");
898 return NULL;
899 }
900 for (i = 0; i < numBands; i++) {
902 if (!band) {
903 rterror(
"rt_raster_gdal_warp: Could not process bands for nodata values");
905 return NULL;
906 }
907
909
910
911
912
913 arg->
wopts->padfSrcNoDataReal[i] = -123456.789;
914 }
915 else {
917 }
918
919 arg->
wopts->padfDstNoDataReal[i] = arg->
wopts->padfSrcNoDataReal[i];
920 arg->
wopts->padfDstNoDataImag[i] = arg->
wopts->padfSrcNoDataImag[i] = 0.0;
921 RASTER_DEBUGF(4,
"Mapped nodata value for band %d: %f (%f) => %f (%f)",
922 i,
923 arg->
wopts->padfSrcNoDataReal[i], arg->
wopts->padfSrcNoDataImag[i],
924 arg->
wopts->padfDstNoDataReal[i], arg->
wopts->padfDstNoDataImag[i]
925 );
926 }
927 }
928
929
931 cplerr = GDALInitializeWarpedVRT(arg->
dst.
ds, arg->
wopts);
932 if (cplerr != CE_None) {
933 rterror(
"rt_raster_gdal_warp: Could not warp raster");
935 return NULL;
936 }
937
938
939
940 GDALFlushCache(arg->
dst.
ds);
942
943
946
948
949 if (NULL == rast) {
950 rterror(
"rt_raster_gdal_warp: Could not warp raster");
951 return NULL;
952 }
953
954
955 if (subspatial) {
956 double gt[6] = {0, 1, 0, 0, 0, -1};
957
958
959
960
961 gt[1] = _scale[0] * 10;
962 gt[5] = -1 * _scale[1] * 10;
963
966 }
967
969
971}
#define SRID_UNKNOWN
Unknown SRID value.
void rterror(const char *fmt,...)
Wrappers used for reporting errors and info.
void * rtalloc(size_t size)
Wrappers used for managing memory.
#define RASTER_DEBUG(level, msg)
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.
int32_t rt_raster_get_srid(rt_raster raster)
Get raster's SRID.
#define RASTER_DEBUGF(level, msg,...)
void rt_raster_set_geotransform_matrix(rt_raster raster, double *gt)
Set raster's geotransform using 6-element array.
void rt_raster_set_scale(rt_raster raster, double scaleX, double scaleY)
Set scale in projection units.
int rt_band_get_hasnodata_flag(rt_band band)
Get hasnodata flag value.
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.
void rt_raster_destroy(rt_raster raster)
Release memory associated to a raster.
void rt_raster_set_skews(rt_raster raster, double skewX, double skewY)
Set skews about the X and Y axis.
rt_raster rt_raster_new(uint32_t width, uint32_t height)
Construct a raster with given dimensions.
char * rt_util_gdal_convert_sr(const char *srs, int proj4)
GDALDataType rt_util_pixtype_to_gdal_datatype(rt_pixtype pt)
Convert rt_pixtype to GDALDataType.
rt_raster rt_raster_compute_skewed_raster(rt_envelope extent, double *skew, double *scale, double tolerance)
rt_raster rt_raster_from_gdal_dataset(GDALDatasetH ds)
Return a raster from a GDAL dataset.
void rtwarn(const char *fmt,...)
uint16_t rt_raster_get_num_bands(rt_raster raster)
int rt_util_gdal_driver_registered(const char *drv)
void rt_raster_set_srid(rt_raster raster, int32_t srid)
Set raster's SRID.
rt_errorstate rt_band_get_nodata(rt_band band, double *nodata)
Get NODATA value.
rt_pixtype rt_band_get_pixtype(rt_band band)
Return pixeltype of this band.
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.
void rt_raster_get_geotransform_matrix(rt_raster raster, double *gt)
Get 6-element array of raster geotransform matrix.
void rt_raster_set_offsets(rt_raster raster, double x, double y)
Set insertion points in projection units.
rt_band rt_raster_get_band(rt_raster raster, int bandNum)
Return Nth band, or NULL if unavailable.
static void _rti_warp_arg_destroy(_rti_warp_arg arg)
static _rti_warp_arg _rti_warp_arg_init()
struct _rti_warp_arg_t::@14 dst
struct _rti_warp_arg_t::@14 src