Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gti/gdaltileindexdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Virtual GDAL Datasets
4
 * Purpose:  Tile index based VRT
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
/*! @cond Doxygen_Suppress */
14
15
#include <array>
16
#include <algorithm>
17
#include <atomic>
18
#include <cmath>
19
#include <limits>
20
#include <mutex>
21
#include <set>
22
#include <tuple>
23
#include <utility>
24
#include <vector>
25
26
#include "cpl_port.h"
27
#include "cpl_error_internal.h"
28
#include "cpl_json.h"
29
#include "cpl_mem_cache.h"
30
#include "cpl_minixml.h"
31
#include "cpl_quad_tree.h"
32
#include "vrtdataset.h"
33
#include "vrt_priv.h"
34
#include "ogrsf_frmts.h"
35
#include "ogrwarpedlayer.h"
36
#include "gdal_frmts.h"
37
#include "gdal_proxy.h"
38
#include "gdalsubdatasetinfo.h"
39
#include "gdal_thread_pool.h"
40
#include "gdal_utils.h"
41
42
#include "gdalalg_raster_index.h"
43
44
#ifdef USE_NEON_OPTIMIZATIONS
45
#define USE_SSE2_OPTIM
46
#define USE_SSE41_OPTIM
47
#include "include_sse2neon.h"
48
#elif defined(__SSE2__) || defined(_M_X64)
49
#define USE_SSE2_OPTIM
50
#include <emmintrin.h>
51
// MSVC doesn't define __SSE4_1__, but if -arch:AVX2 is enabled, we do have SSE4.1
52
#if defined(__SSE4_1__) || defined(__AVX2__)
53
#define USE_SSE41_OPTIM
54
#include <smmintrin.h>
55
#endif
56
#endif
57
58
#ifndef _
59
0
#define _(x) (x)
60
#endif
61
62
// Semantincs of indices of a GeoTransform (double[6]) matrix
63
constexpr int GT_TOPLEFT_X = 0;
64
constexpr int GT_WE_RES = 1;
65
constexpr int GT_ROTATION_PARAM1 = 2;
66
constexpr int GT_TOPLEFT_Y = 3;
67
constexpr int GT_ROTATION_PARAM2 = 4;
68
constexpr int GT_NS_RES = 5;
69
70
constexpr const char *GTI_PREFIX = "GTI:";
71
72
constexpr const char *MD_DS_TILE_INDEX_LAYER = "TILE_INDEX_LAYER";
73
constexpr const char *MD_DS_TILE_INDEX_SQL = "TILE_INDEX_SQL";
74
constexpr const char *MD_DS_TILE_INDEX_SPATIAL_SQL = "TILE_INDEX_SPATIAL_SQL";
75
76
constexpr const char *MD_RESX = "RESX";
77
constexpr const char *MD_RESY = "RESY";
78
constexpr const char *MD_BAND_COUNT = "BAND_COUNT";
79
constexpr const char *MD_DATA_TYPE = "DATA_TYPE";
80
constexpr const char *MD_NODATA = "NODATA";
81
constexpr const char *MD_MINX = "MINX";
82
constexpr const char *MD_MINY = "MINY";
83
constexpr const char *MD_MAXX = "MAXX";
84
constexpr const char *MD_MAXY = "MAXY";
85
constexpr const char *MD_GEOTRANSFORM = "GEOTRANSFORM";
86
constexpr const char *MD_XSIZE = "XSIZE";
87
constexpr const char *MD_YSIZE = "YSIZE";
88
constexpr const char *MD_COLOR_INTERPRETATION = "COLOR_INTERPRETATION";
89
constexpr const char *MD_SRS = "SRS";
90
constexpr const char *MD_LOCATION_FIELD = "LOCATION_FIELD";
91
constexpr const char *MD_SORT_FIELD = "SORT_FIELD";
92
constexpr const char *MD_SORT_FIELD_ASC = "SORT_FIELD_ASC";
93
constexpr const char *MD_BLOCK_X_SIZE = "BLOCKXSIZE";
94
constexpr const char *MD_BLOCK_Y_SIZE = "BLOCKYSIZE";
95
constexpr const char *MD_MASK_BAND = "MASK_BAND";
96
constexpr const char *MD_RESAMPLING = "RESAMPLING";
97
98
constexpr const char *const apszTIOptions[] = {MD_RESX,
99
                                               MD_RESY,
100
                                               MD_BAND_COUNT,
101
                                               MD_DATA_TYPE,
102
                                               MD_NODATA,
103
                                               MD_MINX,
104
                                               MD_MINY,
105
                                               MD_MAXX,
106
                                               MD_MAXY,
107
                                               MD_GEOTRANSFORM,
108
                                               MD_XSIZE,
109
                                               MD_YSIZE,
110
                                               MD_COLOR_INTERPRETATION,
111
                                               MD_SRS,
112
                                               MD_LOCATION_FIELD,
113
                                               MD_SORT_FIELD,
114
                                               MD_SORT_FIELD_ASC,
115
                                               MD_BLOCK_X_SIZE,
116
                                               MD_BLOCK_Y_SIZE,
117
                                               MD_MASK_BAND,
118
                                               MD_RESAMPLING};
119
120
constexpr const char *const MD_BAND_OFFSET = "OFFSET";
121
constexpr const char *const MD_BAND_SCALE = "SCALE";
122
constexpr const char *const MD_BAND_UNITTYPE = "UNITTYPE";
123
constexpr const char *const apszReservedBandItems[] = {
124
    MD_BAND_OFFSET, MD_BAND_SCALE, MD_BAND_UNITTYPE};
125
126
constexpr const char *GTI_XML_BANDCOUNT = "BandCount";
127
constexpr const char *GTI_XML_DATATYPE = "DataType";
128
constexpr const char *GTI_XML_NODATAVALUE = "NoDataValue";
129
constexpr const char *GTI_XML_COLORINTERP = "ColorInterp";
130
constexpr const char *GTI_XML_LOCATIONFIELD = "LocationField";
131
constexpr const char *GTI_XML_SORTFIELD = "SortField";
132
constexpr const char *GTI_XML_SORTFIELDASC = "SortFieldAsc";
133
constexpr const char *GTI_XML_MASKBAND = "MaskBand";
134
constexpr const char *GTI_XML_OVERVIEW_ELEMENT = "Overview";
135
constexpr const char *GTI_XML_OVERVIEW_DATASET = "Dataset";
136
constexpr const char *GTI_XML_OVERVIEW_LAYER = "Layer";
137
constexpr const char *GTI_XML_OVERVIEW_FACTOR = "Factor";
138
139
constexpr const char *GTI_XML_BAND_ELEMENT = "Band";
140
constexpr const char *GTI_XML_BAND_NUMBER = "band";
141
constexpr const char *GTI_XML_BAND_DATATYPE = "dataType";
142
constexpr const char *GTI_XML_BAND_DESCRIPTION = "Description";
143
constexpr const char *GTI_XML_BAND_OFFSET = "Offset";
144
constexpr const char *GTI_XML_BAND_SCALE = "Scale";
145
constexpr const char *GTI_XML_BAND_NODATAVALUE = "NoDataValue";
146
constexpr const char *GTI_XML_BAND_UNITTYPE = "UnitType";
147
constexpr const char *GTI_XML_BAND_COLORINTERP = "ColorInterp";
148
constexpr const char *GTI_XML_CATEGORYNAMES = "CategoryNames";
149
constexpr const char *GTI_XML_COLORTABLE = "ColorTable";
150
constexpr const char *GTI_XML_RAT = "GDALRasterAttributeTable";
151
152
/************************************************************************/
153
/*                           ENDS_WITH_CI()                             */
154
/************************************************************************/
155
156
static inline bool ENDS_WITH_CI(const char *a, const char *b)
157
192k
{
158
192k
    return strlen(a) >= strlen(b) && EQUAL(a + strlen(a) - strlen(b), b);
159
192k
}
160
161
/************************************************************************/
162
/*                       GDALTileIndexDataset                           */
163
/************************************************************************/
164
165
class GDALTileIndexBand;
166
167
class GDALTileIndexDataset final : public GDALPamDataset
168
{
169
  public:
170
    GDALTileIndexDataset();
171
    ~GDALTileIndexDataset() override;
172
173
    bool Open(GDALOpenInfo *poOpenInfo);
174
175
    CPLErr FlushCache(bool bAtClosing) override;
176
177
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
178
    const OGRSpatialReference *GetSpatialRef() const override;
179
180
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
181
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
182
                     GDALDataType eBufType, int nBandCount,
183
                     BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
184
                     GSpacing nLineSpace, GSpacing nBandSpace,
185
                     GDALRasterIOExtraArg *psExtraArg) override;
186
187
    const char *GetMetadataItem(const char *pszName,
188
                                const char *pszDomain) override;
189
    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
190
                           const char *pszDomain) override;
191
    CPLErr SetMetadata(char **papszMD, const char *pszDomain) override;
192
193
    void LoadOverviews();
194
195
    std::vector<GTISourceDesc> GetSourcesMoreRecentThan(int64_t mTime);
196
197
  private:
198
    friend class GDALTileIndexBand;
199
200
    //! Optional GTI XML
201
    CPLXMLTreeCloser m_psXMLTree{nullptr};
202
203
    //! Whether the GTI XML might be modified (by SetMetadata/SetMetadataItem)
204
    bool m_bXMLUpdatable = false;
205
206
    //! Whether the GTI XML has been modified (by SetMetadata/SetMetadataItem)
207
    bool m_bXMLModified = false;
208
209
    //! Unique string (without the process) for this tile index. Passed to
210
    //! GDALProxyPoolDataset to ensure that sources are unique for a given owner
211
    const std::string m_osUniqueHandle;
212
213
    //! Vector dataset with the sources
214
    std::unique_ptr<GDALDataset> m_poVectorDS{};
215
216
    //! Generic SQL request to return features. May be empty.
217
    std::string m_osSQL{};
218
219
    //! SQL request to return features with placeholders for spatial filtering. May be empty
220
    std::string m_osSpatialSQL{};
221
222
    //! Vector layer with the sources
223
    OGRLayer *m_poLayer = nullptr;
224
225
    //! Whether m_poLayer should be freed with m_poVectorDS->ReleaseResultSet()
226
    bool m_bIsSQLResultLayer = false;
227
228
    //! When the SRS of m_poLayer is not the one we expose
229
    std::unique_ptr<OGRLayer> m_poWarpedLayerKeeper{};
230
231
    //! Geotransform matrix of the tile index
232
    GDALGeoTransform m_gt{};
233
234
    //! Index of the "location" (or alternate name given by user) field
235
    //! (within m_poLayer->GetLayerDefn()), that contain source dataset names.
236
    int m_nLocationFieldIndex = -1;
237
238
    //! SRS of the tile index.
239
    OGRSpatialReference m_oSRS{};
240
241
    //! Cache from dataset name to dataset handle.
242
    //! Note that the dataset objects are ultimately GDALProxyPoolDataset,
243
    //! and that the GDALProxyPoolDataset limits the number of simultaneously
244
    //! opened real datasets (controlled by GDAL_MAX_DATASET_POOL_SIZE). Hence 500 is not too big.
245
    lru11::Cache<std::string, std::shared_ptr<GDALDataset>> m_oMapSharedSources{
246
        500};
247
248
    //! Mask band (e.g. for JPEG compressed + mask band)
249
    std::unique_ptr<GDALTileIndexBand> m_poMaskBand{};
250
251
    //! Whether all bands of the tile index have the same data type.
252
    bool m_bSameDataType = true;
253
254
    //! Whether all bands of the tile index have the same nodata value.
255
    bool m_bSameNoData = true;
256
257
    //! Minimum X of the current pixel request, in georeferenced units.
258
    double m_dfLastMinXFilter = std::numeric_limits<double>::quiet_NaN();
259
260
    //! Minimum Y of the current pixel request, in georeferenced units.
261
    double m_dfLastMinYFilter = std::numeric_limits<double>::quiet_NaN();
262
263
    //! Maximum X of the current pixel request, in georeferenced units.
264
    double m_dfLastMaxXFilter = std::numeric_limits<double>::quiet_NaN();
265
266
    //! Maximum Y of the current pixel request, in georeferenced units.
267
    double m_dfLastMaxYFilter = std::numeric_limits<double>::quiet_NaN();
268
269
    //! Index of the field (within m_poLayer->GetLayerDefn()) used to sort, or -1 if none.
270
    int m_nSortFieldIndex = -1;
271
272
    //! Whether sorting must be ascending (true) or descending (false).
273
    bool m_bSortFieldAsc = true;
274
275
    //! Resampling method by default for warping or when a source has not
276
    //! the same resolution as the tile index.
277
    std::string m_osResampling = "near";
278
    GDALRIOResampleAlg m_eResampling = GRIORA_NearestNeighbour;
279
280
    //! WKT2 representation of the tile index SRS (if needed, typically for on-the-fly warping).
281
    std::string m_osWKT{};
282
283
    //! Whether we had to open of the sources at tile index opening.
284
    bool m_bScannedOneFeatureAtOpening = false;
285
286
    //! Array of overview descriptors.
287
    //! Each descriptor is a tuple (dataset_name, concatenated_open_options, layer_name, overview_factor).
288
    std::vector<std::tuple<std::string, CPLStringList, std::string, double>>
289
        m_aoOverviewDescriptor{};
290
291
    //! Array of overview datasets.
292
    std::vector<std::unique_ptr<GDALDataset>> m_apoOverviews{};
293
294
    //! Cache of buffers used by VRTComplexSource to avoid memory reallocation.
295
    VRTSource::WorkingState m_oWorkingState{};
296
297
    //! Used by IRasterIO() when using multi-threading
298
    struct QueueWorkingStates
299
    {
300
        std::mutex oMutex{};
301
        std::vector<std::unique_ptr<VRTSource::WorkingState>> oStates{};
302
    };
303
304
    //! Used by IRasterIO() when using multi-threading
305
    QueueWorkingStates m_oQueueWorkingStates{};
306
307
    //! Structure describing one of the source raster in the tile index.
308
    struct SourceDesc
309
    {
310
        //! Source dataset name.
311
        std::string osName{};
312
313
        //! Source dataset handle.
314
        std::shared_ptr<GDALDataset> poDS{};
315
316
        //! VRTSimpleSource or VRTComplexSource for the source.
317
        std::unique_ptr<VRTSimpleSource> poSource{};
318
319
        //! OGRFeature corresponding to the source in the tile index.
320
        std::unique_ptr<OGRFeature> poFeature{};
321
322
        //! Work buffer containing the value of the mask band for the current pixel query.
323
        mutable std::vector<GByte> abyMask{};
324
325
        //! Whether the source covers the whole area of interest of the current pixel query.
326
        bool bCoversWholeAOI = false;
327
328
        //! Whether the source has a nodata value at least in one of its band.
329
        bool bHasNoData = false;
330
331
        //! Whether all bands of the source have the same nodata value.
332
        bool bSameNoData = false;
333
334
        //! Nodata value of all bands (when bSameNoData == true).
335
        double dfSameNoData = 0;
336
337
        //! Mask band of the source.
338
        GDALRasterBand *poMaskBand = nullptr;
339
    };
340
341
    //! Array of sources participating to the current pixel query.
342
    std::vector<SourceDesc> m_aoSourceDesc{};
343
344
    //! Maximum number of threads. Updated by CollectSources().
345
    int m_nNumThreads = -1;
346
347
    //! Whereas the multi-threading rendering code path must be used. Updated by CollectSources().
348
    bool m_bLastMustUseMultiThreading = false;
349
350
    //! From a source dataset name, return its SourceDesc description structure.
351
    bool GetSourceDesc(const std::string &osTileName, SourceDesc &oSourceDesc,
352
                       std::mutex *pMutex);
353
354
    //! Collect sources corresponding to the georeferenced window of interest,
355
    //! and store them in m_aoSourceDesc[].
356
    bool CollectSources(double dfXOff, double dfYOff, double dfXSize,
357
                        double dfYSize, bool bMultiThreadAllowed);
358
359
    //! Sort sources according to m_nSortFieldIndex.
360
    void SortSourceDesc();
361
362
    //! Whether the output buffer needs to be nodata initialized, or if
363
    //! sources are fully covering it.
364
    bool NeedInitBuffer(int nBandCount, const int *panBandMap) const;
365
366
    //! Nodata initialize the output buffer.
367
    void InitBuffer(void *pData, int nBufXSize, int nBufYSize,
368
                    GDALDataType eBufType, int nBandCount,
369
                    const int *panBandMap, GSpacing nPixelSpace,
370
                    GSpacing nLineSpace, GSpacing nBandSpace) const;
371
372
    //! Render one source. Used by IRasterIO()
373
    CPLErr RenderSource(const SourceDesc &oSourceDesc, bool bNeedInitBuffer,
374
                        int nBandNrMax, int nXOff, int nYOff, int nXSize,
375
                        int nYSize, double dfXOff, double dfYOff,
376
                        double dfXSize, double dfYSize, int nBufXSize,
377
                        int nBufYSize, void *pData, GDALDataType eBufType,
378
                        int nBandCount, BANDMAP_TYPE panBandMap,
379
                        GSpacing nPixelSpace, GSpacing nLineSpace,
380
                        GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg,
381
                        VRTSource::WorkingState &oWorkingState) const;
382
383
    //! Whether m_poVectorDS supports SetMetadata()/SetMetadataItem()
384
    bool TileIndexSupportsEditingLayerMetadata() const;
385
386
    //! Return number of threads that can be used
387
    int GetNumThreads() const;
388
389
    /** Structure used to declare a threaded job to satisfy IRasterIO()
390
     * on a given source.
391
     */
392
    struct RasterIOJob
393
    {
394
        std::atomic<int> *pnCompletedJobs = nullptr;
395
        std::atomic<bool> *pbSuccess = nullptr;
396
        CPLErrorAccumulator *poErrorAccumulator = nullptr;
397
        GDALTileIndexDataset *poDS = nullptr;
398
        GDALTileIndexDataset::QueueWorkingStates *poQueueWorkingStates =
399
            nullptr;
400
        int nBandNrMax = 0;
401
402
        int nXOff = 0;
403
        int nYOff = 0;
404
        int nXSize = 0;
405
        int nYSize = 0;
406
        void *pData = nullptr;
407
        int nBufXSize = 0;
408
        int nBufYSize = 0;
409
        int nBandCount = 0;
410
        BANDMAP_TYPE panBandMap = nullptr;
411
        GDALDataType eBufType = GDT_Unknown;
412
        GSpacing nPixelSpace = 0;
413
        GSpacing nLineSpace = 0;
414
        GSpacing nBandSpace = 0;
415
        GDALRasterIOExtraArg *psExtraArg = nullptr;
416
417
        std::string osTileName{};
418
419
        static void Func(void *pData);
420
    };
421
422
    CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexDataset)
423
};
424
425
/************************************************************************/
426
/*                            GDALTileIndexBand                          */
427
/************************************************************************/
428
429
class GDALTileIndexBand final : public GDALPamRasterBand
430
{
431
  public:
432
    GDALTileIndexBand(GDALTileIndexDataset *poDSIn, int nBandIn,
433
                      GDALDataType eDT, int nBlockXSizeIn, int nBlockYSizeIn);
434
435
    double GetNoDataValue(int *pbHasNoData) override
436
0
    {
437
0
        if (pbHasNoData)
438
0
            *pbHasNoData = m_bNoDataValueSet;
439
0
        return m_dfNoDataValue;
440
0
    }
441
442
    GDALColorInterp GetColorInterpretation() override
443
0
    {
444
0
        return m_eColorInterp;
445
0
    }
446
447
    CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
448
449
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
450
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
451
                     GDALDataType eBufType, GSpacing nPixelSpace,
452
                     GSpacing nLineSpace,
453
                     GDALRasterIOExtraArg *psExtraArg) override;
454
455
    int IGetDataCoverageStatus(int nXOff, int nYOff, int nXSize, int nYSize,
456
                               int nMaskFlagStop, double *pdfDataPct) override;
457
458
    int GetMaskFlags() override
459
0
    {
460
0
        if (m_poDS->m_poMaskBand && m_poDS->m_poMaskBand.get() != this)
461
0
            return GMF_PER_DATASET;
462
0
        return GDALPamRasterBand::GetMaskFlags();
463
0
    }
464
465
    GDALRasterBand *GetMaskBand() override
466
0
    {
467
0
        if (m_poDS->m_poMaskBand && m_poDS->m_poMaskBand.get() != this)
468
0
            return m_poDS->m_poMaskBand.get();
469
0
        return GDALPamRasterBand::GetMaskBand();
470
0
    }
471
472
    double GetOffset(int *pbHasValue) override
473
0
    {
474
0
        int bHasValue = FALSE;
475
0
        double dfVal = GDALPamRasterBand::GetOffset(&bHasValue);
476
0
        if (bHasValue)
477
0
        {
478
0
            if (pbHasValue)
479
0
                *pbHasValue = true;
480
0
            return dfVal;
481
0
        }
482
0
        if (pbHasValue)
483
0
            *pbHasValue = !std::isnan(m_dfOffset);
484
0
        return std::isnan(m_dfOffset) ? 0.0 : m_dfOffset;
485
0
    }
486
487
    double GetScale(int *pbHasValue) override
488
0
    {
489
0
        int bHasValue = FALSE;
490
0
        double dfVal = GDALPamRasterBand::GetScale(&bHasValue);
491
0
        if (bHasValue)
492
0
        {
493
0
            if (pbHasValue)
494
0
                *pbHasValue = true;
495
0
            return dfVal;
496
0
        }
497
0
        if (pbHasValue)
498
0
            *pbHasValue = !std::isnan(m_dfScale);
499
0
        return std::isnan(m_dfScale) ? 1.0 : m_dfScale;
500
0
    }
501
502
    const char *GetUnitType() override
503
0
    {
504
0
        const char *pszVal = GDALPamRasterBand::GetUnitType();
505
0
        if (pszVal && *pszVal)
506
0
            return pszVal;
507
0
        return m_osUnit.c_str();
508
0
    }
509
510
    char **GetCategoryNames() override
511
0
    {
512
0
        return m_aosCategoryNames.List();
513
0
    }
514
515
    GDALColorTable *GetColorTable() override
516
0
    {
517
0
        return m_poColorTable.get();
518
0
    }
519
520
    GDALRasterAttributeTable *GetDefaultRAT() override
521
0
    {
522
0
        return m_poRAT.get();
523
0
    }
524
525
    int GetOverviewCount() override;
526
    GDALRasterBand *GetOverview(int iOvr) override;
527
528
    char **GetMetadataDomainList() override;
529
    const char *GetMetadataItem(const char *pszName,
530
                                const char *pszDomain) override;
531
    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
532
                           const char *pszDomain) override;
533
    CPLErr SetMetadata(char **papszMD, const char *pszDomain) override;
534
535
  private:
536
    friend class GDALTileIndexDataset;
537
538
    //! Dataset that owns this band.
539
    GDALTileIndexDataset *m_poDS = nullptr;
540
541
    //! Whether a nodata value is set to this band.
542
    bool m_bNoDataValueSet = false;
543
544
    //! Nodata value.
545
    double m_dfNoDataValue = 0;
546
547
    //! Color interpretation.
548
    GDALColorInterp m_eColorInterp = GCI_Undefined;
549
550
    //! Cached value for GetMetadataItem("Pixel_X_Y", "LocationInfo").
551
    std::string m_osLastLocationInfo{};
552
553
    //! Scale value (returned by GetScale())
554
    double m_dfScale = std::numeric_limits<double>::quiet_NaN();
555
556
    //! Offset value (returned by GetOffset())
557
    double m_dfOffset = std::numeric_limits<double>::quiet_NaN();
558
559
    //! Unit type (returned by GetUnitType()).
560
    std::string m_osUnit{};
561
562
    //! Category names (returned by GetCategoryNames()).
563
    CPLStringList m_aosCategoryNames{};
564
565
    //! Color table (returned by GetColorTable()).
566
    std::unique_ptr<GDALColorTable> m_poColorTable{};
567
568
    //! Raster attribute table (returned by GetDefaultRAT()).
569
    std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
570
571
    CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexBand)
572
};
573
574
/************************************************************************/
575
/*                        IsSameNaNAware()                              */
576
/************************************************************************/
577
578
static inline bool IsSameNaNAware(double a, double b)
579
0
{
580
0
    return a == b || (std::isnan(a) && std::isnan(b));
581
0
}
582
583
/************************************************************************/
584
/*                         GDALTileIndexDataset()                        */
585
/************************************************************************/
586
587
GDALTileIndexDataset::GDALTileIndexDataset()
588
2.95k
    : m_osUniqueHandle(CPLSPrintf("%p", this))
589
2.95k
{
590
2.95k
}
591
592
/************************************************************************/
593
/*                        GetAbsoluteFileName()                         */
594
/************************************************************************/
595
596
static std::string GetAbsoluteFileName(const char *pszTileName,
597
                                       const char *pszVRTName)
598
0
{
599
0
    std::string osRet = VSIURIToVSIPath(pszTileName);
600
0
    if (osRet != pszTileName)
601
0
        return osRet;
602
603
0
    if (CPLIsFilenameRelative(pszTileName) &&
604
0
        !STARTS_WITH(pszTileName, "<VRTDataset") &&
605
0
        !STARTS_WITH(pszVRTName, "<GDALTileIndexDataset"))
606
0
    {
607
0
        const auto oSubDSInfo(GDALGetSubdatasetInfo(pszTileName));
608
0
        if (oSubDSInfo && !oSubDSInfo->GetPathComponent().empty())
609
0
        {
610
0
            const std::string osPath(oSubDSInfo->GetPathComponent());
611
0
            osRet = CPLIsFilenameRelative(osPath.c_str())
612
0
                        ? oSubDSInfo->ModifyPathComponent(
613
0
                              CPLProjectRelativeFilenameSafe(
614
0
                                  CPLGetPathSafe(pszVRTName).c_str(),
615
0
                                  osPath.c_str()))
616
0
                        : std::string(pszTileName);
617
0
            GDALDestroySubdatasetInfo(oSubDSInfo);
618
0
            return osRet;
619
0
        }
620
621
0
        std::string osRelativeMadeAbsolute = CPLProjectRelativeFilenameSafe(
622
0
            CPLGetPathSafe(pszVRTName).c_str(), pszTileName);
623
0
        VSIStatBufL sStat;
624
0
        if (VSIStatL(osRelativeMadeAbsolute.c_str(), &sStat) == 0)
625
0
            return osRelativeMadeAbsolute;
626
0
    }
627
0
    return pszTileName;
628
0
}
629
630
/************************************************************************/
631
/*                    GTIDoPaletteExpansionIfNeeded()                   */
632
/************************************************************************/
633
634
//! Do palette -> RGB(A) expansion
635
static bool
636
GTIDoPaletteExpansionIfNeeded(std::shared_ptr<GDALDataset> &poTileDS,
637
                              int nBandCount)
638
0
{
639
0
    if (poTileDS->GetRasterCount() == 1 &&
640
0
        (nBandCount == 3 || nBandCount == 4) &&
641
0
        poTileDS->GetRasterBand(1)->GetColorTable() != nullptr)
642
0
    {
643
644
0
        CPLStringList aosOptions;
645
0
        aosOptions.AddString("-of");
646
0
        aosOptions.AddString("VRT");
647
648
0
        aosOptions.AddString("-expand");
649
0
        aosOptions.AddString(nBandCount == 3 ? "rgb" : "rgba");
650
651
0
        GDALTranslateOptions *psOptions =
652
0
            GDALTranslateOptionsNew(aosOptions.List(), nullptr);
653
0
        int bUsageError = false;
654
0
        auto poRGBDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
655
0
            GDALTranslate("", GDALDataset::ToHandle(poTileDS.get()), psOptions,
656
0
                          &bUsageError)));
657
0
        GDALTranslateOptionsFree(psOptions);
658
0
        if (!poRGBDS)
659
0
        {
660
0
            return false;
661
0
        }
662
663
0
        poTileDS.reset(poRGBDS.release());
664
0
    }
665
0
    return true;
666
0
}
667
668
/************************************************************************/
669
/*                                Open()                                */
670
/************************************************************************/
671
672
bool GDALTileIndexDataset::Open(GDALOpenInfo *poOpenInfo)
673
2.95k
{
674
2.95k
    eAccess = poOpenInfo->eAccess;
675
676
2.95k
    CPLXMLNode *psRoot = nullptr;
677
2.95k
    const char *pszIndexDataset = poOpenInfo->pszFilename;
678
679
2.95k
    if (STARTS_WITH(poOpenInfo->pszFilename, GTI_PREFIX))
680
0
    {
681
0
        pszIndexDataset = poOpenInfo->pszFilename + strlen(GTI_PREFIX);
682
0
    }
683
2.95k
    else if (STARTS_WITH(poOpenInfo->pszFilename, "<GDALTileIndexDataset"))
684
8
    {
685
        // CPLParseXMLString() emits an error in case of failure
686
8
        m_psXMLTree.reset(CPLParseXMLString(poOpenInfo->pszFilename));
687
8
        if (m_psXMLTree == nullptr)
688
8
            return false;
689
8
    }
690
2.94k
    else if (poOpenInfo->nHeaderBytes > 0 &&
691
2.94k
             strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
692
2.94k
                    "<GDALTileIndexDataset"))
693
138
    {
694
        // CPLParseXMLFile() emits an error in case of failure
695
138
        m_psXMLTree.reset(CPLParseXMLFile(poOpenInfo->pszFilename));
696
138
        if (m_psXMLTree == nullptr)
697
101
            return false;
698
37
        m_bXMLUpdatable = (poOpenInfo->eAccess == GA_Update);
699
37
    }
700
701
2.84k
    if (m_psXMLTree)
702
37
    {
703
37
        psRoot = CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
704
37
        if (psRoot == nullptr)
705
37
        {
706
37
            CPLError(CE_Failure, CPLE_AppDefined,
707
37
                     "Missing GDALTileIndexDataset root element.");
708
37
            return false;
709
37
        }
710
711
0
        pszIndexDataset = CPLGetXMLValue(psRoot, "IndexDataset", nullptr);
712
0
        if (!pszIndexDataset)
713
0
        {
714
0
            CPLError(CE_Failure, CPLE_AppDefined,
715
0
                     "Missing IndexDataset element.");
716
0
            return false;
717
0
        }
718
0
    }
719
720
2.81k
    if (ENDS_WITH_CI(pszIndexDataset, ".gti.gpkg") &&
721
0
        poOpenInfo->nHeaderBytes >= 100 &&
722
0
        STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
723
2.81k
                    "SQLite format 3"))
724
0
    {
725
0
        const char *const apszAllowedDrivers[] = {"GPKG", nullptr};
726
0
        m_poVectorDS.reset(GDALDataset::Open(
727
0
            std::string("GPKG:\"").append(pszIndexDataset).append("\"").c_str(),
728
0
            GDAL_OF_VECTOR | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR |
729
0
                ((poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) ? GDAL_OF_UPDATE
730
0
                                                           : GDAL_OF_READONLY),
731
0
            apszAllowedDrivers));
732
0
        if (!m_poVectorDS)
733
0
        {
734
0
            return false;
735
0
        }
736
0
        if (m_poVectorDS->GetLayerCount() == 0 &&
737
0
            (m_poVectorDS->GetRasterCount() != 0 ||
738
0
             m_poVectorDS->GetMetadata("SUBDATASETS") != nullptr))
739
0
        {
740
0
            return false;
741
0
        }
742
0
    }
743
2.81k
    else
744
2.81k
    {
745
2.81k
        m_poVectorDS.reset(GDALDataset::Open(
746
2.81k
            pszIndexDataset, GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR |
747
2.81k
                                 ((poOpenInfo->nOpenFlags & GDAL_OF_UPDATE)
748
2.81k
                                      ? GDAL_OF_UPDATE
749
2.81k
                                      : GDAL_OF_READONLY)));
750
2.81k
        if (!m_poVectorDS)
751
1.17k
        {
752
1.17k
            return false;
753
1.17k
        }
754
2.81k
    }
755
756
1.63k
    if (m_poVectorDS->GetLayerCount() == 0)
757
150
    {
758
150
        CPLError(CE_Failure, CPLE_AppDefined, "%s has no vector layer",
759
150
                 poOpenInfo->pszFilename);
760
150
        return false;
761
150
    }
762
763
1.48k
    double dfOvrFactor = 1.0;
764
1.48k
    if (const char *pszFactor =
765
1.48k
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "FACTOR"))
766
0
    {
767
0
        dfOvrFactor = CPLAtof(pszFactor);
768
0
        if (!(dfOvrFactor > 1.0))
769
0
        {
770
0
            CPLError(CE_Failure, CPLE_AppDefined, "Wrong overview factor");
771
0
            return false;
772
0
        }
773
0
    }
774
775
1.48k
    m_osSQL = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "SQL", "");
776
1.48k
    if (m_osSQL.empty())
777
1.48k
    {
778
1.48k
        if (!psRoot)
779
1.48k
        {
780
1.48k
            if (const char *pszVal =
781
1.48k
                    m_poVectorDS->GetMetadataItem(MD_DS_TILE_INDEX_SQL))
782
0
                m_osSQL = pszVal;
783
1.48k
        }
784
0
        else
785
0
            m_osSQL = CPLGetXMLValue(psRoot, "SQL", "");
786
1.48k
    }
787
788
1.48k
    if (!m_osSQL.empty())
789
0
    {
790
0
        m_osSpatialSQL = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
791
0
                                              "SPATIAL_SQL", "");
792
0
        if (m_osSpatialSQL.empty())
793
0
        {
794
0
            if (!psRoot)
795
0
            {
796
0
                if (const char *pszVal = m_poVectorDS->GetMetadataItem(
797
0
                        MD_DS_TILE_INDEX_SPATIAL_SQL))
798
0
                    m_osSpatialSQL = pszVal;
799
0
            }
800
0
            else
801
0
                m_osSpatialSQL = CPLGetXMLValue(psRoot, "SpatialSQL", "");
802
0
        }
803
0
    }
804
805
1.48k
    const char *pszLayerName;
806
807
1.48k
    if ((pszLayerName = CSLFetchNameValue(poOpenInfo->papszOpenOptions,
808
1.48k
                                          "LAYER")) != nullptr)
809
0
    {
810
0
        m_poLayer = m_poVectorDS->GetLayerByName(pszLayerName);
811
0
        if (!m_poLayer)
812
0
        {
813
0
            CPLError(CE_Failure, CPLE_AppDefined, "Layer %s does not exist",
814
0
                     pszLayerName);
815
0
            return false;
816
0
        }
817
0
    }
818
1.48k
    else if (psRoot && (pszLayerName = CPLGetXMLValue(psRoot, "IndexLayer",
819
0
                                                      nullptr)) != nullptr)
820
0
    {
821
0
        m_poLayer = m_poVectorDS->GetLayerByName(pszLayerName);
822
0
        if (!m_poLayer)
823
0
        {
824
0
            CPLError(CE_Failure, CPLE_AppDefined, "Layer %s does not exist",
825
0
                     pszLayerName);
826
0
            return false;
827
0
        }
828
0
    }
829
1.48k
    else if (!psRoot && (pszLayerName = m_poVectorDS->GetMetadataItem(
830
1.48k
                             MD_DS_TILE_INDEX_LAYER)) != nullptr)
831
0
    {
832
0
        m_poLayer = m_poVectorDS->GetLayerByName(pszLayerName);
833
0
        if (!m_poLayer)
834
0
        {
835
0
            CPLError(CE_Failure, CPLE_AppDefined, "Layer %s does not exist",
836
0
                     pszLayerName);
837
0
            return false;
838
0
        }
839
0
    }
840
1.48k
    else if (!m_osSQL.empty())
841
0
    {
842
0
        m_poLayer = m_poVectorDS->ExecuteSQL(m_osSQL.c_str(), nullptr, nullptr);
843
0
        if (!m_poLayer)
844
0
        {
845
0
            CPLError(CE_Failure, CPLE_AppDefined, "SQL request %s failed",
846
0
                     m_osSQL.c_str());
847
0
            return false;
848
0
        }
849
0
        m_bIsSQLResultLayer = true;
850
0
    }
851
1.48k
    else if (m_poVectorDS->GetLayerCount() == 1)
852
966
    {
853
966
        m_poLayer = m_poVectorDS->GetLayer(0);
854
966
        if (!m_poLayer)
855
0
        {
856
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot open layer 0");
857
0
            return false;
858
0
        }
859
966
    }
860
521
    else
861
521
    {
862
521
        if (STARTS_WITH(poOpenInfo->pszFilename, GTI_PREFIX))
863
0
        {
864
0
            CPLError(CE_Failure, CPLE_AppDefined,
865
0
                     "%s has more than one layer. LAYER open option "
866
0
                     "must be defined to specify which one to "
867
0
                     "use as the tile index",
868
0
                     pszIndexDataset);
869
0
        }
870
521
        else if (psRoot)
871
0
        {
872
0
            CPLError(CE_Failure, CPLE_AppDefined,
873
0
                     "%s has more than one layer. IndexLayer element must be "
874
0
                     "defined to specify which one to "
875
0
                     "use as the tile index",
876
0
                     pszIndexDataset);
877
0
        }
878
521
        else
879
521
        {
880
521
            CPLError(CE_Failure, CPLE_AppDefined,
881
521
                     "%s has more than one layer. %s "
882
521
                     "metadata item must be defined to specify which one to "
883
521
                     "use as the tile index",
884
521
                     pszIndexDataset, MD_DS_TILE_INDEX_LAYER);
885
521
        }
886
521
        return false;
887
521
    }
888
889
    // Try to get the metadata from an embedded xml:GTI domain
890
966
    if (!m_psXMLTree)
891
966
    {
892
966
        char **papszMD = m_poLayer->GetMetadata("xml:GTI");
893
966
        if (papszMD && papszMD[0])
894
0
        {
895
0
            m_psXMLTree.reset(CPLParseXMLString(papszMD[0]));
896
0
            if (m_psXMLTree == nullptr)
897
0
                return false;
898
899
0
            psRoot = CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
900
0
            if (psRoot == nullptr)
901
0
            {
902
0
                CPLError(CE_Failure, CPLE_AppDefined,
903
0
                         "Missing GDALTileIndexDataset root element.");
904
0
                return false;
905
0
            }
906
0
        }
907
966
    }
908
909
    // Get the value of an option.
910
    // The order of lookup is the following one (first to last):
911
    // - open options
912
    // - XML file
913
    // - Layer metadata items.
914
966
    const auto GetOption = [poOpenInfo, psRoot, this](const char *pszItem)
915
1.93k
    {
916
1.93k
        const char *pszVal =
917
1.93k
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszItem);
918
1.93k
        if (pszVal)
919
0
            return pszVal;
920
921
1.93k
        if (psRoot)
922
0
        {
923
0
            pszVal = CPLGetXMLValue(psRoot, pszItem, nullptr);
924
0
            if (pszVal)
925
0
                return pszVal;
926
927
0
            if (EQUAL(pszItem, MD_BAND_COUNT))
928
0
                pszItem = GTI_XML_BANDCOUNT;
929
0
            else if (EQUAL(pszItem, MD_DATA_TYPE))
930
0
                pszItem = GTI_XML_DATATYPE;
931
0
            else if (EQUAL(pszItem, MD_NODATA))
932
0
                pszItem = GTI_XML_NODATAVALUE;
933
0
            else if (EQUAL(pszItem, MD_COLOR_INTERPRETATION))
934
0
                pszItem = GTI_XML_COLORINTERP;
935
0
            else if (EQUAL(pszItem, MD_LOCATION_FIELD))
936
0
                pszItem = GTI_XML_LOCATIONFIELD;
937
0
            else if (EQUAL(pszItem, MD_SORT_FIELD))
938
0
                pszItem = GTI_XML_SORTFIELD;
939
0
            else if (EQUAL(pszItem, MD_SORT_FIELD_ASC))
940
0
                pszItem = GTI_XML_SORTFIELDASC;
941
0
            else if (EQUAL(pszItem, MD_MASK_BAND))
942
0
                pszItem = GTI_XML_MASKBAND;
943
0
            pszVal = CPLGetXMLValue(psRoot, pszItem, nullptr);
944
0
            if (pszVal)
945
0
                return pszVal;
946
0
        }
947
948
1.93k
        return m_poLayer->GetMetadataItem(pszItem);
949
1.93k
    };
950
951
966
    const char *pszFilter = GetOption("Filter");
952
966
    if (pszFilter)
953
0
    {
954
0
        if (m_poLayer->SetAttributeFilter(pszFilter) != OGRERR_NONE)
955
0
            return false;
956
0
    }
957
958
966
    const OGRFeatureDefn *poLayerDefn = m_poLayer->GetLayerDefn();
959
960
966
    std::string osLocationFieldName;
961
966
    {
962
966
        const char *pszLocationFieldName = GetOption(MD_LOCATION_FIELD);
963
966
        if (pszLocationFieldName)
964
0
        {
965
0
            osLocationFieldName = pszLocationFieldName;
966
0
        }
967
966
        else
968
966
        {
969
            // Is this a https://stac-utils.github.io/stac-geoparquet/latest/spec/stac-geoparquet-spec ?
970
966
            if (poLayerDefn->GetFieldIndex("assets.data.href") >= 0)
971
0
            {
972
0
                osLocationFieldName = "assets.data.href";
973
0
                CPLDebug("GTI", "Using %s as location field",
974
0
                         osLocationFieldName.c_str());
975
0
            }
976
966
            else if (poLayerDefn->GetFieldIndex("assets.image.href") >= 0)
977
0
            {
978
0
                osLocationFieldName = "assets.image.href";
979
0
                CPLDebug("GTI", "Using %s as location field",
980
0
                         osLocationFieldName.c_str());
981
0
            }
982
966
            else if (poLayerDefn->GetFieldIndex("stac_version") >= 0)
983
0
            {
984
0
                const int nFieldCount = poLayerDefn->GetFieldCount();
985
                // Look for "assets.xxxxx.href" fields
986
0
                int nAssetCount = 0;
987
0
                for (int i = 0; i < nFieldCount; ++i)
988
0
                {
989
0
                    const auto poFDefn = poLayerDefn->GetFieldDefn(i);
990
0
                    const char *pszFieldName = poFDefn->GetNameRef();
991
0
                    if (STARTS_WITH(pszFieldName, "assets.") &&
992
0
                        EQUAL(pszFieldName + strlen(pszFieldName) -
993
0
                                  strlen(".href"),
994
0
                              ".href") &&
995
                        // Assets with "metadata" in them are very much likely
996
                        // not rasters... We could potentially confirm that by
997
                        // inspecting the value of the assets.XXX.type or
998
                        // assets.XXX.roles fields of one feature
999
0
                        !strstr(pszFieldName, "metadata"))
1000
0
                    {
1001
0
                        ++nAssetCount;
1002
0
                        if (!osLocationFieldName.empty())
1003
0
                        {
1004
0
                            osLocationFieldName += ", ";
1005
0
                        }
1006
0
                        osLocationFieldName += pszFieldName;
1007
0
                    }
1008
0
                }
1009
0
                if (nAssetCount > 1)
1010
0
                {
1011
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1012
0
                             "Several potential STAC assets. Please select one "
1013
0
                             "among %s with the LOCATION_FIELD open option",
1014
0
                             osLocationFieldName.c_str());
1015
0
                    return false;
1016
0
                }
1017
0
                else if (nAssetCount == 0)
1018
0
                {
1019
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1020
0
                             "File has stac_version property but lacks assets");
1021
0
                    return false;
1022
0
                }
1023
0
            }
1024
966
            else
1025
966
            {
1026
966
                constexpr const char *DEFAULT_LOCATION_FIELD_NAME = "location";
1027
966
                osLocationFieldName = DEFAULT_LOCATION_FIELD_NAME;
1028
966
            }
1029
966
        }
1030
966
    }
1031
1032
966
    m_nLocationFieldIndex =
1033
966
        poLayerDefn->GetFieldIndex(osLocationFieldName.c_str());
1034
966
    if (m_nLocationFieldIndex < 0)
1035
966
    {
1036
966
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
1037
966
                 osLocationFieldName.c_str());
1038
966
        return false;
1039
966
    }
1040
0
    if (poLayerDefn->GetFieldDefn(m_nLocationFieldIndex)->GetType() !=
1041
0
        OFTString)
1042
0
    {
1043
0
        CPLError(CE_Failure, CPLE_AppDefined, "Field %s is not of type string",
1044
0
                 osLocationFieldName.c_str());
1045
0
        return false;
1046
0
    }
1047
1048
0
    const char *pszSortFieldName = GetOption(MD_SORT_FIELD);
1049
0
    if (pszSortFieldName)
1050
0
    {
1051
0
        m_nSortFieldIndex = poLayerDefn->GetFieldIndex(pszSortFieldName);
1052
0
        if (m_nSortFieldIndex < 0)
1053
0
        {
1054
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
1055
0
                     pszSortFieldName);
1056
0
            return false;
1057
0
        }
1058
1059
0
        const auto eFieldType =
1060
0
            poLayerDefn->GetFieldDefn(m_nSortFieldIndex)->GetType();
1061
0
        if (eFieldType != OFTString && eFieldType != OFTInteger &&
1062
0
            eFieldType != OFTInteger64 && eFieldType != OFTReal &&
1063
0
            eFieldType != OFTDate && eFieldType != OFTDateTime)
1064
0
        {
1065
0
            CPLError(CE_Failure, CPLE_AppDefined,
1066
0
                     "Unsupported type for field %s", pszSortFieldName);
1067
0
            return false;
1068
0
        }
1069
1070
0
        const char *pszSortFieldAsc = GetOption(MD_SORT_FIELD_ASC);
1071
0
        if (pszSortFieldAsc)
1072
0
        {
1073
0
            m_bSortFieldAsc = CPLTestBool(pszSortFieldAsc);
1074
0
        }
1075
0
    }
1076
1077
0
    const char *pszResX = GetOption(MD_RESX);
1078
0
    const char *pszResY = GetOption(MD_RESY);
1079
0
    if (pszResX && !pszResY)
1080
0
    {
1081
0
        CPLError(CE_Failure, CPLE_AppDefined,
1082
0
                 "%s metadata item defined, but not %s", MD_RESX, MD_RESY);
1083
0
        return false;
1084
0
    }
1085
0
    if (!pszResX && pszResY)
1086
0
    {
1087
0
        CPLError(CE_Failure, CPLE_AppDefined,
1088
0
                 "%s metadata item defined, but not %s", MD_RESY, MD_RESX);
1089
0
        return false;
1090
0
    }
1091
1092
0
    const char *pszResampling = GetOption(MD_RESAMPLING);
1093
0
    if (pszResampling)
1094
0
    {
1095
0
        const auto nErrorCountBefore = CPLGetErrorCounter();
1096
0
        m_eResampling = GDALRasterIOGetResampleAlg(pszResampling);
1097
0
        if (nErrorCountBefore != CPLGetErrorCounter())
1098
0
        {
1099
0
            return false;
1100
0
        }
1101
0
        m_osResampling = pszResampling;
1102
0
    }
1103
1104
0
    const char *pszMinX = GetOption(MD_MINX);
1105
0
    const char *pszMinY = GetOption(MD_MINY);
1106
0
    const char *pszMaxX = GetOption(MD_MAXX);
1107
0
    const char *pszMaxY = GetOption(MD_MAXY);
1108
0
    int nCountMinMaxXY = (pszMinX ? 1 : 0) + (pszMinY ? 1 : 0) +
1109
0
                         (pszMaxX ? 1 : 0) + (pszMaxY ? 1 : 0);
1110
0
    if (nCountMinMaxXY != 0 && nCountMinMaxXY != 4)
1111
0
    {
1112
0
        CPLError(CE_Failure, CPLE_AppDefined,
1113
0
                 "None or all of %s, %s, %s and %s must be specified", MD_MINX,
1114
0
                 MD_MINY, MD_MAXX, MD_MAXY);
1115
0
        return false;
1116
0
    }
1117
1118
0
    const char *pszXSize = GetOption(MD_XSIZE);
1119
0
    const char *pszYSize = GetOption(MD_YSIZE);
1120
0
    const char *pszGeoTransform = GetOption(MD_GEOTRANSFORM);
1121
0
    const int nCountXSizeYSizeGT =
1122
0
        (pszXSize ? 1 : 0) + (pszYSize ? 1 : 0) + (pszGeoTransform ? 1 : 0);
1123
0
    if (nCountXSizeYSizeGT != 0 && nCountXSizeYSizeGT != 3)
1124
0
    {
1125
0
        CPLError(CE_Failure, CPLE_AppDefined,
1126
0
                 "None or all of %s, %s, %s must be specified", MD_XSIZE,
1127
0
                 MD_YSIZE, MD_GEOTRANSFORM);
1128
0
        return false;
1129
0
    }
1130
1131
0
    const char *pszDataType = GetOption(MD_DATA_TYPE);
1132
0
    const char *pszColorInterp = GetOption(MD_COLOR_INTERPRETATION);
1133
0
    int nBandCount = 0;
1134
0
    std::vector<GDALDataType> aeDataTypes;
1135
0
    std::vector<std::pair<bool, double>> aNoData;
1136
0
    std::vector<GDALColorInterp> aeColorInterp;
1137
1138
0
    const char *pszSRS = GetOption(MD_SRS);
1139
0
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1140
0
    if (pszSRS)
1141
0
    {
1142
0
        if (m_oSRS.SetFromUserInput(
1143
0
                pszSRS,
1144
0
                OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
1145
0
            OGRERR_NONE)
1146
0
        {
1147
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s", MD_SRS);
1148
0
            return false;
1149
0
        }
1150
0
    }
1151
0
    else if (const auto poSRS = m_poLayer->GetSpatialRef())
1152
0
    {
1153
        // Ignore GPKG "Undefined geographic SRS" and "Undefined Cartesian SRS"
1154
0
        if (!STARTS_WITH(poSRS->GetName(), "Undefined "))
1155
0
            m_oSRS = *poSRS;
1156
0
    }
1157
1158
0
    std::vector<const CPLXMLNode *> apoXMLNodeBands;
1159
0
    if (psRoot)
1160
0
    {
1161
0
        int nExpectedBandNumber = 1;
1162
0
        for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
1163
0
             psIter = psIter->psNext)
1164
0
        {
1165
0
            if (psIter->eType == CXT_Element &&
1166
0
                strcmp(psIter->pszValue, GTI_XML_BAND_ELEMENT) == 0)
1167
0
            {
1168
0
                const char *pszBand =
1169
0
                    CPLGetXMLValue(psIter, GTI_XML_BAND_NUMBER, nullptr);
1170
0
                if (!pszBand)
1171
0
                {
1172
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1173
0
                             "%s attribute missing on %s element",
1174
0
                             GTI_XML_BAND_NUMBER, GTI_XML_BAND_ELEMENT);
1175
0
                    return false;
1176
0
                }
1177
0
                const int nBand = atoi(pszBand);
1178
0
                if (nBand <= 0)
1179
0
                {
1180
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1181
0
                             "Invalid band number");
1182
0
                    return false;
1183
0
                }
1184
0
                if (nBand != nExpectedBandNumber)
1185
0
                {
1186
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1187
0
                             "Invalid band number: found %d, expected %d",
1188
0
                             nBand, nExpectedBandNumber);
1189
0
                    return false;
1190
0
                }
1191
0
                apoXMLNodeBands.push_back(psIter);
1192
0
                ++nExpectedBandNumber;
1193
0
            }
1194
0
        }
1195
0
    }
1196
1197
0
    const char *pszBandCount = GetOption(MD_BAND_COUNT);
1198
0
    if (pszBandCount)
1199
0
        nBandCount = atoi(pszBandCount);
1200
1201
0
    if (!apoXMLNodeBands.empty())
1202
0
    {
1203
0
        if (!pszBandCount)
1204
0
            nBandCount = static_cast<int>(apoXMLNodeBands.size());
1205
0
        else if (nBandCount != static_cast<int>(apoXMLNodeBands.size()))
1206
0
        {
1207
0
            CPLError(CE_Failure, CPLE_AppDefined,
1208
0
                     "Inconsistent %s with actual number of %s elements",
1209
0
                     GTI_XML_BANDCOUNT, GTI_XML_BAND_ELEMENT);
1210
0
            return false;
1211
0
        }
1212
0
    }
1213
1214
    // Take into STAC GeoParquet proj:code / proj:epsg / proj:wkt2 / proj:projjson
1215
    // and proj:transform fields
1216
0
    std::unique_ptr<OGRFeature> poFeature;
1217
0
    std::string osResX, osResY, osMinX, osMinY, osMaxX, osMaxY;
1218
0
    int iProjCode = -1;
1219
0
    int iProjEPSG = -1;
1220
0
    int iProjWKT2 = -1;
1221
0
    int iProjPROJSON = -1;
1222
0
    int iProjTransform = -1;
1223
1224
0
    const bool bIsStacGeoParquet =
1225
0
        STARTS_WITH(osLocationFieldName.c_str(), "assets.") &&
1226
0
        EQUAL(osLocationFieldName.c_str() + osLocationFieldName.size() -
1227
0
                  strlen(".href"),
1228
0
              ".href");
1229
0
    std::string osAssetName;
1230
0
    if (bIsStacGeoParquet)
1231
0
    {
1232
0
        osAssetName = osLocationFieldName.substr(
1233
0
            strlen("assets."),
1234
0
            osLocationFieldName.size() - strlen("assets.") - strlen(".href"));
1235
0
    }
1236
1237
0
    const auto GetAssetFieldIndex =
1238
0
        [poLayerDefn, &osAssetName](const char *pszFieldName)
1239
0
    {
1240
0
        const int idx = poLayerDefn->GetFieldIndex(
1241
0
            CPLSPrintf("assets.%s.%s", osAssetName.c_str(), pszFieldName));
1242
0
        if (idx >= 0)
1243
0
            return idx;
1244
0
        return poLayerDefn->GetFieldIndex(pszFieldName);
1245
0
    };
1246
1247
0
    if (bIsStacGeoParquet && !pszSRS && !pszResX && !pszResY && !pszMinX &&
1248
0
        !pszMinY && !pszMaxX && !pszMaxY &&
1249
0
        ((iProjCode = GetAssetFieldIndex("proj:code")) >= 0 ||
1250
0
         (iProjEPSG = GetAssetFieldIndex("proj:epsg")) >= 0 ||
1251
0
         (iProjWKT2 = GetAssetFieldIndex("proj:wkt2")) >= 0 ||
1252
0
         (iProjPROJSON = GetAssetFieldIndex("proj:projjson")) >= 0) &&
1253
0
        ((iProjTransform = GetAssetFieldIndex("proj:transform")) >= 0))
1254
0
    {
1255
0
        poFeature.reset(m_poLayer->GetNextFeature());
1256
0
        const auto poProjTransformField =
1257
0
            poLayerDefn->GetFieldDefn(iProjTransform);
1258
0
        if (poFeature &&
1259
0
            ((iProjCode >= 0 && poFeature->IsFieldSet(iProjCode)) ||
1260
0
             (iProjEPSG >= 0 && poFeature->IsFieldSet(iProjEPSG)) ||
1261
0
             (iProjWKT2 >= 0 && poFeature->IsFieldSet(iProjWKT2)) ||
1262
0
             (iProjPROJSON >= 0 && poFeature->IsFieldSet(iProjPROJSON))) &&
1263
0
            poFeature->IsFieldSet(iProjTransform) &&
1264
0
            (poProjTransformField->GetType() == OFTRealList ||
1265
0
             poProjTransformField->GetType() == OFTIntegerList ||
1266
0
             poProjTransformField->GetType() == OFTInteger64List))
1267
0
        {
1268
0
            OGRSpatialReference oSTACSRS;
1269
0
            oSTACSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1270
1271
0
            if (iProjCode >= 0 && poFeature->IsFieldSet(iProjCode))
1272
0
                oSTACSRS.SetFromUserInput(
1273
0
                    poFeature->GetFieldAsString(iProjCode),
1274
0
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1275
1276
0
            else if (iProjEPSG >= 0 && poFeature->IsFieldSet(iProjEPSG))
1277
0
                oSTACSRS.importFromEPSG(
1278
0
                    poFeature->GetFieldAsInteger(iProjEPSG));
1279
1280
0
            else if (iProjWKT2 >= 0 && poFeature->IsFieldSet(iProjWKT2))
1281
0
                oSTACSRS.SetFromUserInput(
1282
0
                    poFeature->GetFieldAsString(iProjWKT2),
1283
0
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1284
1285
0
            else if (iProjPROJSON >= 0 && poFeature->IsFieldSet(iProjPROJSON))
1286
0
                oSTACSRS.SetFromUserInput(
1287
0
                    poFeature->GetFieldAsString(iProjPROJSON),
1288
0
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1289
1290
0
            if (!oSTACSRS.IsEmpty())
1291
0
            {
1292
0
                int nTransformCount = 0;
1293
0
                double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
1294
0
                if (poProjTransformField->GetType() == OFTRealList)
1295
0
                {
1296
0
                    const auto padfFeatureTransform =
1297
0
                        poFeature->GetFieldAsDoubleList(iProjTransform,
1298
0
                                                        &nTransformCount);
1299
0
                    if (nTransformCount >= 6)
1300
0
                        memcpy(adfGeoTransform, padfFeatureTransform,
1301
0
                               6 * sizeof(double));
1302
0
                }
1303
0
                else if (poProjTransformField->GetType() == OFTInteger64List)
1304
0
                {
1305
0
                    const auto paFeatureTransform =
1306
0
                        poFeature->GetFieldAsInteger64List(iProjTransform,
1307
0
                                                           &nTransformCount);
1308
0
                    if (nTransformCount >= 6)
1309
0
                    {
1310
0
                        for (int i = 0; i < 6; ++i)
1311
0
                            adfGeoTransform[i] =
1312
0
                                static_cast<double>(paFeatureTransform[i]);
1313
0
                    }
1314
0
                }
1315
0
                else if (poProjTransformField->GetType() == OFTIntegerList)
1316
0
                {
1317
0
                    const auto paFeatureTransform =
1318
0
                        poFeature->GetFieldAsIntegerList(iProjTransform,
1319
0
                                                         &nTransformCount);
1320
0
                    if (nTransformCount >= 6)
1321
0
                    {
1322
0
                        for (int i = 0; i < 6; ++i)
1323
0
                            adfGeoTransform[i] = paFeatureTransform[i];
1324
0
                    }
1325
0
                }
1326
0
                OGREnvelope sEnvelope;
1327
0
                if (nTransformCount >= 6 && m_poLayer->GetSpatialRef() &&
1328
0
                    m_poLayer->GetExtent(&sEnvelope, /* bForce = */ true) ==
1329
0
                        OGRERR_NONE)
1330
0
                {
1331
0
                    const double dfResX = adfGeoTransform[0];
1332
0
                    osResX = CPLSPrintf("%.17g", dfResX);
1333
0
                    const double dfResY = std::fabs(adfGeoTransform[4]);
1334
0
                    osResY = CPLSPrintf("%.17g", dfResY);
1335
1336
0
                    auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
1337
0
                        OGRCreateCoordinateTransformation(
1338
0
                            m_poLayer->GetSpatialRef(), &oSTACSRS));
1339
0
                    auto poInvCT = std::unique_ptr<OGRCoordinateTransformation>(
1340
0
                        poCT ? poCT->GetInverse() : nullptr);
1341
0
                    double dfOutMinX = 0;
1342
0
                    double dfOutMinY = 0;
1343
0
                    double dfOutMaxX = 0;
1344
0
                    double dfOutMaxY = 0;
1345
0
                    if (dfResX > 0 && dfResY > 0 && poCT && poInvCT &&
1346
0
                        poCT->TransformBounds(sEnvelope.MinX, sEnvelope.MinY,
1347
0
                                              sEnvelope.MaxX, sEnvelope.MaxY,
1348
0
                                              &dfOutMinX, &dfOutMinY,
1349
0
                                              &dfOutMaxX, &dfOutMaxY, 21))
1350
0
                    {
1351
0
                        constexpr double EPSILON = 1e-3;
1352
0
                        const bool bTileAlignedOnRes =
1353
0
                            (fmod(std::fabs(adfGeoTransform[3]), dfResX) <=
1354
0
                                 EPSILON * dfResX &&
1355
0
                             fmod(std::fabs(adfGeoTransform[5]), dfResY) <=
1356
0
                                 EPSILON * dfResY);
1357
1358
0
                        osMinX = CPLSPrintf(
1359
0
                            "%.17g",
1360
0
                            !bTileAlignedOnRes
1361
0
                                ? dfOutMinX
1362
0
                                : std::floor(dfOutMinX / dfResX) * dfResX);
1363
0
                        osMinY = CPLSPrintf(
1364
0
                            "%.17g",
1365
0
                            !bTileAlignedOnRes
1366
0
                                ? dfOutMinY
1367
0
                                : std::floor(dfOutMinY / dfResY) * dfResY);
1368
0
                        osMaxX = CPLSPrintf(
1369
0
                            "%.17g",
1370
0
                            !bTileAlignedOnRes
1371
0
                                ? dfOutMaxX
1372
0
                                : std::ceil(dfOutMaxX / dfResX) * dfResX);
1373
0
                        osMaxY = CPLSPrintf(
1374
0
                            "%.17g",
1375
0
                            !bTileAlignedOnRes
1376
0
                                ? dfOutMaxY
1377
0
                                : std::ceil(dfOutMaxY / dfResY) * dfResY);
1378
1379
0
                        m_oSRS = std::move(oSTACSRS);
1380
0
                        pszResX = osResX.c_str();
1381
0
                        pszResY = osResY.c_str();
1382
0
                        pszMinX = osMinX.c_str();
1383
0
                        pszMinY = osMinY.c_str();
1384
0
                        pszMaxX = osMaxX.c_str();
1385
0
                        pszMaxY = osMaxY.c_str();
1386
0
                        nCountMinMaxXY = 4;
1387
1388
0
                        poFeature.reset();
1389
0
                        m_poLayer->ResetReading();
1390
1391
0
                        m_poWarpedLayerKeeper =
1392
0
                            std::make_unique<OGRWarpedLayer>(
1393
0
                                m_poLayer, /* iGeomField = */ 0,
1394
0
                                /* bTakeOwnership = */ false, poCT.release(),
1395
0
                                poInvCT.release());
1396
0
                        m_poLayer = m_poWarpedLayerKeeper.get();
1397
0
                        poLayerDefn = m_poLayer->GetLayerDefn();
1398
0
                    }
1399
0
                }
1400
0
            }
1401
0
        }
1402
0
    }
1403
1404
0
    OGREnvelope sEnvelope;
1405
0
    if (nCountMinMaxXY == 4)
1406
0
    {
1407
0
        sEnvelope.MinX = CPLAtof(pszMinX);
1408
0
        sEnvelope.MinY = CPLAtof(pszMinY);
1409
0
        sEnvelope.MaxX = CPLAtof(pszMaxX);
1410
0
        sEnvelope.MaxY = CPLAtof(pszMaxY);
1411
0
        if (!(sEnvelope.MaxX > sEnvelope.MinX))
1412
0
        {
1413
0
            CPLError(CE_Failure, CPLE_AppDefined,
1414
0
                     "%s metadata item must be > %s", MD_MAXX, MD_MINX);
1415
0
            return false;
1416
0
        }
1417
0
        if (!(sEnvelope.MaxY > sEnvelope.MinY))
1418
0
        {
1419
0
            CPLError(CE_Failure, CPLE_AppDefined,
1420
0
                     "%s metadata item must be > %s", MD_MAXY, MD_MINY);
1421
0
            return false;
1422
0
        }
1423
0
    }
1424
1425
0
    bool bHasMaskBand = false;
1426
0
    std::unique_ptr<GDALColorTable> poSingleColorTable;
1427
0
    if ((!pszBandCount && apoXMLNodeBands.empty()) ||
1428
0
        (!(pszResX && pszResY) && nCountXSizeYSizeGT == 0))
1429
0
    {
1430
0
        CPLDebug("GTI", "Inspecting one feature due to missing metadata items");
1431
0
        m_bScannedOneFeatureAtOpening = true;
1432
1433
0
        if (!poFeature)
1434
0
            poFeature.reset(m_poLayer->GetNextFeature());
1435
0
        if (!poFeature ||
1436
0
            !poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
1437
0
        {
1438
0
            CPLError(
1439
0
                CE_Failure, CPLE_AppDefined,
1440
0
                "BAND_COUNT(+DATA_TYPE+COLOR_INTERPRETATION)+ (RESX+RESY or "
1441
0
                "XSIZE+YSIZE+GEOTRANSFORM) metadata items "
1442
0
                "missing");
1443
0
            return false;
1444
0
        }
1445
1446
0
        const char *pszTileName =
1447
0
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
1448
0
        const std::string osTileName(
1449
0
            GetAbsoluteFileName(pszTileName, poOpenInfo->pszFilename));
1450
0
        pszTileName = osTileName.c_str();
1451
1452
0
        auto poTileDS = std::shared_ptr<GDALDataset>(
1453
0
            GDALDataset::Open(pszTileName,
1454
0
                              GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR),
1455
0
            GDALDatasetUniquePtrReleaser());
1456
0
        if (!poTileDS)
1457
0
        {
1458
0
            return false;
1459
0
        }
1460
1461
        // do palette -> RGB(A) expansion if needed
1462
0
        if (!GTIDoPaletteExpansionIfNeeded(poTileDS, nBandCount))
1463
0
            return false;
1464
1465
0
        const int nTileBandCount = poTileDS->GetRasterCount();
1466
0
        for (int i = 0; i < nTileBandCount; ++i)
1467
0
        {
1468
0
            auto poTileBand = poTileDS->GetRasterBand(i + 1);
1469
0
            aeDataTypes.push_back(poTileBand->GetRasterDataType());
1470
0
            int bHasNoData = FALSE;
1471
0
            const double dfNoData = poTileBand->GetNoDataValue(&bHasNoData);
1472
0
            aNoData.emplace_back(CPL_TO_BOOL(bHasNoData), dfNoData);
1473
0
            aeColorInterp.push_back(poTileBand->GetColorInterpretation());
1474
0
            if (nTileBandCount == 1)
1475
0
            {
1476
0
                if (auto poCT = poTileBand->GetColorTable())
1477
0
                {
1478
                    // We assume that this will apply to all tiles...
1479
                    // TODO: detect if that it is really the case, and warn
1480
                    // if not, or do approximate palette matching like
1481
                    // done in GDALRasterBand::GetIndexColorTranslationTo()
1482
0
                    poSingleColorTable.reset(poCT->Clone());
1483
0
                }
1484
0
            }
1485
1486
0
            if (poTileBand->GetMaskFlags() == GMF_PER_DATASET)
1487
0
                bHasMaskBand = true;
1488
0
        }
1489
0
        if (!pszBandCount && nBandCount == 0)
1490
0
            nBandCount = nTileBandCount;
1491
1492
0
        auto poTileSRS = poTileDS->GetSpatialRef();
1493
0
        if (!m_oSRS.IsEmpty() && poTileSRS && !m_oSRS.IsSame(poTileSRS))
1494
0
        {
1495
0
            CPLStringList aosOptions;
1496
0
            aosOptions.AddString("-of");
1497
0
            aosOptions.AddString("VRT");
1498
1499
0
            char *pszWKT = nullptr;
1500
0
            const char *const apszWKTOptions[] = {"FORMAT=WKT2_2019", nullptr};
1501
0
            m_oSRS.exportToWkt(&pszWKT, apszWKTOptions);
1502
0
            if (pszWKT)
1503
0
                m_osWKT = pszWKT;
1504
0
            CPLFree(pszWKT);
1505
1506
0
            if (m_osWKT.empty())
1507
0
            {
1508
0
                CPLError(CE_Failure, CPLE_AppDefined,
1509
0
                         "Cannot export VRT SRS to WKT2");
1510
0
                return false;
1511
0
            }
1512
1513
0
            aosOptions.AddString("-t_srs");
1514
0
            aosOptions.AddString(m_osWKT.c_str());
1515
1516
0
            GDALWarpAppOptions *psWarpOptions =
1517
0
                GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
1518
0
            GDALDatasetH ahSrcDS[] = {GDALDataset::ToHandle(poTileDS.get())};
1519
0
            int bUsageError = false;
1520
0
            auto poWarpDS =
1521
0
                std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALWarp(
1522
0
                    "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
1523
0
            GDALWarpAppOptionsFree(psWarpOptions);
1524
0
            if (!poWarpDS)
1525
0
            {
1526
0
                return false;
1527
0
            }
1528
1529
0
            poTileDS.reset(poWarpDS.release());
1530
0
            poTileSRS = poTileDS->GetSpatialRef();
1531
0
            CPL_IGNORE_RET_VAL(poTileSRS);
1532
0
        }
1533
1534
0
        GDALGeoTransform gtTile;
1535
0
        if (poTileDS->GetGeoTransform(gtTile) != CE_None)
1536
0
        {
1537
0
            CPLError(CE_Failure, CPLE_AppDefined,
1538
0
                     "Cannot find geotransform on %s", pszTileName);
1539
0
            return false;
1540
0
        }
1541
0
        if (!(gtTile[GT_ROTATION_PARAM1] == 0))
1542
0
        {
1543
0
            CPLError(CE_Failure, CPLE_AppDefined,
1544
0
                     "3rd value of GeoTransform of %s must be 0", pszTileName);
1545
0
            return false;
1546
0
        }
1547
0
        if (!(gtTile[GT_ROTATION_PARAM2] == 0))
1548
0
        {
1549
0
            CPLError(CE_Failure, CPLE_AppDefined,
1550
0
                     "5th value of GeoTransform of %s must be 0", pszTileName);
1551
0
            return false;
1552
0
        }
1553
0
        if (!(gtTile[GT_NS_RES] < 0))
1554
0
        {
1555
0
            CPLError(CE_Failure, CPLE_AppDefined,
1556
0
                     "6th value of GeoTransform of %s must be < 0",
1557
0
                     pszTileName);
1558
0
            return false;
1559
0
        }
1560
1561
0
        const double dfResX = gtTile[GT_WE_RES];
1562
0
        const double dfResY = -gtTile[GT_NS_RES];
1563
1564
0
        if (!sEnvelope.IsInit() &&
1565
0
            m_poLayer->GetExtent(&sEnvelope, /* bForce = */ false) ==
1566
0
                OGRERR_FAILURE)
1567
0
        {
1568
0
            if (m_poLayer->GetExtent(&sEnvelope, /* bForce = */ true) ==
1569
0
                OGRERR_FAILURE)
1570
0
            {
1571
0
                CPLError(CE_Failure, CPLE_AppDefined,
1572
0
                         "Cannot get layer extent");
1573
0
                return false;
1574
0
            }
1575
0
            CPLError(CE_Warning, CPLE_AppDefined,
1576
0
                     "Could get layer extent, but using a slower method");
1577
0
        }
1578
1579
0
        const double dfXSize = (sEnvelope.MaxX - sEnvelope.MinX) / dfResX;
1580
0
        if (!(dfXSize >= 0 && dfXSize < INT_MAX))
1581
0
        {
1582
0
            CPLError(CE_Failure, CPLE_AppDefined,
1583
0
                     "Too small %s, or wrong layer extent", MD_RESX);
1584
0
            return false;
1585
0
        }
1586
1587
0
        const double dfYSize = (sEnvelope.MaxY - sEnvelope.MinY) / dfResY;
1588
0
        if (!(dfYSize >= 0 && dfYSize < INT_MAX))
1589
0
        {
1590
0
            CPLError(CE_Failure, CPLE_AppDefined,
1591
0
                     "Too small %s, or wrong layer extent", MD_RESY);
1592
0
            return false;
1593
0
        }
1594
1595
0
        m_gt[GT_TOPLEFT_X] = sEnvelope.MinX;
1596
0
        m_gt[GT_WE_RES] = dfResX;
1597
0
        m_gt[GT_ROTATION_PARAM1] = 0;
1598
0
        m_gt[GT_TOPLEFT_Y] = sEnvelope.MaxY;
1599
0
        m_gt[GT_ROTATION_PARAM2] = 0;
1600
0
        m_gt[GT_NS_RES] = -dfResY;
1601
0
        nRasterXSize = static_cast<int>(std::ceil(dfXSize));
1602
0
        nRasterYSize = static_cast<int>(std::ceil(dfYSize));
1603
0
    }
1604
1605
0
    if (pszXSize && pszYSize && pszGeoTransform)
1606
0
    {
1607
0
        const int nXSize = atoi(pszXSize);
1608
0
        if (nXSize <= 0)
1609
0
        {
1610
0
            CPLError(CE_Failure, CPLE_AppDefined,
1611
0
                     "%s metadata item must be > 0", MD_XSIZE);
1612
0
            return false;
1613
0
        }
1614
1615
0
        const int nYSize = atoi(pszYSize);
1616
0
        if (nYSize <= 0)
1617
0
        {
1618
0
            CPLError(CE_Failure, CPLE_AppDefined,
1619
0
                     "%s metadata item must be > 0", MD_YSIZE);
1620
0
            return false;
1621
0
        }
1622
1623
0
        const CPLStringList aosTokens(
1624
0
            CSLTokenizeString2(pszGeoTransform, ",", 0));
1625
0
        if (aosTokens.size() != 6)
1626
0
        {
1627
0
            CPLError(CE_Failure, CPLE_AppDefined,
1628
0
                     "%s metadata item must be 6 numeric values "
1629
0
                     "separated with comma",
1630
0
                     MD_GEOTRANSFORM);
1631
0
            return false;
1632
0
        }
1633
0
        for (int i = 0; i < 6; ++i)
1634
0
        {
1635
0
            m_gt[i] = CPLAtof(aosTokens[i]);
1636
0
        }
1637
0
        if (!(m_gt[GT_ROTATION_PARAM1] == 0))
1638
0
        {
1639
0
            CPLError(CE_Failure, CPLE_AppDefined, "3rd value of %s must be 0",
1640
0
                     MD_GEOTRANSFORM);
1641
0
            return false;
1642
0
        }
1643
0
        if (!(m_gt[GT_ROTATION_PARAM2] == 0))
1644
0
        {
1645
0
            CPLError(CE_Failure, CPLE_AppDefined, "5th value of %s must be 0",
1646
0
                     MD_GEOTRANSFORM);
1647
0
            return false;
1648
0
        }
1649
0
        if (!(m_gt[GT_NS_RES] < 0))
1650
0
        {
1651
0
            CPLError(CE_Failure, CPLE_AppDefined, "6th value of %s must be < 0",
1652
0
                     MD_GEOTRANSFORM);
1653
0
            return false;
1654
0
        }
1655
1656
0
        nRasterXSize = nXSize;
1657
0
        nRasterYSize = nYSize;
1658
0
    }
1659
0
    else if (pszResX && pszResY)
1660
0
    {
1661
0
        const double dfResX = CPLAtof(pszResX);
1662
0
        if (!(dfResX > 0))
1663
0
        {
1664
0
            CPLError(CE_Failure, CPLE_AppDefined,
1665
0
                     "RESX metadata item must be > 0");
1666
0
            return false;
1667
0
        }
1668
0
        const double dfResY = CPLAtof(pszResY);
1669
0
        if (!(dfResY > 0))
1670
0
        {
1671
0
            CPLError(CE_Failure, CPLE_AppDefined,
1672
0
                     "RESY metadata item must be > 0");
1673
0
            return false;
1674
0
        }
1675
1676
0
        if (nCountMinMaxXY == 4)
1677
0
        {
1678
0
            if (pszXSize || pszYSize || pszGeoTransform)
1679
0
            {
1680
0
                CPLError(CE_Warning, CPLE_AppDefined,
1681
0
                         "Ignoring %s, %s and %s when %s, "
1682
0
                         "%s, %s and %s are specified",
1683
0
                         MD_XSIZE, MD_YSIZE, MD_GEOTRANSFORM, MD_MINX, MD_MINY,
1684
0
                         MD_MAXX, MD_MAXY);
1685
0
            }
1686
0
        }
1687
0
        else if (!sEnvelope.IsInit() &&
1688
0
                 m_poLayer->GetExtent(&sEnvelope, /* bForce = */ false) ==
1689
0
                     OGRERR_FAILURE)
1690
0
        {
1691
0
            if (m_poLayer->GetExtent(&sEnvelope, /* bForce = */ true) ==
1692
0
                OGRERR_FAILURE)
1693
0
            {
1694
0
                CPLError(CE_Failure, CPLE_AppDefined,
1695
0
                         "Cannot get layer extent");
1696
0
                return false;
1697
0
            }
1698
0
            CPLError(CE_Warning, CPLE_AppDefined,
1699
0
                     "Could get layer extent, but using a slower method");
1700
0
        }
1701
1702
0
        const double dfXSize = (sEnvelope.MaxX - sEnvelope.MinX) / dfResX;
1703
0
        if (!(dfXSize >= 0 && dfXSize < INT_MAX))
1704
0
        {
1705
0
            CPLError(CE_Failure, CPLE_AppDefined,
1706
0
                     "Too small %s, or wrong layer extent", MD_RESX);
1707
0
            return false;
1708
0
        }
1709
1710
0
        const double dfYSize = (sEnvelope.MaxY - sEnvelope.MinY) / dfResY;
1711
0
        if (!(dfYSize >= 0 && dfYSize < INT_MAX))
1712
0
        {
1713
0
            CPLError(CE_Failure, CPLE_AppDefined,
1714
0
                     "Too small %s, or wrong layer extent", MD_RESY);
1715
0
            return false;
1716
0
        }
1717
1718
0
        m_gt[GT_TOPLEFT_X] = sEnvelope.MinX;
1719
0
        m_gt[GT_WE_RES] = dfResX;
1720
0
        m_gt[GT_ROTATION_PARAM1] = 0;
1721
0
        m_gt[GT_TOPLEFT_Y] = sEnvelope.MaxY;
1722
0
        m_gt[GT_ROTATION_PARAM2] = 0;
1723
0
        m_gt[GT_NS_RES] = -dfResY;
1724
0
        nRasterXSize = static_cast<int>(std::ceil(dfXSize));
1725
0
        nRasterYSize = static_cast<int>(std::ceil(dfYSize));
1726
0
    }
1727
1728
0
    if (nBandCount == 0 && !pszBandCount)
1729
0
    {
1730
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s metadata item missing",
1731
0
                 MD_BAND_COUNT);
1732
0
        return false;
1733
0
    }
1734
1735
0
    if (!GDALCheckBandCount(nBandCount, false))
1736
0
        return false;
1737
1738
0
    if (aeDataTypes.empty() && !pszDataType)
1739
0
    {
1740
0
        aeDataTypes.resize(nBandCount, GDT_Byte);
1741
0
    }
1742
0
    else if (pszDataType)
1743
0
    {
1744
0
        aeDataTypes.clear();
1745
0
        const CPLStringList aosTokens(CSLTokenizeString2(pszDataType, ", ", 0));
1746
0
        if (aosTokens.size() == 1)
1747
0
        {
1748
0
            const auto eDataType = GDALGetDataTypeByName(aosTokens[0]);
1749
0
            if (eDataType == GDT_Unknown)
1750
0
            {
1751
0
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for %s",
1752
0
                         MD_DATA_TYPE);
1753
0
                return false;
1754
0
            }
1755
0
            aeDataTypes.resize(nBandCount, eDataType);
1756
0
        }
1757
0
        else if (aosTokens.size() == nBandCount)
1758
0
        {
1759
0
            for (int i = 0; i < nBandCount; ++i)
1760
0
            {
1761
0
                const auto eDataType = GDALGetDataTypeByName(aosTokens[i]);
1762
0
                if (eDataType == GDT_Unknown)
1763
0
                {
1764
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1765
0
                             "Invalid value for %s", MD_DATA_TYPE);
1766
0
                    return false;
1767
0
                }
1768
0
                aeDataTypes.push_back(eDataType);
1769
0
            }
1770
0
        }
1771
0
        else
1772
0
        {
1773
0
            CPLError(CE_Failure, CPLE_AppDefined,
1774
0
                     "Number of values in %s must be 1 or %s", MD_DATA_TYPE,
1775
0
                     MD_BAND_COUNT);
1776
0
            return false;
1777
0
        }
1778
0
    }
1779
1780
0
    const char *pszNoData = GetOption(MD_NODATA);
1781
0
    if (pszNoData)
1782
0
    {
1783
0
        const auto IsValidNoDataStr = [](const char *pszStr)
1784
0
        {
1785
0
            if (EQUAL(pszStr, "inf") || EQUAL(pszStr, "-inf") ||
1786
0
                EQUAL(pszStr, "nan"))
1787
0
                return true;
1788
0
            const auto eType = CPLGetValueType(pszStr);
1789
0
            return eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL;
1790
0
        };
1791
1792
0
        aNoData.clear();
1793
0
        const CPLStringList aosTokens(CSLTokenizeString2(pszNoData, ", ", 0));
1794
0
        if (aosTokens.size() == 1)
1795
0
        {
1796
0
            if (!EQUAL(aosTokens[0], "NONE"))
1797
0
            {
1798
0
                if (!IsValidNoDataStr(aosTokens[0]))
1799
0
                {
1800
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1801
0
                             "Invalid value for %s", MD_NODATA);
1802
0
                    return false;
1803
0
                }
1804
0
                aNoData.resize(nBandCount,
1805
0
                               std::pair(true, CPLAtof(aosTokens[0])));
1806
0
            }
1807
0
        }
1808
0
        else if (aosTokens.size() == nBandCount)
1809
0
        {
1810
0
            for (int i = 0; i < nBandCount; ++i)
1811
0
            {
1812
0
                if (EQUAL(aosTokens[i], "NONE"))
1813
0
                {
1814
0
                    aNoData.emplace_back(false, 0);
1815
0
                }
1816
0
                else if (IsValidNoDataStr(aosTokens[i]))
1817
0
                {
1818
0
                    aNoData.emplace_back(true, CPLAtof(aosTokens[i]));
1819
0
                }
1820
0
                else
1821
0
                {
1822
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1823
0
                             "Invalid value for %s", MD_NODATA);
1824
0
                    return false;
1825
0
                }
1826
0
            }
1827
0
        }
1828
0
        else
1829
0
        {
1830
0
            CPLError(CE_Failure, CPLE_AppDefined,
1831
0
                     "Number of values in %s must be 1 or %s", MD_NODATA,
1832
0
                     MD_BAND_COUNT);
1833
0
            return false;
1834
0
        }
1835
0
    }
1836
1837
0
    if (pszColorInterp)
1838
0
    {
1839
0
        aeColorInterp.clear();
1840
0
        const CPLStringList aosTokens(
1841
0
            CSLTokenizeString2(pszColorInterp, ", ", 0));
1842
0
        if (aosTokens.size() == 1)
1843
0
        {
1844
0
            const auto eInterp = GDALGetColorInterpretationByName(aosTokens[0]);
1845
0
            if (eInterp == GCI_Undefined &&
1846
0
                !EQUAL(aosTokens[0],
1847
0
                       GDALGetColorInterpretationName(GCI_Undefined)))
1848
0
            {
1849
0
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for %s",
1850
0
                         MD_COLOR_INTERPRETATION);
1851
0
                return false;
1852
0
            }
1853
0
            aeColorInterp.resize(nBandCount, eInterp);
1854
0
        }
1855
0
        else if (aosTokens.size() == nBandCount)
1856
0
        {
1857
0
            for (int i = 0; i < nBandCount; ++i)
1858
0
            {
1859
0
                const auto eInterp =
1860
0
                    GDALGetColorInterpretationByName(aosTokens[i]);
1861
0
                if (eInterp == GCI_Undefined &&
1862
0
                    !EQUAL(aosTokens[i],
1863
0
                           GDALGetColorInterpretationName(GCI_Undefined)))
1864
0
                {
1865
0
                    CPLError(CE_Failure, CPLE_AppDefined,
1866
0
                             "Invalid value for %s", MD_COLOR_INTERPRETATION);
1867
0
                    return false;
1868
0
                }
1869
0
                aeColorInterp.emplace_back(eInterp);
1870
0
            }
1871
0
        }
1872
0
        else
1873
0
        {
1874
0
            CPLError(CE_Failure, CPLE_AppDefined,
1875
0
                     "Number of values in %s must be 1 or "
1876
0
                     "%s",
1877
0
                     MD_COLOR_INTERPRETATION, MD_BAND_COUNT);
1878
0
            return false;
1879
0
        }
1880
0
    }
1881
1882
    /* -------------------------------------------------------------------- */
1883
    /*      Create bands.                                                   */
1884
    /* -------------------------------------------------------------------- */
1885
0
    if (aeDataTypes.size() != static_cast<size_t>(nBandCount))
1886
0
    {
1887
0
        CPLError(
1888
0
            CE_Failure, CPLE_AppDefined,
1889
0
            "Number of data types values found not matching number of bands");
1890
0
        return false;
1891
0
    }
1892
0
    if (!aNoData.empty() && aNoData.size() != static_cast<size_t>(nBandCount))
1893
0
    {
1894
0
        CPLError(CE_Failure, CPLE_AppDefined,
1895
0
                 "Number of nodata values found not matching number of bands");
1896
0
        return false;
1897
0
    }
1898
0
    if (!aeColorInterp.empty() &&
1899
0
        aeColorInterp.size() != static_cast<size_t>(nBandCount))
1900
0
    {
1901
0
        CPLError(CE_Failure, CPLE_AppDefined,
1902
0
                 "Number of color interpretation values found not matching "
1903
0
                 "number of bands");
1904
0
        return false;
1905
0
    }
1906
1907
0
    int nBlockXSize = 256;
1908
0
    const char *pszBlockXSize = GetOption(MD_BLOCK_X_SIZE);
1909
0
    if (pszBlockXSize)
1910
0
    {
1911
0
        nBlockXSize = atoi(pszBlockXSize);
1912
0
        if (nBlockXSize <= 0)
1913
0
        {
1914
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s",
1915
0
                     MD_BLOCK_X_SIZE);
1916
0
            return false;
1917
0
        }
1918
0
    }
1919
1920
0
    int nBlockYSize = 256;
1921
0
    const char *pszBlockYSize = GetOption(MD_BLOCK_Y_SIZE);
1922
0
    if (pszBlockYSize)
1923
0
    {
1924
0
        nBlockYSize = atoi(pszBlockYSize);
1925
0
        if (nBlockYSize <= 0)
1926
0
        {
1927
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s",
1928
0
                     MD_BLOCK_Y_SIZE);
1929
0
            return false;
1930
0
        }
1931
0
    }
1932
1933
0
    if (nBlockXSize > INT_MAX / nBlockYSize)
1934
0
    {
1935
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too big %s * %s",
1936
0
                 MD_BLOCK_X_SIZE, MD_BLOCK_Y_SIZE);
1937
0
        return false;
1938
0
    }
1939
1940
0
    if (dfOvrFactor > 1.0)
1941
0
    {
1942
0
        m_gt[GT_WE_RES] *= dfOvrFactor;
1943
0
        m_gt[GT_NS_RES] *= dfOvrFactor;
1944
0
        nRasterXSize = static_cast<int>(std::ceil(nRasterXSize / dfOvrFactor));
1945
0
        nRasterYSize = static_cast<int>(std::ceil(nRasterYSize / dfOvrFactor));
1946
0
    }
1947
1948
0
    std::vector<std::string> aosDescriptions;
1949
0
    std::vector<double> adfCenterWavelength;
1950
0
    std::vector<double> adfFullWidthHalfMax;
1951
0
    std::vector<double> adfScale;
1952
0
    std::vector<double> adfOffset;
1953
0
    if (bIsStacGeoParquet && poFeature)
1954
0
    {
1955
0
        const int nEOBandsIdx = poLayerDefn->GetFieldIndex(
1956
0
            CPLSPrintf("assets.%s.eo:bands", osAssetName.c_str()));
1957
0
        if (nEOBandsIdx >= 0 &&
1958
0
            poLayerDefn->GetFieldDefn(nEOBandsIdx)->GetSubType() == OFSTJSON &&
1959
0
            poFeature->IsFieldSet(nEOBandsIdx))
1960
0
        {
1961
0
            const char *pszStr = poFeature->GetFieldAsString(nEOBandsIdx);
1962
0
            CPLJSONDocument oDoc;
1963
0
            if (oDoc.LoadMemory(pszStr) &&
1964
0
                oDoc.GetRoot().GetType() == CPLJSONObject::Type::Array)
1965
0
            {
1966
0
                const auto oArray = oDoc.GetRoot().ToArray();
1967
0
                if (oArray.Size() == nBandCount)
1968
0
                {
1969
0
                    int i = 0;
1970
0
                    aosDescriptions.resize(nBandCount);
1971
0
                    adfCenterWavelength.resize(nBandCount);
1972
0
                    adfFullWidthHalfMax.resize(nBandCount);
1973
0
                    for (const auto &oObj : oArray)
1974
0
                    {
1975
0
                        if (oObj.GetType() == CPLJSONObject::Type::Object)
1976
0
                        {
1977
0
                            const auto osCommonName =
1978
0
                                oObj.GetString("common_name");
1979
0
                            const auto eInterp =
1980
0
                                GDALGetColorInterpFromSTACCommonName(
1981
0
                                    osCommonName.c_str());
1982
0
                            if (eInterp != GCI_Undefined)
1983
0
                                aeColorInterp[i] = eInterp;
1984
1985
0
                            aosDescriptions[i] = oObj.GetString("name");
1986
1987
0
                            std::string osDescription =
1988
0
                                oObj.GetString("description");
1989
0
                            if (!osDescription.empty())
1990
0
                            {
1991
0
                                if (aosDescriptions[i].empty())
1992
0
                                    aosDescriptions[i] =
1993
0
                                        std::move(osDescription);
1994
0
                                else
1995
0
                                    aosDescriptions[i]
1996
0
                                        .append(" (")
1997
0
                                        .append(osDescription)
1998
0
                                        .append(")");
1999
0
                            }
2000
2001
0
                            adfCenterWavelength[i] =
2002
0
                                oObj.GetDouble("center_wavelength");
2003
0
                            adfFullWidthHalfMax[i] =
2004
0
                                oObj.GetDouble("full_width_half_max");
2005
0
                        }
2006
0
                        ++i;
2007
0
                    }
2008
0
                }
2009
0
            }
2010
0
        }
2011
2012
0
        const int nRasterBandsIdx = poLayerDefn->GetFieldIndex(
2013
0
            CPLSPrintf("assets.%s.raster:bands", osAssetName.c_str()));
2014
0
        if (nRasterBandsIdx >= 0 &&
2015
0
            poLayerDefn->GetFieldDefn(nRasterBandsIdx)->GetSubType() ==
2016
0
                OFSTJSON &&
2017
0
            poFeature->IsFieldSet(nRasterBandsIdx))
2018
0
        {
2019
0
            const char *pszStr = poFeature->GetFieldAsString(nRasterBandsIdx);
2020
0
            CPLJSONDocument oDoc;
2021
0
            if (oDoc.LoadMemory(pszStr) &&
2022
0
                oDoc.GetRoot().GetType() == CPLJSONObject::Type::Array)
2023
0
            {
2024
0
                const auto oArray = oDoc.GetRoot().ToArray();
2025
0
                if (oArray.Size() == nBandCount)
2026
0
                {
2027
0
                    int i = 0;
2028
0
                    adfScale.resize(nBandCount,
2029
0
                                    std::numeric_limits<double>::quiet_NaN());
2030
0
                    adfOffset.resize(nBandCount,
2031
0
                                     std::numeric_limits<double>::quiet_NaN());
2032
0
                    for (const auto &oObj : oArray)
2033
0
                    {
2034
0
                        if (oObj.GetType() == CPLJSONObject::Type::Object)
2035
0
                        {
2036
0
                            adfScale[i] = oObj.GetDouble(
2037
0
                                "scale",
2038
0
                                std::numeric_limits<double>::quiet_NaN());
2039
0
                            adfOffset[i] = oObj.GetDouble(
2040
0
                                "offset",
2041
0
                                std::numeric_limits<double>::quiet_NaN());
2042
0
                        }
2043
0
                        ++i;
2044
0
                    }
2045
0
                }
2046
0
            }
2047
0
        }
2048
0
    }
2049
2050
0
    GDALTileIndexBand *poFirstBand = nullptr;
2051
0
    for (int i = 0; i < nBandCount; ++i)
2052
0
    {
2053
0
        GDALDataType eDataType = aeDataTypes[i];
2054
0
        if (!apoXMLNodeBands.empty())
2055
0
        {
2056
0
            const char *pszVal = CPLGetXMLValue(apoXMLNodeBands[i],
2057
0
                                                GTI_XML_BAND_DATATYPE, nullptr);
2058
0
            if (pszVal)
2059
0
            {
2060
0
                eDataType = GDALGetDataTypeByName(pszVal);
2061
0
                if (eDataType == GDT_Unknown)
2062
0
                    return false;
2063
0
            }
2064
0
        }
2065
0
        auto poBandUniquePtr = std::make_unique<GDALTileIndexBand>(
2066
0
            this, i + 1, eDataType, nBlockXSize, nBlockYSize);
2067
0
        auto poBand = poBandUniquePtr.get();
2068
0
        SetBand(i + 1, poBandUniquePtr.release());
2069
0
        if (!poFirstBand)
2070
0
            poFirstBand = poBand;
2071
0
        if (poBand->GetRasterDataType() != poFirstBand->GetRasterDataType())
2072
0
        {
2073
0
            m_bSameDataType = false;
2074
0
        }
2075
2076
0
        if (!aosDescriptions.empty() && !aosDescriptions[i].empty())
2077
0
        {
2078
0
            poBand->GDALRasterBand::SetDescription(aosDescriptions[i].c_str());
2079
0
        }
2080
0
        if (!apoXMLNodeBands.empty())
2081
0
        {
2082
0
            const char *pszVal = CPLGetXMLValue(
2083
0
                apoXMLNodeBands[i], GTI_XML_BAND_DESCRIPTION, nullptr);
2084
0
            if (pszVal)
2085
0
            {
2086
0
                poBand->GDALRasterBand::SetDescription(pszVal);
2087
0
            }
2088
0
        }
2089
2090
0
        if (!aNoData.empty() && aNoData[i].first)
2091
0
        {
2092
0
            poBand->m_bNoDataValueSet = true;
2093
0
            poBand->m_dfNoDataValue = aNoData[i].second;
2094
0
        }
2095
0
        if (!apoXMLNodeBands.empty())
2096
0
        {
2097
0
            const char *pszVal = CPLGetXMLValue(
2098
0
                apoXMLNodeBands[i], GTI_XML_BAND_NODATAVALUE, nullptr);
2099
0
            if (pszVal)
2100
0
            {
2101
0
                poBand->m_bNoDataValueSet = true;
2102
0
                poBand->m_dfNoDataValue = CPLAtof(pszVal);
2103
0
            }
2104
0
        }
2105
0
        if (poBand->m_bNoDataValueSet != poFirstBand->m_bNoDataValueSet ||
2106
0
            !IsSameNaNAware(poBand->m_dfNoDataValue,
2107
0
                            poFirstBand->m_dfNoDataValue))
2108
0
        {
2109
0
            m_bSameNoData = false;
2110
0
        }
2111
2112
0
        if (!aeColorInterp.empty())
2113
0
        {
2114
0
            poBand->m_eColorInterp = aeColorInterp[i];
2115
0
        }
2116
0
        if (!apoXMLNodeBands.empty())
2117
0
        {
2118
0
            const char *pszVal = CPLGetXMLValue(
2119
0
                apoXMLNodeBands[i], GTI_XML_BAND_COLORINTERP, nullptr);
2120
0
            if (pszVal)
2121
0
            {
2122
0
                poBand->m_eColorInterp =
2123
0
                    GDALGetColorInterpretationByName(pszVal);
2124
0
            }
2125
0
        }
2126
2127
0
        if (static_cast<int>(adfScale.size()) == nBandCount &&
2128
0
            !std::isnan(adfScale[i]))
2129
0
        {
2130
0
            poBand->m_dfScale = adfScale[i];
2131
0
        }
2132
0
        if (const char *pszScale =
2133
0
                GetOption(CPLSPrintf("BAND_%d_%s", i + 1, MD_BAND_SCALE)))
2134
0
        {
2135
0
            poBand->m_dfScale = CPLAtof(pszScale);
2136
0
        }
2137
0
        if (!apoXMLNodeBands.empty())
2138
0
        {
2139
0
            const char *pszVal =
2140
0
                CPLGetXMLValue(apoXMLNodeBands[i], GTI_XML_BAND_SCALE, nullptr);
2141
0
            if (pszVal)
2142
0
            {
2143
0
                poBand->m_dfScale = CPLAtof(pszVal);
2144
0
            }
2145
0
        }
2146
2147
0
        if (static_cast<int>(adfOffset.size()) == nBandCount &&
2148
0
            !std::isnan(adfOffset[i]))
2149
0
        {
2150
0
            poBand->m_dfOffset = adfOffset[i];
2151
0
        }
2152
0
        if (const char *pszOffset =
2153
0
                GetOption(CPLSPrintf("BAND_%d_%s", i + 1, MD_BAND_OFFSET)))
2154
0
        {
2155
0
            poBand->m_dfOffset = CPLAtof(pszOffset);
2156
0
        }
2157
0
        if (!apoXMLNodeBands.empty())
2158
0
        {
2159
0
            const char *pszVal = CPLGetXMLValue(apoXMLNodeBands[i],
2160
0
                                                GTI_XML_BAND_OFFSET, nullptr);
2161
0
            if (pszVal)
2162
0
            {
2163
0
                poBand->m_dfOffset = CPLAtof(pszVal);
2164
0
            }
2165
0
        }
2166
2167
0
        if (const char *pszUnit =
2168
0
                GetOption(CPLSPrintf("BAND_%d_%s", i + 1, MD_BAND_UNITTYPE)))
2169
0
        {
2170
0
            poBand->m_osUnit = pszUnit;
2171
0
        }
2172
0
        if (!apoXMLNodeBands.empty())
2173
0
        {
2174
0
            const char *pszVal = CPLGetXMLValue(apoXMLNodeBands[i],
2175
0
                                                GTI_XML_BAND_UNITTYPE, nullptr);
2176
0
            if (pszVal)
2177
0
            {
2178
0
                poBand->m_osUnit = pszVal;
2179
0
            }
2180
0
        }
2181
2182
0
        if (!apoXMLNodeBands.empty())
2183
0
        {
2184
0
            const CPLXMLNode *psBandNode = apoXMLNodeBands[i];
2185
0
            poBand->oMDMD.XMLInit(psBandNode, TRUE);
2186
2187
0
            if (const CPLXMLNode *psCategoryNames =
2188
0
                    CPLGetXMLNode(psBandNode, GTI_XML_CATEGORYNAMES))
2189
0
            {
2190
0
                poBand->m_aosCategoryNames =
2191
0
                    VRTParseCategoryNames(psCategoryNames);
2192
0
            }
2193
2194
0
            if (const CPLXMLNode *psColorTable =
2195
0
                    CPLGetXMLNode(psBandNode, GTI_XML_COLORTABLE))
2196
0
            {
2197
0
                poBand->m_poColorTable = VRTParseColorTable(psColorTable);
2198
0
            }
2199
2200
0
            if (const CPLXMLNode *psRAT =
2201
0
                    CPLGetXMLNode(psBandNode, GTI_XML_RAT))
2202
0
            {
2203
0
                poBand->m_poRAT =
2204
0
                    std::make_unique<GDALDefaultRasterAttributeTable>();
2205
0
                poBand->m_poRAT->XMLInit(psRAT, "");
2206
0
            }
2207
0
        }
2208
2209
0
        if (static_cast<int>(adfCenterWavelength.size()) == nBandCount &&
2210
0
            adfCenterWavelength[i] != 0)
2211
0
        {
2212
0
            poBand->GDALRasterBand::SetMetadataItem(
2213
0
                "CENTRAL_WAVELENGTH_UM",
2214
0
                CPLSPrintf("%g", adfCenterWavelength[i]), "IMAGERY");
2215
0
        }
2216
2217
0
        if (static_cast<int>(adfFullWidthHalfMax.size()) == nBandCount &&
2218
0
            adfFullWidthHalfMax[i] != 0)
2219
0
        {
2220
0
            poBand->GDALRasterBand::SetMetadataItem(
2221
0
                "FWHM_UM", CPLSPrintf("%g", adfFullWidthHalfMax[i]), "IMAGERY");
2222
0
        }
2223
0
    }
2224
2225
0
    if (nBandCount == 1 && poFirstBand && poSingleColorTable &&
2226
0
        !poFirstBand->m_poColorTable)
2227
0
        poFirstBand->m_poColorTable = std::move(poSingleColorTable);
2228
2229
0
    const char *pszMaskBand = GetOption(MD_MASK_BAND);
2230
0
    if (pszMaskBand)
2231
0
        bHasMaskBand = CPLTestBool(pszMaskBand);
2232
0
    if (bHasMaskBand)
2233
0
    {
2234
0
        m_poMaskBand = std::make_unique<GDALTileIndexBand>(
2235
0
            this, 0, GDT_Byte, nBlockXSize, nBlockYSize);
2236
0
    }
2237
2238
0
    if (dfOvrFactor == 1.0)
2239
0
    {
2240
0
        if (psRoot)
2241
0
        {
2242
0
            for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
2243
0
                 psIter = psIter->psNext)
2244
0
            {
2245
0
                if (psIter->eType == CXT_Element &&
2246
0
                    strcmp(psIter->pszValue, GTI_XML_OVERVIEW_ELEMENT) == 0)
2247
0
                {
2248
0
                    const char *pszDataset = CPLGetXMLValue(
2249
0
                        psIter, GTI_XML_OVERVIEW_DATASET, nullptr);
2250
0
                    const char *pszLayer =
2251
0
                        CPLGetXMLValue(psIter, GTI_XML_OVERVIEW_LAYER, nullptr);
2252
0
                    const char *pszFactor = CPLGetXMLValue(
2253
0
                        psIter, GTI_XML_OVERVIEW_FACTOR, nullptr);
2254
0
                    if (!pszDataset && !pszLayer && !pszFactor)
2255
0
                    {
2256
0
                        CPLError(
2257
0
                            CE_Failure, CPLE_AppDefined,
2258
0
                            "At least one of %s, %s or %s element "
2259
0
                            "must be present as an %s child",
2260
0
                            GTI_XML_OVERVIEW_DATASET, GTI_XML_OVERVIEW_LAYER,
2261
0
                            GTI_XML_OVERVIEW_FACTOR, GTI_XML_OVERVIEW_ELEMENT);
2262
0
                        return false;
2263
0
                    }
2264
0
                    m_aoOverviewDescriptor.emplace_back(
2265
0
                        std::string(pszDataset ? pszDataset : ""),
2266
0
                        CPLStringList(
2267
0
                            GDALDeserializeOpenOptionsFromXML(psIter)),
2268
0
                        std::string(pszLayer ? pszLayer : ""),
2269
0
                        pszFactor ? CPLAtof(pszFactor) : 0.0);
2270
0
                }
2271
0
            }
2272
0
        }
2273
0
        else
2274
0
        {
2275
0
            for (int iOvr = 0;; ++iOvr)
2276
0
            {
2277
0
                const char *pszOvrDSName =
2278
0
                    GetOption(CPLSPrintf("OVERVIEW_%d_DATASET", iOvr));
2279
0
                const char *pszOpenOptions =
2280
0
                    GetOption(CPLSPrintf("OVERVIEW_%d_OPEN_OPTIONS", iOvr));
2281
0
                const char *pszOvrLayer =
2282
0
                    GetOption(CPLSPrintf("OVERVIEW_%d_LAYER", iOvr));
2283
0
                const char *pszOvrFactor =
2284
0
                    GetOption(CPLSPrintf("OVERVIEW_%d_FACTOR", iOvr));
2285
0
                if (!pszOvrDSName && !pszOvrLayer && !pszOvrFactor)
2286
0
                {
2287
                    // Before GDAL 3.9.2, we started the iteration at 1.
2288
0
                    if (iOvr == 0)
2289
0
                        continue;
2290
0
                    break;
2291
0
                }
2292
0
                m_aoOverviewDescriptor.emplace_back(
2293
0
                    std::string(pszOvrDSName ? pszOvrDSName : ""),
2294
0
                    pszOpenOptions ? CPLStringList(CSLTokenizeString2(
2295
0
                                         pszOpenOptions, ",", 0))
2296
0
                                   : CPLStringList(),
2297
0
                    std::string(pszOvrLayer ? pszOvrLayer : ""),
2298
0
                    pszOvrFactor ? CPLAtof(pszOvrFactor) : 0.0);
2299
0
            }
2300
0
        }
2301
0
    }
2302
2303
0
    if (psRoot)
2304
0
    {
2305
0
        oMDMD.XMLInit(psRoot, TRUE);
2306
0
    }
2307
0
    else
2308
0
    {
2309
        // Set on the dataset all metadata items from the index layer which are
2310
        // not "reserved" keywords.
2311
0
        CSLConstList papszLayerMD = m_poLayer->GetMetadata();
2312
0
        for (const auto &[pszKey, pszValue] :
2313
0
             cpl::IterateNameValue(papszLayerMD))
2314
0
        {
2315
0
            if (STARTS_WITH_CI(pszKey, "OVERVIEW_"))
2316
0
            {
2317
0
                continue;
2318
0
            }
2319
2320
0
            bool bIsVRTItem = false;
2321
0
            for (const char *pszTest : apszTIOptions)
2322
0
            {
2323
0
                if (EQUAL(pszKey, pszTest))
2324
0
                {
2325
0
                    bIsVRTItem = true;
2326
0
                    break;
2327
0
                }
2328
0
            }
2329
0
            if (!bIsVRTItem)
2330
0
            {
2331
0
                if (STARTS_WITH_CI(pszKey, "BAND_"))
2332
0
                {
2333
0
                    const int nBandNr = atoi(pszKey + strlen("BAND_"));
2334
0
                    const char *pszNextUnderscore =
2335
0
                        strchr(pszKey + strlen("BAND_"), '_');
2336
0
                    if (pszNextUnderscore && nBandNr >= 1 && nBandNr <= nBands)
2337
0
                    {
2338
0
                        const char *pszKeyWithoutBand = pszNextUnderscore + 1;
2339
0
                        bool bIsReservedBandItem = false;
2340
0
                        for (const char *pszItem : apszReservedBandItems)
2341
0
                        {
2342
0
                            if (EQUAL(pszKeyWithoutBand, pszItem))
2343
0
                            {
2344
0
                                bIsReservedBandItem = true;
2345
0
                                break;
2346
0
                            }
2347
0
                        }
2348
0
                        if (!bIsReservedBandItem)
2349
0
                        {
2350
0
                            GetRasterBand(nBandNr)
2351
0
                                ->GDALRasterBand::SetMetadataItem(
2352
0
                                    pszKeyWithoutBand, pszValue);
2353
0
                        }
2354
0
                    }
2355
0
                }
2356
0
                else
2357
0
                {
2358
0
                    GDALDataset::SetMetadataItem(pszKey, pszValue);
2359
0
                }
2360
0
            }
2361
0
        }
2362
0
    }
2363
2364
0
    if (nBandCount > 1 && !GetMetadata("IMAGE_STRUCTURE"))
2365
0
    {
2366
0
        GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2367
0
    }
2368
2369
    /* -------------------------------------------------------------------- */
2370
    /*      Initialize any PAM information.                                 */
2371
    /* -------------------------------------------------------------------- */
2372
0
    SetDescription(poOpenInfo->pszFilename);
2373
0
    TryLoadXML();
2374
2375
    /* -------------------------------------------------------------------- */
2376
    /*      Check for overviews.                                            */
2377
    /* -------------------------------------------------------------------- */
2378
0
    oOvManager.Initialize(this, poOpenInfo->pszFilename);
2379
2380
0
    return true;
2381
0
}
2382
2383
/************************************************************************/
2384
/*                        GetMetadataItem()                             */
2385
/************************************************************************/
2386
2387
const char *GDALTileIndexDataset::GetMetadataItem(const char *pszName,
2388
                                                  const char *pszDomain)
2389
0
{
2390
0
    if (pszName && pszDomain && EQUAL(pszDomain, "__DEBUG__"))
2391
0
    {
2392
0
        if (EQUAL(pszName, "SCANNED_ONE_FEATURE_AT_OPENING"))
2393
0
        {
2394
0
            return m_bScannedOneFeatureAtOpening ? "YES" : "NO";
2395
0
        }
2396
0
        else if (EQUAL(pszName, "NUMBER_OF_CONTRIBUTING_SOURCES"))
2397
0
        {
2398
0
            return CPLSPrintf("%d", static_cast<int>(m_aoSourceDesc.size()));
2399
0
        }
2400
0
        else if (EQUAL(pszName, "MULTI_THREADED_RASTERIO_LAST_USED"))
2401
0
        {
2402
0
            return m_bLastMustUseMultiThreading ? "1" : "0";
2403
0
        }
2404
0
    }
2405
0
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
2406
0
}
2407
2408
/************************************************************************/
2409
/*                TileIndexSupportsEditingLayerMetadata()               */
2410
/************************************************************************/
2411
2412
bool GDALTileIndexDataset::TileIndexSupportsEditingLayerMetadata() const
2413
0
{
2414
0
    return eAccess == GA_Update && m_poVectorDS->GetDriver() &&
2415
0
           EQUAL(m_poVectorDS->GetDriver()->GetDescription(), "GPKG");
2416
0
}
2417
2418
/************************************************************************/
2419
/*                        SetMetadataItem()                             */
2420
/************************************************************************/
2421
2422
CPLErr GDALTileIndexDataset::SetMetadataItem(const char *pszName,
2423
                                             const char *pszValue,
2424
                                             const char *pszDomain)
2425
0
{
2426
0
    if (m_bXMLUpdatable)
2427
0
    {
2428
0
        m_bXMLModified = true;
2429
0
        return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
2430
0
    }
2431
0
    else if (TileIndexSupportsEditingLayerMetadata())
2432
0
    {
2433
0
        m_poLayer->SetMetadataItem(pszName, pszValue, pszDomain);
2434
0
        return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
2435
0
    }
2436
0
    else
2437
0
    {
2438
0
        return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
2439
0
    }
2440
0
}
2441
2442
/************************************************************************/
2443
/*                           SetMetadata()                              */
2444
/************************************************************************/
2445
2446
CPLErr GDALTileIndexDataset::SetMetadata(char **papszMD, const char *pszDomain)
2447
0
{
2448
0
    if (m_bXMLUpdatable)
2449
0
    {
2450
0
        m_bXMLModified = true;
2451
0
        return GDALDataset::SetMetadata(papszMD, pszDomain);
2452
0
    }
2453
0
    else if (TileIndexSupportsEditingLayerMetadata())
2454
0
    {
2455
0
        if (!pszDomain || pszDomain[0] == 0)
2456
0
        {
2457
0
            CPLStringList aosMD(CSLDuplicate(papszMD));
2458
2459
            // Reinject dataset reserved items
2460
0
            for (const char *pszItem : apszTIOptions)
2461
0
            {
2462
0
                if (!aosMD.FetchNameValue(pszItem))
2463
0
                {
2464
0
                    const char *pszValue = m_poLayer->GetMetadataItem(pszItem);
2465
0
                    if (pszValue)
2466
0
                    {
2467
0
                        aosMD.SetNameValue(pszItem, pszValue);
2468
0
                    }
2469
0
                }
2470
0
            }
2471
2472
            // Reinject band metadata
2473
0
            char **papszExistingLayerMD = m_poLayer->GetMetadata();
2474
0
            for (int i = 0; papszExistingLayerMD && papszExistingLayerMD[i];
2475
0
                 ++i)
2476
0
            {
2477
0
                if (STARTS_WITH_CI(papszExistingLayerMD[i], "BAND_"))
2478
0
                {
2479
0
                    aosMD.AddString(papszExistingLayerMD[i]);
2480
0
                }
2481
0
            }
2482
2483
0
            m_poLayer->SetMetadata(aosMD.List(), pszDomain);
2484
0
        }
2485
0
        else
2486
0
        {
2487
0
            m_poLayer->SetMetadata(papszMD, pszDomain);
2488
0
        }
2489
0
        return GDALDataset::SetMetadata(papszMD, pszDomain);
2490
0
    }
2491
0
    else
2492
0
    {
2493
0
        return GDALPamDataset::SetMetadata(papszMD, pszDomain);
2494
0
    }
2495
0
}
2496
2497
/************************************************************************/
2498
/*                     GDALTileIndexDatasetIdentify()                   */
2499
/************************************************************************/
2500
2501
static int GDALTileIndexDatasetIdentify(GDALOpenInfo *poOpenInfo)
2502
683k
{
2503
683k
    if (STARTS_WITH(poOpenInfo->pszFilename, GTI_PREFIX))
2504
0
        return true;
2505
2506
683k
    if (STARTS_WITH(poOpenInfo->pszFilename, "<GDALTileIndexDataset"))
2507
16
        return true;
2508
2509
682k
    if (poOpenInfo->nHeaderBytes >= 100 &&
2510
96.2k
        STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
2511
682k
                    "SQLite format 3"))
2512
33
    {
2513
33
        if (ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.gpkg"))
2514
0
        {
2515
            // Most likely handled by GTI driver, but we can't be sure
2516
0
            return GDAL_IDENTIFY_UNKNOWN;
2517
0
        }
2518
33
        else if (poOpenInfo->IsSingleAllowedDriver("GTI") &&
2519
0
                 poOpenInfo->IsExtensionEqualToCI("gpkg"))
2520
0
        {
2521
0
            return true;
2522
0
        }
2523
33
    }
2524
2525
682k
    if (poOpenInfo->nHeaderBytes > 0 &&
2526
97.8k
        (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0)
2527
97.8k
    {
2528
97.8k
        if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
2529
97.8k
                   "<GDALTileIndexDataset") ||
2530
97.6k
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.fgb") ||
2531
91.9k
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.parquet"))
2532
5.89k
        {
2533
5.89k
            return true;
2534
5.89k
        }
2535
91.9k
        else if (poOpenInfo->IsSingleAllowedDriver("GTI") &&
2536
0
                 (poOpenInfo->IsExtensionEqualToCI("fgb") ||
2537
0
                  poOpenInfo->IsExtensionEqualToCI("parquet")))
2538
0
        {
2539
0
            return true;
2540
0
        }
2541
97.8k
    }
2542
2543
677k
    return false;
2544
682k
}
2545
2546
/************************************************************************/
2547
/*                      GDALTileIndexDatasetOpen()                       */
2548
/************************************************************************/
2549
2550
static GDALDataset *GDALTileIndexDatasetOpen(GDALOpenInfo *poOpenInfo)
2551
2.95k
{
2552
2.95k
    if (GDALTileIndexDatasetIdentify(poOpenInfo) == GDAL_IDENTIFY_FALSE)
2553
0
        return nullptr;
2554
2.95k
    auto poDS = std::make_unique<GDALTileIndexDataset>();
2555
2.95k
    if (!poDS->Open(poOpenInfo))
2556
2.95k
        return nullptr;
2557
0
    return poDS.release();
2558
2.95k
}
2559
2560
/************************************************************************/
2561
/*                          ~GDALTileIndexDataset()                      */
2562
/************************************************************************/
2563
2564
GDALTileIndexDataset::~GDALTileIndexDataset()
2565
2.95k
{
2566
2.95k
    if (m_poVectorDS && m_bIsSQLResultLayer)
2567
0
        m_poVectorDS->ReleaseResultSet(m_poLayer);
2568
2569
2.95k
    GDALTileIndexDataset::FlushCache(true);
2570
2.95k
}
2571
2572
/************************************************************************/
2573
/*                              FlushCache()                            */
2574
/************************************************************************/
2575
2576
CPLErr GDALTileIndexDataset::FlushCache(bool bAtClosing)
2577
2.95k
{
2578
2.95k
    CPLErr eErr = CE_None;
2579
2.95k
    if (bAtClosing && m_bXMLModified)
2580
0
    {
2581
0
        CPLXMLNode *psRoot =
2582
0
            CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
2583
2584
        // Suppress existing dataset metadata
2585
0
        while (true)
2586
0
        {
2587
0
            CPLXMLNode *psExistingMetadata = CPLGetXMLNode(psRoot, "Metadata");
2588
0
            if (!psExistingMetadata)
2589
0
                break;
2590
0
            CPLRemoveXMLChild(psRoot, psExistingMetadata);
2591
0
        }
2592
2593
        // Serialize new dataset metadata
2594
0
        if (CPLXMLNode *psMD = oMDMD.Serialize())
2595
0
            CPLAddXMLChild(psRoot, psMD);
2596
2597
        // Update existing band metadata
2598
0
        if (CPLGetXMLNode(psRoot, GTI_XML_BAND_ELEMENT))
2599
0
        {
2600
0
            for (CPLXMLNode *psIter = psRoot->psChild; psIter;
2601
0
                 psIter = psIter->psNext)
2602
0
            {
2603
0
                if (psIter->eType == CXT_Element &&
2604
0
                    strcmp(psIter->pszValue, GTI_XML_BAND_ELEMENT))
2605
0
                {
2606
0
                    const char *pszBand =
2607
0
                        CPLGetXMLValue(psIter, GTI_XML_BAND_NUMBER, nullptr);
2608
0
                    if (pszBand)
2609
0
                    {
2610
0
                        const int nBand = atoi(pszBand);
2611
0
                        if (nBand >= 1 && nBand <= nBands)
2612
0
                        {
2613
0
                            while (true)
2614
0
                            {
2615
0
                                CPLXMLNode *psExistingMetadata =
2616
0
                                    CPLGetXMLNode(psIter, "Metadata");
2617
0
                                if (!psExistingMetadata)
2618
0
                                    break;
2619
0
                                CPLRemoveXMLChild(psIter, psExistingMetadata);
2620
0
                            }
2621
2622
0
                            auto poBand = cpl::down_cast<GDALTileIndexBand *>(
2623
0
                                papoBands[nBand - 1]);
2624
0
                            if (CPLXMLNode *psMD = poBand->oMDMD.Serialize())
2625
0
                                CPLAddXMLChild(psIter, psMD);
2626
0
                        }
2627
0
                    }
2628
0
                }
2629
0
            }
2630
0
        }
2631
0
        else
2632
0
        {
2633
            // Create new band objects if they have metadata
2634
0
            std::vector<CPLXMLTreeCloser> aoBandXML;
2635
0
            bool bHasBandMD = false;
2636
0
            for (int i = 1; i <= nBands; ++i)
2637
0
            {
2638
0
                auto poBand =
2639
0
                    cpl::down_cast<GDALTileIndexBand *>(papoBands[i - 1]);
2640
0
                auto psMD = poBand->oMDMD.Serialize();
2641
0
                if (psMD)
2642
0
                    bHasBandMD = true;
2643
0
                aoBandXML.emplace_back(CPLXMLTreeCloser(psMD));
2644
0
            }
2645
0
            if (bHasBandMD)
2646
0
            {
2647
0
                for (int i = 1; i <= nBands; ++i)
2648
0
                {
2649
0
                    auto poBand =
2650
0
                        cpl::down_cast<GDALTileIndexBand *>(papoBands[i - 1]);
2651
2652
0
                    CPLXMLNode *psBand = CPLCreateXMLNode(psRoot, CXT_Element,
2653
0
                                                          GTI_XML_BAND_ELEMENT);
2654
0
                    CPLAddXMLAttributeAndValue(psBand, GTI_XML_BAND_NUMBER,
2655
0
                                               CPLSPrintf("%d", i));
2656
0
                    CPLAddXMLAttributeAndValue(
2657
0
                        psBand, GTI_XML_BAND_DATATYPE,
2658
0
                        GDALGetDataTypeName(poBand->GetRasterDataType()));
2659
2660
0
                    const char *pszDescription = poBand->GetDescription();
2661
0
                    if (pszDescription && pszDescription[0])
2662
0
                        CPLSetXMLValue(psBand, GTI_XML_BAND_DESCRIPTION,
2663
0
                                       pszDescription);
2664
2665
0
                    const auto eColorInterp = poBand->GetColorInterpretation();
2666
0
                    if (eColorInterp != GCI_Undefined)
2667
0
                        CPLSetXMLValue(
2668
0
                            psBand, GTI_XML_BAND_COLORINTERP,
2669
0
                            GDALGetColorInterpretationName(eColorInterp));
2670
2671
0
                    if (!std::isnan(poBand->m_dfOffset))
2672
0
                        CPLSetXMLValue(psBand, GTI_XML_BAND_OFFSET,
2673
0
                                       CPLSPrintf("%.16g", poBand->m_dfOffset));
2674
2675
0
                    if (!std::isnan(poBand->m_dfScale))
2676
0
                        CPLSetXMLValue(psBand, GTI_XML_BAND_SCALE,
2677
0
                                       CPLSPrintf("%.16g", poBand->m_dfScale));
2678
2679
0
                    if (!poBand->m_osUnit.empty())
2680
0
                        CPLSetXMLValue(psBand, GTI_XML_BAND_UNITTYPE,
2681
0
                                       poBand->m_osUnit.c_str());
2682
2683
0
                    if (poBand->m_bNoDataValueSet)
2684
0
                    {
2685
0
                        CPLSetXMLValue(
2686
0
                            psBand, GTI_XML_BAND_NODATAVALUE,
2687
0
                            VRTSerializeNoData(poBand->m_dfNoDataValue,
2688
0
                                               poBand->GetRasterDataType(), 18)
2689
0
                                .c_str());
2690
0
                    }
2691
0
                    if (aoBandXML[i - 1])
2692
0
                    {
2693
0
                        CPLAddXMLChild(psBand, aoBandXML[i - 1].release());
2694
0
                    }
2695
0
                }
2696
0
            }
2697
0
        }
2698
2699
0
        if (!CPLSerializeXMLTreeToFile(m_psXMLTree.get(), GetDescription()))
2700
0
            eErr = CE_Failure;
2701
0
    }
2702
2703
    // We also clear the cache of opened sources, in case the user would
2704
    // change the content of a source and would want the GTI dataset to see
2705
    // the refreshed content.
2706
2.95k
    m_oMapSharedSources.clear();
2707
2.95k
    m_dfLastMinXFilter = std::numeric_limits<double>::quiet_NaN();
2708
2.95k
    m_dfLastMinYFilter = std::numeric_limits<double>::quiet_NaN();
2709
2.95k
    m_dfLastMaxXFilter = std::numeric_limits<double>::quiet_NaN();
2710
2.95k
    m_dfLastMaxYFilter = std::numeric_limits<double>::quiet_NaN();
2711
2.95k
    m_aoSourceDesc.clear();
2712
2.95k
    if (GDALPamDataset::FlushCache(bAtClosing) != CE_None)
2713
0
        eErr = CE_Failure;
2714
2.95k
    return eErr;
2715
2.95k
}
2716
2717
/************************************************************************/
2718
/*                            LoadOverviews()                           */
2719
/************************************************************************/
2720
2721
void GDALTileIndexDataset::LoadOverviews()
2722
0
{
2723
0
    if (m_apoOverviews.empty() && !m_aoOverviewDescriptor.empty())
2724
0
    {
2725
0
        for (const auto &[osDSName, aosOpenOptions, osLyrName, dfFactor] :
2726
0
             m_aoOverviewDescriptor)
2727
0
        {
2728
0
            CPLStringList aosNewOpenOptions(aosOpenOptions);
2729
0
            if (dfFactor != 0)
2730
0
            {
2731
0
                aosNewOpenOptions.SetNameValue("@FACTOR",
2732
0
                                               CPLSPrintf("%.17g", dfFactor));
2733
0
            }
2734
0
            if (!osLyrName.empty())
2735
0
            {
2736
0
                aosNewOpenOptions.SetNameValue("@LAYER", osLyrName.c_str());
2737
0
            }
2738
2739
0
            std::unique_ptr<GDALDataset> poOvrDS(GDALDataset::Open(
2740
0
                !osDSName.empty() ? osDSName.c_str() : GetDescription(),
2741
0
                GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
2742
0
                aosNewOpenOptions.List(), nullptr));
2743
2744
0
            const auto IsSmaller =
2745
0
                [](const GDALDataset *a, const GDALDataset *b)
2746
0
            {
2747
0
                return (a->GetRasterXSize() < b->GetRasterXSize() &&
2748
0
                        a->GetRasterYSize() <= b->GetRasterYSize()) ||
2749
0
                       (a->GetRasterYSize() < b->GetRasterYSize() &&
2750
0
                        a->GetRasterXSize() <= b->GetRasterXSize());
2751
0
            };
2752
2753
0
            if (poOvrDS &&
2754
0
                ((m_apoOverviews.empty() && IsSmaller(poOvrDS.get(), this)) ||
2755
0
                 ((!m_apoOverviews.empty() &&
2756
0
                   IsSmaller(poOvrDS.get(), m_apoOverviews.back().get())))))
2757
0
            {
2758
0
                if (poOvrDS->GetRasterCount() == GetRasterCount())
2759
0
                {
2760
0
                    m_apoOverviews.emplace_back(std::move(poOvrDS));
2761
                    // Add the overviews of the overview, unless the OVERVIEW_LEVEL
2762
                    // option option is specified
2763
0
                    if (aosOpenOptions.FetchNameValue("OVERVIEW_LEVEL") ==
2764
0
                        nullptr)
2765
0
                    {
2766
0
                        const int nOverviewCount = m_apoOverviews.back()
2767
0
                                                       ->GetRasterBand(1)
2768
0
                                                       ->GetOverviewCount();
2769
0
                        for (int i = 0; i < nOverviewCount; ++i)
2770
0
                        {
2771
0
                            aosNewOpenOptions.SetNameValue("OVERVIEW_LEVEL",
2772
0
                                                           CPLSPrintf("%d", i));
2773
0
                            std::unique_ptr<GDALDataset> poOvrOfOvrDS(
2774
0
                                GDALDataset::Open(
2775
0
                                    !osDSName.empty() ? osDSName.c_str()
2776
0
                                                      : GetDescription(),
2777
0
                                    GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
2778
0
                                    nullptr, aosNewOpenOptions.List(),
2779
0
                                    nullptr));
2780
0
                            if (poOvrOfOvrDS &&
2781
0
                                poOvrOfOvrDS->GetRasterCount() ==
2782
0
                                    GetRasterCount() &&
2783
0
                                IsSmaller(poOvrOfOvrDS.get(),
2784
0
                                          m_apoOverviews.back().get()))
2785
0
                            {
2786
0
                                m_apoOverviews.emplace_back(
2787
0
                                    std::move(poOvrOfOvrDS));
2788
0
                            }
2789
0
                        }
2790
0
                    }
2791
0
                }
2792
0
                else
2793
0
                {
2794
0
                    CPLError(CE_Warning, CPLE_AppDefined,
2795
0
                             "%s has not the same number of bands as %s",
2796
0
                             poOvrDS->GetDescription(), GetDescription());
2797
0
                }
2798
0
            }
2799
0
        }
2800
0
    }
2801
0
}
2802
2803
/************************************************************************/
2804
/*                          GetOverviewCount()                          */
2805
/************************************************************************/
2806
2807
int GDALTileIndexBand::GetOverviewCount()
2808
0
{
2809
0
    const int nPAMOverviews = GDALPamRasterBand::GetOverviewCount();
2810
0
    if (nPAMOverviews)
2811
0
        return nPAMOverviews;
2812
2813
0
    m_poDS->LoadOverviews();
2814
0
    return static_cast<int>(m_poDS->m_apoOverviews.size());
2815
0
}
2816
2817
/************************************************************************/
2818
/*                             GetOverview()                            */
2819
/************************************************************************/
2820
2821
GDALRasterBand *GDALTileIndexBand::GetOverview(int iOvr)
2822
0
{
2823
0
    if (iOvr < 0 || iOvr >= GetOverviewCount())
2824
0
        return nullptr;
2825
2826
0
    const int nPAMOverviews = GDALPamRasterBand::GetOverviewCount();
2827
0
    if (nPAMOverviews)
2828
0
        return GDALPamRasterBand::GetOverview(iOvr);
2829
2830
0
    if (nBand == 0)
2831
0
    {
2832
0
        auto poBand = m_poDS->m_apoOverviews[iOvr]->GetRasterBand(1);
2833
0
        if (!poBand)
2834
0
            return nullptr;
2835
0
        return poBand->GetMaskBand();
2836
0
    }
2837
0
    else
2838
0
    {
2839
0
        return m_poDS->m_apoOverviews[iOvr]->GetRasterBand(nBand);
2840
0
    }
2841
0
}
2842
2843
/************************************************************************/
2844
/*                           GetGeoTransform()                          */
2845
/************************************************************************/
2846
2847
CPLErr GDALTileIndexDataset::GetGeoTransform(GDALGeoTransform &gt) const
2848
0
{
2849
0
    gt = m_gt;
2850
0
    return CE_None;
2851
0
}
2852
2853
/************************************************************************/
2854
/*                            GetSpatialRef()                           */
2855
/************************************************************************/
2856
2857
const OGRSpatialReference *GDALTileIndexDataset::GetSpatialRef() const
2858
0
{
2859
0
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
2860
0
}
2861
2862
/************************************************************************/
2863
/*                           GDALTileIndexBand()                         */
2864
/************************************************************************/
2865
2866
GDALTileIndexBand::GDALTileIndexBand(GDALTileIndexDataset *poDSIn, int nBandIn,
2867
                                     GDALDataType eDT, int nBlockXSizeIn,
2868
                                     int nBlockYSizeIn)
2869
0
{
2870
0
    m_poDS = poDSIn;
2871
0
    nBand = nBandIn;
2872
0
    eDataType = eDT;
2873
0
    nRasterXSize = poDSIn->GetRasterXSize();
2874
0
    nRasterYSize = poDSIn->GetRasterYSize();
2875
0
    nBlockXSize = nBlockXSizeIn;
2876
0
    nBlockYSize = nBlockYSizeIn;
2877
0
}
2878
2879
/************************************************************************/
2880
/*                             IReadBlock()                             */
2881
/************************************************************************/
2882
2883
CPLErr GDALTileIndexBand::IReadBlock(int nBlockXOff, int nBlockYOff,
2884
                                     void *pImage)
2885
2886
0
{
2887
0
    const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
2888
2889
0
    int nReadXSize = nBlockXSize;
2890
0
    int nReadYSize = nBlockYSize;
2891
0
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nReadXSize, &nReadYSize);
2892
2893
0
    GDALRasterIOExtraArg sExtraArg;
2894
0
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2895
2896
0
    return IRasterIO(
2897
0
        GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
2898
0
        nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
2899
0
        static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
2900
0
}
2901
2902
/************************************************************************/
2903
/*                             IRasterIO()                              */
2904
/************************************************************************/
2905
2906
CPLErr GDALTileIndexBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2907
                                    int nXSize, int nYSize, void *pData,
2908
                                    int nBufXSize, int nBufYSize,
2909
                                    GDALDataType eBufType, GSpacing nPixelSpace,
2910
                                    GSpacing nLineSpace,
2911
                                    GDALRasterIOExtraArg *psExtraArg)
2912
0
{
2913
0
    int anBand[] = {nBand};
2914
2915
0
    return m_poDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
2916
0
                             nBufXSize, nBufYSize, eBufType, 1, anBand,
2917
0
                             nPixelSpace, nLineSpace, 0, psExtraArg);
2918
0
}
2919
2920
/************************************************************************/
2921
/*                         IGetDataCoverageStatus()                     */
2922
/************************************************************************/
2923
2924
#ifndef HAVE_GEOS
2925
int GDALTileIndexBand::IGetDataCoverageStatus(int /* nXOff */, int /* nYOff */,
2926
                                              int /* nXSize */,
2927
                                              int /* nYSize */,
2928
                                              int /* nMaskFlagStop */,
2929
                                              double *pdfDataPct)
2930
0
{
2931
0
    if (pdfDataPct != nullptr)
2932
0
        *pdfDataPct = -1.0;
2933
0
    return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
2934
0
           GDAL_DATA_COVERAGE_STATUS_DATA;
2935
0
}
2936
#else
2937
int GDALTileIndexBand::IGetDataCoverageStatus(int nXOff, int nYOff, int nXSize,
2938
                                              int nYSize, int nMaskFlagStop,
2939
                                              double *pdfDataPct)
2940
{
2941
    if (pdfDataPct != nullptr)
2942
        *pdfDataPct = -1.0;
2943
2944
    const double dfMinX =
2945
        m_poDS->m_gt[GT_TOPLEFT_X] + nXOff * m_poDS->m_gt[GT_WE_RES];
2946
    const double dfMaxX = dfMinX + nXSize * m_poDS->m_gt[GT_WE_RES];
2947
    const double dfMaxY =
2948
        m_poDS->m_gt[GT_TOPLEFT_Y] + nYOff * m_poDS->m_gt[GT_NS_RES];
2949
    const double dfMinY = dfMaxY + nYSize * m_poDS->m_gt[GT_NS_RES];
2950
2951
    OGRLayer *poSQLLayer = nullptr;
2952
    if (!m_poDS->m_osSpatialSQL.empty())
2953
    {
2954
        const std::string osSQL =
2955
            CPLString(m_poDS->m_osSpatialSQL)
2956
                .replaceAll("{XMIN}", CPLSPrintf("%.17g", dfMinX))
2957
                .replaceAll("{YMIN}", CPLSPrintf("%.17g", dfMinY))
2958
                .replaceAll("{XMAX}", CPLSPrintf("%.17g", dfMaxX))
2959
                .replaceAll("{YMAX}", CPLSPrintf("%.17g", dfMaxY));
2960
        poSQLLayer =
2961
            m_poDS->m_poVectorDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
2962
        if (!poSQLLayer)
2963
            return 0;
2964
    }
2965
    else
2966
    {
2967
        m_poDS->m_poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
2968
        m_poDS->m_poLayer->ResetReading();
2969
    }
2970
2971
    OGRLayer *const poLayer = poSQLLayer ? poSQLLayer : m_poDS->m_poLayer;
2972
2973
    int nStatus = 0;
2974
2975
    auto poPolyNonCoveredBySources = std::make_unique<OGRPolygon>();
2976
    {
2977
        auto poLR = std::make_unique<OGRLinearRing>();
2978
        poLR->addPoint(nXOff, nYOff);
2979
        poLR->addPoint(nXOff, nYOff + nYSize);
2980
        poLR->addPoint(nXOff + nXSize, nYOff + nYSize);
2981
        poLR->addPoint(nXOff + nXSize, nYOff);
2982
        poLR->addPoint(nXOff, nYOff);
2983
        poPolyNonCoveredBySources->addRingDirectly(poLR.release());
2984
    }
2985
    while (true)
2986
    {
2987
        auto poFeature = std::unique_ptr<OGRFeature>(poLayer->GetNextFeature());
2988
        if (!poFeature)
2989
            break;
2990
        if (!poFeature->IsFieldSetAndNotNull(m_poDS->m_nLocationFieldIndex))
2991
        {
2992
            continue;
2993
        }
2994
2995
        const auto poGeom = poFeature->GetGeometryRef();
2996
        if (!poGeom || poGeom->IsEmpty())
2997
            continue;
2998
2999
        OGREnvelope sSourceEnvelope;
3000
        poGeom->getEnvelope(&sSourceEnvelope);
3001
3002
        const double dfDstXOff = std::max<double>(
3003
            nXOff, (sSourceEnvelope.MinX - m_poDS->m_gt[GT_TOPLEFT_X]) /
3004
                       m_poDS->m_gt[GT_WE_RES]);
3005
        const double dfDstXOff2 =
3006
            std::min<double>(nXOff + nXSize, (sSourceEnvelope.MaxX -
3007
                                              m_poDS->m_gt[GT_TOPLEFT_X]) /
3008
                                                 m_poDS->m_gt[GT_WE_RES]);
3009
        const double dfDstYOff = std::max<double>(
3010
            nYOff, (sSourceEnvelope.MaxY - m_poDS->m_gt[GT_TOPLEFT_Y]) /
3011
                       m_poDS->m_gt[GT_NS_RES]);
3012
        const double dfDstYOff2 =
3013
            std::min<double>(nYOff + nYSize, (sSourceEnvelope.MinY -
3014
                                              m_poDS->m_gt[GT_TOPLEFT_Y]) /
3015
                                                 m_poDS->m_gt[GT_NS_RES]);
3016
3017
        // CPLDebug("GTI", "dfDstXOff=%f, dfDstXOff2=%f, dfDstYOff=%f, dfDstYOff2=%f",
3018
        //         dfDstXOff, dfDstXOff2, dfDstYOff, dfDstXOff2);
3019
3020
        // Check if the AOI is fully inside the source
3021
        if (nXOff >= dfDstXOff && nYOff >= dfDstYOff &&
3022
            nXOff + nXSize <= dfDstXOff2 && nYOff + nYSize <= dfDstYOff2)
3023
        {
3024
            if (pdfDataPct)
3025
                *pdfDataPct = 100.0;
3026
            return GDAL_DATA_COVERAGE_STATUS_DATA;
3027
        }
3028
3029
        // Check intersection of bounding boxes.
3030
        if (dfDstXOff2 > nXOff && dfDstYOff2 > nYOff &&
3031
            dfDstXOff < nXOff + nXSize && dfDstYOff < nYOff + nYSize)
3032
        {
3033
            nStatus |= GDAL_DATA_COVERAGE_STATUS_DATA;
3034
            if (poPolyNonCoveredBySources)
3035
            {
3036
                OGRPolygon oPolySource;
3037
                auto poLR = std::make_unique<OGRLinearRing>();
3038
                poLR->addPoint(dfDstXOff, dfDstYOff);
3039
                poLR->addPoint(dfDstXOff, dfDstYOff2);
3040
                poLR->addPoint(dfDstXOff2, dfDstYOff2);
3041
                poLR->addPoint(dfDstXOff2, dfDstYOff);
3042
                poLR->addPoint(dfDstXOff, dfDstYOff);
3043
                oPolySource.addRingDirectly(poLR.release());
3044
                auto poRes = std::unique_ptr<OGRGeometry>(
3045
                    poPolyNonCoveredBySources->Difference(&oPolySource));
3046
                if (poRes && poRes->IsEmpty())
3047
                {
3048
                    if (pdfDataPct)
3049
                        *pdfDataPct = 100.0;
3050
                    return GDAL_DATA_COVERAGE_STATUS_DATA;
3051
                }
3052
                else if (poRes && poRes->getGeometryType() == wkbPolygon)
3053
                {
3054
                    poPolyNonCoveredBySources.reset(
3055
                        poRes.release()->toPolygon());
3056
                }
3057
                else
3058
                {
3059
                    poPolyNonCoveredBySources.reset();
3060
                }
3061
            }
3062
        }
3063
        if (nMaskFlagStop != 0 && (nStatus & nMaskFlagStop) != 0)
3064
        {
3065
            if (poSQLLayer)
3066
                m_poDS->ReleaseResultSet(poSQLLayer);
3067
            return nStatus;
3068
        }
3069
    }
3070
3071
    if (poSQLLayer)
3072
        m_poDS->ReleaseResultSet(poSQLLayer);
3073
3074
    if (poPolyNonCoveredBySources)
3075
    {
3076
        if (!poPolyNonCoveredBySources->IsEmpty())
3077
            nStatus |= GDAL_DATA_COVERAGE_STATUS_EMPTY;
3078
        if (pdfDataPct)
3079
            *pdfDataPct = 100.0 * (1.0 - poPolyNonCoveredBySources->get_Area() /
3080
                                             nXSize / nYSize);
3081
    }
3082
    return nStatus;
3083
}
3084
#endif  // HAVE_GEOS
3085
3086
/************************************************************************/
3087
/*                      GetMetadataDomainList()                         */
3088
/************************************************************************/
3089
3090
char **GDALTileIndexBand::GetMetadataDomainList()
3091
0
{
3092
0
    return CSLAddString(GDALRasterBand::GetMetadataDomainList(),
3093
0
                        "LocationInfo");
3094
0
}
3095
3096
/************************************************************************/
3097
/*                          GetMetadataItem()                           */
3098
/************************************************************************/
3099
3100
const char *GDALTileIndexBand::GetMetadataItem(const char *pszName,
3101
                                               const char *pszDomain)
3102
3103
0
{
3104
    /* ==================================================================== */
3105
    /*      LocationInfo handling.                                          */
3106
    /* ==================================================================== */
3107
0
    if (pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") &&
3108
0
        (STARTS_WITH_CI(pszName, "Pixel_") ||
3109
0
         STARTS_WITH_CI(pszName, "GeoPixel_")))
3110
0
    {
3111
        // What pixel are we aiming at?
3112
0
        int iPixel = 0;
3113
0
        int iLine = 0;
3114
3115
0
        if (STARTS_WITH_CI(pszName, "Pixel_"))
3116
0
        {
3117
0
            pszName += strlen("Pixel_");
3118
0
            iPixel = atoi(pszName);
3119
0
            const char *const pszUnderscore = strchr(pszName, '_');
3120
0
            if (!pszUnderscore)
3121
0
                return nullptr;
3122
0
            iLine = atoi(pszUnderscore + 1);
3123
0
        }
3124
0
        else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
3125
0
        {
3126
0
            pszName += strlen("GeoPixel_");
3127
0
            const double dfGeoX = CPLAtof(pszName);
3128
0
            const char *const pszUnderscore = strchr(pszName, '_');
3129
0
            if (!pszUnderscore)
3130
0
                return nullptr;
3131
0
            const double dfGeoY = CPLAtof(pszUnderscore + 1);
3132
3133
0
            double adfInvGeoTransform[6] = {0.0};
3134
0
            if (!GDALInvGeoTransform(m_poDS->m_gt.data(), adfInvGeoTransform))
3135
0
                return nullptr;
3136
3137
0
            iPixel = static_cast<int>(floor(adfInvGeoTransform[0] +
3138
0
                                            adfInvGeoTransform[1] * dfGeoX +
3139
0
                                            adfInvGeoTransform[2] * dfGeoY));
3140
0
            iLine = static_cast<int>(floor(adfInvGeoTransform[3] +
3141
0
                                           adfInvGeoTransform[4] * dfGeoX +
3142
0
                                           adfInvGeoTransform[5] * dfGeoY));
3143
0
        }
3144
0
        else
3145
0
        {
3146
0
            return nullptr;
3147
0
        }
3148
3149
0
        if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
3150
0
            iLine >= GetYSize())
3151
0
            return nullptr;
3152
3153
0
        if (!m_poDS->CollectSources(iPixel, iLine, 1, 1,
3154
0
                                    /* bMultiThreadAllowed = */ false))
3155
0
            return nullptr;
3156
3157
        // Format into XML.
3158
0
        m_osLastLocationInfo = "<LocationInfo>";
3159
3160
0
        if (!m_poDS->m_aoSourceDesc.empty())
3161
0
        {
3162
0
            const auto AddSource =
3163
0
                [&](const GDALTileIndexDataset::SourceDesc &oSourceDesc)
3164
0
            {
3165
0
                m_osLastLocationInfo += "<File>";
3166
0
                char *const pszXMLEscaped =
3167
0
                    CPLEscapeString(oSourceDesc.osName.c_str(), -1, CPLES_XML);
3168
0
                m_osLastLocationInfo += pszXMLEscaped;
3169
0
                CPLFree(pszXMLEscaped);
3170
0
                m_osLastLocationInfo += "</File>";
3171
0
            };
3172
3173
0
            const int anBand[] = {nBand};
3174
0
            if (!m_poDS->NeedInitBuffer(1, anBand))
3175
0
            {
3176
0
                AddSource(m_poDS->m_aoSourceDesc.back());
3177
0
            }
3178
0
            else
3179
0
            {
3180
0
                for (const auto &oSourceDesc : m_poDS->m_aoSourceDesc)
3181
0
                {
3182
0
                    if (oSourceDesc.poDS)
3183
0
                        AddSource(oSourceDesc);
3184
0
                }
3185
0
            }
3186
0
        }
3187
3188
0
        m_osLastLocationInfo += "</LocationInfo>";
3189
3190
0
        return m_osLastLocationInfo.c_str();
3191
0
    }
3192
3193
0
    return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
3194
0
}
3195
3196
/************************************************************************/
3197
/*                        SetMetadataItem()                             */
3198
/************************************************************************/
3199
3200
CPLErr GDALTileIndexBand::SetMetadataItem(const char *pszName,
3201
                                          const char *pszValue,
3202
                                          const char *pszDomain)
3203
0
{
3204
0
    if (nBand > 0 && m_poDS->m_bXMLUpdatable)
3205
0
    {
3206
0
        m_poDS->m_bXMLModified = true;
3207
0
        return GDALRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
3208
0
    }
3209
0
    else if (nBand > 0 && m_poDS->TileIndexSupportsEditingLayerMetadata())
3210
0
    {
3211
0
        m_poDS->m_poLayer->SetMetadataItem(
3212
0
            CPLSPrintf("BAND_%d_%s", nBand, pszName), pszValue, pszDomain);
3213
0
        return GDALRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
3214
0
    }
3215
0
    else
3216
0
    {
3217
0
        return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
3218
0
    }
3219
0
}
3220
3221
/************************************************************************/
3222
/*                           SetMetadata()                              */
3223
/************************************************************************/
3224
3225
CPLErr GDALTileIndexBand::SetMetadata(char **papszMD, const char *pszDomain)
3226
0
{
3227
0
    if (nBand > 0 && m_poDS->m_bXMLUpdatable)
3228
0
    {
3229
0
        m_poDS->m_bXMLModified = true;
3230
0
        return GDALRasterBand::SetMetadata(papszMD, pszDomain);
3231
0
    }
3232
0
    else if (nBand > 0 && m_poDS->TileIndexSupportsEditingLayerMetadata())
3233
0
    {
3234
0
        CPLStringList aosMD;
3235
3236
0
        if (!pszDomain || pszDomain[0] == 0)
3237
0
        {
3238
            // Reinject dataset metadata
3239
0
            char **papszLayerMD = m_poDS->m_poLayer->GetMetadata(pszDomain);
3240
0
            for (const char *const *papszIter = papszLayerMD;
3241
0
                 papszIter && *papszIter; ++papszIter)
3242
0
            {
3243
0
                if (!STARTS_WITH(*papszIter, "BAND_") ||
3244
0
                    STARTS_WITH(*papszIter, MD_BAND_COUNT))
3245
0
                    aosMD.AddString(*papszIter);
3246
0
            }
3247
0
        }
3248
3249
0
        for (int i = 0; papszMD && papszMD[i]; ++i)
3250
0
        {
3251
0
            aosMD.AddString(CPLSPrintf("BAND_%d_%s", nBand, papszMD[i]));
3252
0
        }
3253
3254
0
        if (!pszDomain || pszDomain[0] == 0)
3255
0
        {
3256
0
            for (const char *pszItem : apszReservedBandItems)
3257
0
            {
3258
0
                const char *pszKey = CPLSPrintf("BAND_%d_%s", nBand, pszItem);
3259
0
                if (!aosMD.FetchNameValue(pszKey))
3260
0
                {
3261
0
                    if (const char *pszVal =
3262
0
                            m_poDS->m_poLayer->GetMetadataItem(pszKey))
3263
0
                    {
3264
0
                        aosMD.SetNameValue(pszKey, pszVal);
3265
0
                    }
3266
0
                }
3267
0
            }
3268
0
        }
3269
3270
0
        m_poDS->m_poLayer->SetMetadata(aosMD.List(), pszDomain);
3271
0
        return GDALRasterBand::SetMetadata(papszMD, pszDomain);
3272
0
    }
3273
0
    else
3274
0
    {
3275
0
        return GDALPamRasterBand::SetMetadata(papszMD, pszDomain);
3276
0
    }
3277
0
}
3278
3279
/************************************************************************/
3280
/*                         GetSrcDstWin()                               */
3281
/************************************************************************/
3282
3283
static bool GetSrcDstWin(const GDALGeoTransform &tileGT, int nTileXSize,
3284
                         int nTileYSize, const GDALGeoTransform &vrtGT,
3285
                         int nVRTXSize, int nVRTYSize, double *pdfSrcXOff,
3286
                         double *pdfSrcYOff, double *pdfSrcXSize,
3287
                         double *pdfSrcYSize, double *pdfDstXOff,
3288
                         double *pdfDstYOff, double *pdfDstXSize,
3289
                         double *pdfDstYSize)
3290
0
{
3291
0
    const double minX = vrtGT[GT_TOPLEFT_X];
3292
0
    const double we_res = vrtGT[GT_WE_RES];
3293
0
    const double maxX = minX + nVRTXSize * we_res;
3294
0
    const double maxY = vrtGT[GT_TOPLEFT_Y];
3295
0
    const double ns_res = vrtGT[GT_NS_RES];
3296
0
    const double minY = maxY + nVRTYSize * ns_res;
3297
3298
    /* Check that the destination bounding box intersects the source bounding
3299
     * box */
3300
0
    if (tileGT[GT_TOPLEFT_X] + nTileXSize * tileGT[GT_WE_RES] <= minX)
3301
0
        return false;
3302
0
    if (tileGT[GT_TOPLEFT_X] >= maxX)
3303
0
        return false;
3304
0
    if (tileGT[GT_TOPLEFT_Y] + nTileYSize * tileGT[GT_NS_RES] >= maxY)
3305
0
        return false;
3306
0
    if (tileGT[GT_TOPLEFT_Y] <= minY)
3307
0
        return false;
3308
3309
0
    if (tileGT[GT_TOPLEFT_X] < minX)
3310
0
    {
3311
0
        *pdfSrcXOff = (minX - tileGT[GT_TOPLEFT_X]) / tileGT[GT_WE_RES];
3312
0
        *pdfDstXOff = 0.0;
3313
0
    }
3314
0
    else
3315
0
    {
3316
0
        *pdfSrcXOff = 0.0;
3317
0
        *pdfDstXOff = ((tileGT[GT_TOPLEFT_X] - minX) / we_res);
3318
0
    }
3319
0
    if (maxY < tileGT[GT_TOPLEFT_Y])
3320
0
    {
3321
0
        *pdfSrcYOff = (tileGT[GT_TOPLEFT_Y] - maxY) / -tileGT[GT_NS_RES];
3322
0
        *pdfDstYOff = 0.0;
3323
0
    }
3324
0
    else
3325
0
    {
3326
0
        *pdfSrcYOff = 0.0;
3327
0
        *pdfDstYOff = ((maxY - tileGT[GT_TOPLEFT_Y]) / -ns_res);
3328
0
    }
3329
3330
0
    *pdfSrcXSize = nTileXSize;
3331
0
    *pdfSrcYSize = nTileYSize;
3332
0
    if (*pdfSrcXOff > 0)
3333
0
        *pdfSrcXSize -= *pdfSrcXOff;
3334
0
    if (*pdfSrcYOff > 0)
3335
0
        *pdfSrcYSize -= *pdfSrcYOff;
3336
3337
0
    const double dfSrcToDstXSize = tileGT[GT_WE_RES] / we_res;
3338
0
    *pdfDstXSize = *pdfSrcXSize * dfSrcToDstXSize;
3339
0
    const double dfSrcToDstYSize = tileGT[GT_NS_RES] / ns_res;
3340
0
    *pdfDstYSize = *pdfSrcYSize * dfSrcToDstYSize;
3341
3342
0
    if (*pdfDstXOff + *pdfDstXSize > nVRTXSize)
3343
0
    {
3344
0
        *pdfDstXSize = nVRTXSize - *pdfDstXOff;
3345
0
        *pdfSrcXSize = *pdfDstXSize / dfSrcToDstXSize;
3346
0
    }
3347
3348
0
    if (*pdfDstYOff + *pdfDstYSize > nVRTYSize)
3349
0
    {
3350
0
        *pdfDstYSize = nVRTYSize - *pdfDstYOff;
3351
0
        *pdfSrcYSize = *pdfDstYSize / dfSrcToDstYSize;
3352
0
    }
3353
3354
0
    return *pdfSrcXSize > 0 && *pdfDstXSize > 0 && *pdfSrcYSize > 0 &&
3355
0
           *pdfDstYSize > 0;
3356
0
}
3357
3358
/************************************************************************/
3359
/*                   GDALDatasetCastToGTIDataset()                    */
3360
/************************************************************************/
3361
3362
GDALTileIndexDataset *GDALDatasetCastToGTIDataset(GDALDataset *poDS)
3363
0
{
3364
0
    return dynamic_cast<GDALTileIndexDataset *>(poDS);
3365
0
}
3366
3367
/************************************************************************/
3368
/*                   GTIGetSourcesMoreRecentThan()                    */
3369
/************************************************************************/
3370
3371
std::vector<GTISourceDesc>
3372
GTIGetSourcesMoreRecentThan(GDALTileIndexDataset *poDS, int64_t mTime)
3373
0
{
3374
0
    return poDS->GetSourcesMoreRecentThan(mTime);
3375
0
}
3376
3377
/************************************************************************/
3378
/*                       GetSourcesMoreRecentThan()                     */
3379
/************************************************************************/
3380
3381
std::vector<GTISourceDesc>
3382
GDALTileIndexDataset::GetSourcesMoreRecentThan(int64_t mTime)
3383
0
{
3384
0
    std::vector<GTISourceDesc> oRes;
3385
3386
0
    m_poLayer->SetSpatialFilter(nullptr);
3387
0
    for (auto &&poFeature : m_poLayer)
3388
0
    {
3389
0
        if (!poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
3390
0
        {
3391
0
            continue;
3392
0
        }
3393
3394
0
        auto poGeom = poFeature->GetGeometryRef();
3395
0
        if (!poGeom || poGeom->IsEmpty())
3396
0
            continue;
3397
3398
0
        OGREnvelope sEnvelope;
3399
0
        poGeom->getEnvelope(&sEnvelope);
3400
3401
0
        double dfXOff = (sEnvelope.MinX - m_gt[GT_TOPLEFT_X]) / m_gt[GT_WE_RES];
3402
0
        if (dfXOff >= nRasterXSize)
3403
0
            continue;
3404
3405
0
        double dfYOff = (sEnvelope.MaxY - m_gt[GT_TOPLEFT_Y]) / m_gt[GT_NS_RES];
3406
0
        if (dfYOff >= nRasterYSize)
3407
0
            continue;
3408
3409
0
        double dfXSize = (sEnvelope.MaxX - sEnvelope.MinX) / m_gt[GT_WE_RES];
3410
0
        if (dfXOff < 0)
3411
0
        {
3412
0
            dfXSize += dfXOff;
3413
0
            dfXOff = 0;
3414
0
            if (dfXSize <= 0)
3415
0
                continue;
3416
0
        }
3417
3418
0
        double dfYSize =
3419
0
            (sEnvelope.MaxY - sEnvelope.MinY) / std::fabs(m_gt[GT_NS_RES]);
3420
0
        if (dfYOff < 0)
3421
0
        {
3422
0
            dfYSize += dfYOff;
3423
0
            dfYOff = 0;
3424
0
            if (dfYSize <= 0)
3425
0
                continue;
3426
0
        }
3427
3428
0
        const char *pszTileName =
3429
0
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
3430
0
        std::string osTileName(
3431
0
            GetAbsoluteFileName(pszTileName, GetDescription()));
3432
0
        VSIStatBufL sStatSource;
3433
0
        if (VSIStatL(osTileName.c_str(), &sStatSource) != 0 ||
3434
0
            sStatSource.st_mtime <= mTime)
3435
0
        {
3436
0
            continue;
3437
0
        }
3438
3439
0
        constexpr double EPS = 1e-8;
3440
0
        GTISourceDesc oSourceDesc;
3441
0
        oSourceDesc.osFilename = std::move(osTileName);
3442
0
        oSourceDesc.nDstXOff = static_cast<int>(dfXOff + EPS);
3443
0
        oSourceDesc.nDstYOff = static_cast<int>(dfYOff + EPS);
3444
0
        oSourceDesc.nDstXSize = static_cast<int>(dfXSize + 0.5);
3445
0
        oSourceDesc.nDstYSize = static_cast<int>(dfYSize + 0.5);
3446
0
        oRes.emplace_back(std::move(oSourceDesc));
3447
0
    }
3448
3449
0
    return oRes;
3450
0
}
3451
3452
/************************************************************************/
3453
/*                         GetSourceDesc()                              */
3454
/************************************************************************/
3455
3456
bool GDALTileIndexDataset::GetSourceDesc(const std::string &osTileName,
3457
                                         SourceDesc &oSourceDesc,
3458
                                         std::mutex *pMutex)
3459
0
{
3460
0
    std::shared_ptr<GDALDataset> poTileDS;
3461
3462
0
    if (pMutex)
3463
0
        pMutex->lock();
3464
0
    const bool bTileKnown = m_oMapSharedSources.tryGet(osTileName, poTileDS);
3465
0
    if (pMutex)
3466
0
        pMutex->unlock();
3467
3468
0
    if (!bTileKnown)
3469
0
    {
3470
0
        poTileDS = std::shared_ptr<GDALDataset>(
3471
0
            GDALProxyPoolDataset::Create(
3472
0
                osTileName.c_str(), nullptr, GA_ReadOnly,
3473
0
                /* bShared = */ true, m_osUniqueHandle.c_str()),
3474
0
            GDALDatasetUniquePtrReleaser());
3475
0
        if (!poTileDS)
3476
0
        {
3477
0
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot open source %s",
3478
0
                     osTileName.c_str());
3479
0
            return false;
3480
0
        }
3481
0
        if (poTileDS->GetRasterCount() == 0)
3482
0
        {
3483
0
            CPLError(CE_Failure, CPLE_AppDefined,
3484
0
                     "Source %s has no raster bands", osTileName.c_str());
3485
0
            return false;
3486
0
        }
3487
3488
        // do palette -> RGB(A) expansion if needed
3489
0
        if (!GTIDoPaletteExpansionIfNeeded(poTileDS, nBands))
3490
0
            return false;
3491
3492
0
        const OGRSpatialReference *poTileSRS;
3493
0
        if (!m_oSRS.IsEmpty() &&
3494
0
            (poTileSRS = poTileDS->GetSpatialRef()) != nullptr &&
3495
0
            !m_oSRS.IsSame(poTileSRS))
3496
0
        {
3497
0
            CPLDebug("VRT",
3498
0
                     "Tile %s has not the same SRS as the VRT. "
3499
0
                     "Proceed to on-the-fly warping",
3500
0
                     osTileName.c_str());
3501
3502
0
            CPLStringList aosOptions;
3503
0
            aosOptions.AddString("-of");
3504
0
            aosOptions.AddString("VRT");
3505
3506
0
            if ((poTileDS->GetRasterBand(1)->GetColorTable() == nullptr &&
3507
0
                 poTileDS->GetRasterBand(1)->GetCategoryNames() == nullptr) ||
3508
0
                m_eResampling == GRIORA_Mode)
3509
0
            {
3510
0
                aosOptions.AddString("-r");
3511
0
                aosOptions.AddString(m_osResampling.c_str());
3512
0
            }
3513
3514
0
            if (m_osWKT.empty())
3515
0
            {
3516
0
                char *pszWKT = nullptr;
3517
0
                const char *const apszWKTOptions[] = {"FORMAT=WKT2_2019",
3518
0
                                                      nullptr};
3519
0
                m_oSRS.exportToWkt(&pszWKT, apszWKTOptions);
3520
0
                if (pszWKT)
3521
0
                    m_osWKT = pszWKT;
3522
0
                CPLFree(pszWKT);
3523
0
            }
3524
0
            if (m_osWKT.empty())
3525
0
            {
3526
0
                CPLError(CE_Failure, CPLE_AppDefined,
3527
0
                         "Cannot export VRT SRS to WKT2");
3528
0
                return false;
3529
0
            }
3530
3531
0
            aosOptions.AddString("-t_srs");
3532
0
            aosOptions.AddString(m_osWKT.c_str());
3533
3534
            // First pass to get the extent of the tile in the
3535
            // target VRT SRS
3536
0
            GDALWarpAppOptions *psWarpOptions =
3537
0
                GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
3538
0
            GDALDatasetH ahSrcDS[] = {GDALDataset::ToHandle(poTileDS.get())};
3539
0
            int bUsageError = false;
3540
0
            auto poWarpDS =
3541
0
                std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALWarp(
3542
0
                    "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
3543
0
            GDALWarpAppOptionsFree(psWarpOptions);
3544
0
            if (!poWarpDS)
3545
0
            {
3546
0
                return false;
3547
0
            }
3548
3549
            // Second pass to create a warped source VRT whose
3550
            // extent is aligned on the one of the target VRT
3551
0
            GDALGeoTransform warpDSGT;
3552
0
            const auto eErr = poWarpDS->GetGeoTransform(warpDSGT);
3553
0
            CPL_IGNORE_RET_VAL(eErr);
3554
0
            CPLAssert(eErr == CE_None);
3555
0
            const double dfVRTMinX = m_gt[GT_TOPLEFT_X];
3556
0
            const double dfVRTResX = m_gt[GT_WE_RES];
3557
0
            const double dfVRTMaxY = m_gt[GT_TOPLEFT_Y];
3558
0
            const double dfVRTResYAbs = -m_gt[GT_NS_RES];
3559
0
            const double dfWarpMinX =
3560
0
                std::floor((warpDSGT[GT_TOPLEFT_X] - dfVRTMinX) / dfVRTResX) *
3561
0
                    dfVRTResX +
3562
0
                dfVRTMinX;
3563
0
            const double dfWarpMaxX =
3564
0
                std::ceil((warpDSGT[GT_TOPLEFT_X] +
3565
0
                           warpDSGT[GT_WE_RES] * poWarpDS->GetRasterXSize() -
3566
0
                           dfVRTMinX) /
3567
0
                          dfVRTResX) *
3568
0
                    dfVRTResX +
3569
0
                dfVRTMinX;
3570
0
            const double dfWarpMaxY =
3571
0
                dfVRTMaxY - std::floor((dfVRTMaxY - warpDSGT[GT_TOPLEFT_Y]) /
3572
0
                                       dfVRTResYAbs) *
3573
0
                                dfVRTResYAbs;
3574
0
            const double dfWarpMinY =
3575
0
                dfVRTMaxY -
3576
0
                std::ceil((dfVRTMaxY -
3577
0
                           (warpDSGT[GT_TOPLEFT_Y] +
3578
0
                            warpDSGT[GT_NS_RES] * poWarpDS->GetRasterYSize())) /
3579
0
                          dfVRTResYAbs) *
3580
0
                    dfVRTResYAbs;
3581
3582
0
            aosOptions.AddString("-te");
3583
0
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinX));
3584
0
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinY));
3585
0
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxX));
3586
0
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxY));
3587
3588
0
            aosOptions.AddString("-tr");
3589
0
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResX));
3590
0
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResYAbs));
3591
3592
0
            aosOptions.AddString("-dstalpha");
3593
3594
0
            psWarpOptions = GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
3595
0
            poWarpDS.reset(GDALDataset::FromHandle(GDALWarp(
3596
0
                "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
3597
0
            GDALWarpAppOptionsFree(psWarpOptions);
3598
0
            if (!poWarpDS)
3599
0
            {
3600
0
                return false;
3601
0
            }
3602
3603
0
            poTileDS.reset(poWarpDS.release());
3604
0
        }
3605
3606
0
        if (pMutex)
3607
0
            pMutex->lock();
3608
0
        m_oMapSharedSources.insert(osTileName, poTileDS);
3609
0
        if (pMutex)
3610
0
            pMutex->unlock();
3611
0
    }
3612
3613
0
    GDALGeoTransform gtTile;
3614
0
    if (poTileDS->GetGeoTransform(gtTile) != CE_None)
3615
0
    {
3616
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s lacks geotransform",
3617
0
                 osTileName.c_str());
3618
0
        return false;
3619
0
    }
3620
3621
0
    bool bHasNoData = false;
3622
0
    bool bSameNoData = true;
3623
0
    double dfNoDataValue = 0;
3624
0
    GDALRasterBand *poMaskBand = nullptr;
3625
0
    const int nBandCount = poTileDS->GetRasterCount();
3626
0
    for (int iBand = 0; iBand < nBandCount; ++iBand)
3627
0
    {
3628
0
        auto poTileBand = poTileDS->GetRasterBand(iBand + 1);
3629
0
        int bThisBandHasNoData = false;
3630
0
        const double dfThisBandNoDataValue =
3631
0
            poTileBand->GetNoDataValue(&bThisBandHasNoData);
3632
0
        if (bThisBandHasNoData)
3633
0
        {
3634
0
            bHasNoData = true;
3635
0
            dfNoDataValue = dfThisBandNoDataValue;
3636
0
        }
3637
0
        if (iBand > 0 &&
3638
0
            (static_cast<int>(bThisBandHasNoData) !=
3639
0
                 static_cast<int>(bHasNoData) ||
3640
0
             (bHasNoData &&
3641
0
              !IsSameNaNAware(dfNoDataValue, dfThisBandNoDataValue))))
3642
0
        {
3643
0
            bSameNoData = false;
3644
0
        }
3645
3646
0
        if (poTileBand->GetMaskFlags() == GMF_PER_DATASET)
3647
0
            poMaskBand = poTileBand->GetMaskBand();
3648
0
        else if (poTileBand->GetColorInterpretation() == GCI_AlphaBand)
3649
0
            poMaskBand = poTileBand;
3650
0
    }
3651
3652
0
    std::unique_ptr<VRTSimpleSource> poSource;
3653
0
    if (!bHasNoData)
3654
0
    {
3655
0
        poSource = std::make_unique<VRTSimpleSource>();
3656
0
    }
3657
0
    else
3658
0
    {
3659
0
        auto poComplexSource = std::make_unique<VRTComplexSource>();
3660
0
        poComplexSource->SetNoDataValue(dfNoDataValue);
3661
0
        poSource = std::move(poComplexSource);
3662
0
    }
3663
3664
0
    GetSrcDstWin(gtTile, poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
3665
0
                 m_gt, GetRasterXSize(), GetRasterYSize(),
3666
0
                 &poSource->m_dfSrcXOff, &poSource->m_dfSrcYOff,
3667
0
                 &poSource->m_dfSrcXSize, &poSource->m_dfSrcYSize,
3668
0
                 &poSource->m_dfDstXOff, &poSource->m_dfDstYOff,
3669
0
                 &poSource->m_dfDstXSize, &poSource->m_dfDstYSize);
3670
3671
0
    oSourceDesc.osName = osTileName;
3672
0
    oSourceDesc.poDS = std::move(poTileDS);
3673
0
    oSourceDesc.poSource = std::move(poSource);
3674
0
    oSourceDesc.bHasNoData = bHasNoData;
3675
0
    oSourceDesc.bSameNoData = bSameNoData;
3676
0
    if (bSameNoData)
3677
0
        oSourceDesc.dfSameNoData = dfNoDataValue;
3678
0
    oSourceDesc.poMaskBand = poMaskBand;
3679
0
    return true;
3680
0
}
3681
3682
/************************************************************************/
3683
/*                           GetNumThreads()                            */
3684
/************************************************************************/
3685
3686
int GDALTileIndexDataset::GetNumThreads() const
3687
0
{
3688
0
    const char *pszNumThreads =
3689
0
        CSLFetchNameValueDef(GetOpenOptions(), "NUM_THREADS", nullptr);
3690
0
    if (!pszNumThreads)
3691
0
        pszNumThreads = CPLGetConfigOption("GTI_NUM_THREADS", nullptr);
3692
0
    if (!pszNumThreads)
3693
0
        pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS");
3694
0
    if (EQUAL(pszNumThreads, "0") || EQUAL(pszNumThreads, "1"))
3695
0
        return atoi(pszNumThreads);
3696
0
    const int nMaxPoolSize = GDALGetMaxDatasetPoolSize();
3697
0
    const int nLimit = std::min(CPLGetNumCPUs(), nMaxPoolSize);
3698
0
    if (EQUAL(pszNumThreads, "ALL_CPUS"))
3699
0
        return nLimit;
3700
0
    return std::min(atoi(pszNumThreads), nLimit);
3701
0
}
3702
3703
/************************************************************************/
3704
/*                        CollectSources()                              */
3705
/************************************************************************/
3706
3707
bool GDALTileIndexDataset::CollectSources(double dfXOff, double dfYOff,
3708
                                          double dfXSize, double dfYSize,
3709
                                          bool bMultiThreadAllowed)
3710
0
{
3711
0
    const double dfMinX = m_gt[GT_TOPLEFT_X] + dfXOff * m_gt[GT_WE_RES];
3712
0
    const double dfMaxX = dfMinX + dfXSize * m_gt[GT_WE_RES];
3713
0
    const double dfMaxY = m_gt[GT_TOPLEFT_Y] + dfYOff * m_gt[GT_NS_RES];
3714
0
    const double dfMinY = dfMaxY + dfYSize * m_gt[GT_NS_RES];
3715
3716
0
    if (dfMinX == m_dfLastMinXFilter && dfMinY == m_dfLastMinYFilter &&
3717
0
        dfMaxX == m_dfLastMaxXFilter && dfMaxY == m_dfLastMaxYFilter)
3718
0
    {
3719
0
        return true;
3720
0
    }
3721
3722
0
    m_dfLastMinXFilter = dfMinX;
3723
0
    m_dfLastMinYFilter = dfMinY;
3724
0
    m_dfLastMaxXFilter = dfMaxX;
3725
0
    m_dfLastMaxYFilter = dfMaxY;
3726
0
    m_bLastMustUseMultiThreading = false;
3727
3728
0
    OGRLayer *poSQLLayer = nullptr;
3729
0
    if (!m_osSpatialSQL.empty())
3730
0
    {
3731
0
        const std::string osSQL =
3732
0
            CPLString(m_osSpatialSQL)
3733
0
                .replaceAll("{XMIN}", CPLSPrintf("%.17g", dfMinX))
3734
0
                .replaceAll("{YMIN}", CPLSPrintf("%.17g", dfMinY))
3735
0
                .replaceAll("{XMAX}", CPLSPrintf("%.17g", dfMaxX))
3736
0
                .replaceAll("{YMAX}", CPLSPrintf("%.17g", dfMaxY));
3737
0
        poSQLLayer = m_poVectorDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
3738
0
        if (!poSQLLayer)
3739
0
            return 0;
3740
0
    }
3741
0
    else
3742
0
    {
3743
0
        m_poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
3744
0
        m_poLayer->ResetReading();
3745
0
    }
3746
3747
0
    OGRLayer *const poLayer = poSQLLayer ? poSQLLayer : m_poLayer;
3748
3749
0
    m_aoSourceDesc.clear();
3750
0
    while (true)
3751
0
    {
3752
0
        auto poFeature = std::unique_ptr<OGRFeature>(poLayer->GetNextFeature());
3753
0
        if (!poFeature)
3754
0
            break;
3755
0
        if (!poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
3756
0
        {
3757
0
            continue;
3758
0
        }
3759
3760
0
        SourceDesc oSourceDesc;
3761
0
        oSourceDesc.poFeature = std::move(poFeature);
3762
0
        m_aoSourceDesc.emplace_back(std::move(oSourceDesc));
3763
3764
0
        if (m_aoSourceDesc.size() > 10 * 1000 * 1000)
3765
0
        {
3766
            // Safety belt...
3767
0
            CPLError(CE_Failure, CPLE_AppDefined,
3768
0
                     "More than 10 million contributing sources to a "
3769
0
                     "single RasterIO() request is not supported");
3770
0
            return false;
3771
0
        }
3772
0
    }
3773
3774
0
    if (poSQLLayer)
3775
0
        ReleaseResultSet(poSQLLayer);
3776
3777
0
    constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
3778
0
    if (bMultiThreadAllowed && m_aoSourceDesc.size() > 1 &&
3779
0
        dfXSize * dfYSize > MINIMUM_PIXEL_COUNT_FOR_THREADED_IO)
3780
0
    {
3781
0
        if (m_nNumThreads < 0)
3782
0
            m_nNumThreads = GetNumThreads();
3783
0
        bMultiThreadAllowed = m_nNumThreads > 1;
3784
0
    }
3785
0
    else
3786
0
    {
3787
0
        bMultiThreadAllowed = false;
3788
0
    }
3789
3790
0
    if (bMultiThreadAllowed)
3791
0
    {
3792
0
        CPLRectObj sGlobalBounds;
3793
0
        sGlobalBounds.minx = dfXOff;
3794
0
        sGlobalBounds.miny = dfYOff;
3795
0
        sGlobalBounds.maxx = dfXOff + dfXSize;
3796
0
        sGlobalBounds.maxy = dfYOff + dfYSize;
3797
0
        CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
3798
3799
0
        bool bCompatibleOfMultiThread = true;
3800
0
        std::set<std::string> oSetTileNames;
3801
0
        for (const auto &oSourceDesc : m_aoSourceDesc)
3802
0
        {
3803
0
            const char *pszTileName =
3804
0
                oSourceDesc.poFeature->GetFieldAsString(m_nLocationFieldIndex);
3805
0
            if (oSetTileNames.find(pszTileName) != oSetTileNames.end())
3806
0
            {
3807
0
                bCompatibleOfMultiThread = false;
3808
0
                break;
3809
0
            }
3810
0
            oSetTileNames.insert(pszTileName);
3811
3812
0
            const auto poGeom = oSourceDesc.poFeature->GetGeometryRef();
3813
0
            if (!poGeom || poGeom->IsEmpty())
3814
0
                continue;
3815
3816
0
            OGREnvelope sEnvelope;
3817
0
            poGeom->getEnvelope(&sEnvelope);
3818
3819
0
            CPLRectObj sSourceBounds;
3820
0
            sSourceBounds.minx =
3821
0
                (sEnvelope.MinX - m_gt[GT_TOPLEFT_X]) / m_gt[GT_WE_RES];
3822
0
            sSourceBounds.maxx =
3823
0
                (sEnvelope.MaxX - m_gt[GT_TOPLEFT_X]) / m_gt[GT_WE_RES];
3824
            // Yes use of MaxY to compute miny is intended given that MaxY is
3825
            // in georeferenced space whereas miny is in pixel space.
3826
0
            sSourceBounds.miny =
3827
0
                (sEnvelope.MaxY - m_gt[GT_TOPLEFT_Y]) / m_gt[GT_NS_RES];
3828
            // Same here for maxy vs Miny
3829
0
            sSourceBounds.maxy =
3830
0
                (sEnvelope.MinY - m_gt[GT_TOPLEFT_Y]) / m_gt[GT_NS_RES];
3831
3832
            // Clamp to global bounds and some epsilon to avoid adjacent tiles
3833
            // to be considered as overlapping
3834
0
            constexpr double EPSILON = 0.1;
3835
0
            sSourceBounds.minx =
3836
0
                std::max(sGlobalBounds.minx, sSourceBounds.minx) + EPSILON;
3837
0
            sSourceBounds.maxx =
3838
0
                std::min(sGlobalBounds.maxx, sSourceBounds.maxx) - EPSILON;
3839
0
            sSourceBounds.miny =
3840
0
                std::max(sGlobalBounds.miny, sSourceBounds.miny) + EPSILON;
3841
0
            sSourceBounds.maxy =
3842
0
                std::min(sGlobalBounds.maxy, sSourceBounds.maxy) - EPSILON;
3843
3844
            // Check that the new source doesn't overlap an existing one.
3845
0
            if (CPLQuadTreeHasMatch(hQuadTree, &sSourceBounds))
3846
0
            {
3847
0
                bCompatibleOfMultiThread = false;
3848
0
                break;
3849
0
            }
3850
3851
0
            CPLQuadTreeInsertWithBounds(
3852
0
                hQuadTree,
3853
0
                const_cast<void *>(static_cast<const void *>(&oSourceDesc)),
3854
0
                &sSourceBounds);
3855
0
        }
3856
3857
0
        CPLQuadTreeDestroy(hQuadTree);
3858
3859
0
        if (bCompatibleOfMultiThread)
3860
0
        {
3861
0
            m_bLastMustUseMultiThreading = true;
3862
0
            return true;
3863
0
        }
3864
0
    }
3865
3866
0
    if (m_aoSourceDesc.size() > 1)
3867
0
    {
3868
0
        SortSourceDesc();
3869
0
    }
3870
3871
    // Try to find the last (most prioritary) fully opaque source covering
3872
    // the whole AOI. We only need to start rendering from it.
3873
0
    size_t i = m_aoSourceDesc.size();
3874
0
    while (i > 0)
3875
0
    {
3876
0
        --i;
3877
0
        auto &poFeature = m_aoSourceDesc[i].poFeature;
3878
0
        const char *pszTileName =
3879
0
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
3880
0
        const std::string osTileName(
3881
0
            GetAbsoluteFileName(pszTileName, GetDescription()));
3882
3883
0
        SourceDesc oSourceDesc;
3884
0
        if (!GetSourceDesc(osTileName, oSourceDesc, nullptr))
3885
0
            return false;
3886
3887
        // Check consistency of bounding box in tile index vs actual
3888
        // extent of the tile.
3889
0
        GDALGeoTransform tileGT;
3890
0
        if (oSourceDesc.poDS->GetGeoTransform(tileGT) == CE_None &&
3891
0
            tileGT[GT_ROTATION_PARAM1] == 0 && tileGT[GT_ROTATION_PARAM2] == 0)
3892
0
        {
3893
0
            OGREnvelope sActualTileExtent;
3894
0
            sActualTileExtent.MinX = tileGT[GT_TOPLEFT_X];
3895
0
            sActualTileExtent.MaxX =
3896
0
                sActualTileExtent.MinX +
3897
0
                oSourceDesc.poDS->GetRasterXSize() * tileGT[GT_WE_RES];
3898
0
            sActualTileExtent.MaxY = tileGT[GT_TOPLEFT_Y];
3899
0
            sActualTileExtent.MinY =
3900
0
                sActualTileExtent.MaxY +
3901
0
                oSourceDesc.poDS->GetRasterYSize() * tileGT[GT_NS_RES];
3902
0
            const auto poGeom = poFeature->GetGeometryRef();
3903
0
            if (poGeom && !poGeom->IsEmpty())
3904
0
            {
3905
0
                OGREnvelope sGeomTileExtent;
3906
0
                poGeom->getEnvelope(&sGeomTileExtent);
3907
0
                sGeomTileExtent.MinX -= m_gt[GT_WE_RES];
3908
0
                sGeomTileExtent.MaxX += m_gt[GT_WE_RES];
3909
0
                sGeomTileExtent.MinY -= std::fabs(m_gt[GT_NS_RES]);
3910
0
                sGeomTileExtent.MaxY += std::fabs(m_gt[GT_NS_RES]);
3911
0
                if (!sGeomTileExtent.Contains(sActualTileExtent))
3912
0
                {
3913
0
                    if (!sGeomTileExtent.Intersects(sActualTileExtent))
3914
0
                    {
3915
0
                        CPLError(CE_Warning, CPLE_AppDefined,
3916
0
                                 "Tile index is out of sync with actual "
3917
0
                                 "extent of %s. Bounding box from tile index "
3918
0
                                 "is (%g, %g, %g, %g) does not intersect at "
3919
0
                                 "all bounding box from tile (%g, %g, %g, %g)",
3920
0
                                 osTileName.c_str(), sGeomTileExtent.MinX,
3921
0
                                 sGeomTileExtent.MinY, sGeomTileExtent.MaxX,
3922
0
                                 sGeomTileExtent.MaxY, sActualTileExtent.MinX,
3923
0
                                 sActualTileExtent.MinY, sActualTileExtent.MaxX,
3924
0
                                 sActualTileExtent.MaxY);
3925
0
                        continue;
3926
0
                    }
3927
0
                    CPLError(CE_Warning, CPLE_AppDefined,
3928
0
                             "Tile index is out of sync with actual extent "
3929
0
                             "of %s. Bounding box from tile index is (%g, %g, "
3930
0
                             "%g, %g) does not fully contain bounding box from "
3931
0
                             "tile (%g, %g, %g, %g)",
3932
0
                             osTileName.c_str(), sGeomTileExtent.MinX,
3933
0
                             sGeomTileExtent.MinY, sGeomTileExtent.MaxX,
3934
0
                             sGeomTileExtent.MaxY, sActualTileExtent.MinX,
3935
0
                             sActualTileExtent.MinY, sActualTileExtent.MaxX,
3936
0
                             sActualTileExtent.MaxY);
3937
0
                }
3938
0
            }
3939
0
        }
3940
3941
0
        const auto &poSource = oSourceDesc.poSource;
3942
0
        if (dfXOff >= poSource->m_dfDstXOff + poSource->m_dfDstXSize ||
3943
0
            dfYOff >= poSource->m_dfDstYOff + poSource->m_dfDstYSize ||
3944
0
            poSource->m_dfDstXOff >= dfXOff + dfXSize ||
3945
0
            poSource->m_dfDstYOff >= dfYOff + dfYSize)
3946
0
        {
3947
            // Can happen as some spatial filters select slightly more features
3948
            // than strictly needed.
3949
0
            continue;
3950
0
        }
3951
3952
0
        const bool bCoversWholeAOI =
3953
0
            (poSource->m_dfDstXOff <= dfXOff &&
3954
0
             poSource->m_dfDstYOff <= dfYOff &&
3955
0
             poSource->m_dfDstXOff + poSource->m_dfDstXSize >=
3956
0
                 dfXOff + dfXSize &&
3957
0
             poSource->m_dfDstYOff + poSource->m_dfDstYSize >=
3958
0
                 dfYOff + dfYSize);
3959
0
        oSourceDesc.bCoversWholeAOI = bCoversWholeAOI;
3960
3961
0
        m_aoSourceDesc[i] = std::move(oSourceDesc);
3962
3963
0
        if (m_aoSourceDesc[i].bCoversWholeAOI &&
3964
0
            !m_aoSourceDesc[i].bHasNoData && !m_aoSourceDesc[i].poMaskBand)
3965
0
        {
3966
0
            break;
3967
0
        }
3968
0
    }
3969
3970
0
    if (i > 0)
3971
0
    {
3972
        // Remove sources that will not be rendered
3973
0
        m_aoSourceDesc.erase(m_aoSourceDesc.begin(),
3974
0
                             m_aoSourceDesc.begin() + i);
3975
0
    }
3976
3977
    // Remove elements that have no dataset
3978
0
    m_aoSourceDesc.erase(std::remove_if(m_aoSourceDesc.begin(),
3979
0
                                        m_aoSourceDesc.end(),
3980
0
                                        [](const SourceDesc &desc)
3981
0
                                        { return desc.poDS == nullptr; }),
3982
0
                         m_aoSourceDesc.end());
3983
3984
0
    return true;
3985
0
}
3986
3987
/************************************************************************/
3988
/*                          SortSourceDesc()                            */
3989
/************************************************************************/
3990
3991
void GDALTileIndexDataset::SortSourceDesc()
3992
0
{
3993
0
    const auto eFieldType = m_nSortFieldIndex >= 0
3994
0
                                ? m_poLayer->GetLayerDefn()
3995
0
                                      ->GetFieldDefn(m_nSortFieldIndex)
3996
0
                                      ->GetType()
3997
0
                                : OFTMaxType;
3998
0
    std::sort(
3999
0
        m_aoSourceDesc.begin(), m_aoSourceDesc.end(),
4000
0
        [this, eFieldType](const SourceDesc &a, const SourceDesc &b)
4001
0
        {
4002
0
            const auto &poFeatureA = (m_bSortFieldAsc ? a : b).poFeature;
4003
0
            const auto &poFeatureB = (m_bSortFieldAsc ? b : a).poFeature;
4004
0
            if (m_nSortFieldIndex >= 0 &&
4005
0
                poFeatureA->IsFieldSetAndNotNull(m_nSortFieldIndex) &&
4006
0
                poFeatureB->IsFieldSetAndNotNull(m_nSortFieldIndex))
4007
0
            {
4008
0
                if (eFieldType == OFTString)
4009
0
                {
4010
0
                    const int nCmp =
4011
0
                        strcmp(poFeatureA->GetFieldAsString(m_nSortFieldIndex),
4012
0
                               poFeatureB->GetFieldAsString(m_nSortFieldIndex));
4013
0
                    if (nCmp < 0)
4014
0
                        return true;
4015
0
                    if (nCmp > 0)
4016
0
                        return false;
4017
0
                }
4018
0
                else if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
4019
0
                {
4020
0
                    const auto nA =
4021
0
                        poFeatureA->GetFieldAsInteger64(m_nSortFieldIndex);
4022
0
                    const auto nB =
4023
0
                        poFeatureB->GetFieldAsInteger64(m_nSortFieldIndex);
4024
0
                    if (nA < nB)
4025
0
                        return true;
4026
0
                    if (nA > nB)
4027
0
                        return false;
4028
0
                }
4029
0
                else if (eFieldType == OFTReal)
4030
0
                {
4031
0
                    const auto dfA =
4032
0
                        poFeatureA->GetFieldAsDouble(m_nSortFieldIndex);
4033
0
                    const auto dfB =
4034
0
                        poFeatureB->GetFieldAsDouble(m_nSortFieldIndex);
4035
0
                    if (dfA < dfB)
4036
0
                        return true;
4037
0
                    if (dfA > dfB)
4038
0
                        return false;
4039
0
                }
4040
0
                else if (eFieldType == OFTDate || eFieldType == OFTDateTime)
4041
0
                {
4042
0
                    const auto poFieldA =
4043
0
                        poFeatureA->GetRawFieldRef(m_nSortFieldIndex);
4044
0
                    const auto poFieldB =
4045
0
                        poFeatureB->GetRawFieldRef(m_nSortFieldIndex);
4046
4047
0
#define COMPARE_DATE_COMPONENT(comp)                                           \
4048
0
    do                                                                         \
4049
0
    {                                                                          \
4050
0
        if (poFieldA->Date.comp < poFieldB->Date.comp)                         \
4051
0
            return true;                                                       \
4052
0
        if (poFieldA->Date.comp > poFieldB->Date.comp)                         \
4053
0
            return false;                                                      \
4054
0
    } while (0)
4055
4056
0
                    COMPARE_DATE_COMPONENT(Year);
4057
0
                    COMPARE_DATE_COMPONENT(Month);
4058
0
                    COMPARE_DATE_COMPONENT(Day);
4059
0
                    COMPARE_DATE_COMPONENT(Hour);
4060
0
                    COMPARE_DATE_COMPONENT(Minute);
4061
0
                    COMPARE_DATE_COMPONENT(Second);
4062
0
                }
4063
0
                else
4064
0
                {
4065
0
                    CPLAssert(false);
4066
0
                }
4067
0
            }
4068
0
            return poFeatureA->GetFID() < poFeatureB->GetFID();
4069
0
        });
4070
0
}
4071
4072
/************************************************************************/
4073
/*                   CompositeSrcWithMaskIntoDest()                     */
4074
/************************************************************************/
4075
4076
static void
4077
CompositeSrcWithMaskIntoDest(const int nOutXSize, const int nOutYSize,
4078
                             const GDALDataType eBufType,
4079
                             const int nBufTypeSize, const GSpacing nPixelSpace,
4080
                             const GSpacing nLineSpace, const GByte *pabySrc,
4081
                             const GByte *const pabyMask, GByte *const pabyDest)
4082
0
{
4083
0
    size_t iMaskIdx = 0;
4084
0
    if (eBufType == GDT_Byte)
4085
0
    {
4086
        // Optimization for byte case
4087
0
        for (int iY = 0; iY < nOutYSize; iY++)
4088
0
        {
4089
0
            GByte *pabyDestLine =
4090
0
                pabyDest + static_cast<GPtrDiff_t>(iY * nLineSpace);
4091
0
            int iX = 0;
4092
0
#ifdef USE_SSE2_OPTIM
4093
0
            if (nPixelSpace == 1)
4094
0
            {
4095
                // SSE2 version up to 6 times faster than portable version
4096
0
                const auto xmm_zero = _mm_setzero_si128();
4097
0
                constexpr int SIZEOF_REG = static_cast<int>(sizeof(xmm_zero));
4098
0
                for (; iX + SIZEOF_REG <= nOutXSize; iX += SIZEOF_REG)
4099
0
                {
4100
0
                    auto xmm_mask = _mm_loadu_si128(
4101
0
                        reinterpret_cast<__m128i const *>(pabyMask + iMaskIdx));
4102
0
                    const auto xmm_src = _mm_loadu_si128(
4103
0
                        reinterpret_cast<__m128i const *>(pabySrc));
4104
0
                    auto xmm_dst = _mm_loadu_si128(
4105
0
                        reinterpret_cast<__m128i const *>(pabyDestLine));
4106
#ifdef USE_SSE41_OPTIM
4107
                    xmm_dst = _mm_blendv_epi8(xmm_dst, xmm_src, xmm_mask);
4108
#else
4109
                    // mask[i] = 0 becomes 255, and mask[i] != 0 becomes 0
4110
0
                    xmm_mask = _mm_cmpeq_epi8(xmm_mask, xmm_zero);
4111
                    // dst_data[i] = (mask[i] & dst_data[i]) |
4112
                    //               (~mask[i] & src_data[i])
4113
                    // That is:
4114
                    // dst_data[i] = dst_data[i] when mask[i] = 255
4115
                    // dst_data[i] = src_data[i] when mask[i] = 0
4116
0
                    xmm_dst = _mm_or_si128(_mm_and_si128(xmm_mask, xmm_dst),
4117
0
                                           _mm_andnot_si128(xmm_mask, xmm_src));
4118
0
#endif
4119
0
                    _mm_storeu_si128(reinterpret_cast<__m128i *>(pabyDestLine),
4120
0
                                     xmm_dst);
4121
0
                    pabyDestLine += SIZEOF_REG;
4122
0
                    pabySrc += SIZEOF_REG;
4123
0
                    iMaskIdx += SIZEOF_REG;
4124
0
                }
4125
0
            }
4126
0
#endif
4127
0
            for (; iX < nOutXSize; iX++)
4128
0
            {
4129
0
                if (pabyMask[iMaskIdx])
4130
0
                {
4131
0
                    *pabyDestLine = *pabySrc;
4132
0
                }
4133
0
                pabyDestLine += static_cast<GPtrDiff_t>(nPixelSpace);
4134
0
                pabySrc++;
4135
0
                iMaskIdx++;
4136
0
            }
4137
0
        }
4138
0
    }
4139
0
    else
4140
0
    {
4141
0
        for (int iY = 0; iY < nOutYSize; iY++)
4142
0
        {
4143
0
            GByte *pabyDestLine =
4144
0
                pabyDest + static_cast<GPtrDiff_t>(iY * nLineSpace);
4145
0
            for (int iX = 0; iX < nOutXSize; iX++)
4146
0
            {
4147
0
                if (pabyMask[iMaskIdx])
4148
0
                {
4149
0
                    memcpy(pabyDestLine, pabySrc, nBufTypeSize);
4150
0
                }
4151
0
                pabyDestLine += static_cast<GPtrDiff_t>(nPixelSpace);
4152
0
                pabySrc += nBufTypeSize;
4153
0
                iMaskIdx++;
4154
0
            }
4155
0
        }
4156
0
    }
4157
0
}
4158
4159
/************************************************************************/
4160
/*                         NeedInitBuffer()                             */
4161
/************************************************************************/
4162
4163
// Must be called after CollectSources()
4164
bool GDALTileIndexDataset::NeedInitBuffer(int nBandCount,
4165
                                          const int *panBandMap) const
4166
0
{
4167
0
    bool bNeedInitBuffer = true;
4168
    // If the last source (that is the most prioritary one) covers at least
4169
    // the window of interest and is fully opaque, then we don't need to
4170
    // initialize the buffer, and can directly render that source.
4171
0
    int bHasNoData = false;
4172
0
    if (!m_aoSourceDesc.empty() && m_aoSourceDesc.back().bCoversWholeAOI &&
4173
0
        (!m_aoSourceDesc.back().bHasNoData ||
4174
         // Also, if there's a single source and that the VRT bands and the
4175
         // source bands have the same nodata value, we can skip initialization.
4176
0
         (m_aoSourceDesc.size() == 1 && m_aoSourceDesc.back().bSameNoData &&
4177
0
          m_bSameNoData && m_bSameDataType &&
4178
0
          IsSameNaNAware(papoBands[0]->GetNoDataValue(&bHasNoData),
4179
0
                         m_aoSourceDesc.back().dfSameNoData) &&
4180
0
          bHasNoData)) &&
4181
0
        (!m_aoSourceDesc.back().poMaskBand ||
4182
         // Also, if there's a single source that has a mask band, and the VRT
4183
         // bands have no-nodata or a 0-nodata value, we can skip
4184
         // initialization.
4185
0
         (m_aoSourceDesc.size() == 1 && m_bSameDataType &&
4186
0
          !(nBandCount == 1 && panBandMap[0] == 0) && m_bSameNoData &&
4187
0
          papoBands[0]->GetNoDataValue(&bHasNoData) == 0)))
4188
0
    {
4189
0
        bNeedInitBuffer = false;
4190
0
    }
4191
0
    return bNeedInitBuffer;
4192
0
}
4193
4194
/************************************************************************/
4195
/*                            InitBuffer()                              */
4196
/************************************************************************/
4197
4198
void GDALTileIndexDataset::InitBuffer(void *pData, int nBufXSize, int nBufYSize,
4199
                                      GDALDataType eBufType, int nBandCount,
4200
                                      const int *panBandMap,
4201
                                      GSpacing nPixelSpace, GSpacing nLineSpace,
4202
                                      GSpacing nBandSpace) const
4203
0
{
4204
0
    const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
4205
0
    if (m_bSameNoData && nBandCount > 1 &&
4206
0
        ((nPixelSpace == nBufTypeSize &&
4207
0
          nLineSpace == nBufXSize * nPixelSpace &&
4208
0
          nBandSpace == nBufYSize * nLineSpace) ||
4209
0
         (nBandSpace == nBufTypeSize &&
4210
0
          nPixelSpace == nBandCount * nBandSpace &&
4211
0
          nLineSpace == nBufXSize * nPixelSpace)))
4212
0
    {
4213
0
        const int nBandNr = panBandMap[0];
4214
0
        auto poVRTBand =
4215
0
            nBandNr == 0
4216
0
                ? m_poMaskBand.get()
4217
0
                : cpl::down_cast<GDALTileIndexBand *>(papoBands[nBandNr - 1]);
4218
0
        CPLAssert(poVRTBand);
4219
0
        const double dfNoData = poVRTBand->m_dfNoDataValue;
4220
0
        if (dfNoData == 0.0)
4221
0
        {
4222
0
            memset(pData, 0,
4223
0
                   static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
4224
0
                       nBufTypeSize);
4225
0
        }
4226
0
        else
4227
0
        {
4228
0
            GDALCopyWords64(
4229
0
                &dfNoData, GDT_Float64, 0, pData, eBufType, nBufTypeSize,
4230
0
                static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount);
4231
0
        }
4232
0
    }
4233
0
    else
4234
0
    {
4235
0
        for (int i = 0; i < nBandCount; ++i)
4236
0
        {
4237
0
            const int nBandNr = panBandMap[i];
4238
0
            auto poVRTBand = nBandNr == 0 ? m_poMaskBand.get()
4239
0
                                          : cpl::down_cast<GDALTileIndexBand *>(
4240
0
                                                papoBands[nBandNr - 1]);
4241
0
            GByte *pabyBandData = static_cast<GByte *>(pData) + i * nBandSpace;
4242
0
            if (nPixelSpace == nBufTypeSize &&
4243
0
                poVRTBand->m_dfNoDataValue == 0.0)
4244
0
            {
4245
0
                if (nLineSpace == nBufXSize * nPixelSpace)
4246
0
                {
4247
0
                    memset(pabyBandData, 0,
4248
0
                           static_cast<size_t>(nBufYSize * nLineSpace));
4249
0
                }
4250
0
                else
4251
0
                {
4252
0
                    for (int iLine = 0; iLine < nBufYSize; iLine++)
4253
0
                    {
4254
0
                        memset(static_cast<GByte *>(pabyBandData) +
4255
0
                                   static_cast<GIntBig>(iLine) * nLineSpace,
4256
0
                               0, static_cast<size_t>(nBufXSize * nPixelSpace));
4257
0
                    }
4258
0
                }
4259
0
            }
4260
0
            else
4261
0
            {
4262
0
                double dfWriteValue = poVRTBand->m_dfNoDataValue;
4263
4264
0
                for (int iLine = 0; iLine < nBufYSize; iLine++)
4265
0
                {
4266
0
                    GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
4267
0
                                  static_cast<GByte *>(pabyBandData) +
4268
0
                                      static_cast<GIntBig>(nLineSpace) * iLine,
4269
0
                                  eBufType, static_cast<int>(nPixelSpace),
4270
0
                                  nBufXSize);
4271
0
                }
4272
0
            }
4273
0
        }
4274
0
    }
4275
0
}
4276
4277
/************************************************************************/
4278
/*                            RenderSource()                            */
4279
/************************************************************************/
4280
4281
CPLErr GDALTileIndexDataset::RenderSource(
4282
    const SourceDesc &oSourceDesc, bool bNeedInitBuffer, int nBandNrMax,
4283
    int nXOff, int nYOff, int nXSize, int nYSize, double dfXOff, double dfYOff,
4284
    double dfXSize, double dfYSize, int nBufXSize, int nBufYSize, void *pData,
4285
    GDALDataType eBufType, int nBandCount, BANDMAP_TYPE panBandMap,
4286
    GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace,
4287
    GDALRasterIOExtraArg *psExtraArg,
4288
    VRTSource::WorkingState &oWorkingState) const
4289
0
{
4290
0
    auto &poTileDS = oSourceDesc.poDS;
4291
0
    auto &poSource = oSourceDesc.poSource;
4292
0
    auto poComplexSource = dynamic_cast<VRTComplexSource *>(poSource.get());
4293
0
    CPLErr eErr = CE_None;
4294
4295
0
    if (poTileDS->GetRasterCount() + 1 == nBandNrMax &&
4296
0
        papoBands[nBandNrMax - 1]->GetColorInterpretation() == GCI_AlphaBand &&
4297
0
        papoBands[nBandNrMax - 1]->GetRasterDataType() == GDT_Byte)
4298
0
    {
4299
        // Special case when there's typically a mix of RGB and RGBA source
4300
        // datasets and we read a RGB one.
4301
0
        for (int iBand = 0; iBand < nBandCount && eErr == CE_None; ++iBand)
4302
0
        {
4303
0
            const int nBandNr = panBandMap[iBand];
4304
0
            if (nBandNr == nBandNrMax)
4305
0
            {
4306
                // The window we will actually request from the source raster band.
4307
0
                double dfReqXOff = 0.0;
4308
0
                double dfReqYOff = 0.0;
4309
0
                double dfReqXSize = 0.0;
4310
0
                double dfReqYSize = 0.0;
4311
0
                int nReqXOff = 0;
4312
0
                int nReqYOff = 0;
4313
0
                int nReqXSize = 0;
4314
0
                int nReqYSize = 0;
4315
4316
                // The window we will actual set _within_ the pData buffer.
4317
0
                int nOutXOff = 0;
4318
0
                int nOutYOff = 0;
4319
0
                int nOutXSize = 0;
4320
0
                int nOutYSize = 0;
4321
4322
0
                bool bError = false;
4323
4324
0
                auto poTileBand = poTileDS->GetRasterBand(1);
4325
0
                poSource->SetRasterBand(poTileBand, false);
4326
0
                if (poSource->GetSrcDstWindow(
4327
0
                        dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
4328
0
                        &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
4329
0
                        &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
4330
0
                        &nOutYOff, &nOutXSize, &nOutYSize, bError))
4331
0
                {
4332
0
                    GByte *pabyOut =
4333
0
                        static_cast<GByte *>(pData) +
4334
0
                        static_cast<GPtrDiff_t>(iBand * nBandSpace +
4335
0
                                                nOutXOff * nPixelSpace +
4336
0
                                                nOutYOff * nLineSpace);
4337
4338
0
                    constexpr GByte n255 = 255;
4339
0
                    for (int iY = 0; iY < nOutYSize; iY++)
4340
0
                    {
4341
0
                        GDALCopyWords(
4342
0
                            &n255, GDT_Byte, 0,
4343
0
                            pabyOut + static_cast<GPtrDiff_t>(iY * nLineSpace),
4344
0
                            eBufType, static_cast<int>(nPixelSpace), nOutXSize);
4345
0
                    }
4346
0
                }
4347
0
            }
4348
0
            else
4349
0
            {
4350
0
                auto poTileBand = poTileDS->GetRasterBand(nBandNr);
4351
0
                if (poComplexSource)
4352
0
                {
4353
0
                    int bHasNoData = false;
4354
0
                    const double dfNoDataValue =
4355
0
                        poTileBand->GetNoDataValue(&bHasNoData);
4356
0
                    poComplexSource->SetNoDataValue(
4357
0
                        bHasNoData ? dfNoDataValue : VRT_NODATA_UNSET);
4358
0
                }
4359
0
                poSource->SetRasterBand(poTileBand, false);
4360
4361
0
                GDALRasterIOExtraArg sExtraArg;
4362
0
                INIT_RASTERIO_EXTRA_ARG(sExtraArg);
4363
0
                if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
4364
0
                {
4365
                    // cppcheck-suppress redundantAssignment
4366
0
                    sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
4367
0
                }
4368
0
                else
4369
0
                {
4370
                    // cppcheck-suppress redundantAssignment
4371
0
                    sExtraArg.eResampleAlg = m_eResampling;
4372
0
                }
4373
4374
0
                GByte *pabyBandData =
4375
0
                    static_cast<GByte *>(pData) + iBand * nBandSpace;
4376
0
                eErr = poSource->RasterIO(
4377
0
                    poTileBand->GetRasterDataType(), nXOff, nYOff, nXSize,
4378
0
                    nYSize, pabyBandData, nBufXSize, nBufYSize, eBufType,
4379
0
                    nPixelSpace, nLineSpace, &sExtraArg, oWorkingState);
4380
0
            }
4381
0
        }
4382
0
        return eErr;
4383
0
    }
4384
0
    else if (poTileDS->GetRasterCount() < nBandNrMax)
4385
0
    {
4386
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s has not enough bands.",
4387
0
                 oSourceDesc.osName.c_str());
4388
0
        return CE_Failure;
4389
0
    }
4390
4391
0
    if ((oSourceDesc.poMaskBand && bNeedInitBuffer) || nBandNrMax == 0)
4392
0
    {
4393
        // The window we will actually request from the source raster band.
4394
0
        double dfReqXOff = 0.0;
4395
0
        double dfReqYOff = 0.0;
4396
0
        double dfReqXSize = 0.0;
4397
0
        double dfReqYSize = 0.0;
4398
0
        int nReqXOff = 0;
4399
0
        int nReqYOff = 0;
4400
0
        int nReqXSize = 0;
4401
0
        int nReqYSize = 0;
4402
4403
        // The window we will actual set _within_ the pData buffer.
4404
0
        int nOutXOff = 0;
4405
0
        int nOutYOff = 0;
4406
0
        int nOutXSize = 0;
4407
0
        int nOutYSize = 0;
4408
4409
0
        bool bError = false;
4410
4411
0
        auto poFirstTileBand = poTileDS->GetRasterBand(1);
4412
0
        poSource->SetRasterBand(poFirstTileBand, false);
4413
0
        if (poSource->GetSrcDstWindow(
4414
0
                dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
4415
0
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
4416
0
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
4417
0
                &nOutXSize, &nOutYSize, bError))
4418
0
        {
4419
0
            int iMaskBandIdx = -1;
4420
0
            if (eBufType == GDT_Byte && nBandNrMax == 0)
4421
0
            {
4422
                // when called from m_poMaskBand
4423
0
                iMaskBandIdx = 0;
4424
0
            }
4425
0
            else if (oSourceDesc.poMaskBand)
4426
0
            {
4427
                // If we request a Byte buffer and the mask band is actually
4428
                // one of the queried bands of this request, we can save
4429
                // requesting it separately.
4430
0
                const int nMaskBandNr = oSourceDesc.poMaskBand->GetBand();
4431
0
                if (eBufType == GDT_Byte && nMaskBandNr >= 1 &&
4432
0
                    nMaskBandNr <= poTileDS->GetRasterCount() &&
4433
0
                    poTileDS->GetRasterBand(nMaskBandNr) ==
4434
0
                        oSourceDesc.poMaskBand)
4435
0
                {
4436
0
                    for (int iBand = 0; iBand < nBandCount; ++iBand)
4437
0
                    {
4438
0
                        if (panBandMap[iBand] == nMaskBandNr)
4439
0
                        {
4440
0
                            iMaskBandIdx = iBand;
4441
0
                            break;
4442
0
                        }
4443
0
                    }
4444
0
                }
4445
0
            }
4446
4447
0
            GDALRasterIOExtraArg sExtraArg;
4448
0
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
4449
0
            if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
4450
0
            {
4451
                // cppcheck-suppress redundantAssignment
4452
0
                sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
4453
0
            }
4454
0
            else
4455
0
            {
4456
                // cppcheck-suppress redundantAssignment
4457
0
                sExtraArg.eResampleAlg = m_eResampling;
4458
0
            }
4459
0
            sExtraArg.bFloatingPointWindowValidity = TRUE;
4460
0
            sExtraArg.dfXOff = dfReqXOff;
4461
0
            sExtraArg.dfYOff = dfReqYOff;
4462
0
            sExtraArg.dfXSize = dfReqXSize;
4463
0
            sExtraArg.dfYSize = dfReqYSize;
4464
4465
0
            if (iMaskBandIdx < 0 && oSourceDesc.abyMask.empty() &&
4466
0
                oSourceDesc.poMaskBand)
4467
0
            {
4468
                // Fetch the mask band
4469
0
                try
4470
0
                {
4471
0
                    oSourceDesc.abyMask.resize(static_cast<size_t>(nOutXSize) *
4472
0
                                               nOutYSize);
4473
0
                }
4474
0
                catch (const std::bad_alloc &)
4475
0
                {
4476
0
                    CPLError(CE_Failure, CPLE_OutOfMemory,
4477
0
                             "Cannot allocate working buffer for mask");
4478
0
                    return CE_Failure;
4479
0
                }
4480
4481
0
                if (oSourceDesc.poMaskBand->RasterIO(
4482
0
                        GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
4483
0
                        oSourceDesc.abyMask.data(), nOutXSize, nOutYSize,
4484
0
                        GDT_Byte, 0, 0, &sExtraArg) != CE_None)
4485
0
                {
4486
0
                    oSourceDesc.abyMask.clear();
4487
0
                    return CE_Failure;
4488
0
                }
4489
0
            }
4490
4491
            // Allocate a temporary contiguous buffer to receive pixel data
4492
0
            const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
4493
0
            const size_t nWorkBufferBandSize =
4494
0
                static_cast<size_t>(nOutXSize) * nOutYSize * nBufTypeSize;
4495
0
            std::vector<GByte> abyWorkBuffer;
4496
0
            try
4497
0
            {
4498
0
                abyWorkBuffer.resize(nBandCount * nWorkBufferBandSize);
4499
0
            }
4500
0
            catch (const std::bad_alloc &)
4501
0
            {
4502
0
                CPLError(CE_Failure, CPLE_OutOfMemory,
4503
0
                         "Cannot allocate working buffer");
4504
0
                return CE_Failure;
4505
0
            }
4506
4507
0
            const GByte *const pabyMask =
4508
0
                iMaskBandIdx >= 0
4509
0
                    ? abyWorkBuffer.data() + iMaskBandIdx * nWorkBufferBandSize
4510
0
                    : oSourceDesc.abyMask.data();
4511
4512
0
            if (nBandNrMax == 0)
4513
0
            {
4514
                // Special case when called from m_poMaskBand
4515
0
                if (poTileDS->GetRasterBand(1)->GetMaskBand()->RasterIO(
4516
0
                        GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
4517
0
                        abyWorkBuffer.data(), nOutXSize, nOutYSize, eBufType, 0,
4518
0
                        0, &sExtraArg) != CE_None)
4519
0
                {
4520
0
                    return CE_Failure;
4521
0
                }
4522
0
            }
4523
0
            else if (poTileDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
4524
0
                                        nReqYSize, abyWorkBuffer.data(),
4525
0
                                        nOutXSize, nOutYSize, eBufType,
4526
0
                                        nBandCount, panBandMap, 0, 0, 0,
4527
0
                                        &sExtraArg) != CE_None)
4528
0
            {
4529
0
                return CE_Failure;
4530
0
            }
4531
4532
            // Compose the temporary contiguous buffer into the target
4533
            // buffer, taking into account the mask
4534
0
            GByte *pabyOut = static_cast<GByte *>(pData) +
4535
0
                             static_cast<GPtrDiff_t>(nOutXOff * nPixelSpace +
4536
0
                                                     nOutYOff * nLineSpace);
4537
4538
0
            for (int iBand = 0; iBand < nBandCount && eErr == CE_None; ++iBand)
4539
0
            {
4540
0
                GByte *pabyDestBand =
4541
0
                    pabyOut + static_cast<GPtrDiff_t>(iBand * nBandSpace);
4542
0
                const GByte *pabySrc =
4543
0
                    abyWorkBuffer.data() + iBand * nWorkBufferBandSize;
4544
4545
0
                CompositeSrcWithMaskIntoDest(
4546
0
                    nOutXSize, nOutYSize, eBufType, nBufTypeSize, nPixelSpace,
4547
0
                    nLineSpace, pabySrc, pabyMask, pabyDestBand);
4548
0
            }
4549
0
        }
4550
0
    }
4551
0
    else if (m_bSameDataType && !bNeedInitBuffer && oSourceDesc.bHasNoData)
4552
0
    {
4553
        // We create a non-VRTComplexSource SimpleSource copy of poSource
4554
        // to be able to call DatasetRasterIO()
4555
0
        VRTSimpleSource oSimpleSource(poSource.get(), 1.0, 1.0);
4556
4557
0
        GDALRasterIOExtraArg sExtraArg;
4558
0
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
4559
0
        if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
4560
0
        {
4561
            // cppcheck-suppress redundantAssignment
4562
0
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
4563
0
        }
4564
0
        else
4565
0
        {
4566
            // cppcheck-suppress redundantAssignment
4567
0
            sExtraArg.eResampleAlg = m_eResampling;
4568
0
        }
4569
4570
0
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
4571
0
        oSimpleSource.SetRasterBand(poTileBand, false);
4572
0
        eErr = oSimpleSource.DatasetRasterIO(
4573
0
            papoBands[0]->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
4574
0
            pData, nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
4575
0
            nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
4576
0
    }
4577
0
    else if (m_bSameDataType && !poComplexSource)
4578
0
    {
4579
0
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
4580
0
        poSource->SetRasterBand(poTileBand, false);
4581
4582
0
        GDALRasterIOExtraArg sExtraArg;
4583
0
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
4584
0
        if (poTileBand->GetColorTable())
4585
0
        {
4586
            // cppcheck-suppress redundantAssignment
4587
0
            sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
4588
0
        }
4589
0
        else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
4590
0
        {
4591
            // cppcheck-suppress redundantAssignment
4592
0
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
4593
0
        }
4594
0
        else
4595
0
        {
4596
            // cppcheck-suppress redundantAssignment
4597
0
            sExtraArg.eResampleAlg = m_eResampling;
4598
0
        }
4599
4600
0
        eErr = poSource->DatasetRasterIO(
4601
0
            papoBands[0]->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
4602
0
            pData, nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
4603
0
            nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
4604
0
    }
4605
0
    else
4606
0
    {
4607
0
        for (int i = 0; i < nBandCount && eErr == CE_None; ++i)
4608
0
        {
4609
0
            const int nBandNr = panBandMap[i];
4610
0
            GByte *pabyBandData = static_cast<GByte *>(pData) + i * nBandSpace;
4611
0
            auto poTileBand = poTileDS->GetRasterBand(nBandNr);
4612
0
            if (poComplexSource)
4613
0
            {
4614
0
                int bHasNoData = false;
4615
0
                const double dfNoDataValue =
4616
0
                    poTileBand->GetNoDataValue(&bHasNoData);
4617
0
                poComplexSource->SetNoDataValue(bHasNoData ? dfNoDataValue
4618
0
                                                           : VRT_NODATA_UNSET);
4619
0
            }
4620
0
            poSource->SetRasterBand(poTileBand, false);
4621
4622
0
            GDALRasterIOExtraArg sExtraArg;
4623
0
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
4624
0
            if (poTileBand->GetColorTable())
4625
0
            {
4626
                // cppcheck-suppress redundantAssignment
4627
0
                sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
4628
0
            }
4629
0
            else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
4630
0
            {
4631
                // cppcheck-suppress redundantAssignment
4632
0
                sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
4633
0
            }
4634
0
            else
4635
0
            {
4636
                // cppcheck-suppress redundantAssignment
4637
0
                sExtraArg.eResampleAlg = m_eResampling;
4638
0
            }
4639
4640
0
            eErr = poSource->RasterIO(
4641
0
                papoBands[nBandNr - 1]->GetRasterDataType(), nXOff, nYOff,
4642
0
                nXSize, nYSize, pabyBandData, nBufXSize, nBufYSize, eBufType,
4643
0
                nPixelSpace, nLineSpace, &sExtraArg, oWorkingState);
4644
0
        }
4645
0
    }
4646
0
    return eErr;
4647
0
}
4648
4649
/************************************************************************/
4650
/*                             IRasterIO()                              */
4651
/************************************************************************/
4652
4653
CPLErr GDALTileIndexDataset::IRasterIO(
4654
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
4655
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
4656
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
4657
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
4658
0
{
4659
0
    if (eRWFlag != GF_Read)
4660
0
        return CE_Failure;
4661
4662
0
    if (nBufXSize < nXSize && nBufYSize < nYSize && AreOverviewsEnabled())
4663
0
    {
4664
0
        int bTried = FALSE;
4665
0
        const CPLErr eErr = TryOverviewRasterIO(
4666
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
4667
0
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
4668
0
            nBandSpace, psExtraArg, &bTried);
4669
0
        if (bTried)
4670
0
            return eErr;
4671
0
    }
4672
4673
0
    double dfXOff = nXOff;
4674
0
    double dfYOff = nYOff;
4675
0
    double dfXSize = nXSize;
4676
0
    double dfYSize = nYSize;
4677
0
    if (psExtraArg->bFloatingPointWindowValidity)
4678
0
    {
4679
0
        dfXOff = psExtraArg->dfXOff;
4680
0
        dfYOff = psExtraArg->dfYOff;
4681
0
        dfXSize = psExtraArg->dfXSize;
4682
0
        dfYSize = psExtraArg->dfYSize;
4683
0
    }
4684
4685
0
    if (!CollectSources(dfXOff, dfYOff, dfXSize, dfYSize,
4686
0
                        /* bMultiThreadAllowed = */ true))
4687
0
    {
4688
0
        return CE_Failure;
4689
0
    }
4690
4691
    // We might be called with nBandCount == 1 && panBandMap[0] == 0
4692
    // to mean m_poMaskBand
4693
0
    int nBandNrMax = 0;
4694
0
    for (int i = 0; i < nBandCount; ++i)
4695
0
    {
4696
0
        const int nBandNr = panBandMap[i];
4697
0
        nBandNrMax = std::max(nBandNrMax, nBandNr);
4698
0
    }
4699
4700
0
    const bool bNeedInitBuffer =
4701
0
        m_bLastMustUseMultiThreading || NeedInitBuffer(nBandCount, panBandMap);
4702
4703
0
    if (!bNeedInitBuffer)
4704
0
    {
4705
0
        return RenderSource(
4706
0
            m_aoSourceDesc.back(), bNeedInitBuffer, nBandNrMax, nXOff, nYOff,
4707
0
            nXSize, nYSize, dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize,
4708
0
            nBufYSize, pData, eBufType, nBandCount, panBandMap, nPixelSpace,
4709
0
            nLineSpace, nBandSpace, psExtraArg, m_oWorkingState);
4710
0
    }
4711
0
    else
4712
0
    {
4713
0
        InitBuffer(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
4714
0
                   panBandMap, nPixelSpace, nLineSpace, nBandSpace);
4715
4716
0
        if (m_bLastMustUseMultiThreading)
4717
0
        {
4718
0
            CPLErrorAccumulator oErrorAccumulator;
4719
0
            std::atomic<bool> bSuccess = true;
4720
0
            const int nContributingSources =
4721
0
                static_cast<int>(m_aoSourceDesc.size());
4722
0
            CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
4723
0
                std::min(nContributingSources, m_nNumThreads));
4724
0
            const int nThreads =
4725
0
                std::min(nContributingSources, psThreadPool->GetThreadCount());
4726
0
            CPLDebugOnly("GTI",
4727
0
                         "IRasterIO(): use optimized "
4728
0
                         "multi-threaded code path. "
4729
0
                         "Using %d threads",
4730
0
                         nThreads);
4731
4732
0
            {
4733
0
                std::lock_guard oLock(m_oQueueWorkingStates.oMutex);
4734
0
                if (m_oQueueWorkingStates.oStates.size() <
4735
0
                    static_cast<size_t>(nThreads))
4736
0
                {
4737
0
                    m_oQueueWorkingStates.oStates.resize(nThreads);
4738
0
                }
4739
0
                for (int i = 0; i < nThreads; ++i)
4740
0
                {
4741
0
                    if (!m_oQueueWorkingStates.oStates[i])
4742
0
                        m_oQueueWorkingStates.oStates[i] =
4743
0
                            std::make_unique<VRTSource::WorkingState>();
4744
0
                }
4745
0
            }
4746
4747
0
            auto oQueue = psThreadPool->CreateJobQueue();
4748
0
            std::atomic<int> nCompletedJobs = 0;
4749
0
            for (auto &oSourceDesc : m_aoSourceDesc)
4750
0
            {
4751
0
                auto psJob = new RasterIOJob();
4752
0
                psJob->poDS = this;
4753
0
                psJob->pbSuccess = &bSuccess;
4754
0
                psJob->poErrorAccumulator = &oErrorAccumulator;
4755
0
                psJob->pnCompletedJobs = &nCompletedJobs;
4756
0
                psJob->poQueueWorkingStates = &m_oQueueWorkingStates;
4757
0
                psJob->nBandNrMax = nBandNrMax;
4758
0
                psJob->nXOff = nXOff;
4759
0
                psJob->nYOff = nYOff;
4760
0
                psJob->nXSize = nXSize;
4761
0
                psJob->nYSize = nYSize;
4762
0
                psJob->pData = pData;
4763
0
                psJob->nBufXSize = nBufXSize;
4764
0
                psJob->nBufYSize = nBufYSize;
4765
0
                psJob->eBufType = eBufType;
4766
0
                psJob->nBandCount = nBandCount;
4767
0
                psJob->panBandMap = panBandMap;
4768
0
                psJob->nPixelSpace = nPixelSpace;
4769
0
                psJob->nLineSpace = nLineSpace;
4770
0
                psJob->nBandSpace = nBandSpace;
4771
0
                psJob->psExtraArg = psExtraArg;
4772
4773
0
                psJob->osTileName = oSourceDesc.poFeature->GetFieldAsString(
4774
0
                    m_nLocationFieldIndex);
4775
4776
0
                if (!oQueue->SubmitJob(RasterIOJob::Func, psJob))
4777
0
                {
4778
0
                    delete psJob;
4779
0
                    bSuccess = false;
4780
0
                    break;
4781
0
                }
4782
0
            }
4783
4784
0
            while (oQueue->WaitEvent())
4785
0
            {
4786
                // Quite rough progress callback. We could do better by counting
4787
                // the number of contributing pixels.
4788
0
                if (psExtraArg->pfnProgress)
4789
0
                {
4790
0
                    psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
4791
0
                                                nContributingSources,
4792
0
                                            "", psExtraArg->pProgressData);
4793
0
                }
4794
0
            }
4795
4796
0
            oErrorAccumulator.ReplayErrors();
4797
4798
0
            if (bSuccess && psExtraArg->pfnProgress)
4799
0
            {
4800
0
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4801
0
            }
4802
4803
0
            return bSuccess ? CE_None : CE_Failure;
4804
0
        }
4805
0
        else
4806
0
        {
4807
            // Now render from bottom of the stack to top.
4808
0
            for (auto &oSourceDesc : m_aoSourceDesc)
4809
0
            {
4810
0
                if (oSourceDesc.poDS &&
4811
0
                    RenderSource(oSourceDesc, bNeedInitBuffer, nBandNrMax,
4812
0
                                 nXOff, nYOff, nXSize, nYSize, dfXOff, dfYOff,
4813
0
                                 dfXSize, dfYSize, nBufXSize, nBufYSize, pData,
4814
0
                                 eBufType, nBandCount, panBandMap, nPixelSpace,
4815
0
                                 nLineSpace, nBandSpace, psExtraArg,
4816
0
                                 m_oWorkingState) != CE_None)
4817
0
                    return CE_Failure;
4818
0
            }
4819
4820
0
            if (psExtraArg->pfnProgress)
4821
0
            {
4822
0
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4823
0
            }
4824
4825
0
            return CE_None;
4826
0
        }
4827
0
    }
4828
0
}
4829
4830
/************************************************************************/
4831
/*                 GDALTileIndexDataset::RasterIOJob::Func()            */
4832
/************************************************************************/
4833
4834
void GDALTileIndexDataset::RasterIOJob::Func(void *pData)
4835
0
{
4836
0
    auto psJob =
4837
0
        std::unique_ptr<RasterIOJob>(static_cast<RasterIOJob *>(pData));
4838
0
    if (*psJob->pbSuccess)
4839
0
    {
4840
0
        const std::string osTileName(GetAbsoluteFileName(
4841
0
            psJob->osTileName.c_str(), psJob->poDS->GetDescription()));
4842
4843
0
        SourceDesc oSourceDesc;
4844
4845
0
        auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
4846
0
        CPL_IGNORE_RET_VAL(oAccumulator);
4847
4848
0
        const bool bCanOpenSource =
4849
0
            psJob->poDS->GetSourceDesc(osTileName, oSourceDesc,
4850
0
                                       &psJob->poQueueWorkingStates->oMutex) &&
4851
0
            oSourceDesc.poDS;
4852
4853
0
        if (!bCanOpenSource)
4854
0
        {
4855
0
            *psJob->pbSuccess = false;
4856
0
        }
4857
0
        else
4858
0
        {
4859
0
            GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
4860
0
            sArg.pfnProgress = nullptr;
4861
0
            sArg.pProgressData = nullptr;
4862
4863
0
            std::unique_ptr<VRTSource::WorkingState> poWorkingState;
4864
0
            {
4865
0
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
4866
0
                poWorkingState =
4867
0
                    std::move(psJob->poQueueWorkingStates->oStates.back());
4868
0
                psJob->poQueueWorkingStates->oStates.pop_back();
4869
0
                CPLAssert(poWorkingState.get());
4870
0
            }
4871
4872
0
            double dfXOff = psJob->nXOff;
4873
0
            double dfYOff = psJob->nYOff;
4874
0
            double dfXSize = psJob->nXSize;
4875
0
            double dfYSize = psJob->nYSize;
4876
0
            if (psJob->psExtraArg->bFloatingPointWindowValidity)
4877
0
            {
4878
0
                dfXOff = psJob->psExtraArg->dfXOff;
4879
0
                dfYOff = psJob->psExtraArg->dfYOff;
4880
0
                dfXSize = psJob->psExtraArg->dfXSize;
4881
0
                dfYSize = psJob->psExtraArg->dfYSize;
4882
0
            }
4883
4884
0
            const bool bRenderOK =
4885
0
                psJob->poDS->RenderSource(
4886
0
                    oSourceDesc, /*bNeedInitBuffer = */ true, psJob->nBandNrMax,
4887
0
                    psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
4888
0
                    dfXOff, dfYOff, dfXSize, dfYSize, psJob->nBufXSize,
4889
0
                    psJob->nBufYSize, psJob->pData, psJob->eBufType,
4890
0
                    psJob->nBandCount, psJob->panBandMap, psJob->nPixelSpace,
4891
0
                    psJob->nLineSpace, psJob->nBandSpace, &sArg,
4892
0
                    *(poWorkingState.get())) == CE_None;
4893
4894
0
            if (!bRenderOK)
4895
0
            {
4896
0
                *psJob->pbSuccess = false;
4897
0
            }
4898
4899
0
            {
4900
0
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
4901
0
                psJob->poQueueWorkingStates->oStates.push_back(
4902
0
                    std::move(poWorkingState));
4903
0
            }
4904
0
        }
4905
0
    }
4906
4907
0
    ++(*psJob->pnCompletedJobs);
4908
0
}
4909
4910
#ifdef GDAL_ENABLE_ALGORITHMS
4911
4912
/************************************************************************/
4913
/*                     GDALGTICreateAlgorithm                           */
4914
/************************************************************************/
4915
4916
class GDALGTICreateAlgorithm final : public GDALRasterIndexAlgorithm
4917
{
4918
  public:
4919
    static constexpr const char *NAME = "create";
4920
    static constexpr const char *DESCRIPTION =
4921
        "Create an index of raster datasets compatible of the GDAL Tile Index "
4922
        "(GTI) driver.";
4923
    static constexpr const char *HELP_URL =
4924
        "/programs/gdal_driver_gti_create.html";
4925
4926
    GDALGTICreateAlgorithm();
4927
4928
  protected:
4929
    bool AddExtraOptions(CPLStringList &aosOptions) override;
4930
4931
  private:
4932
    std::string m_xmlFilename{};
4933
    std::vector<double> m_resolution{};
4934
    std::vector<double> m_bbox{};
4935
    std::string m_dataType{};
4936
    int m_bandCount = 0;
4937
    std::vector<double> m_nodata{};
4938
    std::vector<std::string> m_colorInterpretation{};
4939
    bool m_mask = false;
4940
    std::vector<std::string> m_fetchedMetadata{};
4941
};
4942
4943
/************************************************************************/
4944
/*          GDALGTICreateAlgorithm::GDALGTICreateAlgorithm()            */
4945
/************************************************************************/
4946
4947
GDALGTICreateAlgorithm::GDALGTICreateAlgorithm()
4948
0
    : GDALRasterIndexAlgorithm(NAME, DESCRIPTION, HELP_URL)
4949
0
{
4950
0
    AddProgressArg();
4951
0
    AddInputDatasetArg(&m_inputDatasets, GDAL_OF_RASTER)
4952
0
        .SetAutoOpenDataset(false);
4953
0
    GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs();
4954
4955
0
    AddCommonOptions();
4956
4957
0
    AddArg("xml-filename", 0,
4958
0
           _("Filename of the XML Virtual Tile Index file to generate, that "
4959
0
             "can be used as an input for the GDAL GTI / Virtual Raster Tile "
4960
0
             "Index driver"),
4961
0
           &m_xmlFilename)
4962
0
        .SetMinCharCount(1);
4963
4964
0
    AddArg("resolution", 0,
4965
0
           _("Resolution (in destination CRS units) of the virtual mosaic"),
4966
0
           &m_resolution)
4967
0
        .SetMinCount(2)
4968
0
        .SetMaxCount(2)
4969
0
        .SetMinValueExcluded(0)
4970
0
        .SetRepeatedArgAllowed(false)
4971
0
        .SetDisplayHintAboutRepetition(false)
4972
0
        .SetMetaVar("<xres>,<yres>");
4973
4974
0
    AddBBOXArg(
4975
0
        &m_bbox,
4976
0
        _("Bounding box (in destination CRS units) of the virtual mosaic"));
4977
0
    AddOutputDataTypeArg(&m_dataType, _("Datatype of the virtual mosaic"));
4978
0
    AddArg("band-count", 0, _("Number of bands of the virtual mosaic"),
4979
0
           &m_bandCount)
4980
0
        .SetMinValueIncluded(1);
4981
0
    AddArg("nodata", 0, _("Nodata value(s) of the bands of the virtual mosaic"),
4982
0
           &m_nodata);
4983
0
    AddArg("color-interpretation", 0,
4984
0
           _("Color interpretation(s) of the bands of the virtual mosaic"),
4985
0
           &m_colorInterpretation)
4986
0
        .SetChoices("red", "green", "blue", "alpha", "gray", "undefined");
4987
0
    AddArg("mask", 0, _("Defines that the virtual mosaic has a mask band"),
4988
0
           &m_mask);
4989
0
    AddArg("fetch-metadata", 0,
4990
0
           _("Fetch a metadata item from source rasters and write it as a "
4991
0
             "field in the index."),
4992
0
           &m_fetchedMetadata)
4993
0
        .SetMetaVar("<gdal-metadata-name>,<field-name>,<field-type>")
4994
0
        .SetPackedValuesAllowed(false)
4995
0
        .AddValidationAction(
4996
0
            [this]()
4997
0
            {
4998
0
                for (const std::string &s : m_fetchedMetadata)
4999
0
                {
5000
0
                    const CPLStringList aosTokens(
5001
0
                        CSLTokenizeString2(s.c_str(), ",", 0));
5002
0
                    if (aosTokens.size() != 3)
5003
0
                    {
5004
0
                        ReportError(
5005
0
                            CE_Failure, CPLE_IllegalArg,
5006
0
                            "'%s' is not of the form "
5007
0
                            "<gdal-metadata-name>,<field-name>,<field-type>",
5008
0
                            s.c_str());
5009
0
                        return false;
5010
0
                    }
5011
0
                    bool ok = false;
5012
0
                    for (const char *type : {"String", "Integer", "Integer64",
5013
0
                                             "Real", "Date", "DateTime"})
5014
0
                    {
5015
0
                        if (EQUAL(aosTokens[2], type))
5016
0
                            ok = true;
5017
0
                    }
5018
0
                    if (!ok)
5019
0
                    {
5020
0
                        ReportError(CE_Failure, CPLE_IllegalArg,
5021
0
                                    "'%s' has an invalid field type '%s'. It "
5022
0
                                    "should be one of 'String', 'Integer', "
5023
0
                                    "'Integer64', 'Real', 'Date', 'DateTime'.",
5024
0
                                    s.c_str(), aosTokens[2]);
5025
0
                        return false;
5026
0
                    }
5027
0
                }
5028
0
                return true;
5029
0
            });
5030
0
}
5031
5032
/************************************************************************/
5033
/*            GDALGTICreateAlgorithm::AddExtraOptions()                 */
5034
/************************************************************************/
5035
5036
bool GDALGTICreateAlgorithm::AddExtraOptions(CPLStringList &aosOptions)
5037
0
{
5038
0
    if (!m_xmlFilename.empty())
5039
0
    {
5040
0
        aosOptions.push_back("-gti_filename");
5041
0
        aosOptions.push_back(m_xmlFilename);
5042
0
    }
5043
0
    if (!m_resolution.empty())
5044
0
    {
5045
0
        aosOptions.push_back("-tr");
5046
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_resolution[0]));
5047
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_resolution[1]));
5048
0
    }
5049
0
    if (!m_bbox.empty())
5050
0
    {
5051
0
        aosOptions.push_back("-te");
5052
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
5053
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
5054
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
5055
0
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
5056
0
    }
5057
0
    if (!m_dataType.empty())
5058
0
    {
5059
0
        aosOptions.push_back("-ot");
5060
0
        aosOptions.push_back(m_dataType);
5061
0
    }
5062
0
    if (m_bandCount > 0)
5063
0
    {
5064
0
        aosOptions.push_back("-bandcount");
5065
0
        aosOptions.push_back(CPLSPrintf("%d", m_bandCount));
5066
5067
0
        if (!m_nodata.empty() && m_nodata.size() != 1 &&
5068
0
            static_cast<int>(m_nodata.size()) != m_bandCount)
5069
0
        {
5070
0
            ReportError(CE_Failure, CPLE_IllegalArg,
5071
0
                        "%d nodata values whereas one or %d were expected",
5072
0
                        static_cast<int>(m_nodata.size()), m_bandCount);
5073
0
            return false;
5074
0
        }
5075
5076
0
        if (!m_colorInterpretation.empty() &&
5077
0
            m_colorInterpretation.size() != 1 &&
5078
0
            static_cast<int>(m_colorInterpretation.size()) != m_bandCount)
5079
0
        {
5080
0
            ReportError(
5081
0
                CE_Failure, CPLE_IllegalArg,
5082
0
                "%d color interpretations whereas one or %d were expected",
5083
0
                static_cast<int>(m_colorInterpretation.size()), m_bandCount);
5084
0
            return false;
5085
0
        }
5086
0
    }
5087
0
    if (!m_nodata.empty())
5088
0
    {
5089
0
        std::string val;
5090
0
        for (double v : m_nodata)
5091
0
        {
5092
0
            if (!val.empty())
5093
0
                val += ',';
5094
0
            val += CPLSPrintf("%.17g", v);
5095
0
        }
5096
0
        aosOptions.push_back("-nodata");
5097
0
        aosOptions.push_back(val);
5098
0
    }
5099
0
    if (!m_colorInterpretation.empty())
5100
0
    {
5101
0
        std::string val;
5102
0
        for (const std::string &s : m_colorInterpretation)
5103
0
        {
5104
0
            if (!val.empty())
5105
0
                val += ',';
5106
0
            val += s;
5107
0
        }
5108
0
        aosOptions.push_back("-colorinterp");
5109
0
        aosOptions.push_back(val);
5110
0
    }
5111
0
    if (m_mask)
5112
0
        aosOptions.push_back("-mask");
5113
0
    for (const std::string &s : m_fetchedMetadata)
5114
0
    {
5115
0
        aosOptions.push_back("-fetch_md");
5116
0
        const CPLStringList aosTokens(CSLTokenizeString2(s.c_str(), ",", 0));
5117
0
        for (const char *token : aosTokens)
5118
0
        {
5119
0
            aosOptions.push_back(token);
5120
0
        }
5121
0
    }
5122
0
    return true;
5123
0
}
5124
5125
/************************************************************************/
5126
/*                 GDALTileIndexInstantiateAlgorithm()                  */
5127
/************************************************************************/
5128
5129
static GDALAlgorithm *
5130
GDALTileIndexInstantiateAlgorithm(const std::vector<std::string> &aosPath)
5131
0
{
5132
0
    if (aosPath.size() == 1 && aosPath[0] == "create")
5133
0
    {
5134
0
        return std::make_unique<GDALGTICreateAlgorithm>().release();
5135
0
    }
5136
0
    else
5137
0
    {
5138
0
        return nullptr;
5139
0
    }
5140
0
}
5141
5142
#endif
5143
5144
/************************************************************************/
5145
/*                         GDALRegister_GTI()                           */
5146
/************************************************************************/
5147
5148
void GDALRegister_GTI()
5149
22
{
5150
22
    if (GDALGetDriverByName("GTI") != nullptr)
5151
0
        return;
5152
5153
22
    auto poDriver = std::make_unique<GDALDriver>();
5154
5155
22
    poDriver->SetDescription("GTI");
5156
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
5157
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GDAL Raster Tile Index");
5158
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gti.gpkg gti.fgb gti");
5159
22
    poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, GTI_PREFIX);
5160
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gti.html");
5161
5162
22
    poDriver->pfnOpen = GDALTileIndexDatasetOpen;
5163
22
    poDriver->pfnIdentify = GDALTileIndexDatasetIdentify;
5164
5165
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
5166
5167
22
    poDriver->SetMetadataItem(
5168
22
        GDAL_DMD_OPENOPTIONLIST,
5169
22
        "<OpenOptionList>"
5170
22
        "  <Option name='LAYER' type='string'/>"
5171
22
        "  <Option name='SQL' type='string'/>"
5172
22
        "  <Option name='SPATIAL_SQL' type='string'/>"
5173
22
        "  <Option name='LOCATION_FIELD' type='string'/>"
5174
22
        "  <Option name='SORT_FIELD' type='string'/>"
5175
22
        "  <Option name='SORT_FIELD_ASC' type='boolean'/>"
5176
22
        "  <Option name='FILTER' type='string'/>"
5177
22
        "  <Option name='SRS' type='string'/>"
5178
22
        "  <Option name='RESX' type='float'/>"
5179
22
        "  <Option name='RESY' type='float'/>"
5180
22
        "  <Option name='MINX' type='float'/>"
5181
22
        "  <Option name='MINY' type='float'/>"
5182
22
        "  <Option name='MAXX' type='float'/>"
5183
22
        "  <Option name='MAXY' type='float'/>"
5184
22
        "<Option name='NUM_THREADS' type='string' description="
5185
22
        "'Number of worker threads for reading. Can be set to ALL_CPUS' "
5186
22
        "default='ALL_CPUS'/>"
5187
22
        "</OpenOptionList>");
5188
5189
22
#ifdef GDAL_ENABLE_ALGORITHMS
5190
22
    poDriver->DeclareAlgorithm({"create"});
5191
22
    poDriver->pfnInstantiateAlgorithm = GDALTileIndexInstantiateAlgorithm;
5192
22
#endif
5193
5194
#ifdef BUILT_AS_PLUGIN
5195
    // Used by gdaladdo and test_gdaladdo.py
5196
    poDriver->SetMetadataItem("IS_PLUGIN", "YES");
5197
#endif
5198
5199
22
    GetGDALDriverManager()->RegisterDriver(poDriver.release());
5200
22
}