Coverage Report

Created: 2025-06-22 06:59

/src/gdal/frmts/vrt/vrtwarped.cpp
Line
Count
Source (jump to first uncovered line)
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
 * Traditonal 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
 * Traditonal 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
                                             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(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("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
623
0
    }
624
625
0
    return eErr;
626
0
}
627
628
/************************************************************************/
629
/*                        GDALWarpCoordRescaler                         */
630
/************************************************************************/
631
632
class GDALWarpCoordRescaler : public OGRCoordinateTransformation
633
{
634
    const double m_dfRatioX;
635
    const double m_dfRatioY;
636
637
    GDALWarpCoordRescaler &operator=(const GDALWarpCoordRescaler &) = delete;
638
    GDALWarpCoordRescaler(GDALWarpCoordRescaler &&) = delete;
639
    GDALWarpCoordRescaler &operator=(GDALWarpCoordRescaler &&) = delete;
640
641
  public:
642
    GDALWarpCoordRescaler(double dfRatioX, double dfRatioY)
643
0
        : m_dfRatioX(dfRatioX), m_dfRatioY(dfRatioY)
644
0
    {
645
0
    }
646
647
0
    GDALWarpCoordRescaler(const GDALWarpCoordRescaler &) = default;
648
649
    ~GDALWarpCoordRescaler() override;
650
651
    virtual const OGRSpatialReference *GetSourceCS() const override
652
0
    {
653
0
        return nullptr;
654
0
    }
655
656
    virtual const OGRSpatialReference *GetTargetCS() const override
657
0
    {
658
0
        return nullptr;
659
0
    }
660
661
    virtual int Transform(size_t nCount, double *x, double *y, double * /*z*/,
662
                          double * /*t*/, int *pabSuccess) override
663
0
    {
664
0
        for (size_t i = 0; i < nCount; i++)
665
0
        {
666
0
            x[i] *= m_dfRatioX;
667
0
            y[i] *= m_dfRatioY;
668
0
            if (pabSuccess)
669
0
                pabSuccess[i] = TRUE;
670
0
        }
671
0
        return TRUE;
672
0
    }
673
674
    virtual OGRCoordinateTransformation *Clone() const override
675
0
    {
676
0
        return new GDALWarpCoordRescaler(*this);
677
0
    }
678
679
    virtual OGRCoordinateTransformation *GetInverse() const override
680
0
    {
681
0
        return nullptr;
682
0
    }
683
};
684
685
0
GDALWarpCoordRescaler::~GDALWarpCoordRescaler() = default;
686
687
/************************************************************************/
688
/*                        RescaleDstGeoTransform()                      */
689
/************************************************************************/
690
691
static void RescaleDstGeoTransform(double adfDstGeoTransform[6],
692
                                   int nRasterXSize, int nDstPixels,
693
                                   int nRasterYSize, int nDstLines)
694
0
{
695
0
    adfDstGeoTransform[1] *= static_cast<double>(nRasterXSize) / nDstPixels;
696
0
    adfDstGeoTransform[2] *= static_cast<double>(nRasterXSize) / nDstPixels;
697
0
    adfDstGeoTransform[4] *= static_cast<double>(nRasterYSize) / nDstLines;
698
0
    adfDstGeoTransform[5] *= static_cast<double>(nRasterYSize) / nDstLines;
699
0
}
700
701
/************************************************************************/
702
/*                        GetSrcOverviewLevel()                         */
703
/************************************************************************/
704
705
int VRTWarpedDataset::GetSrcOverviewLevel(int iOvr,
706
                                          bool &bThisLevelOnlyOut) const
707
0
{
708
0
    bThisLevelOnlyOut = false;
709
0
    if (m_nSrcOvrLevel < -2)
710
0
    {
711
0
        if (iOvr + m_nSrcOvrLevel + 2 >= 0)
712
0
        {
713
0
            return iOvr + m_nSrcOvrLevel + 2;
714
0
        }
715
0
    }
716
0
    else if (m_nSrcOvrLevel == -2)
717
0
    {
718
0
        return iOvr;
719
0
    }
720
0
    else if (m_nSrcOvrLevel >= 0)
721
0
    {
722
0
        bThisLevelOnlyOut = true;
723
0
        return m_nSrcOvrLevel;
724
0
    }
725
0
    return -1;
726
0
}
727
728
/************************************************************************/
729
/*                            GetOverviewSize()                         */
730
/************************************************************************/
731
732
bool VRTWarpedDataset::GetOverviewSize(GDALDataset *poSrcDS, int iOvr,
733
                                       int iSrcOvr, int &nOvrXSize,
734
                                       int &nOvrYSize, double &dfSrcRatioX,
735
                                       double &dfSrcRatioY) const
736
0
{
737
0
    auto poSrcOvrBand = iSrcOvr >= 0
738
0
                            ? poSrcDS->GetRasterBand(1)->GetOverview(iSrcOvr)
739
0
                            : poSrcDS->GetRasterBand(1);
740
0
    if (!poSrcOvrBand)
741
0
    {
742
0
        return false;
743
0
    }
744
0
    dfSrcRatioX = static_cast<double>(poSrcDS->GetRasterXSize()) /
745
0
                  poSrcOvrBand->GetXSize();
746
0
    dfSrcRatioY = static_cast<double>(poSrcDS->GetRasterYSize()) /
747
0
                  poSrcOvrBand->GetYSize();
748
0
    const double dfTargetRatio =
749
0
        static_cast<double>(poSrcDS->GetRasterXSize()) /
750
0
        poSrcDS->GetRasterBand(1)->GetOverview(iOvr)->GetXSize();
751
752
0
    nOvrXSize = static_cast<int>(nRasterXSize / dfTargetRatio + 0.5);
753
0
    nOvrYSize = static_cast<int>(nRasterYSize / dfTargetRatio + 0.5);
754
0
    return nOvrXSize >= 1 && nOvrYSize >= 1;
755
0
}
756
757
/************************************************************************/
758
/*                        CreateImplicitOverview()                      */
759
/************************************************************************/
760
761
VRTWarpedDataset *VRTWarpedDataset::CreateImplicitOverview(int iOvr) const
762
0
{
763
0
    if (!m_poWarper)
764
0
        return nullptr;
765
0
    const GDALWarpOptions *psWO = m_poWarper->GetOptions();
766
0
    if (!psWO->hSrcDS || GDALGetRasterCount(psWO->hSrcDS) == 0)
767
0
        return nullptr;
768
0
    GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
769
0
    GDALDataset *poSrcOvrDS = poSrcDS;
770
0
    bool bThisLevelOnly = false;
771
0
    const int iSrcOvr = GetSrcOverviewLevel(iOvr, bThisLevelOnly);
772
0
    if (iSrcOvr >= 0)
773
0
    {
774
0
        poSrcOvrDS =
775
0
            GDALCreateOverviewDataset(poSrcDS, iSrcOvr, bThisLevelOnly);
776
0
    }
777
0
    if (poSrcOvrDS == nullptr)
778
0
        return nullptr;
779
0
    if (poSrcOvrDS == poSrcDS)
780
0
        poSrcOvrDS->Reference();
781
782
0
    int nDstPixels = 0;
783
0
    int nDstLines = 0;
784
0
    double dfSrcRatioX = 0;
785
0
    double dfSrcRatioY = 0;
786
    // Figure out the desired output bounds and resolution.
787
0
    if (!GetOverviewSize(poSrcDS, iOvr, iSrcOvr, nDstPixels, nDstLines,
788
0
                         dfSrcRatioX, dfSrcRatioY))
789
0
    {
790
0
        poSrcOvrDS->ReleaseRef();
791
0
        return nullptr;
792
0
    }
793
794
    /* --------------------------------------------------------------------
795
     */
796
    /*      Create transformer and warping options. */
797
    /* --------------------------------------------------------------------
798
     */
799
0
    void *pTransformerArg = GDALCreateSimilarTransformer(
800
0
        psWO->pTransformerArg, dfSrcRatioX, dfSrcRatioY);
801
0
    if (pTransformerArg == nullptr)
802
0
    {
803
0
        poSrcOvrDS->ReleaseRef();
804
0
        return nullptr;
805
0
    }
806
807
0
    GDALWarpOptions *psWOOvr = GDALCloneWarpOptions(psWO);
808
0
    psWOOvr->hSrcDS = poSrcOvrDS;
809
0
    psWOOvr->pfnTransformer = psWO->pfnTransformer;
810
0
    psWOOvr->pTransformerArg = pTransformerArg;
811
812
    /* --------------------------------------------------------------------
813
     */
814
    /*      We need to rescale the potential CUTLINE */
815
    /* --------------------------------------------------------------------
816
     */
817
0
    if (psWOOvr->hCutline)
818
0
    {
819
0
        GDALWarpCoordRescaler oRescaler(1.0 / dfSrcRatioX, 1.0 / dfSrcRatioY);
820
0
        static_cast<OGRGeometry *>(psWOOvr->hCutline)->transform(&oRescaler);
821
0
    }
822
823
    /* --------------------------------------------------------------------
824
     */
825
    /*      Rescale the output geotransform on the transformer. */
826
    /* --------------------------------------------------------------------
827
     */
828
0
    double adfDstGeoTransform[6] = {0.0};
829
0
    GDALGetTransformerDstGeoTransform(psWOOvr->pTransformerArg,
830
0
                                      adfDstGeoTransform);
831
0
    RescaleDstGeoTransform(adfDstGeoTransform, nRasterXSize, nDstPixels,
832
0
                           nRasterYSize, nDstLines);
833
0
    GDALSetTransformerDstGeoTransform(psWOOvr->pTransformerArg,
834
0
                                      adfDstGeoTransform);
835
836
    /* --------------------------------------------------------------------
837
     */
838
    /*      Create the VRT file. */
839
    /* --------------------------------------------------------------------
840
     */
841
0
    GDALDatasetH hDstDS = GDALCreateWarpedVRT(poSrcOvrDS, nDstPixels, nDstLines,
842
0
                                              adfDstGeoTransform, psWOOvr);
843
844
0
    poSrcOvrDS->ReleaseRef();
845
846
0
    GDALDestroyWarpOptions(psWOOvr);
847
848
0
    if (hDstDS == nullptr)
849
0
    {
850
0
        GDALDestroyTransformer(pTransformerArg);
851
0
        return nullptr;
852
0
    }
853
854
0
    auto poOvrDS = static_cast<VRTWarpedDataset *>(hDstDS);
855
0
    poOvrDS->m_bIsOverview = true;
856
0
    return poOvrDS;
857
0
}
858
859
/************************************************************************/
860
/*                           GetOverviewCount()                         */
861
/************************************************************************/
862
863
int VRTWarpedDataset::GetOverviewCount() const
864
0
{
865
0
    if (m_poWarper)
866
0
    {
867
0
        const GDALWarpOptions *psWO = m_poWarper->GetOptions();
868
0
        if (!m_bIsOverview && psWO->hSrcDS && GDALGetRasterCount(psWO->hSrcDS))
869
0
        {
870
0
            GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
871
0
            int nSrcOverviewCount =
872
0
                poSrcDS->GetRasterBand(1)->GetOverviewCount();
873
0
            int nCount = 0;
874
0
            for (int i = 0; i < nSrcOverviewCount; ++i)
875
0
            {
876
0
                bool bThisLevelOnly = false;
877
0
                const int iSrcOvr = GetSrcOverviewLevel(i, bThisLevelOnly);
878
0
                if (iSrcOvr >= 0)
879
0
                {
880
0
                    int nDstPixels = 0;
881
0
                    int nDstLines = 0;
882
0
                    double dfSrcRatioX = 0;
883
0
                    double dfSrcRatioY = 0;
884
0
                    if (!GetOverviewSize(poSrcDS, i, iSrcOvr, nDstPixels,
885
0
                                         nDstLines, dfSrcRatioX, dfSrcRatioY))
886
0
                    {
887
0
                        break;
888
0
                    }
889
0
                }
890
0
                ++nCount;
891
0
            }
892
0
            return nCount;
893
0
        }
894
0
    }
895
0
    return 0;
896
0
}
897
898
/************************************************************************/
899
/*                        CreateImplicitOverviews()                     */
900
/*                                                                      */
901
/*      For each overview of the source dataset, create an overview     */
902
/*      in the warped VRT dataset.                                      */
903
/************************************************************************/
904
905
void VRTWarpedDataset::CreateImplicitOverviews()
906
0
{
907
0
    if (m_bIsOverview)
908
0
        return;
909
0
    const int nOvrCount = GetOverviewCount();
910
0
    if (m_apoOverviews.empty())
911
0
        m_apoOverviews.resize(nOvrCount);
912
0
    for (int iOvr = 0; iOvr < nOvrCount; iOvr++)
913
0
    {
914
0
        if (!m_apoOverviews[iOvr])
915
0
        {
916
0
            m_apoOverviews[iOvr] = CreateImplicitOverview(iOvr);
917
0
        }
918
0
    }
919
0
}
920
921
/************************************************************************/
922
/*                            GetFileList()                             */
923
/************************************************************************/
924
925
char **VRTWarpedDataset::GetFileList()
926
0
{
927
0
    char **papszFileList = GDALDataset::GetFileList();
928
929
0
    if (m_poWarper != nullptr)
930
0
    {
931
0
        const GDALWarpOptions *psWO = m_poWarper->GetOptions();
932
0
        const char *pszFilename = nullptr;
933
934
0
        if (psWO->hSrcDS != nullptr &&
935
0
            (pszFilename =
936
0
                 static_cast<GDALDataset *>(psWO->hSrcDS)->GetDescription()) !=
937
0
                nullptr)
938
0
        {
939
0
            VSIStatBufL sStat;
940
0
            if (VSIStatL(pszFilename, &sStat) == 0)
941
0
            {
942
0
                papszFileList = CSLAddString(papszFileList, pszFilename);
943
0
            }
944
0
        }
945
0
    }
946
947
0
    return papszFileList;
948
0
}
949
950
/************************************************************************/
951
/* ==================================================================== */
952
/*                    VRTWarpedOverviewTransformer                      */
953
/* ==================================================================== */
954
/************************************************************************/
955
956
typedef struct
957
{
958
    GDALTransformerInfo sTI;
959
960
    GDALTransformerFunc pfnBaseTransformer;
961
    void *pBaseTransformerArg;
962
    bool bOwnSubtransformer;
963
964
    double dfXOverviewFactor;
965
    double dfYOverviewFactor;
966
} VWOTInfo;
967
968
static void *VRTCreateWarpedOverviewTransformer(
969
    GDALTransformerFunc pfnBaseTransformer, void *pBaseTransformArg,
970
    double dfXOverviewFactor, double dfYOverviewFactor);
971
static void VRTDestroyWarpedOverviewTransformer(void *pTransformArg);
972
973
static int VRTWarpedOverviewTransform(void *pTransformArg, int bDstToSrc,
974
                                      int nPointCount, double *padfX,
975
                                      double *padfY, double *padfZ,
976
                                      int *panSuccess);
977
978
#if 0   // TODO: Why?
979
/************************************************************************/
980
/*                VRTSerializeWarpedOverviewTransformer()               */
981
/************************************************************************/
982
983
static CPLXMLNode *
984
VRTSerializeWarpedOverviewTransformer( void *pTransformArg )
985
986
{
987
    VWOTInfo *psInfo = static_cast<VWOTInfo *>( pTransformArg );
988
989
    CPLXMLNode *psTree
990
        = CPLCreateXMLNode( NULL, CXT_Element, "WarpedOverviewTransformer" );
991
992
    CPLCreateXMLElementAndValue(
993
        psTree, "XFactor",
994
        CPLString().Printf("%g",psInfo->dfXOverviewFactor) );
995
    CPLCreateXMLElementAndValue(
996
        psTree, "YFactor",
997
        CPLString().Printf("%g",psInfo->dfYOverviewFactor) );
998
999
/* -------------------------------------------------------------------- */
1000
/*      Capture underlying transformer.                                 */
1001
/* -------------------------------------------------------------------- */
1002
    CPLXMLNode *psTransformerContainer
1003
        = CPLCreateXMLNode( psTree, CXT_Element, "BaseTransformer" );
1004
1005
    CPLXMLNode *psTransformer
1006
        = GDALSerializeTransformer( psInfo->pfnBaseTransformer,
1007
                                    psInfo->pBaseTransformerArg );
1008
    if( psTransformer != NULL )
1009
        CPLAddXMLChild( psTransformerContainer, psTransformer );
1010
1011
    return psTree;
1012
}
1013
1014
/************************************************************************/
1015
/*           VRTWarpedOverviewTransformerOwnsSubtransformer()           */
1016
/************************************************************************/
1017
1018
static void VRTWarpedOverviewTransformerOwnsSubtransformer( void *pTransformArg,
1019
                                                            bool bOwnFlag )
1020
{
1021
    VWOTInfo *psInfo = static_cast<VWOTInfo *>( pTransformArg );
1022
1023
    psInfo->bOwnSubtransformer = bOwnFlag;
1024
}
1025
1026
/************************************************************************/
1027
/*            VRTDeserializeWarpedOverviewTransformer()                 */
1028
/************************************************************************/
1029
1030
void* VRTDeserializeWarpedOverviewTransformer( CPLXMLNode *psTree )
1031
1032
{
1033
    const double dfXOverviewFactor =
1034
        CPLAtof(CPLGetXMLValue( psTree, "XFactor",  "1" ));
1035
    const double dfYOverviewFactor =
1036
        CPLAtof(CPLGetXMLValue( psTree, "YFactor",  "1" ));
1037
    GDALTransformerFunc pfnBaseTransform = NULL;
1038
    void *pBaseTransformerArg = NULL;
1039
1040
    CPLXMLNode *psContainer = CPLGetXMLNode( psTree, "BaseTransformer" );
1041
1042
    if( psContainer != NULL && psContainer->psChild != NULL )
1043
    {
1044
        GDALDeserializeTransformer( psContainer->psChild,
1045
                                    &pfnBaseTransform,
1046
                                    &pBaseTransformerArg );
1047
    }
1048
1049
    if( pfnBaseTransform == NULL )
1050
    {
1051
        CPLError( CE_Failure, CPLE_AppDefined,
1052
                  "Cannot get base transform for scaled coord transformer." );
1053
        return NULL;
1054
    }
1055
    else
1056
    {
1057
        void *pApproxCBData =
1058
                       VRTCreateWarpedOverviewTransformer( pfnBaseTransform,
1059
                                                           pBaseTransformerArg,
1060
                                                           dfXOverviewFactor,
1061
                                                           dfYOverviewFactor );
1062
        VRTWarpedOverviewTransformerOwnsSubtransformer( pApproxCBData, true );
1063
1064
        return pApproxCBData;
1065
    }
1066
}
1067
#endif  // TODO: Why disabled?
1068
1069
/************************************************************************/
1070
/*                   VRTCreateWarpedOverviewTransformer()               */
1071
/************************************************************************/
1072
1073
static void *VRTCreateWarpedOverviewTransformer(
1074
    GDALTransformerFunc pfnBaseTransformer, void *pBaseTransformerArg,
1075
    double dfXOverviewFactor, double dfYOverviewFactor)
1076
1077
0
{
1078
0
    if (pfnBaseTransformer == nullptr)
1079
0
        return nullptr;
1080
1081
0
    VWOTInfo *psSCTInfo = static_cast<VWOTInfo *>(CPLMalloc(sizeof(VWOTInfo)));
1082
0
    psSCTInfo->pfnBaseTransformer = pfnBaseTransformer;
1083
0
    psSCTInfo->pBaseTransformerArg = pBaseTransformerArg;
1084
0
    psSCTInfo->dfXOverviewFactor = dfXOverviewFactor;
1085
0
    psSCTInfo->dfYOverviewFactor = dfYOverviewFactor;
1086
0
    psSCTInfo->bOwnSubtransformer = false;
1087
1088
0
    memcpy(psSCTInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
1089
0
           strlen(GDAL_GTI2_SIGNATURE));
1090
0
    psSCTInfo->sTI.pszClassName = "VRTWarpedOverviewTransformer";
1091
0
    psSCTInfo->sTI.pfnTransform = VRTWarpedOverviewTransform;
1092
0
    psSCTInfo->sTI.pfnCleanup = VRTDestroyWarpedOverviewTransformer;
1093
#if 0
1094
    psSCTInfo->sTI.pfnSerialize = VRTSerializeWarpedOverviewTransformer;
1095
#endif
1096
0
    return psSCTInfo;
1097
0
}
1098
1099
/************************************************************************/
1100
/*               VRTDestroyWarpedOverviewTransformer()                  */
1101
/************************************************************************/
1102
1103
static void VRTDestroyWarpedOverviewTransformer(void *pTransformArg)
1104
0
{
1105
0
    VWOTInfo *psInfo = static_cast<VWOTInfo *>(pTransformArg);
1106
1107
0
    if (psInfo->bOwnSubtransformer)
1108
0
        GDALDestroyTransformer(psInfo->pBaseTransformerArg);
1109
1110
0
    CPLFree(psInfo);
1111
0
}
1112
1113
/************************************************************************/
1114
/*                     VRTWarpedOverviewTransform()                     */
1115
/************************************************************************/
1116
1117
static int VRTWarpedOverviewTransform(void *pTransformArg, int bDstToSrc,
1118
                                      int nPointCount, double *padfX,
1119
                                      double *padfY, double *padfZ,
1120
                                      int *panSuccess)
1121
1122
0
{
1123
0
    VWOTInfo *psInfo = static_cast<VWOTInfo *>(pTransformArg);
1124
1125
0
    if (bDstToSrc)
1126
0
    {
1127
0
        for (int i = 0; i < nPointCount; i++)
1128
0
        {
1129
0
            padfX[i] *= psInfo->dfXOverviewFactor;
1130
0
            padfY[i] *= psInfo->dfYOverviewFactor;
1131
0
        }
1132
0
    }
1133
1134
0
    const int bSuccess = psInfo->pfnBaseTransformer(
1135
0
        psInfo->pBaseTransformerArg, bDstToSrc, nPointCount, padfX, padfY,
1136
0
        padfZ, panSuccess);
1137
1138
0
    if (!bDstToSrc)
1139
0
    {
1140
0
        for (int i = 0; i < nPointCount; i++)
1141
0
        {
1142
0
            padfX[i] /= psInfo->dfXOverviewFactor;
1143
0
            padfY[i] /= psInfo->dfYOverviewFactor;
1144
0
        }
1145
0
    }
1146
1147
0
    return bSuccess;
1148
0
}
1149
1150
/************************************************************************/
1151
/*                           BuildOverviews()                           */
1152
/*                                                                      */
1153
/*      For overviews, we actually just build a whole new dataset       */
1154
/*      with an extra layer of transformation on the warper used to     */
1155
/*      accomplish downsampling by the desired factor.                  */
1156
/************************************************************************/
1157
1158
CPLErr VRTWarpedDataset::IBuildOverviews(
1159
    const char * /* pszResampling */, int nOverviews,
1160
    const int *panOverviewList, int /* nListBands */,
1161
    const int * /* panBandList */, GDALProgressFunc pfnProgress,
1162
    void *pProgressData, CSLConstList /*papszOptions*/)
1163
0
{
1164
0
    if (m_poWarper == nullptr || m_bIsOverview)
1165
0
        return CE_Failure;
1166
1167
    /* -------------------------------------------------------------------- */
1168
    /*      Initial progress result.                                        */
1169
    /* -------------------------------------------------------------------- */
1170
0
    if (!pfnProgress(0.0, nullptr, pProgressData))
1171
0
    {
1172
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1173
0
        return CE_Failure;
1174
0
    }
1175
1176
0
    CreateImplicitOverviews();
1177
1178
    /* -------------------------------------------------------------------- */
1179
    /*      Establish which of the overview levels we already have, and     */
1180
    /*      which are new.                                                  */
1181
    /* -------------------------------------------------------------------- */
1182
0
    int nNewOverviews = 0;
1183
0
    int *panNewOverviewList =
1184
0
        static_cast<int *>(CPLCalloc(sizeof(int), nOverviews));
1185
0
    std::vector<bool> abFoundOverviewFactor(nOverviews);
1186
0
    for (int i = 0; i < nOverviews; i++)
1187
0
    {
1188
0
        for (GDALDataset *const poOverview : m_apoOverviews)
1189
0
        {
1190
0
            if (poOverview)
1191
0
            {
1192
0
                const int nOvFactor = GDALComputeOvFactor(
1193
0
                    poOverview->GetRasterXSize(), GetRasterXSize(),
1194
0
                    poOverview->GetRasterYSize(), GetRasterYSize());
1195
1196
0
                if (nOvFactor == panOverviewList[i] ||
1197
0
                    nOvFactor == GDALOvLevelAdjust2(panOverviewList[i],
1198
0
                                                    GetRasterXSize(),
1199
0
                                                    GetRasterYSize()))
1200
0
                    abFoundOverviewFactor[i] = true;
1201
0
            }
1202
0
        }
1203
1204
0
        if (!abFoundOverviewFactor[i])
1205
0
            panNewOverviewList[nNewOverviews++] = panOverviewList[i];
1206
0
    }
1207
1208
    /* -------------------------------------------------------------------- */
1209
    /*      Create each missing overview (we don't need to do anything      */
1210
    /*      to update existing overviews).                                  */
1211
    /* -------------------------------------------------------------------- */
1212
0
    CPLErr eErr = CE_None;
1213
0
    for (int i = 0; i < nNewOverviews; i++)
1214
0
    {
1215
        /* --------------------------------------------------------------------
1216
         */
1217
        /*      What size should this overview be. */
1218
        /* --------------------------------------------------------------------
1219
         */
1220
0
        const int nOXSize = (GetRasterXSize() + panNewOverviewList[i] - 1) /
1221
0
                            panNewOverviewList[i];
1222
1223
0
        const int nOYSize = (GetRasterYSize() + panNewOverviewList[i] - 1) /
1224
0
                            panNewOverviewList[i];
1225
1226
        /* --------------------------------------------------------------------
1227
         */
1228
        /*      Find the most appropriate base dataset onto which to build the
1229
         */
1230
        /*      new one. The preference will be an overview dataset with a
1231
         * ratio*/
1232
        /*      greater than ours, and which is not using */
1233
        /*      VRTWarpedOverviewTransform, since those ones are slow. The
1234
         * other*/
1235
        /*      ones are based on overviews of the source dataset. */
1236
        /* --------------------------------------------------------------------
1237
         */
1238
0
        VRTWarpedDataset *poBaseDataset = this;
1239
0
        for (auto *poOverview : m_apoOverviews)
1240
0
        {
1241
0
            if (poOverview && poOverview->GetRasterXSize() > nOXSize &&
1242
0
                poOverview->m_poWarper->GetOptions()->pfnTransformer !=
1243
0
                    VRTWarpedOverviewTransform &&
1244
0
                poOverview->GetRasterXSize() < poBaseDataset->GetRasterXSize())
1245
0
            {
1246
0
                poBaseDataset = poOverview;
1247
0
            }
1248
0
        }
1249
1250
        /* --------------------------------------------------------------------
1251
         */
1252
        /*      Create the overview dataset. */
1253
        /* --------------------------------------------------------------------
1254
         */
1255
0
        VRTWarpedDataset *poOverviewDS = new VRTWarpedDataset(nOXSize, nOYSize);
1256
1257
0
        for (int iBand = 0; iBand < GetRasterCount(); iBand++)
1258
0
        {
1259
0
            GDALRasterBand *const poOldBand = GetRasterBand(iBand + 1);
1260
0
            VRTWarpedRasterBand *const poNewBand = new VRTWarpedRasterBand(
1261
0
                poOverviewDS, iBand + 1, poOldBand->GetRasterDataType());
1262
1263
0
            poNewBand->CopyCommonInfoFrom(poOldBand);
1264
0
            poOverviewDS->SetBand(iBand + 1, poNewBand);
1265
0
        }
1266
1267
        /* --------------------------------------------------------------------
1268
         */
1269
        /*      Prepare update transformation information that will apply */
1270
        /*      the overview decimation. */
1271
        /* --------------------------------------------------------------------
1272
         */
1273
0
        GDALWarpOptions *psWO = const_cast<GDALWarpOptions *>(
1274
0
            poBaseDataset->m_poWarper->GetOptions());
1275
1276
        /* --------------------------------------------------------------------
1277
         */
1278
        /*      Initialize the new dataset with adjusted warp options, and */
1279
        /*      then restore to original condition. */
1280
        /* --------------------------------------------------------------------
1281
         */
1282
0
        GDALTransformerFunc pfnTransformerBase = psWO->pfnTransformer;
1283
0
        void *pTransformerBaseArg = psWO->pTransformerArg;
1284
1285
0
        psWO->pfnTransformer = VRTWarpedOverviewTransform;
1286
0
        psWO->pTransformerArg = VRTCreateWarpedOverviewTransformer(
1287
0
            pfnTransformerBase, pTransformerBaseArg,
1288
0
            poBaseDataset->GetRasterXSize() / static_cast<double>(nOXSize),
1289
0
            poBaseDataset->GetRasterYSize() / static_cast<double>(nOYSize));
1290
1291
0
        eErr = poOverviewDS->Initialize(psWO);
1292
1293
0
        psWO->pfnTransformer = pfnTransformerBase;
1294
0
        psWO->pTransformerArg = pTransformerBaseArg;
1295
1296
0
        if (eErr != CE_None)
1297
0
        {
1298
0
            delete poOverviewDS;
1299
0
            break;
1300
0
        }
1301
1302
0
        m_apoOverviews.push_back(poOverviewDS);
1303
0
    }
1304
1305
0
    CPLFree(panNewOverviewList);
1306
1307
    /* -------------------------------------------------------------------- */
1308
    /*      Progress finished.                                              */
1309
    /* -------------------------------------------------------------------- */
1310
0
    pfnProgress(1.0, nullptr, pProgressData);
1311
1312
0
    SetNeedsFlush();
1313
1314
0
    return eErr;
1315
0
}
1316
1317
/*! @endcond */
1318
1319
/************************************************************************/
1320
/*                      GDALInitializeWarpedVRT()                       */
1321
/************************************************************************/
1322
1323
/**
1324
 * Set warp info on virtual warped dataset.
1325
 *
1326
 * Initializes all the warping information for a virtual warped dataset.
1327
 *
1328
 * This method is the same as the C++ method VRTWarpedDataset::Initialize().
1329
 *
1330
 * @param hDS dataset previously created with the VRT driver, and a
1331
 * SUBCLASS of "VRTWarpedDataset".
1332
 *
1333
 * @param psWO the warp options to apply.  Note that ownership of the
1334
 * transformation information is taken over by the function though everything
1335
 * else remains the property of the caller.
1336
 *
1337
 * @return CE_None on success or CE_Failure if an error occurs.
1338
 */
1339
1340
CPLErr CPL_STDCALL GDALInitializeWarpedVRT(GDALDatasetH hDS,
1341
                                           GDALWarpOptions *psWO)
1342
1343
0
{
1344
0
    VALIDATE_POINTER1(hDS, "GDALInitializeWarpedVRT", CE_Failure);
1345
1346
0
    return static_cast<VRTWarpedDataset *>(GDALDataset::FromHandle(hDS))
1347
0
        ->Initialize(psWO);
1348
0
}
1349
1350
/*! @cond Doxygen_Suppress */
1351
1352
/************************************************************************/
1353
/*                              XMLInit()                               */
1354
/************************************************************************/
1355
1356
CPLErr VRTWarpedDataset::XMLInit(const CPLXMLNode *psTree,
1357
                                 const char *pszVRTPathIn)
1358
1359
0
{
1360
1361
    /* -------------------------------------------------------------------- */
1362
    /*      Initialize blocksize before calling sub-init so that the        */
1363
    /*      band initializers can get it from the dataset object when       */
1364
    /*      they are created.                                               */
1365
    /* -------------------------------------------------------------------- */
1366
0
    m_nBlockXSize = atoi(CPLGetXMLValue(psTree, "BlockXSize", "512"));
1367
0
    m_nBlockYSize = atoi(CPLGetXMLValue(psTree, "BlockYSize", "128"));
1368
1369
    /* -------------------------------------------------------------------- */
1370
    /*      Initialize all the general VRT stuff.  This will even           */
1371
    /*      create the VRTWarpedRasterBands and initialize them.            */
1372
    /* -------------------------------------------------------------------- */
1373
0
    {
1374
0
        const CPLErr eErr = VRTDataset::XMLInit(psTree, pszVRTPathIn);
1375
1376
0
        if (eErr != CE_None)
1377
0
            return eErr;
1378
0
    }
1379
1380
    // Check that band block sizes didn't change the dataset block size.
1381
0
    for (int i = 1; i <= nBands; i++)
1382
0
    {
1383
0
        int nBlockXSize = 0;
1384
0
        int nBlockYSize = 0;
1385
0
        GetRasterBand(i)->GetBlockSize(&nBlockXSize, &nBlockYSize);
1386
0
        if (nBlockXSize != m_nBlockXSize || nBlockYSize != m_nBlockYSize)
1387
0
        {
1388
0
            CPLError(CE_Failure, CPLE_AppDefined,
1389
0
                     "Block size specified on band %d not consistent with "
1390
0
                     "dataset block size",
1391
0
                     i);
1392
0
            return CE_Failure;
1393
0
        }
1394
0
    }
1395
1396
0
    if (nBands > 1)
1397
0
    {
1398
0
        GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1399
0
    }
1400
1401
    /* -------------------------------------------------------------------- */
1402
    /*      Find the GDALWarpOptions XML tree.                              */
1403
    /* -------------------------------------------------------------------- */
1404
0
    const CPLXMLNode *const psOptionsTree =
1405
0
        CPLGetXMLNode(psTree, "GDALWarpOptions");
1406
0
    if (psOptionsTree == nullptr)
1407
0
    {
1408
0
        CPLError(CE_Failure, CPLE_AppDefined,
1409
0
                 "Count not find required GDALWarpOptions in XML.");
1410
0
        return CE_Failure;
1411
0
    }
1412
1413
    /* -------------------------------------------------------------------- */
1414
    /*      Adjust the SourceDataset in the warp options to take into       */
1415
    /*      account that it is relative to the VRT if appropriate.          */
1416
    /* -------------------------------------------------------------------- */
1417
0
    const bool bRelativeToVRT = CPL_TO_BOOL(atoi(
1418
0
        CPLGetXMLValue(psOptionsTree, "SourceDataset.relativeToVRT", "0")));
1419
1420
0
    const char *pszRelativePath =
1421
0
        CPLGetXMLValue(psOptionsTree, "SourceDataset", "");
1422
0
    char *pszAbsolutePath = nullptr;
1423
1424
0
    if (bRelativeToVRT)
1425
0
        pszAbsolutePath = CPLStrdup(
1426
0
            CPLProjectRelativeFilenameSafe(pszVRTPathIn, pszRelativePath)
1427
0
                .c_str());
1428
0
    else
1429
0
        pszAbsolutePath = CPLStrdup(pszRelativePath);
1430
1431
0
    CPLXMLNode *psOptionsTreeCloned = CPLCloneXMLTree(psOptionsTree);
1432
0
    CPLSetXMLValue(psOptionsTreeCloned, "SourceDataset", pszAbsolutePath);
1433
0
    CPLFree(pszAbsolutePath);
1434
1435
    /* -------------------------------------------------------------------- */
1436
    /*      And instantiate the warp options, and corresponding warp        */
1437
    /*      operation.                                                      */
1438
    /* -------------------------------------------------------------------- */
1439
0
    GDALWarpOptions *psWO = GDALDeserializeWarpOptions(psOptionsTreeCloned);
1440
0
    CPLDestroyXMLNode(psOptionsTreeCloned);
1441
0
    if (psWO == nullptr)
1442
0
        return CE_Failure;
1443
1444
0
    psWO->papszWarpOptions = VRTWarpedAddOptions(psWO->papszWarpOptions);
1445
1446
0
    eAccess = GA_Update;
1447
1448
0
    if (psWO->hDstDS != nullptr)
1449
0
    {
1450
0
        GDALClose(psWO->hDstDS);
1451
0
        psWO->hDstDS = nullptr;
1452
0
    }
1453
1454
0
    psWO->hDstDS = this;
1455
1456
    /* -------------------------------------------------------------------- */
1457
    /*      Deserialize vertical shift grids.                               */
1458
    /* -------------------------------------------------------------------- */
1459
0
    CPLXMLNode *psIter = psTree->psChild;
1460
0
    for (; psWO->hSrcDS != nullptr && psIter != nullptr;
1461
0
         psIter = psIter->psNext)
1462
0
    {
1463
0
        if (psIter->eType != CXT_Element ||
1464
0
            !EQUAL(psIter->pszValue, "VerticalShiftGrids"))
1465
0
        {
1466
0
            continue;
1467
0
        }
1468
1469
0
        CPLError(CE_Warning, CPLE_AppDefined,
1470
0
                 "The VerticalShiftGrids in a warped VRT is now deprecated, "
1471
0
                 "and will no longer be handled in GDAL 4.0");
1472
1473
0
        const char *pszVGrids = CPLGetXMLValue(psIter, "Grids", nullptr);
1474
0
        if (pszVGrids)
1475
0
        {
1476
0
            int bInverse =
1477
0
                CSLTestBoolean(CPLGetXMLValue(psIter, "Inverse", "FALSE"));
1478
0
            double dfToMeterSrc =
1479
0
                CPLAtof(CPLGetXMLValue(psIter, "ToMeterSrc", "1.0"));
1480
0
            double dfToMeterDest =
1481
0
                CPLAtof(CPLGetXMLValue(psIter, "ToMeterDest", "1.0"));
1482
0
            char **papszOptions = nullptr;
1483
0
            CPLXMLNode *psIter2 = psIter->psChild;
1484
0
            for (; psIter2 != nullptr; psIter2 = psIter2->psNext)
1485
0
            {
1486
0
                if (psIter2->eType != CXT_Element ||
1487
0
                    !EQUAL(psIter2->pszValue, "Option"))
1488
0
                {
1489
0
                    continue;
1490
0
                }
1491
0
                const char *pszName = CPLGetXMLValue(psIter2, "name", nullptr);
1492
0
                const char *pszValue =
1493
0
                    CPLGetXMLValue(psIter2, nullptr, nullptr);
1494
0
                if (pszName && pszValue)
1495
0
                {
1496
0
                    papszOptions =
1497
0
                        CSLSetNameValue(papszOptions, pszName, pszValue);
1498
0
                }
1499
0
            }
1500
1501
0
            int bError = FALSE;
1502
0
            GDALDatasetH hGridDataset =
1503
0
                GDALOpenVerticalShiftGrid(pszVGrids, &bError);
1504
0
            if (bError && hGridDataset == nullptr)
1505
0
            {
1506
0
                CPLError(CE_Warning, CPLE_AppDefined,
1507
0
                         "Cannot open %s. Source dataset will no "
1508
0
                         "be vertically adjusted regarding "
1509
0
                         "vertical datum",
1510
0
                         pszVGrids);
1511
0
            }
1512
0
            else if (hGridDataset != nullptr)
1513
0
            {
1514
                // Transform from source vertical datum to WGS84
1515
0
                GDALDatasetH hTmpDS = GDALApplyVerticalShiftGrid(
1516
0
                    psWO->hSrcDS, hGridDataset, bInverse, dfToMeterSrc,
1517
0
                    dfToMeterDest, papszOptions);
1518
0
                GDALReleaseDataset(hGridDataset);
1519
0
                if (hTmpDS == nullptr)
1520
0
                {
1521
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1522
0
                             "Source dataset will no "
1523
0
                             "be vertically adjusted regarding "
1524
0
                             "vertical datum %s",
1525
0
                             pszVGrids);
1526
0
                }
1527
0
                else
1528
0
                {
1529
0
                    CPLDebug("GDALWARP",
1530
0
                             "Adjusting source dataset "
1531
0
                             "with vertical datum using %s",
1532
0
                             pszVGrids);
1533
0
                    GDALReleaseDataset(psWO->hSrcDS);
1534
0
                    psWO->hSrcDS = hTmpDS;
1535
0
                }
1536
0
            }
1537
1538
0
            CSLDestroy(papszOptions);
1539
0
        }
1540
0
    }
1541
1542
    /* -------------------------------------------------------------------- */
1543
    /*      Instantiate the warp operation.                                 */
1544
    /* -------------------------------------------------------------------- */
1545
0
    m_poWarper = new GDALWarpOperation();
1546
1547
0
    const CPLErr eErr = m_poWarper->Initialize(psWO);
1548
0
    if (eErr != CE_None)
1549
0
    {
1550
        /* --------------------------------------------------------------------
1551
         */
1552
        /*      We are responsible for cleaning up the transformer ourselves. */
1553
        /* --------------------------------------------------------------------
1554
         */
1555
0
        if (psWO->pTransformerArg != nullptr)
1556
0
        {
1557
0
            GDALDestroyTransformer(psWO->pTransformerArg);
1558
0
            psWO->pTransformerArg = nullptr;
1559
0
        }
1560
1561
0
        if (psWO->hSrcDS != nullptr)
1562
0
        {
1563
0
            GDALClose(psWO->hSrcDS);
1564
0
            psWO->hSrcDS = nullptr;
1565
0
        }
1566
0
    }
1567
1568
0
    GDALDestroyWarpOptions(psWO);
1569
0
    if (eErr != CE_None)
1570
0
    {
1571
0
        delete m_poWarper;
1572
0
        m_poWarper = nullptr;
1573
0
    }
1574
1575
    /* -------------------------------------------------------------------- */
1576
    /*      Deserialize SrcOvrLevel                                         */
1577
    /* -------------------------------------------------------------------- */
1578
0
    const char *pszSrcOvrLevel = CPLGetXMLValue(psTree, "SrcOvrLevel", nullptr);
1579
0
    if (pszSrcOvrLevel != nullptr)
1580
0
    {
1581
0
        SetMetadataItem("SrcOvrLevel", pszSrcOvrLevel);
1582
0
    }
1583
1584
    /* -------------------------------------------------------------------- */
1585
    /*      Generate overviews, if appropriate.                             */
1586
    /* -------------------------------------------------------------------- */
1587
1588
    // OverviewList is historical, and quite inefficient, since it uses
1589
    // the full resolution source dataset, so only build it afterwards.
1590
0
    const CPLStringList aosOverviews(
1591
0
        CSLTokenizeString(CPLGetXMLValue(psTree, "OverviewList", "")));
1592
0
    if (!aosOverviews.empty())
1593
0
        CreateImplicitOverviews();
1594
1595
0
    for (int iOverview = 0; iOverview < aosOverviews.size(); ++iOverview)
1596
0
    {
1597
0
        int nOvFactor = atoi(aosOverviews[iOverview]);
1598
1599
0
        if (nOvFactor > 0)
1600
0
            BuildOverviews("NEAREST", 1, &nOvFactor, 0, nullptr, nullptr,
1601
0
                           nullptr,
1602
0
                           /*papszOptions=*/nullptr);
1603
0
        else
1604
0
            CPLError(CE_Failure, CPLE_AppDefined,
1605
0
                     "Bad value for overview factor : %s",
1606
0
                     aosOverviews[iOverview]);
1607
0
    }
1608
1609
0
    return eErr;
1610
0
}
1611
1612
/************************************************************************/
1613
/*                           SerializeToXML()                           */
1614
/************************************************************************/
1615
1616
CPLXMLNode *VRTWarpedDataset::SerializeToXML(const char *pszVRTPathIn)
1617
1618
0
{
1619
0
    CPLXMLNode *psTree = VRTDataset::SerializeToXML(pszVRTPathIn);
1620
1621
0
    if (psTree == nullptr)
1622
0
        return psTree;
1623
1624
    /* -------------------------------------------------------------------- */
1625
    /*      Set subclass.                                                   */
1626
    /* -------------------------------------------------------------------- */
1627
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
1628
0
                     CXT_Text, "VRTWarpedDataset");
1629
1630
    /* -------------------------------------------------------------------- */
1631
    /*      Serialize the block size.                                       */
1632
    /* -------------------------------------------------------------------- */
1633
0
    CPLCreateXMLElementAndValue(psTree, "BlockXSize",
1634
0
                                CPLSPrintf("%d", m_nBlockXSize));
1635
0
    CPLCreateXMLElementAndValue(psTree, "BlockYSize",
1636
0
                                CPLSPrintf("%d", m_nBlockYSize));
1637
1638
    /* -------------------------------------------------------------------- */
1639
    /*      Serialize the overview list (only for non implicit overviews)   */
1640
    /* -------------------------------------------------------------------- */
1641
0
    if (!m_apoOverviews.empty())
1642
0
    {
1643
0
        int nSrcDSOvrCount = 0;
1644
0
        if (m_poWarper != nullptr && m_poWarper->GetOptions() != nullptr &&
1645
0
            m_poWarper->GetOptions()->hSrcDS != nullptr &&
1646
0
            GDALGetRasterCount(m_poWarper->GetOptions()->hSrcDS) > 0)
1647
0
        {
1648
0
            nSrcDSOvrCount =
1649
0
                static_cast<GDALDataset *>(m_poWarper->GetOptions()->hSrcDS)
1650
0
                    ->GetRasterBand(1)
1651
0
                    ->GetOverviewCount();
1652
0
        }
1653
1654
0
        if (static_cast<int>(m_apoOverviews.size()) != nSrcDSOvrCount)
1655
0
        {
1656
0
            const size_t nLen = m_apoOverviews.size() * 8 + 10;
1657
0
            char *pszOverviewList = static_cast<char *>(CPLMalloc(nLen));
1658
0
            pszOverviewList[0] = '\0';
1659
0
            for (auto *poOverviewDS : m_apoOverviews)
1660
0
            {
1661
0
                if (poOverviewDS)
1662
0
                {
1663
0
                    const int nOvFactor = static_cast<int>(
1664
0
                        0.5 +
1665
0
                        GetRasterXSize() / static_cast<double>(
1666
0
                                               poOverviewDS->GetRasterXSize()));
1667
1668
0
                    snprintf(pszOverviewList + strlen(pszOverviewList),
1669
0
                             nLen - strlen(pszOverviewList), "%d ", nOvFactor);
1670
0
                }
1671
0
            }
1672
1673
0
            CPLCreateXMLElementAndValue(psTree, "OverviewList",
1674
0
                                        pszOverviewList);
1675
1676
0
            CPLFree(pszOverviewList);
1677
0
        }
1678
0
    }
1679
1680
    /* -------------------------------------------------------------------- */
1681
    /*      Serialize source overview level.                                */
1682
    /* -------------------------------------------------------------------- */
1683
0
    if (m_nSrcOvrLevel != -2)
1684
0
    {
1685
0
        if (m_nSrcOvrLevel < -2)
1686
0
            CPLCreateXMLElementAndValue(
1687
0
                psTree, "SrcOvrLevel",
1688
0
                CPLSPrintf("AUTO%d", m_nSrcOvrLevel + 2));
1689
0
        else if (m_nSrcOvrLevel == -1)
1690
0
            CPLCreateXMLElementAndValue(psTree, "SrcOvrLevel", "NONE");
1691
0
        else
1692
0
            CPLCreateXMLElementAndValue(psTree, "SrcOvrLevel",
1693
0
                                        CPLSPrintf("%d", m_nSrcOvrLevel));
1694
0
    }
1695
1696
    /* ==================================================================== */
1697
    /*      Serialize the warp options.                                     */
1698
    /* ==================================================================== */
1699
0
    if (m_poWarper != nullptr)
1700
0
    {
1701
        /* --------------------------------------------------------------------
1702
         */
1703
        /*      We reset the destination dataset name so it doesn't get */
1704
        /*      written out in the serialize warp options. */
1705
        /* --------------------------------------------------------------------
1706
         */
1707
0
        char *const pszSavedName = CPLStrdup(GetDescription());
1708
0
        SetDescription("");
1709
1710
0
        CPLXMLNode *const psWOTree =
1711
0
            GDALSerializeWarpOptions(m_poWarper->GetOptions());
1712
0
        CPLAddXMLChild(psTree, psWOTree);
1713
1714
0
        SetDescription(pszSavedName);
1715
0
        CPLFree(pszSavedName);
1716
1717
        /* --------------------------------------------------------------------
1718
         */
1719
        /*      We need to consider making the source dataset relative to */
1720
        /*      the VRT file if possible.  Adjust accordingly. */
1721
        /* --------------------------------------------------------------------
1722
         */
1723
0
        CPLXMLNode *psSDS = CPLGetXMLNode(psWOTree, "SourceDataset");
1724
0
        int bRelativeToVRT = FALSE;
1725
0
        VSIStatBufL sStat;
1726
1727
0
        if (VSIStatExL(psSDS->psChild->pszValue, &sStat,
1728
0
                       VSI_STAT_EXISTS_FLAG) == 0)
1729
0
        {
1730
0
            std::string osVRTFilename = pszVRTPathIn;
1731
0
            std::string osSourceDataset = psSDS->psChild->pszValue;
1732
0
            char *pszCurDir = CPLGetCurrentDir();
1733
0
            if (CPLIsFilenameRelative(osSourceDataset.c_str()) &&
1734
0
                !CPLIsFilenameRelative(osVRTFilename.c_str()) &&
1735
0
                pszCurDir != nullptr)
1736
0
            {
1737
0
                osSourceDataset = CPLFormFilenameSafe(
1738
0
                    pszCurDir, osSourceDataset.c_str(), nullptr);
1739
0
            }
1740
0
            else if (!CPLIsFilenameRelative(osSourceDataset.c_str()) &&
1741
0
                     CPLIsFilenameRelative(osVRTFilename.c_str()) &&
1742
0
                     pszCurDir != nullptr)
1743
0
            {
1744
0
                osVRTFilename = CPLFormFilenameSafe(
1745
0
                    pszCurDir, osVRTFilename.c_str(), nullptr);
1746
0
            }
1747
0
            CPLFree(pszCurDir);
1748
0
            char *pszRelativePath = CPLStrdup(CPLExtractRelativePath(
1749
0
                osVRTFilename.c_str(), osSourceDataset.c_str(),
1750
0
                &bRelativeToVRT));
1751
1752
0
            CPLFree(psSDS->psChild->pszValue);
1753
0
            psSDS->psChild->pszValue = pszRelativePath;
1754
0
        }
1755
1756
0
        CPLCreateXMLNode(
1757
0
            CPLCreateXMLNode(psSDS, CXT_Attribute, "relativeToVRT"), CXT_Text,
1758
0
            bRelativeToVRT ? "1" : "0");
1759
0
    }
1760
1761
0
    return psTree;
1762
0
}
1763
1764
/************************************************************************/
1765
/*                            GetBlockSize()                            */
1766
/************************************************************************/
1767
1768
void VRTWarpedDataset::GetBlockSize(int *pnBlockXSize, int *pnBlockYSize) const
1769
1770
0
{
1771
0
    CPLAssert(nullptr != pnBlockXSize);
1772
0
    CPLAssert(nullptr != pnBlockYSize);
1773
1774
0
    *pnBlockXSize = m_nBlockXSize;
1775
0
    *pnBlockYSize = m_nBlockYSize;
1776
0
}
1777
1778
/************************************************************************/
1779
/*                            ProcessBlock()                            */
1780
/*                                                                      */
1781
/*      Warp a single requested block, and then push each band of       */
1782
/*      the result into the block cache.                                */
1783
/************************************************************************/
1784
1785
CPLErr VRTWarpedDataset::ProcessBlock(int iBlockX, int iBlockY)
1786
1787
0
{
1788
0
    if (m_poWarper == nullptr)
1789
0
        return CE_Failure;
1790
1791
0
    int nReqXSize = m_nBlockXSize;
1792
0
    if (iBlockX * m_nBlockXSize + nReqXSize > nRasterXSize)
1793
0
        nReqXSize = nRasterXSize - iBlockX * m_nBlockXSize;
1794
0
    int nReqYSize = m_nBlockYSize;
1795
0
    if (iBlockY * m_nBlockYSize + nReqYSize > nRasterYSize)
1796
0
        nReqYSize = nRasterYSize - iBlockY * m_nBlockYSize;
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 = m_poWarper->WarpRegionToBuffer(
1812
0
        iBlockX * m_nBlockXSize, iBlockY * m_nBlockYSize, 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, char ** /* papszOptions */)
2173
2174
0
{
2175
0
    if (eType == GDT_Unknown || eType == GDT_TypeCount)
2176
0
    {
2177
0
        ReportError(CE_Failure, CPLE_IllegalArg,
2178
0
                    "Illegal GDT_Unknown/GDT_TypeCount argument");
2179
0
        return CE_Failure;
2180
0
    }
2181
2182
0
    SetBand(GetRasterCount() + 1,
2183
0
            new VRTWarpedRasterBand(this, GetRasterCount() + 1, eType));
2184
2185
0
    return CE_None;
2186
0
}
2187
2188
/************************************************************************/
2189
/* ==================================================================== */
2190
/*                        VRTWarpedRasterBand                           */
2191
/* ==================================================================== */
2192
/************************************************************************/
2193
2194
/************************************************************************/
2195
/*                        VRTWarpedRasterBand()                         */
2196
/************************************************************************/
2197
2198
VRTWarpedRasterBand::VRTWarpedRasterBand(GDALDataset *poDSIn, int nBandIn,
2199
                                         GDALDataType eType)
2200
2201
0
{
2202
0
    Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
2203
2204
0
    poDS = poDSIn;
2205
0
    nBand = nBandIn;
2206
0
    eAccess = GA_Update;
2207
2208
0
    static_cast<VRTWarpedDataset *>(poDS)->GetBlockSize(&nBlockXSize,
2209
0
                                                        &nBlockYSize);
2210
2211
0
    if (eType != GDT_Unknown)
2212
0
        eDataType = eType;
2213
0
}
2214
2215
/************************************************************************/
2216
/*                        ~VRTWarpedRasterBand()                        */
2217
/************************************************************************/
2218
2219
VRTWarpedRasterBand::~VRTWarpedRasterBand()
2220
2221
0
{
2222
0
    FlushCache(true);
2223
0
}
2224
2225
/************************************************************************/
2226
/*                             IReadBlock()                             */
2227
/************************************************************************/
2228
2229
CPLErr VRTWarpedRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
2230
                                       void *pImage)
2231
2232
0
{
2233
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2234
0
    const GPtrDiff_t nDataBytes =
2235
0
        static_cast<GPtrDiff_t>(GDALGetDataTypeSizeBytes(eDataType)) *
2236
0
        nBlockXSize * nBlockYSize;
2237
2238
0
    GDALRasterBlock *poBlock = GetLockedBlockRef(nBlockXOff, nBlockYOff, TRUE);
2239
0
    if (poBlock == nullptr)
2240
0
        return CE_Failure;
2241
2242
0
    if (poWDS->m_poWarper)
2243
0
    {
2244
0
        const GDALWarpOptions *psWO = poWDS->m_poWarper->GetOptions();
2245
0
        if (nBand == psWO->nDstAlphaBand)
2246
0
        {
2247
            // For a reader starting by asking on band 1, we should normally
2248
            // not reach here, because ProcessBlock() on band 1 will have
2249
            // populated the block cache for the regular bands and the alpha
2250
            // band.
2251
            // But if there's no source window corresponding to the block,
2252
            // the alpha band block will not be written through RasterIO(),
2253
            // so we nee to initialize it.
2254
0
            memset(poBlock->GetDataRef(), 0, nDataBytes);
2255
0
        }
2256
0
    }
2257
2258
0
    const CPLErr eErr = poWDS->ProcessBlock(nBlockXOff, nBlockYOff);
2259
2260
0
    if (eErr == CE_None && pImage != poBlock->GetDataRef())
2261
0
    {
2262
0
        memcpy(pImage, poBlock->GetDataRef(), nDataBytes);
2263
0
    }
2264
2265
0
    poBlock->DropLock();
2266
2267
0
    return eErr;
2268
0
}
2269
2270
/************************************************************************/
2271
/*                            IWriteBlock()                             */
2272
/************************************************************************/
2273
2274
CPLErr VRTWarpedRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
2275
                                        void *pImage)
2276
2277
0
{
2278
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2279
2280
    // This is a bit tricky. In the case we are warping a VRTWarpedDataset
2281
    // with a destination alpha band, IWriteBlock can be called on that alpha
2282
    // band by GDALWarpDstAlphaMasker
2283
    // We don't need to do anything since the data will have hopefully been
2284
    // read from the block cache before if the reader processes all the bands
2285
    // of a same block.
2286
0
    if (poWDS->m_poWarper->GetOptions()->nDstAlphaBand != nBand)
2287
0
    {
2288
        /* Otherwise, call the superclass method, that will fail of course */
2289
0
        return VRTRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage);
2290
0
    }
2291
2292
0
    return CE_None;
2293
0
}
2294
2295
/************************************************************************/
2296
/*                   EmitErrorMessageIfWriteNotSupported()              */
2297
/************************************************************************/
2298
2299
bool VRTWarpedRasterBand::EmitErrorMessageIfWriteNotSupported(
2300
    const char *pszCaller) const
2301
0
{
2302
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2303
    // Cf comment in IWriteBlock()
2304
0
    if (poWDS->m_poWarper->GetOptions()->nDstAlphaBand != nBand)
2305
0
    {
2306
0
        ReportError(CE_Failure, CPLE_NoWriteAccess,
2307
0
                    "%s: attempt to write to a VRTWarpedRasterBand.",
2308
0
                    pszCaller);
2309
2310
0
        return true;
2311
0
    }
2312
0
    return false;
2313
0
}
2314
2315
/************************************************************************/
2316
/*                       GetBestOverviewLevel()                         */
2317
/************************************************************************/
2318
2319
int VRTWarpedRasterBand::GetBestOverviewLevel(
2320
    int &nXOff, int &nYOff, int &nXSize, int &nYSize, int nBufXSize,
2321
    int nBufYSize, GDALRasterIOExtraArg *psExtraArg) const
2322
0
{
2323
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2324
2325
    /* -------------------------------------------------------------------- */
2326
    /*      Compute the desired downsampling factor.  It is                 */
2327
    /*      based on the least reduced axis, and represents the number      */
2328
    /*      of source pixels to one destination pixel.                      */
2329
    /* -------------------------------------------------------------------- */
2330
0
    const double dfDesiredDownsamplingFactor =
2331
0
        ((nXSize / static_cast<double>(nBufXSize)) <
2332
0
             (nYSize / static_cast<double>(nBufYSize)) ||
2333
0
         nBufYSize == 1)
2334
0
            ? nXSize / static_cast<double>(nBufXSize)
2335
0
            : nYSize / static_cast<double>(nBufYSize);
2336
2337
    /* -------------------------------------------------------------------- */
2338
    /*      Find the overview level that largest downsampling factor (most  */
2339
    /*      downsampled) that is still less than (or only a little more)    */
2340
    /*      downsampled than the request.                                   */
2341
    /* -------------------------------------------------------------------- */
2342
0
    const GDALWarpOptions *psWO = poWDS->m_poWarper->GetOptions();
2343
0
    GDALDataset *poSrcDS = GDALDataset::FromHandle(psWO->hSrcDS);
2344
0
    const int nOverviewCount = poSrcDS->GetRasterBand(1)->GetOverviewCount();
2345
2346
0
    int nBestOverviewXSize = 1;
2347
0
    int nBestOverviewYSize = 1;
2348
0
    double dfBestDownsamplingFactor = 0;
2349
0
    int nBestOverviewLevel = -1;
2350
2351
0
    const char *pszOversampligThreshold =
2352
0
        CPLGetConfigOption("GDAL_OVERVIEW_OVERSAMPLING_THRESHOLD", nullptr);
2353
2354
    // Cf https://github.com/OSGeo/gdal/pull/9040#issuecomment-1898524693
2355
    // Do not exactly use a oversampling threshold of 1.0 because of numerical
2356
    // instability.
2357
0
    const auto AdjustThreshold = [](double x)
2358
0
    {
2359
0
        constexpr double EPS = 1e-2;
2360
0
        return x == 1.0 ? x + EPS : x;
2361
0
    };
2362
0
    const double dfOversamplingThreshold = AdjustThreshold(
2363
0
        pszOversampligThreshold ? CPLAtof(pszOversampligThreshold)
2364
0
        : psExtraArg && psExtraArg->eResampleAlg != GRIORA_NearestNeighbour
2365
0
            ? 1.0
2366
0
            : 1.2);
2367
0
    for (int iOverview = 0; iOverview < nOverviewCount; iOverview++)
2368
0
    {
2369
0
        const GDALRasterBand *poSrcOvrBand = this;
2370
0
        bool bThisLevelOnly = false;
2371
0
        const int iSrcOvr =
2372
0
            poWDS->GetSrcOverviewLevel(iOverview, bThisLevelOnly);
2373
0
        if (iSrcOvr >= 0)
2374
0
        {
2375
0
            poSrcOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(iSrcOvr);
2376
0
        }
2377
0
        if (poSrcOvrBand == nullptr)
2378
0
            break;
2379
2380
0
        int nDstPixels = 0;
2381
0
        int nDstLines = 0;
2382
0
        double dfSrcRatioX = 0;
2383
0
        double dfSrcRatioY = 0;
2384
0
        if (!poWDS->GetOverviewSize(poSrcDS, iOverview, iSrcOvr, nDstPixels,
2385
0
                                    nDstLines, dfSrcRatioX, dfSrcRatioY))
2386
0
        {
2387
0
            break;
2388
0
        }
2389
2390
        // Compute downsampling factor of this overview
2391
0
        const double dfDownsamplingFactor =
2392
0
            std::min(nRasterXSize / static_cast<double>(nDstPixels),
2393
0
                     nRasterYSize / static_cast<double>(nDstLines));
2394
2395
        // Is it nearly the requested factor and better (lower) than
2396
        // the current best factor?
2397
0
        if (dfDownsamplingFactor >=
2398
0
                dfDesiredDownsamplingFactor * dfOversamplingThreshold ||
2399
0
            dfDownsamplingFactor <= dfBestDownsamplingFactor)
2400
0
        {
2401
0
            continue;
2402
0
        }
2403
2404
        // Ignore AVERAGE_BIT2GRAYSCALE overviews for RasterIO purposes.
2405
0
        const char *pszResampling = const_cast<GDALRasterBand *>(poSrcOvrBand)
2406
0
                                        ->GetMetadataItem("RESAMPLING");
2407
2408
0
        if (pszResampling != nullptr &&
2409
0
            STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
2410
0
            continue;
2411
2412
        // OK, this is our new best overview.
2413
0
        nBestOverviewXSize = nDstPixels;
2414
0
        nBestOverviewYSize = nDstLines;
2415
0
        nBestOverviewLevel = iOverview;
2416
0
        dfBestDownsamplingFactor = dfDownsamplingFactor;
2417
0
    }
2418
2419
    /* -------------------------------------------------------------------- */
2420
    /*      If we didn't find an overview that helps us, just return        */
2421
    /*      indicating failure and the full resolution image will be used.  */
2422
    /* -------------------------------------------------------------------- */
2423
0
    if (nBestOverviewLevel < 0)
2424
0
        return -1;
2425
2426
    /* -------------------------------------------------------------------- */
2427
    /*      Recompute the source window in terms of the selected            */
2428
    /*      overview.                                                       */
2429
    /* -------------------------------------------------------------------- */
2430
0
    const double dfXFactor =
2431
0
        nRasterXSize / static_cast<double>(nBestOverviewXSize);
2432
0
    const double dfYFactor =
2433
0
        nRasterYSize / static_cast<double>(nBestOverviewYSize);
2434
0
    CPLDebug("GDAL", "Selecting overview %d x %d", nBestOverviewXSize,
2435
0
             nBestOverviewYSize);
2436
2437
0
    const int nOXOff = std::min(nBestOverviewXSize - 1,
2438
0
                                static_cast<int>(nXOff / dfXFactor + 0.5));
2439
0
    const int nOYOff = std::min(nBestOverviewYSize - 1,
2440
0
                                static_cast<int>(nYOff / dfYFactor + 0.5));
2441
0
    int nOXSize = std::max(1, static_cast<int>(nXSize / dfXFactor + 0.5));
2442
0
    int nOYSize = std::max(1, static_cast<int>(nYSize / dfYFactor + 0.5));
2443
0
    if (nOXOff + nOXSize > nBestOverviewXSize)
2444
0
        nOXSize = nBestOverviewXSize - nOXOff;
2445
0
    if (nOYOff + nOYSize > nBestOverviewYSize)
2446
0
        nOYSize = nBestOverviewYSize - nOYOff;
2447
2448
0
    if (psExtraArg)
2449
0
    {
2450
0
        if (psExtraArg->bFloatingPointWindowValidity)
2451
0
        {
2452
0
            psExtraArg->dfXOff /= dfXFactor;
2453
0
            psExtraArg->dfXSize /= dfXFactor;
2454
0
            psExtraArg->dfYOff /= dfYFactor;
2455
0
            psExtraArg->dfYSize /= dfYFactor;
2456
0
        }
2457
0
        else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2458
0
        {
2459
0
            psExtraArg->bFloatingPointWindowValidity = true;
2460
0
            psExtraArg->dfXOff = nXOff / dfXFactor;
2461
0
            psExtraArg->dfXSize = nXSize / dfXFactor;
2462
0
            psExtraArg->dfYOff = nYOff / dfYFactor;
2463
0
            psExtraArg->dfYSize = nYSize / dfYFactor;
2464
0
        }
2465
0
    }
2466
2467
0
    nXOff = nOXOff;
2468
0
    nYOff = nOYOff;
2469
0
    nXSize = nOXSize;
2470
0
    nYSize = nOYSize;
2471
2472
0
    return nBestOverviewLevel;
2473
0
}
2474
2475
/************************************************************************/
2476
/*                              IRasterIO()                             */
2477
/************************************************************************/
2478
2479
CPLErr VRTWarpedRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2480
                                      int nXSize, int nYSize, void *pData,
2481
                                      int nBufXSize, int nBufYSize,
2482
                                      GDALDataType eBufType,
2483
                                      GSpacing nPixelSpace, GSpacing nLineSpace,
2484
                                      GDALRasterIOExtraArg *psExtraArg)
2485
0
{
2486
0
    VRTWarpedDataset *poWDS = static_cast<VRTWarpedDataset *>(poDS);
2487
0
    if (m_nIRasterIOCounter == 0 && poWDS->GetRasterCount() == 1)
2488
0
    {
2489
0
        int anBandMap[] = {nBand};
2490
0
        ++m_nIRasterIOCounter;
2491
0
        const CPLErr eErr = poWDS->IRasterIO(
2492
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2493
0
            eBufType, 1, anBandMap, nPixelSpace, nLineSpace, 0, psExtraArg);
2494
0
        --m_nIRasterIOCounter;
2495
0
        return eErr;
2496
0
    }
2497
2498
    /* ==================================================================== */
2499
    /*      Do we have overviews that would be appropriate to satisfy       */
2500
    /*      this request?                                                   */
2501
    /* ==================================================================== */
2502
0
    if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() &&
2503
0
        eRWFlag == GF_Read)
2504
0
    {
2505
0
        GDALRasterIOExtraArg sExtraArg;
2506
0
        GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg);
2507
2508
0
        const int nOverview = GetBestOverviewLevel(
2509
0
            nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, &sExtraArg);
2510
0
        if (nOverview >= 0)
2511
0
        {
2512
0
            auto poOvrBand = GetOverview(nOverview);
2513
0
            if (!poOvrBand)
2514
0
                return CE_Failure;
2515
2516
0
            return poOvrBand->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2517
0
                                       pData, nBufXSize, nBufYSize, eBufType,
2518
0
                                       nPixelSpace, nLineSpace, &sExtraArg);
2519
0
        }
2520
0
    }
2521
2522
0
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2523
0
                                     pData, nBufXSize, nBufYSize, eBufType,
2524
0
                                     nPixelSpace, nLineSpace, psExtraArg);
2525
0
}
2526
2527
/************************************************************************/
2528
/*                           SerializeToXML()                           */
2529
/************************************************************************/
2530
2531
CPLXMLNode *VRTWarpedRasterBand::SerializeToXML(const char *pszVRTPathIn,
2532
                                                bool &bHasWarnedAboutRAMUsage,
2533
                                                size_t &nAccRAMUsage)
2534
2535
0
{
2536
0
    CPLXMLNode *const psTree = VRTRasterBand::SerializeToXML(
2537
0
        pszVRTPathIn, bHasWarnedAboutRAMUsage, nAccRAMUsage);
2538
2539
    /* -------------------------------------------------------------------- */
2540
    /*      Set subclass.                                                   */
2541
    /* -------------------------------------------------------------------- */
2542
0
    CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
2543
0
                     CXT_Text, "VRTWarpedRasterBand");
2544
2545
0
    return psTree;
2546
0
}
2547
2548
/************************************************************************/
2549
/*                          GetOverviewCount()                          */
2550
/************************************************************************/
2551
2552
int VRTWarpedRasterBand::GetOverviewCount()
2553
2554
0
{
2555
0
    VRTWarpedDataset *const poWDS = static_cast<VRTWarpedDataset *>(poDS);
2556
0
    if (poWDS->m_bIsOverview)
2557
0
        return 0;
2558
2559
0
    if (poWDS->m_apoOverviews.empty())
2560
0
    {
2561
0
        return poWDS->GetOverviewCount();
2562
0
    }
2563
2564
0
    return static_cast<int>(poWDS->m_apoOverviews.size());
2565
0
}
2566
2567
/************************************************************************/
2568
/*                            GetOverview()                             */
2569
/************************************************************************/
2570
2571
GDALRasterBand *VRTWarpedRasterBand::GetOverview(int iOverview)
2572
2573
0
{
2574
0
    VRTWarpedDataset *const poWDS = static_cast<VRTWarpedDataset *>(poDS);
2575
2576
0
    const int nOvrCount = GetOverviewCount();
2577
0
    if (iOverview < 0 || iOverview >= nOvrCount)
2578
0
        return nullptr;
2579
2580
0
    if (poWDS->m_apoOverviews.empty())
2581
0
        poWDS->m_apoOverviews.resize(nOvrCount);
2582
0
    if (!poWDS->m_apoOverviews[iOverview])
2583
0
        poWDS->m_apoOverviews[iOverview] =
2584
0
            poWDS->CreateImplicitOverview(iOverview);
2585
0
    if (!poWDS->m_apoOverviews[iOverview])
2586
0
        return nullptr;
2587
0
    return poWDS->m_apoOverviews[iOverview]->GetRasterBand(nBand);
2588
0
}
2589
2590
/*! @endcond */