Coverage Report

Created: 2026-07-25 06:50

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