Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/vrt/vrtwarped.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Implementation of VRTWarpedRasterBand *and VRTWarpedDataset.
5
 * Author:   Frank Warmerdam <warmerdam@pobox.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "vrtdataset.h"
16
17
#include <stddef.h>
18
#include <stdio.h>
19
#include <stdlib.h>
20
#include <string.h>
21
#include <algorithm>
22
#include <map>
23
24
// Suppress deprecation warning for GDALOpenVerticalShiftGrid and
25
// GDALApplyVerticalShiftGrid
26
#ifndef CPL_WARN_DEPRECATED_GDALOpenVerticalShiftGrid
27
#define CPL_WARN_DEPRECATED_GDALOpenVerticalShiftGrid(x)
28
#define CPL_WARN_DEPRECATED_GDALApplyVerticalShiftGrid(x)
29
#endif
30
31
#include "cpl_conv.h"
32
#include "cpl_error.h"
33
#include "cpl_minixml.h"
34
#include "cpl_progress.h"
35
#include "cpl_string.h"
36
#include "cpl_vsi.h"
37
#include "gdal.h"
38
#include "gdal_alg.h"
39
#include "gdal_alg_priv.h"
40
#include "gdal_priv.h"
41
#include "gdalwarper.h"
42
#include "ogr_geometry.h"
43
44
/************************************************************************/
45
/*                      GDALAutoCreateWarpedVRT()                       */
46
/************************************************************************/
47
48
/**
49
 * Create virtual warped dataset automatically.
50
 *
51
 * This function will create a warped virtual file representing the
52
 * input image warped into the target coordinate system.  A GenImgProj
53
 * transformation is created to accomplish any required GCP/Geotransform
54
 * warp and reprojection to the target coordinate system.  The output virtual
55
 * dataset will be "northup" in the target coordinate system.   The
56
 * GDALSuggestedWarpOutput() function is used to determine the bounds and
57
 * resolution of the output virtual file which should be large enough to
58
 * include all the input image
59
 *
60
 * If you want to create an alpha band if the source dataset has none, set
61
 * psOptionsIn->nDstAlphaBand = GDALGetRasterCount(hSrcDS) + 1.
62
 *
63
 * Note that the constructed GDALDatasetH will acquire one or more references
64
 * to the passed in hSrcDS.  Reference counting semantics on the source
65
 * dataset should be honoured.  That is, don't just GDALClose() it unless it
66
 * was opened with GDALOpenShared().
67
 *
68
 * It is possible to "transfer" the ownership of the source dataset
69
 * to the warped dataset in the following way:
70
 *
71
 * \code{.c}
72
 *      GDALDatasetH src_ds = GDALOpen("source.tif");
73
 *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
74
 *      GDALReleaseDataset(src_ds); // src_ds is not "owned" fully by warped_ds.
75
 * Do NOT use GDALClose(src_ds) here
76
 *      ...
77
 *      ...
78
 *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
79
 * \endcode
80
 *
81
 * Traditional nested calls are also possible of course:
82
 *
83
 * \code{.c}
84
 *      GDALDatasetH src_ds = GDALOpen("source.tif");
85
 *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
86
 *      ...
87
 *      ...
88
 *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
89
 *      GDALReleaseDataset(src_ds); // or GDALClose(src_ds);
90
 * \endcode
91
 *
92
 * The returned dataset will have no associated filename for itself.  If you
93
 * want to write the virtual dataset description to a file, use the
94
 * GDALSetDescription() function (or SetDescription() method) on the dataset
95
 * to assign a filename before it is closed.
96
 *
97
 * @param hSrcDS The source dataset.
98
 *
99
 * @param pszSrcWKT The coordinate system of the source image.  If NULL, it
100
 * will be read from the source image.
101
 *
102
 * @param pszDstWKT The coordinate system to convert to.  If NULL no change
103
 * of coordinate system will take place.
104
 *
105
 * @param eResampleAlg One of GRA_NearestNeighbour, GRA_Bilinear, GRA_Cubic,
106
 * GRA_CubicSpline, GRA_Lanczos, GRA_Average, GRA_RMS or GRA_Mode.
107
 * Controls the sampling method used.
108
 *
109
 * @param dfMaxError Maximum error measured in input pixels that is allowed in
110
 * approximating the transformation (0.0 for exact calculations).
111
 *
112
 * @param psOptionsIn Additional warp options, normally NULL.
113
 *
114
 * @return NULL on failure, or a new virtual dataset handle on success.
115
 */
116
117
GDALDatasetH CPL_STDCALL
118
GDALAutoCreateWarpedVRT(GDALDatasetH hSrcDS, const char *pszSrcWKT,
119
                        const char *pszDstWKT, GDALResampleAlg eResampleAlg,
120
                        double dfMaxError, const GDALWarpOptions *psOptionsIn)
121
0
{
122
0
    return GDALAutoCreateWarpedVRTEx(hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg,
123
0
                                     dfMaxError, psOptionsIn, nullptr);
124
0
}
125
126
/************************************************************************/
127
/*                     GDALAutoCreateWarpedVRTEx()                      */
128
/************************************************************************/
129
130
/**
131
 * Create virtual warped dataset automatically.
132
 *
133
 * Compared to GDALAutoCreateWarpedVRT() this function adds one extra
134
 * argument: options to be passed to GDALCreateGenImgProjTransformer2().
135
 *
136
 * @since 3.2
137
 */
138
139
GDALDatasetH CPL_STDCALL GDALAutoCreateWarpedVRTEx(
140
    GDALDatasetH hSrcDS, const char *pszSrcWKT, const char *pszDstWKT,
141
    GDALResampleAlg eResampleAlg, double dfMaxError,
142
    const GDALWarpOptions *psOptionsIn, CSLConstList papszTransformerOptions)
143
0
{
144
0
    VALIDATE_POINTER1(hSrcDS, "GDALAutoCreateWarpedVRT", nullptr);
145
146
    /* -------------------------------------------------------------------- */
147
    /*      Populate the warp options.                                      */
148
    /* -------------------------------------------------------------------- */
149
0
    GDALWarpOptions *psWO = nullptr;
150
0
    if (psOptionsIn != nullptr)
151
0
        psWO = GDALCloneWarpOptions(psOptionsIn);
152
0
    else
153
0
        psWO = GDALCreateWarpOptions();
154
155
0
    psWO->eResampleAlg = eResampleAlg;
156
157
0
    psWO->hSrcDS = hSrcDS;
158
159
0
    GDALWarpInitDefaultBandMapping(psWO, GDALGetRasterCount(hSrcDS));
160
161
    /* -------------------------------------------------------------------- */
162
    /*      Setup no data values (if not done in psOptionsIn)               */
163
    /* -------------------------------------------------------------------- */
164
0
    if (psWO->padfSrcNoDataReal == nullptr &&
165
0
        psWO->padfDstNoDataReal == nullptr && psWO->nSrcAlphaBand == 0)
166
0
    {
167
        // If none of the provided input nodata values can be represented in the
168
        // data type of the corresponding source band, ignore them.
169
0
        int nCountInvalidSrcNoDataReal = 0;
170
0
        for (int i = 0; i < psWO->nBandCount; i++)
171
0
        {
172
0
            GDALRasterBandH rasterBand =
173
0
                GDALGetRasterBand(psWO->hSrcDS, psWO->panSrcBands[i]);
174
175
0
            int hasNoDataValue;
176
0
            double noDataValue =
177
0
                GDALGetRasterNoDataValue(rasterBand, &hasNoDataValue);
178
179
0
            if (hasNoDataValue &&
180
0
                !GDALIsValueExactAs(noDataValue,
181
0
                                    GDALGetRasterDataType(rasterBand)))
182
0
            {
183
0
                nCountInvalidSrcNoDataReal++;
184
0
            }
185
0
        }
186
187
0
        if (nCountInvalidSrcNoDataReal != psWO->nBandCount)
188
0
        {
189
0
            for (int i = 0; i < psWO->nBandCount; i++)
190
0
            {
191
0
                GDALRasterBandH rasterBand =
192
0
                    GDALGetRasterBand(psWO->hSrcDS, psWO->panSrcBands[i]);
193
194
0
                int hasNoDataValue;
195
0
                double noDataValue =
196
0
                    GDALGetRasterNoDataValue(rasterBand, &hasNoDataValue);
197
198
0
                if (hasNoDataValue)
199
0
                {
200
                    // Check if the nodata value is out of range
201
0
                    int bClamped = FALSE;
202
0
                    int bRounded = FALSE;
203
0
                    CPL_IGNORE_RET_VAL(GDALAdjustValueToDataType(
204
0
                        GDALGetRasterDataType(rasterBand), noDataValue,
205
0
                        &bClamped, &bRounded));
206
0
                    if (!bClamped)
207
0
                    {
208
0
                        GDALWarpInitNoDataReal(psWO, -1e10);
209
0
                        if (psWO->padfSrcNoDataReal != nullptr &&
210
0
                            psWO->padfDstNoDataReal != nullptr)
211
0
                        {
212
0
                            psWO->padfSrcNoDataReal[i] = noDataValue;
213
0
                            psWO->padfDstNoDataReal[i] = noDataValue;
214
0
                        }
215
0
                    }
216
0
                }
217
0
            }
218
0
        }
219
220
0
        if (psWO->padfDstNoDataReal != nullptr)
221
0
        {
222
0
            if (CSLFetchNameValue(psWO->papszWarpOptions, "INIT_DEST") ==
223
0
                nullptr)
224
0
            {
225
0
                psWO->papszWarpOptions = CSLSetNameValue(
226
0
                    psWO->papszWarpOptions, "INIT_DEST", "NO_DATA");
227
0
            }
228
0
        }
229
0
    }
230
231
    /* -------------------------------------------------------------------- */
232
    /*      Create the transformer.                                         */
233
    /* -------------------------------------------------------------------- */
234
0
    psWO->pfnTransformer = GDALGenImgProjTransform;
235
236
0
    char **papszOptions = nullptr;
237
0
    if (pszSrcWKT != nullptr)
238
0
        papszOptions = CSLSetNameValue(papszOptions, "SRC_SRS", pszSrcWKT);
239
0
    if (pszDstWKT != nullptr)
240
0
        papszOptions = CSLSetNameValue(papszOptions, "DST_SRS", pszDstWKT);
241
0
    papszOptions = CSLMerge(papszOptions, papszTransformerOptions);
242
0
    psWO->pTransformerArg =
243
0
        GDALCreateGenImgProjTransformer2(psWO->hSrcDS, nullptr, papszOptions);
244
0
    CSLDestroy(papszOptions);
245
246
0
    if (psWO->pTransformerArg == nullptr)
247
0
    {
248
0
        GDALDestroyWarpOptions(psWO);
249
0
        return nullptr;
250
0
    }
251
252
    /* -------------------------------------------------------------------- */
253
    /*      Figure out the desired output bounds and resolution.            */
254
    /* -------------------------------------------------------------------- */
255
0
    double adfDstGeoTransform[6] = {0.0};
256
0
    int nDstPixels = 0;
257
0
    int nDstLines = 0;
258
0
    CPLErr eErr = GDALSuggestedWarpOutput(
259
0
        hSrcDS, psWO->pfnTransformer, psWO->pTransformerArg, adfDstGeoTransform,
260
0
        &nDstPixels, &nDstLines);
261
0
    if (eErr != CE_None)
262
0
    {
263
0
        GDALDestroyTransformer(psWO->pTransformerArg);
264
0
        GDALDestroyWarpOptions(psWO);
265
0
        return nullptr;
266
0
    }
267
268
0
    if (nDstPixels <= 0 || nDstLines <= 0)
269
0
    {
270
        // Can happen when the extent of the source in the target CRS is
271
        // degenerate, for example when the source crosses the antimeridian
272
        // and the suggested longitude span covers the whole globe, so that
273
        // the suggested latitude span rounds down to zero line.
274
0
        CPLError(CE_Failure, CPLE_AppDefined,
275
0
                 "Computed output size is %d x %d. The extent of the source "
276
0
                 "in the target CRS is degenerate.",
277
0
                 nDstPixels, nDstLines);
278
0
        GDALDestroyTransformer(psWO->pTransformerArg);
279
0
        GDALDestroyWarpOptions(psWO);
280
0
        return nullptr;
281
0
    }
282
283
    /* -------------------------------------------------------------------- */
284
    /*      Update the transformer to include an output geotransform        */
285
    /*      back to pixel/line coordinates.                                 */
286
    /*                                                                      */
287
    /* -------------------------------------------------------------------- */
288
0
    GDALSetGenImgProjTransformerDstGeoTransform(psWO->pTransformerArg,
289
0
                                                adfDstGeoTransform);
290
291
    /* -------------------------------------------------------------------- */
292
    /*      Do we want to apply an approximating transformation?            */
293
    /* -------------------------------------------------------------------- */
294
0
    if (dfMaxError > 0.0)
295
0
    {
296
0
        psWO->pTransformerArg = GDALCreateApproxTransformer(
297
0
            psWO->pfnTransformer, psWO->pTransformerArg, dfMaxError);
298
0
        psWO->pfnTransformer = GDALApproxTransform;
299
0
        GDALApproxTransformerOwnsSubtransformer(psWO->pTransformerArg, TRUE);
300
0
    }
301
302
    /* -------------------------------------------------------------------- */
303
    /*      Create the VRT file.                                            */
304
    /* -------------------------------------------------------------------- */
305
0
    GDALDatasetH hDstDS = GDALCreateWarpedVRT(hSrcDS, nDstPixels, nDstLines,
306
0
                                              adfDstGeoTransform, psWO);
307
308
0
    GDALDestroyWarpOptions(psWO);
309
310
0
    if (hDstDS != nullptr)
311
0
    {
312
0
        if (pszDstWKT != nullptr)
313
0
            GDALSetProjection(hDstDS, pszDstWKT);
314
0
        else if (pszSrcWKT != nullptr)
315
0
            GDALSetProjection(hDstDS, pszSrcWKT);
316
0
        else if (GDALGetGCPCount(hSrcDS) > 0)
317
0
            GDALSetProjection(hDstDS, GDALGetGCPProjection(hSrcDS));
318
0
        else
319
0
            GDALSetProjection(hDstDS, GDALGetProjectionRef(hSrcDS));
320
0
    }
321
322
0
    return hDstDS;
323
0
}
324
325
/************************************************************************/
326
/*                        GDALCreateWarpedVRT()                         */
327
/************************************************************************/
328
329
/**
330
 * Create virtual warped dataset.
331
 *
332
 * This function will create a warped virtual file representing the
333
 * input image warped based on a provided transformation.  Output bounds
334
 * and resolution are provided explicitly.
335
 *
336
 * If you want to create an alpha band if the source dataset has none, set
337
 * psOptions->nDstAlphaBand = GDALGetRasterCount(hSrcDS) + 1.
338
 *
339
 * Note that the constructed GDALDatasetH will acquire one or more references
340
 * to the passed in hSrcDS.  Reference counting semantics on the source
341
 * dataset should be honoured.  That is, don't just GDALClose() it unless it
342
 * was opened with GDALOpenShared().
343
 *
344
 * It is possible to "transfer" the ownership of the source dataset
345
 * to the warped dataset in the following way:
346
 *
347
 * \code{.c}
348
 *      GDALDatasetH src_ds = GDALOpen("source.tif");
349
 *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
350
 *      GDALReleaseDataset(src_ds); // src_ds is not "owned" fully by warped_ds.
351
 * Do NOT use GDALClose(src_ds) here
352
 *      ...
353
 *      ...
354
 *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
355
 * \endcode
356
 *
357
 * Traditional nested calls are also possible of course:
358
 *
359
 * \code{.c}
360
 *      GDALDatasetH src_ds = GDALOpen("source.tif");
361
 *      GDALDatasetH warped_ds = GDALAutoCreateWarpedVRT( src_ds, ... );
362
 *      ...
363
 *      ...
364
 *      GDALReleaseDataset(warped_ds); // or GDALClose(warped_ds);
365
 *      GDALReleaseDataset(src_ds); // or GDALClose(src_ds);
366
 * \endcode
367
 *
368
 * The returned dataset will have no associated filename for itself.  If you
369
 * want to write the virtual dataset description to a file, use the
370
 * GDALSetDescription() function (or SetDescription() method) on the dataset
371
 * to assign a filename before it is closed.
372
 *
373
 * @param hSrcDS The source dataset.
374
 *
375
 * @param nPixels Width of the virtual warped dataset to create
376
 *
377
 * @param nLines Height of the virtual warped dataset to create
378
 *
379
 * @param padfGeoTransform Geotransform matrix of the virtual warped dataset
380
 * to create
381
 *
382
 * @param psOptions Warp options. Must be different from NULL.
383
 *
384
 * @return NULL on failure, or a new virtual dataset handle on success.
385
 */
386
387
GDALDatasetH CPL_STDCALL GDALCreateWarpedVRT(GDALDatasetH hSrcDS, int nPixels,
388
                                             int nLines,
389
                                             const double *padfGeoTransform,
390
                                             GDALWarpOptions *psOptions)
391
392
0
{
393
0
    VALIDATE_POINTER1(hSrcDS, "GDALCreateWarpedVRT", nullptr);
394
0
    VALIDATE_POINTER1(psOptions, "GDALCreateWarpedVRT", nullptr);
395
396
0
    if (nPixels <= 0 || nLines <= 0)
397
0
    {
398
0
        CPLError(CE_Failure, CPLE_IllegalArg,
399
0
                 "Attempt to create %dx%d warped VRT is illegal, sizes must "
400
0
                 "be larger than zero.",
401
0
                 nPixels, nLines);
402
0
        return nullptr;
403
0
    }
404
405
    /* -------------------------------------------------------------------- */
406
    /*      Create the VRTDataset and populate it with bands.               */
407
    /* -------------------------------------------------------------------- */
408
0
    VRTWarpedDataset *poDS = new VRTWarpedDataset(nPixels, nLines);
409
410
    // Call this before assigning hDstDS
411
0
    GDALWarpResolveWorkingDataType(psOptions);
412
413
0
    psOptions->hDstDS = poDS;
414
0
    poDS->SetGeoTransform(GDALGeoTransform(padfGeoTransform));
415
416
0
    for (int i = 0; i < psOptions->nBandCount; i++)
417
0
    {
418
0
        int nDstBand = psOptions->panDstBands[i];
419
0
        while (poDS->GetRasterCount() < nDstBand)
420
0
        {
421
0
            poDS->AddBand(psOptions->eWorkingDataType, nullptr);
422
0
        }
423
424
0
        VRTWarpedRasterBand *poBand =
425
0
            static_cast<VRTWarpedRasterBand *>(poDS->GetRasterBand(nDstBand));
426
0
        GDALRasterBand *poSrcBand = static_cast<GDALRasterBand *>(
427
0
            GDALGetRasterBand(hSrcDS, psOptions->panSrcBands[i]));
428
429
0
        poBand->CopyCommonInfoFrom(poSrcBand);
430
0
    }
431
432
0
    while (poDS->GetRasterCount() < psOptions->nDstAlphaBand)
433
0
    {
434
0
        poDS->AddBand(psOptions->eWorkingDataType, nullptr);
435
0
    }
436
0
    if (psOptions->nDstAlphaBand)
437
0
    {
438
0
        poDS->GetRasterBand(psOptions->nDstAlphaBand)
439
0
            ->SetColorInterpretation(GCI_AlphaBand);
440
0
    }
441
442
    /* -------------------------------------------------------------------- */
443
    /*      Initialize the warp on the VRTWarpedDataset.                    */
444
    /* -------------------------------------------------------------------- */
445
0
    const CPLErr eErr = poDS->Initialize(psOptions);
446
0
    if (eErr == CE_Failure)
447
0
    {
448
0
        psOptions->hDstDS = nullptr;
449
0
        delete poDS;
450
0
        return nullptr;
451
0
    }
452
453
0
    return poDS;
454
0
}
455
456
/*! @cond Doxygen_Suppress */
457
458
/************************************************************************/
459
/* ==================================================================== */
460
/*                          VRTWarpedDataset                            */
461
/* ==================================================================== */
462
/************************************************************************/
463
464
/************************************************************************/
465
/*                          VRTWarpedDataset()                          */
466
/************************************************************************/
467
468
VRTWarpedDataset::VRTWarpedDataset(int nXSize, int nYSize, int nBlockXSize,
469
                                   int nBlockYSize)
470
0
    : VRTDataset(nXSize, nYSize,
471
0
                 nBlockXSize > 0 ? nBlockXSize : std::min(nXSize, 512),
472
0
                 nBlockYSize > 0 ? nBlockYSize : std::min(nYSize, 128)),
473
0
      m_poWarper(nullptr), m_nSrcOvrLevel(-2)
474
0
{
475
0
    eAccess = GA_Update;
476
0
    DisableReadWriteMutex();
477
0
}
478
479
/************************************************************************/
480
/*                         ~VRTWarpedDataset()                          */
481
/************************************************************************/
482
483
VRTWarpedDataset::~VRTWarpedDataset()
484
485
0
{
486
0
    VRTWarpedDataset::FlushCache(true);
487
0
    VRTWarpedDataset::CloseDependentDatasets();
488
0
}
489
490
/************************************************************************/
491
/*                       CloseDependentDatasets()                       */
492
/************************************************************************/
493
494
int VRTWarpedDataset::CloseDependentDatasets()
495
0
{
496
0
    bool bHasDroppedRef = CPL_TO_BOOL(VRTDataset::CloseDependentDatasets());
497
498
    /* -------------------------------------------------------------------- */
499
    /*      Cleanup overviews.                                              */
500
    /* -------------------------------------------------------------------- */
501
0
    for (auto &poDS : m_apoOverviews)
502
0
    {
503
0
        if (poDS && poDS->Release())
504
0
        {
505
0
            bHasDroppedRef = true;
506
0
        }
507
0
    }
508
509
0
    m_apoOverviews.clear();
510
511
    /* -------------------------------------------------------------------- */
512
    /*      Cleanup warper if one is in effect.                             */
513
    /* -------------------------------------------------------------------- */
514
0
    if (m_poWarper != nullptr)
515
0
    {
516
0
        const GDALWarpOptions *psWO = m_poWarper->GetOptions();
517
518
        /* --------------------------------------------------------------------
519
         */
520
        /*      We take care to only call GDALClose() on psWO->hSrcDS if the */
521
        /*      reference count drops to zero.  This is makes it so that we */
522
        /*      can operate reference counting semantics more-or-less */
523
        /*      properly even if the dataset isn't open in shared mode, */
524
        /*      though we require that the caller also honour the reference */
525
        /*      counting semantics even though it isn't a shared dataset. */
526
        /* --------------------------------------------------------------------
527
         */
528
0
        if (psWO != nullptr && psWO->hSrcDS != nullptr)
529
0
        {
530
0
            if (GDALReleaseDataset(psWO->hSrcDS))
531
0
            {
532
0
                bHasDroppedRef = true;
533
0
            }
534
0
        }
535
536
        /* --------------------------------------------------------------------
537
         */
538
        /*      We are responsible for cleaning up the transformer ourselves. */
539
        /* --------------------------------------------------------------------
540
         */
541
0
        if (psWO != nullptr && psWO->pTransformerArg != nullptr)
542
0
            GDALDestroyTransformer(psWO->pTransformerArg);
543
544
0
        delete m_poWarper;
545
0
        m_poWarper = nullptr;
546
0
    }
547
548
    /* -------------------------------------------------------------------- */
549
    /*      Destroy the raster bands if they exist.                         */
550
    /* -------------------------------------------------------------------- */
551
0
    for (int iBand = 0; iBand < nBands; iBand++)
552
0
    {
553
0
        delete papoBands[iBand];
554
0
    }
555
0
    nBands = 0;
556
557
0
    return bHasDroppedRef;
558
0
}
559
560
/************************************************************************/
561
/*                        SetSrcOverviewLevel()                         */
562
/************************************************************************/
563
564
CPLErr VRTWarpedDataset::SetMetadataItem(const char *pszName,
565
                                         const char *pszValue,
566
                                         const char *pszDomain)
567
568
0
{
569
0
    if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
570
0
        EQUAL(pszName, "SrcOvrLevel"))
571
0
    {
572
0
        const int nOldValue = m_nSrcOvrLevel;
573
0
        if (pszValue == nullptr || EQUAL(pszValue, "AUTO"))
574
0
            m_nSrcOvrLevel = -2;
575
0
        else if (STARTS_WITH_CI(pszValue, "AUTO-"))
576
0
            m_nSrcOvrLevel = -2 - atoi(pszValue + 5);
577
0
        else if (EQUAL(pszValue, "NONE"))
578
0
            m_nSrcOvrLevel = -1;
579
0
        else if (CPLGetValueType(pszValue) == CPL_VALUE_INTEGER)
580
0
            m_nSrcOvrLevel = atoi(pszValue);
581
0
        if (m_nSrcOvrLevel != nOldValue)
582
0
            SetNeedsFlush();
583
0
        return CE_None;
584
0
    }
585
0
    return VRTDataset::SetMetadataItem(pszName, pszValue, pszDomain);
586
0
}
587
588
/************************************************************************/
589
/*                        VRTWarpedAddOptions()                         */
590
/************************************************************************/
591
592
static char **VRTWarpedAddOptions(char **papszWarpOptions)
593
0
{
594
    /* Avoid errors when adding an alpha band, but source dataset has */
595
    /* no alpha band (#4571), and generally don't leave our buffer uninitialized
596
     */
597
0
    if (CSLFetchNameValue(papszWarpOptions, "INIT_DEST") == nullptr)
598
0
        papszWarpOptions = CSLSetNameValue(papszWarpOptions, "INIT_DEST", "0");
599
600
    /* For https://github.com/OSGeo/gdal/issues/1985 */
601
0
    if (CSLFetchNameValue(papszWarpOptions,
602
0
                          "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW") == nullptr)
603
0
    {
604
0
        papszWarpOptions = CSLSetNameValue(
605
0
            papszWarpOptions, "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", "FALSE");
606
0
    }
607
0
    return papszWarpOptions;
608
0
}
609
610
/************************************************************************/
611
/*                             Initialize()                             */
612
/*                                                                      */
613
/*      Initialize a dataset from passed in warp options.               */
614
/************************************************************************/
615
616
CPLErr VRTWarpedDataset::Initialize(void *psWO)
617
618
0
{
619
0
    if (m_poWarper != nullptr)
620
0
        delete m_poWarper;
621
622
0
    m_poWarper = new GDALWarpOperation();
623
624
0
    GDALWarpOptions *psWO_Dup =
625
0
        GDALCloneWarpOptions(static_cast<GDALWarpOptions *>(psWO));
626
627
0
    psWO_Dup->papszWarpOptions =
628
0
        VRTWarpedAddOptions(psWO_Dup->papszWarpOptions);
629
630
0
    CPLErr eErr = m_poWarper->Initialize(psWO_Dup);
631
632
    // The act of initializing this warped dataset with this warp options
633
    // will result in our assuming ownership of a reference to the
634
    // hSrcDS.
635
636
0
    if (eErr == CE_None &&
637
0
        static_cast<GDALWarpOptions *>(psWO)->hSrcDS != nullptr)
638
0
    {
639
0
        GDALReferenceDataset(psWO_Dup->hSrcDS);
640
0
    }
641
642
0
    GDALDestroyWarpOptions(psWO_Dup);
643
644
0
    if (nBands > 1)
645
0
    {
646
0
        GDALDataset::SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL",
647
0
                                     GDAL_MDD_IMAGE_STRUCTURE);
648
0
    }
649
650
0
    return eErr;
651
0
}
652
653
/************************************************************************/
654
/*                        GDALWarpCoordRescaler                         */
655
/************************************************************************/
656
657
class GDALWarpCoordRescaler final : public OGRCoordinateTransformation
658
{
659
    const double m_dfRatioX;
660
    const double m_dfRatioY;
661
662
    GDALWarpCoordRescaler &operator=(const GDALWarpCoordRescaler &) = delete;
663
    GDALWarpCoordRescaler(GDALWarpCoordRescaler &&) = delete;
664
    GDALWarpCoordRescaler &operator=(GDALWarpCoordRescaler &&) = delete;
665
666
  public:
667
    GDALWarpCoordRescaler(double dfRatioX, double dfRatioY)
668
0
        : m_dfRatioX(dfRatioX), m_dfRatioY(dfRatioY)
669
0
    {
670
0
    }
671
672
0
    GDALWarpCoordRescaler(const GDALWarpCoordRescaler &) = default;
673
674
    ~GDALWarpCoordRescaler() override;
675
676
    const OGRSpatialReference *GetSourceCS() const override
677
0
    {
678
0
        return nullptr;
679
0
    }
680
681
    const OGRSpatialReference *GetTargetCS() const override
682
0
    {
683
0
        return nullptr;
684
0
    }
685
686
    virtual int Transform(size_t nCount, double *x, double *y, double * /*z*/,
687
                          double * /*t*/, int *pabSuccess) override
688
0
    {
689
0
        for (size_t i = 0; i < nCount; i++)
690
0
        {
691
0
            x[i] *= m_dfRatioX;
692
0
            y[i] *= m_dfRatioY;
693
0
            if (pabSuccess)
694
0
                pabSuccess[i] = TRUE;
695
0
        }
696
0
        return TRUE;
697
0
    }
698
699
    OGRCoordinateTransformation *Clone() const override
700
0
    {
701
0
        return new GDALWarpCoordRescaler(*this);
702
0
    }
703
704
    OGRCoordinateTransformation *GetInverse() const override
705
0
    {
706
0
        return nullptr;
707
0
    }
708
};
709
710
0
GDALWarpCoordRescaler::~GDALWarpCoordRescaler() = default;
711
712
/************************************************************************/
713
/*                       RescaleDstGeoTransform()                       */
714
/************************************************************************/
715
716
static void RescaleDstGeoTransform(double adfDstGeoTransform[6],
717
                                   int nRasterXSize, int nDstPixels,
718
                                   int nRasterYSize, int nDstLines)
719
0
{
720
0
    adfDstGeoTransform[1] *= static_cast<double>(nRasterXSize) / nDstPixels;
721
0
    adfDstGeoTransform[2] *= static_cast<double>(nRasterXSize) / nDstPixels;
722
0
    adfDstGeoTransform[4] *= static_cast<double>(nRasterYSize) / nDstLines;
723
0
    adfDstGeoTransform[5] *= static_cast<double>(nRasterYSize) / nDstLines;
724
0
}
725
726
/************************************************************************/
727
/*                        GetSrcOverviewLevel()                         */
728
/************************************************************************/
729
730
int VRTWarpedDataset::GetSrcOverviewLevel(int iOvr,
731
                                          bool &bThisLevelOnlyOut) const
732
0
{
733
0
    bThisLevelOnlyOut = false;
734
0
    if (m_nSrcOvrLevel < -2)
735
0
    {
736
0
        if (iOvr + m_nSrcOvrLevel + 2 >= 0)
737
0
        {
738
0
            return iOvr + m_nSrcOvrLevel + 2;
739
0
        }
740
0
    }
741
0
    else if (m_nSrcOvrLevel == -2)
742
0
    {
743
0
        return iOvr;
744
0
    }
745
0
    else if (m_nSrcOvrLevel >= 0)
746
0
    {
747
0
        bThisLevelOnlyOut = true;
748
0
        return m_nSrcOvrLevel;
749
0
    }
750
0
    return -1;
751
0
}
752
753
/************************************************************************/
754
/*                          GetOverviewSize()                           */
755
/************************************************************************/
756
757
bool VRTWarpedDataset::GetOverviewSize(GDALDataset *poSrcDS, int iOvr,
758
                                       int iSrcOvr, int &nOvrXSize,
759
                                       int &nOvrYSize, double &dfSrcRatioX,
760
                                       double &dfSrcRatioY) const
761
0
{
762
0
    auto poSrcOvrBand = iSrcOvr >= 0
763
0
                            ? poSrcDS->GetRasterBand(1)->GetOverview(iSrcOvr)
764
0
                            : poSrcDS->GetRasterBand(1);
765
0
    if (!poSrcOvrBand)
766
0
    {
767
0
        return false;
768
0
    }
769
0
    dfSrcRatioX = static_cast<double>(poSrcDS->GetRasterXSize()) /
770
0
                  poSrcOvrBand->GetXSize();
771
0
    dfSrcRatioY = static_cast<double>(poSrcDS->GetRasterYSize()) /
772
0
                  poSrcOvrBand->GetYSize();
773
0
    const double dfTargetRatio =
774
0
        static_cast<double>(poSrcDS->GetRasterXSize()) /
775
0
        poSrcDS->GetRasterBand(1)->GetOverview(iOvr)->GetXSize();
776
777
0
    nOvrXSize = static_cast<int>(nRasterXSize / dfTargetRatio + 0.5);
778
0
    nOvrYSize = static_cast<int>(nRasterYSize / dfTargetRatio + 0.5);
779
0
    return nOvrXSize >= 1 && nOvrYSize >= 1;
780
0
}
781
782
/************************************************************************/
783
/*                       CreateImplicitOverview()                       */
784
/************************************************************************/
785
786
VRTWarpedDataset *VRTWarpedDataset::CreateImplicitOverview(int iOvr) const
787
0
{
788
0
    if (!m_poWarper)
789
0
        return nullptr;
790
0
    const GDALWarpOptions *psWO = m_poWarper->GetOptions();
791
0
    if (!psWO->hSrcDS || GDALGetRasterCount(psWO->hSrcDS) == 0)
792
0
        return nullptr;
793
0
    GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
794
0
    GDALDataset *poSrcOvrDS = poSrcDS;
795
0
    bool bThisLevelOnly = false;
796
0
    const int iSrcOvr = GetSrcOverviewLevel(iOvr, bThisLevelOnly);
797
0
    if (iSrcOvr >= 0)
798
0
    {
799
0
        poSrcOvrDS =
800
0
            GDALCreateOverviewDataset(poSrcDS, iSrcOvr, bThisLevelOnly);
801
0
    }
802
0
    if (poSrcOvrDS == nullptr)
803
0
        return nullptr;
804
0
    if (poSrcOvrDS == poSrcDS)
805
0
        poSrcOvrDS->Reference();
806
807
0
    int nDstPixels = 0;
808
0
    int nDstLines = 0;
809
0
    double dfSrcRatioX = 0;
810
0
    double dfSrcRatioY = 0;
811
    // Figure out the desired output bounds and resolution.
812
0
    if (!GetOverviewSize(poSrcDS, iOvr, iSrcOvr, nDstPixels, nDstLines,
813
0
                         dfSrcRatioX, dfSrcRatioY))
814
0
    {
815
0
        poSrcOvrDS->ReleaseRef();
816
0
        return nullptr;
817
0
    }
818
819
    /* --------------------------------------------------------------------
820
     */
821
    /*      Create transformer and warping options. */
822
    /* --------------------------------------------------------------------
823
     */
824
0
    void *pTransformerArg = GDALCreateSimilarTransformer(
825
0
        psWO->pTransformerArg, dfSrcRatioX, dfSrcRatioY);
826
0
    if (pTransformerArg == nullptr)
827
0
    {
828
0
        poSrcOvrDS->ReleaseRef();
829
0
        return nullptr;
830
0
    }
831
832
0
    GDALWarpOptions *psWOOvr = GDALCloneWarpOptions(psWO);
833
0
    psWOOvr->hSrcDS = poSrcOvrDS;
834
0
    psWOOvr->pfnTransformer = psWO->pfnTransformer;
835
0
    psWOOvr->pTransformerArg = pTransformerArg;
836
837
    /* --------------------------------------------------------------------
838
     */
839
    /*      We need to rescale the potential CUTLINE */
840
    /* --------------------------------------------------------------------
841
     */
842
0
    if (psWOOvr->hCutline)
843
0
    {
844
0
        GDALWarpCoordRescaler oRescaler(1.0 / dfSrcRatioX, 1.0 / dfSrcRatioY);
845
0
        static_cast<OGRGeometry *>(psWOOvr->hCutline)->transform(&oRescaler);
846
0
    }
847
848
    /* --------------------------------------------------------------------
849
     */
850
    /*      Rescale the output geotransform on the transformer. */
851
    /* --------------------------------------------------------------------
852
     */
853
0
    double adfDstGeoTransform[6] = {0.0};
854
0
    GDALGetTransformerDstGeoTransform(psWOOvr->pTransformerArg,
855
0
                                      adfDstGeoTransform);
856
0
    RescaleDstGeoTransform(adfDstGeoTransform, nRasterXSize, nDstPixels,
857
0
                           nRasterYSize, nDstLines);
858
0
    GDALSetTransformerDstGeoTransform(psWOOvr->pTransformerArg,
859
0
                                      adfDstGeoTransform);
860
861
    /* --------------------------------------------------------------------
862
     */
863
    /*      Create the VRT file. */
864
    /* --------------------------------------------------------------------
865
     */
866
0
    GDALDatasetH hDstDS = GDALCreateWarpedVRT(poSrcOvrDS, nDstPixels, nDstLines,
867
0
                                              adfDstGeoTransform, psWOOvr);
868
869
0
    poSrcOvrDS->ReleaseRef();
870
871
0
    GDALDestroyWarpOptions(psWOOvr);
872
873
0
    if (hDstDS == nullptr)
874
0
    {
875
0
        GDALDestroyTransformer(pTransformerArg);
876
0
        return nullptr;
877
0
    }
878
879
0
    auto poOvrDS = static_cast<VRTWarpedDataset *>(hDstDS);
880
0
    poOvrDS->m_bIsOverview = true;
881
0
    return poOvrDS;
882
0
}
883
884
/************************************************************************/
885
/*                          GetOverviewCount()                          */
886
/************************************************************************/
887
888
int VRTWarpedDataset::GetOverviewCount() const
889
0
{
890
0
    if (m_poWarper)
891
0
    {
892
0
        const GDALWarpOptions *psWO = m_poWarper->GetOptions();
893
0
        if (!m_bIsOverview && psWO->hSrcDS && GDALGetRasterCount(psWO->hSrcDS))
894
0
        {
895
0
            GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
896
0
            int nSrcOverviewCount =
897
0
                poSrcDS->GetRasterBand(1)->GetOverviewCount();
898
0
            int nCount = 0;
899
0
            for (int i = 0; i < nSrcOverviewCount; ++i)
900
0
            {
901
0
                bool bThisLevelOnly = false;
902
0
                const int iSrcOvr = GetSrcOverviewLevel(i, bThisLevelOnly);
903
0
                if (iSrcOvr >= 0)
904
0
                {
905
0
                    int nDstPixels = 0;
906
0
                    int nDstLines = 0;
907
0
                    double dfSrcRatioX = 0;
908
0
                    double dfSrcRatioY = 0;
909
0
                    if (!GetOverviewSize(poSrcDS, i, iSrcOvr, nDstPixels,
910
0
                                         nDstLines, dfSrcRatioX, dfSrcRatioY))
911
0
                    {
912
0
                        break;
913
0
                    }
914
0
                }
915
0
                ++nCount;
916
0
            }
917
0
            return nCount;
918
0
        }
919
0
    }
920
0
    return 0;
921
0
}
922
923
/************************************************************************/
924
/*                        CreateImplicitOverviews()                     */
925
/*                                                                      */
926
/*      For each overview of the source dataset, create an overview     */
927
/*      in the warped VRT dataset.                                      */
928
/************************************************************************/
929
930
void VRTWarpedDataset::CreateImplicitOverviews()
931
0
{
932
0
    if (m_bIsOverview)
933
0
        return;
934
0
    const int nOvrCount = GetOverviewCount();
935
0
    if (m_apoOverviews.empty())
936
0
        m_apoOverviews.resize(nOvrCount);
937
0
    for (int iOvr = 0; iOvr < nOvrCount; iOvr++)
938
0
    {
939
0
        if (!m_apoOverviews[iOvr])
940
0
        {
941
0
            m_apoOverviews[iOvr] = CreateImplicitOverview(iOvr);
942
0
        }
943
0
    }
944
0
}
945
946
/************************************************************************/
947
/*                            GetFileList()                             */
948
/************************************************************************/
949
950
char **VRTWarpedDataset::GetFileList()
951
0
{
952
0
    char **papszFileList = GDALDataset::GetFileList();
953
954
0
    if (m_poWarper != nullptr)
955
0
    {
956
0
        const GDALWarpOptions *psWO = m_poWarper->GetOptions();
957
0
        const char *pszFilename = nullptr;
958
959
0
        if (psWO->hSrcDS != nullptr &&
960
0
            (pszFilename =
961
0
                 static_cast<GDALDataset *>(psWO->hSrcDS)->GetDescription()) !=
962
0
                nullptr)
963
0
        {
964
0
            VSIStatBufL sStat;
965
0
            if (VSIStatL(pszFilename, &sStat) == 0)
966
0
            {
967
0
                papszFileList = CSLAddString(papszFileList, pszFilename);
968
0
            }
969
0
        }
970
0
    }
971
972
0
    return papszFileList;
973
0
}
974
975
/************************************************************************/
976
/* ==================================================================== */
977
/*                    VRTWarpedOverviewTransformer                      */
978
/* ==================================================================== */
979
/************************************************************************/
980
981
typedef struct
982
{
983
    GDALTransformerInfo sTI;
984
985
    GDALTransformerFunc pfnBaseTransformer;
986
    void *pBaseTransformerArg;
987
    bool bOwnSubtransformer;
988
989
    double dfXOverviewFactor;
990
    double dfYOverviewFactor;
991
} VWOTInfo;
992
993
static void *VRTCreateWarpedOverviewTransformer(
994
    GDALTransformerFunc pfnBaseTransformer, void *pBaseTransformArg,
995
    double dfXOverviewFactor, double dfYOverviewFactor);
996
static void VRTDestroyWarpedOverviewTransformer(void *pTransformArg);
997
998
static int VRTWarpedOverviewTransform(void *pTransformArg, int bDstToSrc,
999
                                      int nPointCount, double *padfX,
1000
                                      double *padfY, double *padfZ,
1001
                                      int *panSuccess);
1002
1003
#if 0   // TODO: Why?
1004
/************************************************************************/
1005
/*               VRTSerializeWarpedOverviewTransformer()                */
1006
/************************************************************************/
1007
1008
static CPLXMLNode *
1009
VRTSerializeWarpedOverviewTransformer( void *pTransformArg )
1010
1011
{
1012
    VWOTInfo *psInfo = static_cast<VWOTInfo *>( pTransformArg );
1013
1014
    CPLXMLNode *psTree
1015
        = CPLCreateXMLNode( NULL, CXT_Element, "WarpedOverviewTransformer" );
1016
1017
    CPLCreateXMLElementAndValue(
1018
        psTree, "XFactor",
1019
        CPLString().Printf("%g",psInfo->dfXOverviewFactor) );
1020
    CPLCreateXMLElementAndValue(
1021
        psTree, "YFactor",
1022
        CPLString().Printf("%g",psInfo->dfYOverviewFactor) );
1023
1024
/* -------------------------------------------------------------------- */
1025
/*      Capture underlying transformer.                                 */
1026
/* -------------------------------------------------------------------- */
1027
    CPLXMLNode *psTransformerContainer
1028
        = CPLCreateXMLNode( psTree, CXT_Element, "BaseTransformer" );
1029
1030
    CPLXMLNode *psTransformer
1031
        = GDALSerializeTransformer( psInfo->pfnBaseTransformer,
1032
                                    psInfo->pBaseTransformerArg );
1033
    if( psTransformer != NULL )
1034
        CPLAddXMLChild( psTransformerContainer, psTransformer );
1035
1036
    return psTree;
1037
}
1038
1039
/************************************************************************/
1040
/*           VRTWarpedOverviewTransformerOwnsSubtransformer()           */
1041
/************************************************************************/
1042
1043
static void VRTWarpedOverviewTransformerOwnsSubtransformer( void *pTransformArg,
1044
                                                            bool bOwnFlag )
1045
{
1046
    VWOTInfo *psInfo = static_cast<VWOTInfo *>( pTransformArg );
1047
1048
    psInfo->bOwnSubtransformer = bOwnFlag;
1049
}
1050
1051
/************************************************************************/
1052
/*              VRTDeserializeWarpedOverviewTransformer()               */
1053
/************************************************************************/
1054
1055
void* VRTDeserializeWarpedOverviewTransformer( CPLXMLNode *psTree )
1056
1057
{
1058
    const double dfXOverviewFactor =
1059
        CPLAtof(CPLGetXMLValue( psTree, "XFactor",  "1" ));
1060
    const double dfYOverviewFactor =
1061
        CPLAtof(CPLGetXMLValue( psTree, "YFactor",  "1" ));
1062
    GDALTransformerFunc pfnBaseTransform = NULL;
1063
    void *pBaseTransformerArg = NULL;
1064
1065
    CPLXMLNode *psContainer = CPLGetXMLNode( psTree, "BaseTransformer" );
1066
1067
    if( psContainer != NULL && psContainer->psChild != NULL )
1068
    {
1069
        GDALDeserializeTransformer( psContainer->psChild,
1070
                                    &pfnBaseTransform,
1071
                                    &pBaseTransformerArg );
1072
    }
1073
1074
    if( pfnBaseTransform == NULL )
1075
    {
1076
        CPLError( CE_Failure, CPLE_AppDefined,
1077
                  "Cannot get base transform for scaled coord transformer." );
1078
        return NULL;
1079
    }
1080
    else
1081
    {
1082
        void *pApproxCBData =
1083
                       VRTCreateWarpedOverviewTransformer( pfnBaseTransform,
1084
                                                           pBaseTransformerArg,
1085
                                                           dfXOverviewFactor,
1086
                                                           dfYOverviewFactor );
1087
        VRTWarpedOverviewTransformerOwnsSubtransformer( pApproxCBData, true );
1088
1089
        return pApproxCBData;
1090
    }
1091
}
1092
#endif  // TODO: Why disabled?
1093
1094
/************************************************************************/
1095
/*                 VRTCreateWarpedOverviewTransformer()                 */
1096
/************************************************************************/
1097
1098
static void *VRTCreateWarpedOverviewTransformer(
1099
    GDALTransformerFunc pfnBaseTransformer, void *pBaseTransformerArg,
1100
    double dfXOverviewFactor, double dfYOverviewFactor)
1101
1102
0
{
1103
0
    if (pfnBaseTransformer == nullptr)
1104
0
        return nullptr;
1105
1106
0
    VWOTInfo *psSCTInfo = static_cast<VWOTInfo *>(CPLMalloc(sizeof(VWOTInfo)));
1107
0
    psSCTInfo->pfnBaseTransformer = pfnBaseTransformer;
1108
0
    psSCTInfo->pBaseTransformerArg = pBaseTransformerArg;
1109
0
    psSCTInfo->dfXOverviewFactor = dfXOverviewFactor;
1110
0
    psSCTInfo->dfYOverviewFactor = dfYOverviewFactor;
1111
0
    psSCTInfo->bOwnSubtransformer = false;
1112
1113
0
    memcpy(psSCTInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
1114
0
           strlen(GDAL_GTI2_SIGNATURE));
1115
0
    psSCTInfo->sTI.pszClassName = "VRTWarpedOverviewTransformer";
1116
0
    psSCTInfo->sTI.pfnTransform = VRTWarpedOverviewTransform;
1117
0
    psSCTInfo->sTI.pfnCleanup = VRTDestroyWarpedOverviewTransformer;
1118
#if 0
1119
    psSCTInfo->sTI.pfnSerialize = VRTSerializeWarpedOverviewTransformer;
1120
#endif
1121
0
    return psSCTInfo;
1122
0
}
1123
1124
/************************************************************************/
1125
/*                VRTDestroyWarpedOverviewTransformer()                 */
1126
/************************************************************************/
1127
1128
static void VRTDestroyWarpedOverviewTransformer(void *pTransformArg)
1129
0
{
1130
0
    VWOTInfo *psInfo = static_cast<VWOTInfo *>(pTransformArg);
1131
1132
0
    if (psInfo->bOwnSubtransformer)
1133
0
        GDALDestroyTransformer(psInfo->pBaseTransformerArg);
1134
1135
0
    CPLFree(psInfo);
1136
0
}
1137
1138
/************************************************************************/
1139
/*                     VRTWarpedOverviewTransform()                     */
1140
/************************************************************************/
1141
1142
static int VRTWarpedOverviewTransform(void *pTransformArg, int bDstToSrc,
1143
                                      int nPointCount, double *padfX,
1144
                                      double *padfY, double *padfZ,
1145
                                      int *panSuccess)
1146
1147
0
{
1148
0
    VWOTInfo *psInfo = static_cast<VWOTInfo *>(pTransformArg);
1149
1150
0
    if (bDstToSrc)
1151
0
    {
1152
0
        for (int i = 0; i < nPointCount; i++)
1153
0
        {
1154
0
            padfX[i] *= psInfo->dfXOverviewFactor;
1155
0
            padfY[i] *= psInfo->dfYOverviewFactor;
1156
0
        }
1157
0
    }
1158
1159
0
    const int bSuccess = psInfo->pfnBaseTransformer(
1160
0
        psInfo->pBaseTransformerArg, bDstToSrc, nPointCount, padfX, padfY,
1161
0
        padfZ, panSuccess);
1162
1163
0
    if (!bDstToSrc)
1164
0
    {
1165
0
        for (int i = 0; i < nPointCount; i++)
1166
0
        {
1167
0
            padfX[i] /= psInfo->dfXOverviewFactor;
1168
0
            padfY[i] /= psInfo->dfYOverviewFactor;
1169
0
        }
1170
0
    }
1171
1172
0
    return bSuccess;
1173
0
}
1174
1175
/************************************************************************/
1176
/*                           BuildOverviews()                           */
1177
/*                                                                      */
1178
/*      For overviews, we actually just build a whole new dataset       */
1179
/*      with an extra layer of transformation on the warper used to     */
1180
/*      accomplish downsampling by the desired factor.                  */
1181
/************************************************************************/
1182
1183
CPLErr VRTWarpedDataset::IBuildOverviews(
1184
    const char * /* pszResampling */, int nOverviews,
1185
    const int *panOverviewList, int /* nListBands */,
1186
    const int * /* panBandList */, GDALProgressFunc pfnProgress,
1187
    void *pProgressData, CSLConstList /*papszOptions*/)
1188
0
{
1189
0
    if (m_poWarper == nullptr || m_bIsOverview)
1190
0
        return CE_Failure;
1191
1192
    /* -------------------------------------------------------------------- */
1193
    /*      Initial progress result.                                        */
1194
    /* -------------------------------------------------------------------- */
1195
0
    if (!pfnProgress(0.0, nullptr, pProgressData))
1196
0
    {
1197
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1198
0
        return CE_Failure;
1199
0
    }
1200
1201
0
    CreateImplicitOverviews();
1202
1203
    /* -------------------------------------------------------------------- */
1204
    /*      Establish which of the overview levels we already have, and     */
1205
    /*      which are new.                                                  */
1206
    /* -------------------------------------------------------------------- */
1207
0
    int nNewOverviews = 0;
1208
0
    int *panNewOverviewList =
1209
0
        static_cast<int *>(CPLCalloc(sizeof(int), nOverviews));
1210
0
    std::vector<bool> abFoundOverviewFactor(nOverviews);
1211
0
    for (int i = 0; i < nOverviews; i++)
1212
0
    {
1213
0
        for (GDALDataset *const poOverview : m_apoOverviews)
1214
0
        {
1215
0
            if (poOverview)
1216
0
            {
1217
0
                const int nOvFactor = GDALComputeOvFactor(
1218
0
                    poOverview->GetRasterXSize(), GetRasterXSize(),
1219
0
                    poOverview->GetRasterYSize(), GetRasterYSize());
1220
1221
0
                if (nOvFactor == panOverviewList[i] ||
1222
0
                    nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
1223
0
                                                    GetRasterXSize(),
1224
0
                                                    GetRasterYSize()))
1225
0
                    abFoundOverviewFactor[i] = true;
1226
0
            }
1227
0
        }
1228
1229
0
        if (!abFoundOverviewFactor[i])
1230
0
            panNewOverviewList[nNewOverviews++] = panOverviewList[i];
1231
0
    }
1232
1233
    /* -------------------------------------------------------------------- */
1234
    /*      Create each missing overview (we don't need to do anything      */
1235
    /*      to update existing overviews).                                  */
1236
    /* -------------------------------------------------------------------- */
1237
0
    CPLErr eErr = CE_None;
1238
0
    for (int i = 0; i < nNewOverviews; i++)
1239
0
    {
1240
        /* --------------------------------------------------------------------
1241
         */
1242
        /*      What size should this overview be. */
1243
        /* --------------------------------------------------------------------
1244
         */
1245
0
        const int nOXSize = (GetRasterXSize() + panNewOverviewList[i] - 1) /
1246
0
                            panNewOverviewList[i];
1247
1248
0
        const int nOYSize = (GetRasterYSize() + panNewOverviewList[i] - 1) /
1249
0
                            panNewOverviewList[i];
1250
1251
        /* --------------------------------------------------------------------
1252
         */
1253
        /*      Find the most appropriate base dataset onto which to build the
1254
         */
1255
        /*      new one. The preference will be an overview dataset with a
1256
         * ratio*/
1257
        /*      greater than ours, and which is not using */
1258
        /*      VRTWarpedOverviewTransform, since those ones are slow. The
1259
         * other*/
1260
        /*      ones are based on overviews of the source dataset. */
1261
        /* --------------------------------------------------------------------
1262
         */
1263
0
        VRTWarpedDataset *poBaseDataset = this;
1264
0
        for (auto *poOverview : m_apoOverviews)
1265
0
        {
1266
0
            if (poOverview && poOverview->GetRasterXSize() > nOXSize &&
1267
0
                poOverview->m_poWarper->GetOptions()->pfnTransformer !=
1268
0
                    VRTWarpedOverviewTransform &&
1269
0
                poOverview->GetRasterXSize() < poBaseDataset->GetRasterXSize())
1270
0
            {
1271
0
                poBaseDataset = poOverview;
1272
0
            }
1273
0
        }
1274
1275
        /* --------------------------------------------------------------------
1276
         */
1277
        /*      Create the overview dataset. */
1278
        /* --------------------------------------------------------------------
1279
         */
1280
0
        VRTWarpedDataset *poOverviewDS = new VRTWarpedDataset(nOXSize, nOYSize);
1281
1282
0
        for (int iBand = 0; iBand < GetRasterCount(); iBand++)
1283
0
        {
1284
0
            GDALRasterBand *const poOldBand = GetRasterBand(iBand + 1);
1285
0
            VRTWarpedRasterBand *const poNewBand = new VRTWarpedRasterBand(
1286
0
                poOverviewDS, iBand + 1, poOldBand->GetRasterDataType());
1287
1288
0
            poNewBand->CopyCommonInfoFrom(poOldBand);
1289
0
            poOverviewDS->SetBand(iBand + 1, poNewBand);
1290
0
        }
1291
1292
        /* --------------------------------------------------------------------
1293
         */
1294
        /*      Prepare update transformation information that will apply */
1295
        /*      the overview decimation. */
1296
        /* --------------------------------------------------------------------
1297
         */
1298
0
        GDALWarpOptions *psWO = const_cast<GDALWarpOptions *>(
1299
0
            poBaseDataset->m_poWarper->GetOptions());
1300
1301
        /* --------------------------------------------------------------------
1302
         */
1303
        /*      Initialize the new dataset with adjusted warp options, and */
1304
        /*      then restore to original condition. */
1305
        /* --------------------------------------------------------------------
1306
         */
1307
0
        GDALTransformerFunc pfnTransformerBase = psWO->pfnTransformer;
1308
0
        void *pTransformerBaseArg = psWO->pTransformerArg;
1309
1310
0
        psWO->pfnTransformer = VRTWarpedOverviewTransform;
1311
0
        psWO->pTransformerArg = VRTCreateWarpedOverviewTransformer(
1312
0
            pfnTransformerBase, pTransformerBaseArg,
1313
0
            poBaseDataset->GetRasterXSize() / static_cast<double>(nOXSize),
1314
0
            poBaseDataset->GetRasterYSize() / static_cast<double>(nOYSize));
1315
1316
0
        eErr = poOverviewDS->Initialize(psWO);
1317
1318
0
        psWO->pfnTransformer = pfnTransformerBase;
1319
0
        psWO->pTransformerArg = pTransformerBaseArg;
1320
1321
0
        if (eErr != CE_None)
1322
0
        {
1323
0
            delete poOverviewDS;
1324
0
            break;
1325
0
        }
1326
1327
0
        m_apoOverviews.push_back(poOverviewDS);
1328
0
    }
1329
1330
0
    CPLFree(panNewOverviewList);
1331
1332
    /* -------------------------------------------------------------------- */
1333
    /*      Progress finished.                                              */
1334
    /* -------------------------------------------------------------------- */
1335
0
    pfnProgress(1.0, nullptr, pProgressData);
1336
1337
0
    SetNeedsFlush();
1338
1339
0
    return eErr;
1340
0
}
1341
1342
/*! @endcond */
1343
1344
/************************************************************************/
1345
/*                      GDALInitializeWarpedVRT()                       */
1346
/************************************************************************/
1347
1348
/**
1349
 * Set warp info on virtual warped dataset.
1350
 *
1351
 * Initializes all the warping information for a virtual warped dataset.
1352
 *
1353
 * This method is the same as the C++ method VRTWarpedDataset::Initialize().
1354
 *
1355
 * @param hDS dataset previously created with the VRT driver, and a
1356
 * SUBCLASS of "VRTWarpedDataset".
1357
 *
1358
 * @param psWO the warp options to apply.  Note that ownership of the
1359
 * transformation information is taken over by the function though everything
1360
 * else remains the property of the caller.
1361
 *
1362
 * @return CE_None on success or CE_Failure if an error occurs.
1363
 */
1364
1365
CPLErr CPL_STDCALL GDALInitializeWarpedVRT(GDALDatasetH hDS,
1366
                                           GDALWarpOptions *psWO)
1367
1368
0
{
1369
0
    VALIDATE_POINTER1(hDS, "GDALInitializeWarpedVRT", CE_Failure);
1370
1371
0
    return static_cast<VRTWarpedDataset *>(GDALDataset::FromHandle(hDS))
1372
0
        ->Initialize(psWO);
1373
0
}
1374
1375
/*! @cond Doxygen_Suppress */
1376
1377
/************************************************************************/
1378
/*                              XMLInit()                               */
1379
/************************************************************************/
1380
1381
CPLErr VRTWarpedDataset::XMLInit(const CPLXMLNode *psTree,
1382
                                 const char *pszVRTPathIn)
1383
1384
0
{
1385
1386
    /* -------------------------------------------------------------------- */
1387
    /*      Initialize blocksize before calling sub-init so that the        */
1388
    /*      band initializers can get it from the dataset object when       */
1389
    /*      they are created.                                               */
1390
    /* -------------------------------------------------------------------- */
1391
0
    m_nBlockXSize = atoi(CPLGetXMLValue(psTree, "BlockXSize", "512"));
1392
0
    m_nBlockYSize = atoi(CPLGetXMLValue(psTree, "BlockYSize", "128"));
1393
1394
    /* -------------------------------------------------------------------- */
1395
    /*      Initialize all the general VRT stuff.  This will even           */
1396
    /*      create the VRTWarpedRasterBands and initialize them.            */
1397
    /* -------------------------------------------------------------------- */
1398
0
    {
1399
0
        const CPLErr eErr = VRTDataset::XMLInit(psTree, pszVRTPathIn);
1400
1401
0
        if (eErr != CE_None)
1402
0
            return eErr;
1403
0
    }
1404
1405
    // Check that band block sizes didn't change the dataset block size.
1406
0
    for (int i = 1; i <= nBands; i++)
1407
0
    {
1408
0
        int nBlockXSize = 0;
1409
0
        int nBlockYSize = 0;
1410
0
        GetRasterBand(i)->GetBlockSize(&nBlockXSize, &nBlockYSize);
1411
0
        if (nBlockXSize != m_nBlockXSize || nBlockYSize != m_nBlockYSize)
1412
0
        {
1413
0
            CPLError(CE_Failure, CPLE_AppDefined,
1414
0
                     "Block size specified on band %d not consistent with "
1415
0
                     "dataset block size",
1416
0
                     i);
1417
0
            return CE_Failure;
1418
0
        }
1419
0
    }
1420
1421
0
    if (nBands > 1)
1422
0
    {
1423
0
        GDALDataset::SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL",
1424
0
                                     GDAL_MDD_IMAGE_STRUCTURE);
1425
0
    }
1426
1427
    /* -------------------------------------------------------------------- */
1428
    /*      Find the GDALWarpOptions XML tree.                              */
1429
    /* -------------------------------------------------------------------- */
1430
0
    const CPLXMLNode *const psOptionsTree =
1431
0
        CPLGetXMLNode(psTree, "GDALWarpOptions");
1432
0
    if (psOptionsTree == nullptr)
1433
0
    {
1434
0
        CPLError(CE_Failure, CPLE_AppDefined,
1435
0
                 "Count not find required GDALWarpOptions in XML.");
1436
0
        return CE_Failure;
1437
0
    }
1438
1439
    /* -------------------------------------------------------------------- */
1440
    /*      Adjust the SourceDataset in the warp options to take into       */
1441
    /*      account that it is relative to the VRT if appropriate.          */
1442
    /* -------------------------------------------------------------------- */
1443
0
    const bool bRelativeToVRT = CPL_TO_BOOL(atoi(
1444
0
        CPLGetXMLValue(psOptionsTree, "SourceDataset.relativeToVRT", "0")));
1445
1446
0
    const char *pszRelativePath =
1447
0
        CPLGetXMLValue(psOptionsTree, "SourceDataset", "");
1448
0
    char *pszAbsolutePath = nullptr;
1449
1450
0
    if (bRelativeToVRT)
1451
0
        pszAbsolutePath = CPLStrdup(
1452
0
            CPLProjectRelativeFilenameSafe(pszVRTPathIn, pszRelativePath)
1453
0
                .c_str());
1454
0
    else
1455
0
        pszAbsolutePath = CPLStrdup(pszRelativePath);
1456
1457
0
    CPLXMLNode *psOptionsTreeCloned = CPLCloneXMLTree(psOptionsTree);
1458
0
    CPLSetXMLValue(psOptionsTreeCloned, "SourceDataset", pszAbsolutePath);
1459
0
    CPLFree(pszAbsolutePath);
1460
1461
    /* -------------------------------------------------------------------- */
1462
    /*      And instantiate the warp options, and corresponding warp        */
1463
    /*      operation.                                                      */
1464
    /* -------------------------------------------------------------------- */
1465
0
    GDALWarpOptions *psWO = GDALDeserializeWarpOptions(psOptionsTreeCloned);
1466
0
    CPLDestroyXMLNode(psOptionsTreeCloned);
1467
0
    if (psWO == nullptr)
1468
0
        return CE_Failure;
1469
1470
0
    psWO->papszWarpOptions = VRTWarpedAddOptions(psWO->papszWarpOptions);
1471
1472
0
    eAccess = GA_Update;
1473
1474
0
    if (psWO->hDstDS != nullptr)
1475
0
    {
1476
0
        GDALClose(psWO->hDstDS);
1477
0
        psWO->hDstDS = nullptr;
1478
0
    }
1479
1480
0
    psWO->hDstDS = this;
1481
1482
    /* -------------------------------------------------------------------- */
1483
    /*      Deserialize vertical shift grids.                               */
1484
    /* -------------------------------------------------------------------- */
1485
0
    CPLXMLNode *psIter = psTree->psChild;
1486
0
    for (; psWO->hSrcDS != nullptr && psIter != nullptr;
1487
0
         psIter = psIter->psNext)
1488
0
    {
1489
0
        if (psIter->eType != CXT_Element ||
1490
0
            !EQUAL(psIter->pszValue, "VerticalShiftGrids"))
1491
0
        {
1492
0
            continue;
1493
0
        }
1494
1495
0
        CPLError(CE_Warning, CPLE_AppDefined,
1496
0
                 "The VerticalShiftGrids in a warped VRT is now deprecated, "
1497
0
                 "and will no longer be handled in GDAL 4.0");
1498
1499
0
        const char *pszVGrids = CPLGetXMLValue(psIter, "Grids", nullptr);
1500
0
        if (pszVGrids)
1501
0
        {
1502
0
            int bInverse =
1503
0
                CSLTestBoolean(CPLGetXMLValue(psIter, "Inverse", "FALSE"));
1504
0
            double dfToMeterSrc =
1505
0
                CPLAtof(CPLGetXMLValue(psIter, "ToMeterSrc", "1.0"));
1506
0
            double dfToMeterDest =
1507
0
                CPLAtof(CPLGetXMLValue(psIter, "ToMeterDest", "1.0"));
1508
0
            char **papszOptions = nullptr;
1509
0
            CPLXMLNode *psIter2 = psIter->psChild;
1510
0
            for (; psIter2 != nullptr; psIter2 = psIter2->psNext)
1511
0
            {
1512
0
                if (psIter2->eType != CXT_Element ||
1513
0
                    !EQUAL(psIter2->pszValue, "Option"))
1514
0
                {
1515
0
                    continue;
1516
0
                }
1517
0
                const char *pszName = CPLGetXMLValue(psIter2, "name", nullptr);
1518
0
                const char *pszValue =
1519
0
                    CPLGetXMLValue(psIter2, nullptr, nullptr);
1520
0
                if (pszName && pszValue)
1521
0
                {
1522
0
                    papszOptions =
1523
0
                        CSLSetNameValue(papszOptions, pszName, pszValue);
1524
0
                }
1525
0
            }
1526
1527
0
            int bError = FALSE;
1528
0
            GDALDatasetH hGridDataset =
1529
0
                GDALOpenVerticalShiftGrid(pszVGrids, &bError);
1530
0
            if (bError && hGridDataset == nullptr)
1531
0
            {
1532
0
                CPLError(CE_Warning, CPLE_AppDefined,
1533
0
                         "Cannot open %s. Source dataset will no "
1534
0
                         "be vertically adjusted regarding "
1535
0
                         "vertical datum",
1536
0
                         pszVGrids);
1537
0
            }
1538
0
            else if (hGridDataset != nullptr)
1539
0
            {
1540
                // Transform from source vertical datum to WGS84
1541
0
                GDALDatasetH hTmpDS = GDALApplyVerticalShiftGrid(
1542
0
                    psWO->hSrcDS, hGridDataset, bInverse, dfToMeterSrc,
1543
0
                    dfToMeterDest, papszOptions);
1544
0
                GDALReleaseDataset(hGridDataset);
1545
0
                if (hTmpDS == nullptr)
1546
0
                {
1547
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1548
0
                             "Source dataset will no "
1549
0
                             "be vertically adjusted regarding "
1550
0
                             "vertical datum %s",
1551
0
                             pszVGrids);
1552
0
                }
1553
0
                else
1554
0
                {
1555
0
                    CPLDebug("GDALWARP",
1556
0
                             "Adjusting source dataset "
1557
0
                             "with vertical datum using %s",
1558
0
                             pszVGrids);
1559
0
                    GDALReleaseDataset(psWO->hSrcDS);
1560
0
                    psWO->hSrcDS = hTmpDS;
1561
0
                }
1562
0
            }
1563
1564
0
            CSLDestroy(papszOptions);
1565
0
        }
1566
0
    }
1567
1568
    /* -------------------------------------------------------------------- */
1569
    /*      Instantiate the warp operation.                                 */
1570
    /* -------------------------------------------------------------------- */
1571
0
    m_poWarper = new GDALWarpOperation();
1572
1573
0
    const CPLErr eErr = m_poWarper->Initialize(psWO);
1574
0
    if (eErr != CE_None)
1575
0
    {
1576
        /* --------------------------------------------------------------------
1577
         */
1578
        /*      We are responsible for cleaning up the transformer ourselves. */
1579
        /* --------------------------------------------------------------------
1580
         */
1581
0
        if (psWO->pTransformerArg != nullptr)
1582
0
        {
1583
0
            GDALDestroyTransformer(psWO->pTransformerArg);
1584
0
            psWO->pTransformerArg = nullptr;
1585
0
        }
1586
1587
0
        if (psWO->hSrcDS != nullptr)
1588
0
        {
1589
0
            GDALClose(psWO->hSrcDS);
1590
0
            psWO->hSrcDS = nullptr;
1591
0
        }
1592
0
    }
1593
1594
0
    GDALDestroyWarpOptions(psWO);
1595
0
    if (eErr != CE_None)
1596
0
    {
1597
0
        delete m_poWarper;
1598
0
        m_poWarper = nullptr;
1599
0
    }
1600
1601
    /* -------------------------------------------------------------------- */
1602
    /*      Deserialize SrcOvrLevel                                         */
1603
    /* -------------------------------------------------------------------- */
1604
0
    const char *pszSrcOvrLevel = CPLGetXMLValue(psTree, "SrcOvrLevel", nullptr);
1605
0
    if (pszSrcOvrLevel != nullptr)
1606
0
    {
1607
0
        SetMetadataItem("SrcOvrLevel", pszSrcOvrLevel);
1608
0
    }
1609
1610
    /* -------------------------------------------------------------------- */
1611
    /*      Generate overviews, if appropriate.                             */
1612
    /* -------------------------------------------------------------------- */
1613
1614
    // OverviewList is historical, and quite inefficient, since it uses
1615
    // the full resolution source dataset, so only build it afterwards.
1616
0
    const CPLStringList aosOverviews(
1617
0
        CSLTokenizeString(CPLGetXMLValue(psTree, "OverviewList", "")));
1618
0
    if (!aosOverviews.empty())
1619
0
        CreateImplicitOverviews();
1620
1621
0
    for (int iOverview = 0; iOverview < aosOverviews.size(); ++iOverview)
1622
0
    {
1623
0
        int nOvFactor = atoi(aosOverviews[iOverview]);
1624
1625
0
        if (nOvFactor > 0)
1626
0
            BuildOverviews("NEAREST", 1, &nOvFactor, 0, nullptr, nullptr,
1627
0
                           nullptr,
1628
0
                           /*papszOptions=*/nullptr);
1629
0
        else
1630
0
            CPLError(CE_Failure, CPLE_AppDefined,
1631
0
                     "Bad value for overview factor : %s",
1632
0
                     aosOverviews[iOverview]);
1633
0
    }
1634
1635
0
    return eErr;
1636
0
}
1637
1638
/************************************************************************/
1639
/*                           SerializeToXML()                           */
1640
/************************************************************************/
1641
1642
CPLXMLNode *VRTWarpedDataset::SerializeToXML(const char *pszVRTPathIn)
1643
1644
0
{
1645
0
    CPLXMLNode *psTree = VRTDataset::SerializeToXML(pszVRTPathIn);
1646
1647
0
    if (psTree == nullptr)
1648
0
        return psTree;
1649
1650
    /* -------------------------------------------------------------------- */
1651
    /*      Set subclass.                                                   */
1652
    /* -------------------------------------------------------------------- */
1653
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1654
0
                     CXT_Text, "VRTWarpedDataset");
1655
1656
    /* -------------------------------------------------------------------- */
1657
    /*      Serialize the block size.                                       */
1658
    /* -------------------------------------------------------------------- */
1659
0
    CPLCreateXMLElementAndValue(psTree, "BlockXSize",
1660
0
                                CPLSPrintf("%d", m_nBlockXSize));
1661
0
    CPLCreateXMLElementAndValue(psTree, "BlockYSize",
1662
0
                                CPLSPrintf("%d", m_nBlockYSize));
1663
1664
    /* -------------------------------------------------------------------- */
1665
    /*      Serialize the overview list (only for non implicit overviews)   */
1666
    /* -------------------------------------------------------------------- */
1667
0
    if (!m_apoOverviews.empty())
1668
0
    {
1669
0
        int nSrcDSOvrCount = 0;
1670
0
        if (m_poWarper != nullptr && m_poWarper->GetOptions() != nullptr &&
1671
0
            m_poWarper->GetOptions()->hSrcDS != nullptr &&
1672
0
            GDALGetRasterCount(m_poWarper->GetOptions()->hSrcDS) > 0)
1673
0
        {
1674
0
            nSrcDSOvrCount =
1675
0
                static_cast<GDALDataset *>(m_poWarper->GetOptions()->hSrcDS)
1676
0
                    ->GetRasterBand(1)
1677
0
                    ->GetOverviewCount();
1678
0
        }
1679
1680
0
        if (static_cast<int>(m_apoOverviews.size()) != nSrcDSOvrCount)
1681
0
        {
1682
0
            const size_t nLen = m_apoOverviews.size() * 8 + 10;
1683
0
            char *pszOverviewList = static_cast<char *>(CPLMalloc(nLen));
1684
0
            pszOverviewList[0] = '\0';
1685
0
            for (auto *poOverviewDS : m_apoOverviews)
1686
0
            {
1687
0
                if (poOverviewDS)
1688
0
                {
1689
0
                    const int nOvFactor = static_cast<int>(
1690
0
                        0.5 +
1691
0
                        GetRasterXSize() / static_cast<double>(
1692
0
                                               poOverviewDS->GetRasterXSize()));
1693
1694
0
                    snprintf(pszOverviewList + strlen(pszOverviewList),
1695
0
                             nLen - strlen(pszOverviewList), "%d ", nOvFactor);
1696
0
                }
1697
0
            }
1698
1699
0
            CPLCreateXMLElementAndValue(psTree, "OverviewList",
1700
0
                                        pszOverviewList);
1701
1702
0
            CPLFree(pszOverviewList);
1703
0
        }
1704
0
    }
1705
1706
    /* -------------------------------------------------------------------- */
1707
    /*      Serialize source overview level.                                */
1708
    /* -------------------------------------------------------------------- */
1709
0
    if (m_nSrcOvrLevel != -2)
1710
0
    {
1711
0
        if (m_nSrcOvrLevel < -2)
1712
0
            CPLCreateXMLElementAndValue(
1713
0
                psTree, "SrcOvrLevel",
1714
0
                CPLSPrintf("AUTO%d", m_nSrcOvrLevel + 2));
1715
0
        else if (m_nSrcOvrLevel == -1)
1716
0
            CPLCreateXMLElementAndValue(psTree, "SrcOvrLevel", "NONE");
1717
0
        else
1718
0
            CPLCreateXMLElementAndValue(psTree, "SrcOvrLevel",
1719
0
                                        CPLSPrintf("%d", m_nSrcOvrLevel));
1720
0
    }
1721
1722
    /* ==================================================================== */
1723
    /*      Serialize the warp options.                                     */
1724
    /* ==================================================================== */
1725
0
    if (m_poWarper != nullptr)
1726
0
    {
1727
        /* --------------------------------------------------------------------
1728
         */
1729
        /*      We reset the destination dataset name so it doesn't get */
1730
        /*      written out in the serialize warp options. */
1731
        /* --------------------------------------------------------------------
1732
         */
1733
0
        char *const pszSavedName = CPLStrdup(GetDescription());
1734
0
        SetDescription("");
1735
1736
0
        CPLXMLNode *const psWOTree =
1737
0
            GDALSerializeWarpOptions(m_poWarper->GetOptions());
1738
0
        CPLAddXMLChild(psTree, psWOTree);
1739
1740
0
        SetDescription(pszSavedName);
1741
0
        CPLFree(pszSavedName);
1742
1743
        /* --------------------------------------------------------------------
1744
         */
1745
        /*      We need to consider making the source dataset relative to */
1746
        /*      the VRT file if possible.  Adjust accordingly. */
1747
        /* --------------------------------------------------------------------
1748
         */
1749
0
        CPLXMLNode *psSDS = CPLGetXMLNode(psWOTree, "SourceDataset");
1750
0
        int bRelativeToVRT = FALSE;
1751
0
        VSIStatBufL sStat;
1752
1753
0
        if (VSIStatExL(psSDS->psChild->pszValue, &sStat,
1754
0
                       VSI_STAT_EXISTS_FLAG) == 0)
1755
0
        {
1756
0
            std::string osVRTFilename = pszVRTPathIn;
1757
0
            std::string osSourceDataset = psSDS->psChild->pszValue;
1758
0
            char *pszCurDir = CPLGetCurrentDir();
1759
0
            if (CPLIsFilenameRelative(osSourceDataset.c_str()) &&
1760
0
                !CPLIsFilenameRelative(osVRTFilename.c_str()) &&
1761
0
                pszCurDir != nullptr)
1762
0
            {
1763
0
                osSourceDataset = CPLFormFilenameSafe(
1764
0
                    pszCurDir, osSourceDataset.c_str(), nullptr);
1765
0
            }
1766
0
            else if (!CPLIsFilenameRelative(osSourceDataset.c_str()) &&
1767
0
                     CPLIsFilenameRelative(osVRTFilename.c_str()) &&
1768
0
                     pszCurDir != nullptr)
1769
0
            {
1770
0
                osVRTFilename = CPLFormFilenameSafe(
1771
0
                    pszCurDir, osVRTFilename.c_str(), nullptr);
1772
0
            }
1773
0
            CPLFree(pszCurDir);
1774
0
            char *pszRelativePath = CPLStrdup(CPLExtractRelativePath(
1775
0
                osVRTFilename.c_str(), osSourceDataset.c_str(),
1776
0
                &bRelativeToVRT));
1777
1778
0
            CPLFree(psSDS->psChild->pszValue);
1779
0
            psSDS->psChild->pszValue = pszRelativePath;
1780
0
        }
1781
1782
0
        CPLCreateXMLNode(
1783
0
            CPLCreateXMLNode(psSDS, CXT_Attribute, "relativeToVRT"), CXT_Text,
1784
0
            bRelativeToVRT ? "1" : "0");
1785
0
    }
1786
1787
0
    return psTree;
1788
0
}
1789
1790
/************************************************************************/
1791
/*                            GetBlockSize()                            */
1792
/************************************************************************/
1793
1794
void VRTWarpedDataset::GetBlockSize(int *pnBlockXSize, int *pnBlockYSize) const
1795
1796
0
{
1797
0
    CPLAssert(nullptr != pnBlockXSize);
1798
0
    CPLAssert(nullptr != pnBlockYSize);
1799
1800
0
    *pnBlockXSize = m_nBlockXSize;
1801
0
    *pnBlockYSize = m_nBlockYSize;
1802
0
}
1803
1804
/************************************************************************/
1805
/*                            ProcessBlock()                            */
1806
/*                                                                      */
1807
/*      Warp a single requested block, and then push each band of       */
1808
/*      the result into the block cache.                                */
1809
/************************************************************************/
1810
1811
CPLErr VRTWarpedDataset::ProcessBlock(int iBlockX, int iBlockY)
1812
1813
0
{
1814
0
    if (m_poWarper == nullptr)
1815
0
        return CE_Failure;
1816
1817
0
    const int nReqXOff = iBlockX * m_nBlockXSize;
1818
0
    const int nReqYOff = iBlockY * m_nBlockYSize;
1819
0
    const int nReqXSize = std::min(m_nBlockXSize, nRasterXSize - nReqXOff);
1820
0
    const int nReqYSize = std::min(m_nBlockYSize, nRasterYSize - nReqYOff);
1821
1822
0
    GByte *pabyDstBuffer = static_cast<GByte *>(
1823
0
        m_poWarper->CreateDestinationBuffer(nReqXSize, nReqYSize));
1824
1825
0
    if (pabyDstBuffer == nullptr)
1826
0
    {
1827
0
        return CE_Failure;
1828
0
    }
1829
1830
    /* -------------------------------------------------------------------- */
1831
    /*      Warp into this buffer.                                          */
1832
    /* -------------------------------------------------------------------- */
1833
1834
0
    const GDALWarpOptions *psWO = m_poWarper->GetOptions();
1835
0
    const CPLErr eErr =
1836
0
        m_poWarper->WarpRegionToBuffer(nReqXOff, nReqYOff, nReqXSize, nReqYSize,
1837
0
                                       pabyDstBuffer, psWO->eWorkingDataType);
1838
1839
0
    if (eErr != CE_None)
1840
0
    {
1841
0
        m_poWarper->DestroyDestinationBuffer(pabyDstBuffer);
1842
0
        return eErr;
1843
0
    }
1844
1845
    /* -------------------------------------------------------------------- */
1846
    /*      Copy out into cache blocks for each band.                       */
1847
    /* -------------------------------------------------------------------- */
1848
0
    const int nWordSize = GDALGetDataTypeSizeBytes(psWO->eWorkingDataType);
1849
0
    for (int i = 0; i < psWO->nBandCount; i++)
1850
0
    {
1851
0
        int nDstBand = psWO->panDstBands[i];
1852
0
        if (GetRasterCount() < nDstBand)
1853
0
        {
1854
0
            continue;
1855
0
        }
1856
1857
0
        GDALRasterBand *poBand = GetRasterBand(nDstBand);
1858
0
        GDALRasterBlock *poBlock =
1859
0
            poBand->GetLockedBlockRef(iBlockX, iBlockY, TRUE);
1860
1861
0
        const GByte *pabyDstBandBuffer =
1862
0
            pabyDstBuffer +
1863
0
            static_cast<GPtrDiff_t>(i) * nReqXSize * nReqYSize * nWordSize;
1864
1865
0
        if (poBlock != nullptr)
1866
0
        {
1867
0
            if (poBlock->GetDataRef() != nullptr)
1868
0
            {
1869
0
                if (nReqXSize == m_nBlockXSize && nReqYSize == m_nBlockYSize)
1870
0
                {
1871
0
                    GDALCopyWords64(
1872
0
                        pabyDstBandBuffer, psWO->eWorkingDataType, nWordSize,
1873
0
                        poBlock->GetDataRef(), poBlock->GetDataType(),
1874
0
                        GDALGetDataTypeSizeBytes(poBlock->GetDataType()),
1875
0
                        static_cast<GPtrDiff_t>(m_nBlockXSize) * m_nBlockYSize);
1876
0
                }
1877
0
                else
1878
0
                {
1879
0
                    GByte *pabyBlock =
1880
0
                        static_cast<GByte *>(poBlock->GetDataRef());
1881
0
                    const int nDTSize =
1882
0
                        GDALGetDataTypeSizeBytes(poBlock->GetDataType());
1883
0
                    for (int iY = 0; iY < nReqYSize; iY++)
1884
0
                    {
1885
0
                        GDALCopyWords(
1886
0
                            pabyDstBandBuffer + static_cast<GPtrDiff_t>(iY) *
1887
0
                                                    nReqXSize * nWordSize,
1888
0
                            psWO->eWorkingDataType, nWordSize,
1889
0
                            pabyBlock + static_cast<GPtrDiff_t>(iY) *
1890
0
                                            m_nBlockXSize * nDTSize,
1891
0
                            poBlock->GetDataType(), nDTSize, nReqXSize);
1892
0
                    }
1893
0
                }
1894
0
            }
1895
1896
0
            poBlock->DropLock();
1897
0
        }
1898
0
    }
1899
1900
0
    m_poWarper->DestroyDestinationBuffer(pabyDstBuffer);
1901
1902
0
    return CE_None;
1903
0
}
1904
1905
/************************************************************************/
1906
/*                             IRasterIO()                              */
1907
/************************************************************************/
1908
1909
// Specialized implementation of IRasterIO() that will be faster than
1910
// using the VRTWarpedRasterBand::IReadBlock() method in situations where
1911
// - a large enough chunk of data is requested at once
1912
// - and multi-threaded warping is enabled (it only kicks in if the warped
1913
//   chunk is large enough) and/or when reading the source dataset is
1914
//   multi-threaded (e.g JP2KAK or JP2OpenJPEG driver).
1915
CPLErr VRTWarpedDataset::IRasterIO(
1916
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1917
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1918
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1919
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
1920
0
{
1921
0
    const bool bWholeImage = nXOff == 0 && nYOff == 0 &&
1922
0
                             nXSize == nRasterXSize && nYSize == nRasterYSize;
1923
1924
0
    if (eRWFlag == GF_Write ||
1925
        // For too small request fall back to the block-based approach to
1926
        // benefit from caching
1927
0
        (!bWholeImage &&
1928
0
         (nBufXSize <= m_nBlockXSize || nBufYSize <= m_nBlockYSize)) ||
1929
        // Or if we don't request all bands at once
1930
0
        nBandCount < nBands ||
1931
0
        !CPLTestBool(
1932
0
            CPLGetConfigOption("GDAL_VRT_WARP_USE_DATASET_RASTERIO", "YES")))
1933
0
    {
1934
0
        return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1935
0
                                      pData, nBufXSize, nBufYSize, eBufType,
1936
0
                                      nBandCount, panBandMap, nPixelSpace,
1937
0
                                      nLineSpace, nBandSpace, psExtraArg);
1938
0
    }
1939
1940
    // Try overviews for sub-sampled requests
1941
0
    if (nBufXSize < nXSize || nBufYSize < nYSize)
1942
0
    {
1943
0
        int bTried = FALSE;
1944
0
        const CPLErr eErr = TryOverviewRasterIO(
1945
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1946
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1947
0
            nBandSpace, psExtraArg, &bTried);
1948
1949
0
        if (bTried)
1950
0
        {
1951
0
            return eErr;
1952
0
        }
1953
0
    }
1954
1955
0
    if (m_poWarper == nullptr)
1956
0
        return CE_Failure;
1957
1958
0
    const GDALWarpOptions *psWO = m_poWarper->GetOptions();
1959
1960
0
    if (nBufXSize != nXSize || nBufYSize != nYSize)
1961
0
    {
1962
0
        if (!bWholeImage || !GDALTransformHasFastClone(psWO->pTransformerArg))
1963
0
        {
1964
0
            return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1965
0
                                          pData, nBufXSize, nBufYSize, eBufType,
1966
0
                                          nBandCount, panBandMap, nPixelSpace,
1967
0
                                          nLineSpace, nBandSpace, psExtraArg);
1968
0
        }
1969
1970
        // Build a temporary dataset taking into account the rescaling
1971
0
        void *pTransformerArg = GDALCloneTransformer(psWO->pTransformerArg);
1972
0
        if (pTransformerArg == nullptr)
1973
0
        {
1974
0
            return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1975
0
                                          pData, nBufXSize, nBufYSize, eBufType,
1976
0
                                          nBandCount, panBandMap, nPixelSpace,
1977
0
                                          nLineSpace, nBandSpace, psExtraArg);
1978
0
        }
1979
1980
0
        GDALWarpOptions *psRescaledWO = GDALCloneWarpOptions(psWO);
1981
0
        psRescaledWO->hSrcDS = psWO->hSrcDS;
1982
0
        psRescaledWO->pfnTransformer = psWO->pfnTransformer;
1983
0
        psRescaledWO->pTransformerArg = pTransformerArg;
1984
1985
        // Rescale the output geotransform on the transformer.
1986
0
        double adfDstGeoTransform[6] = {0.0};
1987
0
        GDALGetTransformerDstGeoTransform(psRescaledWO->pTransformerArg,
1988
0
                                          adfDstGeoTransform);
1989
0
        RescaleDstGeoTransform(adfDstGeoTransform, nRasterXSize, nBufXSize,
1990
0
                               nRasterYSize, nBufYSize);
1991
0
        GDALSetTransformerDstGeoTransform(psRescaledWO->pTransformerArg,
1992
0
                                          adfDstGeoTransform);
1993
1994
0
        GDALDatasetH hDstDS =
1995
0
            GDALCreateWarpedVRT(psWO->hSrcDS, nBufXSize, nBufYSize,
1996
0
                                adfDstGeoTransform, psRescaledWO);
1997
1998
0
        GDALDestroyWarpOptions(psRescaledWO);
1999
2000
0
        if (hDstDS == nullptr)
2001
0
        {
2002
            // Not supposed to happen in nominal circumstances. Could perhaps
2003
            // happen if some memory allocation error occurred in code called
2004
            // by GDALCreateWarpedVRT()
2005
0
            GDALDestroyTransformer(pTransformerArg);
2006
0
            return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2007
0
                                          pData, nBufXSize, nBufYSize, eBufType,
2008
0
                                          nBandCount, panBandMap, nPixelSpace,
2009
0
                                          nLineSpace, nBandSpace, psExtraArg);
2010
0
        }
2011
2012
0
        auto poOvrDS = static_cast<VRTWarpedDataset *>(hDstDS);
2013
0
        poOvrDS->m_bIsOverview = true;
2014
2015
0
        GDALRasterIOExtraArg sExtraArg;
2016
0
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2017
0
        CPLErr eErr = poOvrDS->IRasterIO(GF_Read, 0, 0, nBufXSize, nBufYSize,
2018
0
                                         pData, nBufXSize, nBufYSize, eBufType,
2019
0
                                         nBandCount, panBandMap, nPixelSpace,
2020
0
                                         nLineSpace, nBandSpace, &sExtraArg);
2021
2022
0
        poOvrDS->ReleaseRef();
2023
0
        return eErr;
2024
0
    }
2025
2026
    // Build a map from warped output bands to their index
2027
0
    std::map<int, int> oMapBandToWarpingBandIndex;
2028
0
    bool bAllBandsIncreasingOrder =
2029
0
        (psWO->nBandCount == nBands && nBands == nBandCount);
2030
0
    for (int i = 0; i < psWO->nBandCount; ++i)
2031
0
    {
2032
0
        oMapBandToWarpingBandIndex[psWO->panDstBands[i]] = i;
2033
0
        if (psWO->panDstBands[i] != i + 1 || panBandMap[i] != i + 1)
2034
0
        {
2035
0
            bAllBandsIncreasingOrder = false;
2036
0
        }
2037
0
    }
2038
2039
    // Check that all requested bands are actually warped output bands.
2040
0
    for (int i = 0; i < nBandCount; ++i)
2041
0
    {
2042
0
        const int nRasterIOBand = panBandMap[i];
2043
0
        if (oMapBandToWarpingBandIndex.find(nRasterIOBand) ==
2044
0
            oMapBandToWarpingBandIndex.end())
2045
0
        {
2046
            // Not sure if that can happen...
2047
            // but if that does, that will likely later fail in ProcessBlock()
2048
0
            return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2049
0
                                          pData, nBufXSize, nBufYSize, eBufType,
2050
0
                                          nBandCount, panBandMap, nPixelSpace,
2051
0
                                          nLineSpace, nBandSpace, psExtraArg);
2052
0
        }
2053
0
    }
2054
2055
0
    int nSrcXOff = 0;
2056
0
    int nSrcYOff = 0;
2057
0
    int nSrcXSize = 0;
2058
0
    int nSrcYSize = 0;
2059
0
    double dfSrcXExtraSize = 0;
2060
0
    double dfSrcYExtraSize = 0;
2061
0
    double dfSrcFillRatio = 0;
2062
    // Find the source window that corresponds to our target window
2063
0
    if (m_poWarper->ComputeSourceWindow(nXOff, nYOff, nXSize, nYSize, &nSrcXOff,
2064
0
                                        &nSrcYOff, &nSrcXSize, &nSrcYSize,
2065
0
                                        &dfSrcXExtraSize, &dfSrcYExtraSize,
2066
0
                                        &dfSrcFillRatio) != CE_None)
2067
0
    {
2068
0
        return CE_Failure;
2069
0
    }
2070
2071
0
    GByte *const pabyDst = static_cast<GByte *>(pData);
2072
0
    const int nWarpDTSize = GDALGetDataTypeSizeBytes(psWO->eWorkingDataType);
2073
2074
0
    const double dfMemRequired = m_poWarper->GetWorkingMemoryForWindow(
2075
0
        nSrcXSize, nSrcYSize, nXSize, nYSize);
2076
    // If we need more warp working memory than allowed, we have to use a
2077
    // splitting strategy until we get below the limit.
2078
0
    if (dfMemRequired > psWO->dfWarpMemoryLimit && nXSize >= 2 && nYSize >= 2)
2079
0
    {
2080
0
        CPLDebugOnly("VRT", "VRTWarpedDataset::IRasterIO(): exceeding warp "
2081
0
                            "memory. Splitting region");
2082
2083
0
        GDALRasterIOExtraArg sExtraArg;
2084
0
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2085
2086
0
        bool bOK;
2087
        // Split along the longest dimension
2088
0
        if (nXSize >= nYSize)
2089
0
        {
2090
0
            const int nHalfXSize = nXSize / 2;
2091
0
            bOK = IRasterIO(GF_Read, nXOff, nYOff, nHalfXSize, nYSize, pabyDst,
2092
0
                            nHalfXSize, nYSize, eBufType, nBandCount,
2093
0
                            panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2094
0
                            &sExtraArg) == CE_None &&
2095
0
                  IRasterIO(GF_Read, nXOff + nHalfXSize, nYOff,
2096
0
                            nXSize - nHalfXSize, nYSize,
2097
0
                            pabyDst + nHalfXSize * nPixelSpace,
2098
0
                            nXSize - nHalfXSize, nYSize, eBufType, nBandCount,
2099
0
                            panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2100
0
                            &sExtraArg) == CE_None;
2101
0
        }
2102
0
        else
2103
0
        {
2104
0
            const int nHalfYSize = nYSize / 2;
2105
0
            bOK = IRasterIO(GF_Read, nXOff, nYOff, nXSize, nHalfYSize, pabyDst,
2106
0
                            nXSize, nHalfYSize, eBufType, nBandCount,
2107
0
                            panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2108
0
                            &sExtraArg) == CE_None &&
2109
0
                  IRasterIO(GF_Read, nXOff, nYOff + nHalfYSize, nXSize,
2110
0
                            nYSize - nHalfYSize,
2111
0
                            pabyDst + nHalfYSize * nLineSpace, nXSize,
2112
0
                            nYSize - nHalfYSize, eBufType, nBandCount,
2113
0
                            panBandMap, nPixelSpace, nLineSpace, nBandSpace,
2114
0
                            &sExtraArg) == CE_None;
2115
0
        }
2116
0
        return bOK ? CE_None : CE_Failure;
2117
0
    }
2118
2119
0
    CPLDebugOnly("VRT",
2120
0
                 "Using optimized VRTWarpedDataset::IRasterIO() code path");
2121
2122
    // Allocate a warping destination buffer if needed.
2123
    // We can use directly the output buffer pData if:
2124
    // - we request exactly all warped bands, and that there are as many
2125
    //   warped bands as dataset bands (no alpha)
2126
    // - the output buffer data atype is the warping working data type
2127
    // - the output buffer has a band-sequential layout.
2128
0
    GByte *pabyWarpBuffer;
2129
2130
0
    if (bAllBandsIncreasingOrder && psWO->eWorkingDataType == eBufType &&
2131
0
        nPixelSpace == GDALGetDataTypeSizeBytes(eBufType) &&
2132
0
        nLineSpace == nPixelSpace * nXSize &&
2133
0
        (nBands == 1 || nBandSpace == nLineSpace * nYSize))
2134
0
    {
2135
0
        pabyWarpBuffer = static_cast<GByte *>(pData);
2136
0
        m_poWarper->InitializeDestinationBuffer(pabyWarpBuffer, nXSize, nYSize);
2137
0
    }
2138
0
    else
2139
0
    {
2140
0
        pabyWarpBuffer = static_cast<GByte *>(
2141
0
            m_poWarper->CreateDestinationBuffer(nXSize, nYSize));
2142
2143
0
        if (pabyWarpBuffer == nullptr)
2144
0
        {
2145
0
            return CE_Failure;
2146
0
        }
2147
0
    }
2148
2149
0
    const CPLErr eErr = m_poWarper->WarpRegionToBuffer(
2150
0
        nXOff, nYOff, nXSize, nYSize, pabyWarpBuffer, psWO->eWorkingDataType,
2151
0
        nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize, dfSrcXExtraSize,
2152
0
        dfSrcYExtraSize);
2153
2154
0
    if (pabyWarpBuffer != pData)
2155
0
    {
2156
0
        if (eErr == CE_None)
2157
0
        {
2158
            // Copy warping buffer into user destination buffer
2159
0
            for (int i = 0; i < nBandCount; i++)
2160
0
            {
2161
0
                const int nRasterIOBand = panBandMap[i];
2162
0
                const auto oIterToWarpingBandIndex =
2163
0
                    oMapBandToWarpingBandIndex.find(nRasterIOBand);
2164
                // cannot happen due to earlier check
2165
0
                CPLAssert(oIterToWarpingBandIndex !=
2166
0
                          oMapBandToWarpingBandIndex.end());
2167
2168
0
                const GByte *const pabyWarpBandBuffer =
2169
0
                    pabyWarpBuffer +
2170
0
                    static_cast<GPtrDiff_t>(oIterToWarpingBandIndex->second) *
2171
0
                        nXSize * nYSize * nWarpDTSize;
2172
0
                GByte *const pabyDstBand = pabyDst + i * nBandSpace;
2173
2174
0
                for (int iY = 0; iY < nYSize; iY++)
2175
0
                {
2176
0
                    GDALCopyWords(pabyWarpBandBuffer +
2177
0
                                      static_cast<GPtrDiff_t>(iY) * nXSize *
2178
0
                                          nWarpDTSize,
2179
0
                                  psWO->eWorkingDataType, nWarpDTSize,
2180
0
                                  pabyDstBand + iY * nLineSpace, eBufType,
2181
0
                                  static_cast<int>(nPixelSpace), nXSize);
2182
0
                }
2183
0
            }
2184
0
        }
2185
2186
0
        m_poWarper->DestroyDestinationBuffer(pabyWarpBuffer);
2187
0
    }
2188
2189
0
    return eErr;
2190
0
}
2191
2192
/************************************************************************/
2193
/*                              AddBand()                               */
2194
/************************************************************************/
2195
2196
CPLErr VRTWarpedDataset::AddBand(GDALDataType eType,
2197
                                 CSLConstList /* papszOptions */)
2198
2199
0
{
2200
0
    if (eType == GDT_Unknown || eType == GDT_TypeCount)
2201
0
    {
2202
0
        ReportError(CE_Failure, CPLE_IllegalArg,
2203
0
                    "Illegal GDT_Unknown/GDT_TypeCount argument");
2204
0
        return CE_Failure;
2205
0
    }
2206
2207
0
    SetBand(GetRasterCount() + 1,
2208
0
            new VRTWarpedRasterBand(this, GetRasterCount() + 1, eType));
2209
2210
0
    return CE_None;
2211
0
}
2212
2213
/************************************************************************/
2214
/* ==================================================================== */
2215
/*                        VRTWarpedRasterBand                           */
2216
/* ==================================================================== */
2217
/************************************************************************/
2218
2219
/************************************************************************/
2220
/*                        VRTWarpedRasterBand()                         */
2221
/************************************************************************/
2222
2223
VRTWarpedRasterBand::VRTWarpedRasterBand(GDALDataset *poDSIn, int nBandIn,
2224
                                         GDALDataType eType)
2225
2226
0
{
2227
0
    Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
2228
2229
0
    poDS = poDSIn;
2230
0
    nBand = nBandIn;
2231
0
    eAccess = GA_Update;
2232
2233
0
    static_cast<VRTWarpedDataset *>(poDS)->GetBlockSize(&nBlockXSize,
2234
0
                                                        &nBlockYSize);
2235
2236
0
    if (eType != GDT_Unknown)
2237
0
        eDataType = eType;
2238
0
}
2239
2240
/************************************************************************/
2241
/*                        ~VRTWarpedRasterBand()                        */
2242
/************************************************************************/
2243
2244
VRTWarpedRasterBand::~VRTWarpedRasterBand()
2245
2246
0
{
2247
0
    FlushCache(true);
2248
0
}
2249
2250
/************************************************************************/
2251
/*                             IReadBlock()                             */
2252
/************************************************************************/
2253
2254
CPLErr VRTWarpedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
2255
                                       void *pImage)
2256
2257
0
{
2258
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2259
0
    const GPtrDiff_t nDataBytes =
2260
0
        static_cast<GPtrDiff_t>(GDALGetDataTypeSizeBytes(eDataType)) *
2261
0
        nBlockXSize * nBlockYSize;
2262
2263
0
    GDALRasterBlock *poBlock = GetLockedBlockRef(nBlockXOff, nBlockYOff, TRUE);
2264
0
    if (poBlock == nullptr)
2265
0
        return CE_Failure;
2266
2267
0
    if (poWDS->m_poWarper)
2268
0
    {
2269
0
        const GDALWarpOptions *psWO = poWDS->m_poWarper->GetOptions();
2270
0
        if (nBand == psWO->nDstAlphaBand)
2271
0
        {
2272
            // For a reader starting by asking on band 1, we should normally
2273
            // not reach here, because ProcessBlock() on band 1 will have
2274
            // populated the block cache for the regular bands and the alpha
2275
            // band.
2276
            // But if there's no source window corresponding to the block,
2277
            // the alpha band block will not be written through RasterIO(),
2278
            // so we nee to initialize it.
2279
0
            memset(poBlock->GetDataRef(), 0, nDataBytes);
2280
0
        }
2281
0
    }
2282
2283
0
    const CPLErr eErr = poWDS->ProcessBlock(nBlockXOff, nBlockYOff);
2284
2285
0
    if (eErr == CE_None && pImage != poBlock->GetDataRef())
2286
0
    {
2287
0
        memcpy(pImage, poBlock->GetDataRef(), nDataBytes);
2288
0
    }
2289
2290
0
    poBlock->DropLock();
2291
2292
0
    return eErr;
2293
0
}
2294
2295
/************************************************************************/
2296
/*                            IWriteBlock()                             */
2297
/************************************************************************/
2298
2299
CPLErr VRTWarpedRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
2300
                                        void *pImage)
2301
2302
0
{
2303
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2304
2305
    // This is a bit tricky. In the case we are warping a VRTWarpedDataset
2306
    // with a destination alpha band, IWriteBlock can be called on that alpha
2307
    // band by GDALWarpDstAlphaMasker
2308
    // We don't need to do anything since the data will have hopefully been
2309
    // read from the block cache before if the reader processes all the bands
2310
    // of a same block.
2311
0
    if (poWDS->m_poWarper->GetOptions()->nDstAlphaBand != nBand)
2312
0
    {
2313
        /* Otherwise, call the superclass method, that will fail of course */
2314
0
        return VRTRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage);
2315
0
    }
2316
2317
0
    return CE_None;
2318
0
}
2319
2320
/************************************************************************/
2321
/*                EmitErrorMessageIfWriteNotSupported()                 */
2322
/************************************************************************/
2323
2324
bool VRTWarpedRasterBand::EmitErrorMessageIfWriteNotSupported(
2325
    const char *pszCaller) const
2326
0
{
2327
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2328
    // Cf comment in IWriteBlock()
2329
0
    if (poWDS->m_poWarper->GetOptions()->nDstAlphaBand != nBand)
2330
0
    {
2331
0
        ReportError(CE_Failure, CPLE_NoWriteAccess,
2332
0
                    "%s: attempt to write to a VRTWarpedRasterBand.",
2333
0
                    pszCaller);
2334
2335
0
        return true;
2336
0
    }
2337
0
    return false;
2338
0
}
2339
2340
/************************************************************************/
2341
/*                        GetBestOverviewLevel()                        */
2342
/************************************************************************/
2343
2344
int VRTWarpedRasterBand::GetBestOverviewLevel(
2345
    int &nXOff, int &nYOff, int &nXSize, int &nYSize, int nBufXSize,
2346
    int nBufYSize, GDALRasterIOExtraArg *psExtraArg) const
2347
0
{
2348
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2349
2350
    /* -------------------------------------------------------------------- */
2351
    /*      Compute the desired downsampling factor.  It is                 */
2352
    /*      based on the least reduced axis, and represents the number      */
2353
    /*      of source pixels to one destination pixel.                      */
2354
    /* -------------------------------------------------------------------- */
2355
0
    const double dfDesiredDownsamplingFactor =
2356
0
        ((nXSize / static_cast<double>(nBufXSize)) <
2357
0
             (nYSize / static_cast<double>(nBufYSize)) ||
2358
0
         nBufYSize == 1)
2359
0
            ? nXSize / static_cast<double>(nBufXSize)
2360
0
            : nYSize / static_cast<double>(nBufYSize);
2361
2362
    /* -------------------------------------------------------------------- */
2363
    /*      Find the overview level that largest downsampling factor (most  */
2364
    /*      downsampled) that is still less than (or only a little more)    */
2365
    /*      downsampled than the request.                                   */
2366
    /* -------------------------------------------------------------------- */
2367
0
    const GDALWarpOptions *psWO = poWDS->m_poWarper->GetOptions();
2368
0
    GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
2369
0
    const int nOverviewCount = poSrcDS->GetRasterBand(1)->GetOverviewCount();
2370
2371
0
    int nBestOverviewXSize = 1;
2372
0
    int nBestOverviewYSize = 1;
2373
0
    double dfBestDownsamplingFactor = 0;
2374
0
    int nBestOverviewLevel = -1;
2375
2376
0
    const char *pszOversampligThreshold =
2377
0
        CPLGetConfigOption("GDAL_OVERVIEW_OVERSAMPLING_THRESHOLD", nullptr);
2378
2379
    // Cf https://github.com/OSGeo/gdal/pull/9040#issuecomment-1898524693
2380
    // Do not exactly use a oversampling threshold of 1.0 because of numerical
2381
    // instability.
2382
0
    const auto AdjustThreshold = [](double x)
2383
0
    {
2384
0
        constexpr double EPS = 1e-2;
2385
0
        return x == 1.0 ? x + EPS : x;
2386
0
    };
2387
0
    const double dfOversamplingThreshold = AdjustThreshold(
2388
0
        pszOversampligThreshold ? CPLAtof(pszOversampligThreshold)
2389
0
        : psExtraArg && psExtraArg->eResampleAlg != GRIORA_NearestNeighbour
2390
0
            ? 1.0
2391
0
            : 1.2);
2392
0
    for (int iOverview = 0; iOverview < nOverviewCount; iOverview++)
2393
0
    {
2394
0
        const GDALRasterBand *poSrcOvrBand = this;
2395
0
        bool bThisLevelOnly = false;
2396
0
        const int iSrcOvr =
2397
0
            poWDS->GetSrcOverviewLevel(iOverview, bThisLevelOnly);
2398
0
        if (iSrcOvr >= 0)
2399
0
        {
2400
0
            poSrcOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(iSrcOvr);
2401
0
        }
2402
0
        if (poSrcOvrBand == nullptr)
2403
0
            break;
2404
2405
0
        int nDstPixels = 0;
2406
0
        int nDstLines = 0;
2407
0
        double dfSrcRatioX = 0;
2408
0
        double dfSrcRatioY = 0;
2409
0
        if (!poWDS->GetOverviewSize(poSrcDS, iOverview, iSrcOvr, nDstPixels,
2410
0
                                    nDstLines, dfSrcRatioX, dfSrcRatioY))
2411
0
        {
2412
0
            break;
2413
0
        }
2414
2415
        // Compute downsampling factor of this overview
2416
0
        const double dfDownsamplingFactor =
2417
0
            std::min(nRasterXSize / static_cast<double>(nDstPixels),
2418
0
                     nRasterYSize / static_cast<double>(nDstLines));
2419
2420
        // Is it nearly the requested factor and better (lower) than
2421
        // the current best factor?
2422
0
        if (dfDownsamplingFactor >=
2423
0
                dfDesiredDownsamplingFactor * dfOversamplingThreshold ||
2424
0
            dfDownsamplingFactor <= dfBestDownsamplingFactor)
2425
0
        {
2426
0
            continue;
2427
0
        }
2428
2429
        // Ignore AVERAGE_BIT2GRAYSCALE overviews for RasterIO purposes.
2430
0
        const char *pszResampling = const_cast<GDALRasterBand *>(poSrcOvrBand)
2431
0
                                        ->GetMetadataItem("RESAMPLING");
2432
2433
0
        if (pszResampling != nullptr &&
2434
0
            STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
2435
0
            continue;
2436
2437
        // OK, this is our new best overview.
2438
0
        nBestOverviewXSize = nDstPixels;
2439
0
        nBestOverviewYSize = nDstLines;
2440
0
        nBestOverviewLevel = iOverview;
2441
0
        dfBestDownsamplingFactor = dfDownsamplingFactor;
2442
0
    }
2443
2444
    /* -------------------------------------------------------------------- */
2445
    /*      If we didn't find an overview that helps us, just return        */
2446
    /*      indicating failure and the full resolution image will be used.  */
2447
    /* -------------------------------------------------------------------- */
2448
0
    if (nBestOverviewLevel < 0)
2449
0
        return -1;
2450
2451
    /* -------------------------------------------------------------------- */
2452
    /*      Recompute the source window in terms of the selected            */
2453
    /*      overview.                                                       */
2454
    /* -------------------------------------------------------------------- */
2455
0
    const double dfXFactor =
2456
0
        nRasterXSize / static_cast<double>(nBestOverviewXSize);
2457
0
    const double dfYFactor =
2458
0
        nRasterYSize / static_cast<double>(nBestOverviewYSize);
2459
0
    CPLDebug("GDAL", "Selecting overview %d x %d", nBestOverviewXSize,
2460
0
             nBestOverviewYSize);
2461
2462
0
    const int nOXOff = std::min(nBestOverviewXSize - 1,
2463
0
                                static_cast<int>(nXOff / dfXFactor + 0.5));
2464
0
    const int nOYOff = std::min(nBestOverviewYSize - 1,
2465
0
                                static_cast<int>(nYOff / dfYFactor + 0.5));
2466
0
    int nOXSize = std::max(1, static_cast<int>(nXSize / dfXFactor + 0.5));
2467
0
    int nOYSize = std::max(1, static_cast<int>(nYSize / dfYFactor + 0.5));
2468
0
    if (nOXOff + nOXSize > nBestOverviewXSize)
2469
0
        nOXSize = nBestOverviewXSize - nOXOff;
2470
0
    if (nOYOff + nOYSize > nBestOverviewYSize)
2471
0
        nOYSize = nBestOverviewYSize - nOYOff;
2472
2473
0
    if (psExtraArg)
2474
0
    {
2475
0
        if (psExtraArg->bFloatingPointWindowValidity)
2476
0
        {
2477
0
            psExtraArg->dfXOff /= dfXFactor;
2478
0
            psExtraArg->dfXSize /= dfXFactor;
2479
0
            psExtraArg->dfYOff /= dfYFactor;
2480
0
            psExtraArg->dfYSize /= dfYFactor;
2481
0
        }
2482
0
        else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2483
0
        {
2484
0
            psExtraArg->bFloatingPointWindowValidity = true;
2485
0
            psExtraArg->dfXOff = nXOff / dfXFactor;
2486
0
            psExtraArg->dfXSize = nXSize / dfXFactor;
2487
0
            psExtraArg->dfYOff = nYOff / dfYFactor;
2488
0
            psExtraArg->dfYSize = nYSize / dfYFactor;
2489
0
        }
2490
0
    }
2491
2492
0
    nXOff = nOXOff;
2493
0
    nYOff = nOYOff;
2494
0
    nXSize = nOXSize;
2495
0
    nYSize = nOYSize;
2496
2497
0
    return nBestOverviewLevel;
2498
0
}
2499
2500
/************************************************************************/
2501
/*                             IRasterIO()                              */
2502
/************************************************************************/
2503
2504
CPLErr VRTWarpedRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2505
                                      int nXSize, int nYSize, void *pData,
2506
                                      int nBufXSize, int nBufYSize,
2507
                                      GDALDataType eBufType,
2508
                                      GSpacing nPixelSpace, GSpacing nLineSpace,
2509
                                      GDALRasterIOExtraArg *psExtraArg)
2510
0
{
2511
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2512
0
    if (m_nIRasterIOCounter == 0 && poWDS->GetRasterCount() == 1)
2513
0
    {
2514
0
        int anBandMap[] = {nBand};
2515
0
        ++m_nIRasterIOCounter;
2516
0
        const CPLErr eErr = poWDS->IRasterIO(
2517
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2518
0
            eBufType, 1, anBandMap, nPixelSpace, nLineSpace, 0, psExtraArg);
2519
0
        --m_nIRasterIOCounter;
2520
0
        return eErr;
2521
0
    }
2522
2523
    /* ==================================================================== */
2524
    /*      Do we have overviews that would be appropriate to satisfy       */
2525
    /*      this request?                                                   */
2526
    /* ==================================================================== */
2527
0
    if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() &&
2528
0
        eRWFlag == GF_Read)
2529
0
    {
2530
0
        GDALRasterIOExtraArg sExtraArg;
2531
0
        GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
2532
2533
0
        const int nOverview = GetBestOverviewLevel(
2534
0
            nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, &sExtraArg);
2535
0
        if (nOverview >= 0)
2536
0
        {
2537
0
            auto poOvrBand = GetOverview(nOverview);
2538
0
            if (!poOvrBand)
2539
0
                return CE_Failure;
2540
2541
0
            return poOvrBand->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2542
0
                                       pData, nBufXSize, nBufYSize, eBufType,
2543
0
                                       nPixelSpace, nLineSpace, &sExtraArg);
2544
0
        }
2545
0
    }
2546
2547
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2548
0
                                     pData, nBufXSize, nBufYSize, eBufType,
2549
0
                                     nPixelSpace, nLineSpace, psExtraArg);
2550
0
}
2551
2552
/************************************************************************/
2553
/*                           SerializeToXML()                           */
2554
/************************************************************************/
2555
2556
CPLXMLNode *VRTWarpedRasterBand::SerializeToXML(const char *pszVRTPathIn,
2557
                                                bool &bHasWarnedAboutRAMUsage,
2558
                                                size_t &nAccRAMUsage)
2559
2560
0
{
2561
0
    CPLXMLNode *const psTree = VRTRasterBand::SerializeToXML(
2562
0
        pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
2563
2564
    /* -------------------------------------------------------------------- */
2565
    /*      Set subclass.                                                   */
2566
    /* -------------------------------------------------------------------- */
2567
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
2568
0
                     CXT_Text, "VRTWarpedRasterBand");
2569
2570
0
    return psTree;
2571
0
}
2572
2573
/************************************************************************/
2574
/*                          GetOverviewCount()                          */
2575
/************************************************************************/
2576
2577
int VRTWarpedRasterBand::GetOverviewCount()
2578
2579
0
{
2580
0
    VRTWarpedDataset *const poWDS = static_cast<VRTWarpedDataset *>(poDS);
2581
0
    if (poWDS->m_bIsOverview)
2582
0
        return 0;
2583
2584
0
    if (poWDS->m_apoOverviews.empty())
2585
0
    {
2586
0
        return poWDS->GetOverviewCount();
2587
0
    }
2588
2589
0
    return static_cast<int>(poWDS->m_apoOverviews.size());
2590
0
}
2591
2592
/************************************************************************/
2593
/*                            GetOverview()                             */
2594
/************************************************************************/
2595
2596
GDALRasterBand *VRTWarpedRasterBand::GetOverview(int iOverview)
2597
2598
0
{
2599
0
    VRTWarpedDataset *const poWDS = static_cast<VRTWarpedDataset *>(poDS);
2600
2601
0
    const int nOvrCount = GetOverviewCount();
2602
0
    if (iOverview < 0 || iOverview >= nOvrCount)
2603
0
        return nullptr;
2604
2605
0
    if (poWDS->m_apoOverviews.empty())
2606
0
        poWDS->m_apoOverviews.resize(nOvrCount);
2607
0
    if (!poWDS->m_apoOverviews[iOverview])
2608
0
        poWDS->m_apoOverviews[iOverview] =
2609
0
            poWDS->CreateImplicitOverview(iOverview);
2610
0
    if (!poWDS->m_apoOverviews[iOverview])
2611
0
        return nullptr;
2612
0
    return poWDS->m_apoOverviews[iOverview]->GetRasterBand(nBand);
2613
0
}
2614
2615
/*! @endcond */