• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15171299595

21 May 2025 07:51PM UTC coverage: 70.949% (+0.03%) from 70.921%
15171299595

Pull #12392

github

web-flow
Merge 405a716f5 into ce2393a45
Pull Request #12392: GDALOverviews: Limit external file size in GDALRegenerateOverviewsMultiBand

174 of 189 new or added lines in 2 files covered. (92.06%)

22859 existing lines in 88 files now uncovered.

568261 of 800943 relevant lines covered (70.95%)

235911.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.52
/frmts/gti/gdaltileindexdataset.cpp
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_proxy.h"
37
#include "gdal_thread_pool.h"
38
#include "gdal_utils.h"
39

40
#include "gdalalg_raster_index.h"
41

42
#ifdef USE_NEON_OPTIMIZATIONS
43
#define USE_SSE2_OPTIM
44
#define USE_SSE41_OPTIM
45
#include "include_sse2neon.h"
46
#elif defined(__SSE2__) || defined(_M_X64)
47
#define USE_SSE2_OPTIM
48
#include <emmintrin.h>
49
// MSVC doesn't define __SSE4_1__, but if -arch:AVX2 is enabled, we do have SSE4.1
50
#if defined(__SSE4_1__) || defined(__AVX2__)
51
#define USE_SSE41_OPTIM
52
#include <smmintrin.h>
53
#endif
54
#endif
55

56
#ifndef _
57
#define _(x) (x)
58
#endif
59

60
// Semantincs of indices of a GeoTransform (double[6]) matrix
61
constexpr int GT_TOPLEFT_X = 0;
62
constexpr int GT_WE_RES = 1;
63
constexpr int GT_ROTATION_PARAM1 = 2;
64
constexpr int GT_TOPLEFT_Y = 3;
65
constexpr int GT_ROTATION_PARAM2 = 4;
66
constexpr int GT_NS_RES = 5;
67

68
constexpr const char *GTI_PREFIX = "GTI:";
69

70
constexpr const char *MD_DS_TILE_INDEX_LAYER = "TILE_INDEX_LAYER";
71

72
constexpr const char *MD_RESX = "RESX";
73
constexpr const char *MD_RESY = "RESY";
74
constexpr const char *MD_BAND_COUNT = "BAND_COUNT";
75
constexpr const char *MD_DATA_TYPE = "DATA_TYPE";
76
constexpr const char *MD_NODATA = "NODATA";
77
constexpr const char *MD_MINX = "MINX";
78
constexpr const char *MD_MINY = "MINY";
79
constexpr const char *MD_MAXX = "MAXX";
80
constexpr const char *MD_MAXY = "MAXY";
81
constexpr const char *MD_GEOTRANSFORM = "GEOTRANSFORM";
82
constexpr const char *MD_XSIZE = "XSIZE";
83
constexpr const char *MD_YSIZE = "YSIZE";
84
constexpr const char *MD_COLOR_INTERPRETATION = "COLOR_INTERPRETATION";
85
constexpr const char *MD_SRS = "SRS";
86
constexpr const char *MD_LOCATION_FIELD = "LOCATION_FIELD";
87
constexpr const char *MD_SORT_FIELD = "SORT_FIELD";
88
constexpr const char *MD_SORT_FIELD_ASC = "SORT_FIELD_ASC";
89
constexpr const char *MD_BLOCK_X_SIZE = "BLOCKXSIZE";
90
constexpr const char *MD_BLOCK_Y_SIZE = "BLOCKYSIZE";
91
constexpr const char *MD_MASK_BAND = "MASK_BAND";
92
constexpr const char *MD_RESAMPLING = "RESAMPLING";
93

94
constexpr const char *const apszTIOptions[] = {MD_RESX,
95
                                               MD_RESY,
96
                                               MD_BAND_COUNT,
97
                                               MD_DATA_TYPE,
98
                                               MD_NODATA,
99
                                               MD_MINX,
100
                                               MD_MINY,
101
                                               MD_MAXX,
102
                                               MD_MAXY,
103
                                               MD_GEOTRANSFORM,
104
                                               MD_XSIZE,
105
                                               MD_YSIZE,
106
                                               MD_COLOR_INTERPRETATION,
107
                                               MD_SRS,
108
                                               MD_LOCATION_FIELD,
109
                                               MD_SORT_FIELD,
110
                                               MD_SORT_FIELD_ASC,
111
                                               MD_BLOCK_X_SIZE,
112
                                               MD_BLOCK_Y_SIZE,
113
                                               MD_MASK_BAND,
114
                                               MD_RESAMPLING};
115

116
constexpr const char *const MD_BAND_OFFSET = "OFFSET";
117
constexpr const char *const MD_BAND_SCALE = "SCALE";
118
constexpr const char *const MD_BAND_UNITTYPE = "UNITTYPE";
119
constexpr const char *const apszReservedBandItems[] = {
120
    MD_BAND_OFFSET, MD_BAND_SCALE, MD_BAND_UNITTYPE};
121

122
constexpr const char *GTI_XML_BANDCOUNT = "BandCount";
123
constexpr const char *GTI_XML_DATATYPE = "DataType";
124
constexpr const char *GTI_XML_NODATAVALUE = "NoDataValue";
125
constexpr const char *GTI_XML_COLORINTERP = "ColorInterp";
126
constexpr const char *GTI_XML_LOCATIONFIELD = "LocationField";
127
constexpr const char *GTI_XML_SORTFIELD = "SortField";
128
constexpr const char *GTI_XML_SORTFIELDASC = "SortFieldAsc";
129
constexpr const char *GTI_XML_MASKBAND = "MaskBand";
130
constexpr const char *GTI_XML_OVERVIEW_ELEMENT = "Overview";
131
constexpr const char *GTI_XML_OVERVIEW_DATASET = "Dataset";
132
constexpr const char *GTI_XML_OVERVIEW_LAYER = "Layer";
133
constexpr const char *GTI_XML_OVERVIEW_FACTOR = "Factor";
134

135
constexpr const char *GTI_XML_BAND_ELEMENT = "Band";
136
constexpr const char *GTI_XML_BAND_NUMBER = "band";
137
constexpr const char *GTI_XML_BAND_DATATYPE = "dataType";
138
constexpr const char *GTI_XML_BAND_DESCRIPTION = "Description";
139
constexpr const char *GTI_XML_BAND_OFFSET = "Offset";
140
constexpr const char *GTI_XML_BAND_SCALE = "Scale";
141
constexpr const char *GTI_XML_BAND_NODATAVALUE = "NoDataValue";
142
constexpr const char *GTI_XML_BAND_UNITTYPE = "UnitType";
143
constexpr const char *GTI_XML_BAND_COLORINTERP = "ColorInterp";
144
constexpr const char *GTI_XML_CATEGORYNAMES = "CategoryNames";
145
constexpr const char *GTI_XML_COLORTABLE = "ColorTable";
146
constexpr const char *GTI_XML_RAT = "GDALRasterAttributeTable";
147

148
/************************************************************************/
149
/*                           ENDS_WITH_CI()                             */
150
/************************************************************************/
151

152
static inline bool ENDS_WITH_CI(const char *a, const char *b)
60,751✔
153
{
154
    return strlen(a) >= strlen(b) && EQUAL(a + strlen(a) - strlen(b), b);
60,751✔
155
}
156

157
/************************************************************************/
158
/*                       GDALTileIndexDataset                           */
159
/************************************************************************/
160

161
class GDALTileIndexBand;
162

163
class GDALTileIndexDataset final : public GDALPamDataset
164
{
165
  public:
166
    GDALTileIndexDataset();
167
    ~GDALTileIndexDataset() override;
168

169
    bool Open(GDALOpenInfo *poOpenInfo);
170

171
    CPLErr FlushCache(bool bAtClosing) override;
172

173
    CPLErr GetGeoTransform(double *padfGeoTransform) override;
174
    const OGRSpatialReference *GetSpatialRef() const override;
175

176
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
177
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
178
                     GDALDataType eBufType, int nBandCount,
179
                     BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
180
                     GSpacing nLineSpace, GSpacing nBandSpace,
181
                     GDALRasterIOExtraArg *psExtraArg) override;
182

183
    const char *GetMetadataItem(const char *pszName,
184
                                const char *pszDomain) override;
185
    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
186
                           const char *pszDomain) override;
187
    CPLErr SetMetadata(char **papszMD, const char *pszDomain) override;
188

189
    void LoadOverviews();
190

191
    std::vector<GTISourceDesc> GetSourcesMoreRecentThan(int64_t mTime);
192

193
  private:
194
    friend class GDALTileIndexBand;
195

196
    //! Optional GTI XML
197
    CPLXMLTreeCloser m_psXMLTree{nullptr};
198

199
    //! Whether the GTI XML might be modified (by SetMetadata/SetMetadataItem)
200
    bool m_bXMLUpdatable = false;
201

202
    //! Whether the GTI XML has been modified (by SetMetadata/SetMetadataItem)
203
    bool m_bXMLModified = false;
204

205
    //! Unique string (without the process) for this tile index. Passed to
206
    //! GDALProxyPoolDataset to ensure that sources are unique for a given owner
207
    const std::string m_osUniqueHandle;
208

209
    //! Vector dataset with the sources
210
    std::unique_ptr<GDALDataset> m_poVectorDS{};
211

212
    //! Vector layer with the sources
213
    OGRLayer *m_poLayer = nullptr;
214

215
    //! When the SRS of m_poLayer is not the one we expose
216
    std::unique_ptr<OGRLayer> m_poWarpedLayerKeeper{};
217

218
    //! Geotransform matrix of the tile index
219
    std::array<double, 6> m_adfGeoTransform{0, 0, 0, 0, 0, 0};
220

221
    //! Index of the "location" (or alternate name given by user) field
222
    //! (within m_poLayer->GetLayerDefn()), that contain source dataset names.
223
    int m_nLocationFieldIndex = -1;
224

225
    //! SRS of the tile index.
226
    OGRSpatialReference m_oSRS{};
227

228
    //! Cache from dataset name to dataset handle.
229
    //! Note that the dataset objects are ultimately GDALProxyPoolDataset,
230
    //! and that the GDALProxyPoolDataset limits the number of simultaneously
231
    //! opened real datasets (controlled by GDAL_MAX_DATASET_POOL_SIZE). Hence 500 is not too big.
232
    lru11::Cache<std::string, std::shared_ptr<GDALDataset>> m_oMapSharedSources{
233
        500};
234

235
    //! Mask band (e.g. for JPEG compressed + mask band)
236
    std::unique_ptr<GDALTileIndexBand> m_poMaskBand{};
237

238
    //! Whether all bands of the tile index have the same data type.
239
    bool m_bSameDataType = true;
240

241
    //! Whether all bands of the tile index have the same nodata value.
242
    bool m_bSameNoData = true;
243

244
    //! Minimum X of the current pixel request, in georeferenced units.
245
    double m_dfLastMinXFilter = std::numeric_limits<double>::quiet_NaN();
246

247
    //! Minimum Y of the current pixel request, in georeferenced units.
248
    double m_dfLastMinYFilter = std::numeric_limits<double>::quiet_NaN();
249

250
    //! Maximum X of the current pixel request, in georeferenced units.
251
    double m_dfLastMaxXFilter = std::numeric_limits<double>::quiet_NaN();
252

253
    //! Maximum Y of the current pixel request, in georeferenced units.
254
    double m_dfLastMaxYFilter = std::numeric_limits<double>::quiet_NaN();
255

256
    //! Index of the field (within m_poLayer->GetLayerDefn()) used to sort, or -1 if none.
257
    int m_nSortFieldIndex = -1;
258

259
    //! Whether sorting must be ascending (true) or descending (false).
260
    bool m_bSortFieldAsc = true;
261

262
    //! Resampling method by default for warping or when a source has not
263
    //! the same resolution as the tile index.
264
    std::string m_osResampling = "near";
265
    GDALRIOResampleAlg m_eResampling = GRIORA_NearestNeighbour;
266

267
    //! WKT2 representation of the tile index SRS (if needed, typically for on-the-fly warping).
268
    std::string m_osWKT{};
269

270
    //! Whether we had to open of the sources at tile index opening.
271
    bool m_bScannedOneFeatureAtOpening = false;
272

273
    //! Array of overview descriptors.
274
    //! Each descriptor is a tuple (dataset_name, concatenated_open_options, layer_name, overview_factor).
275
    std::vector<std::tuple<std::string, CPLStringList, std::string, double>>
276
        m_aoOverviewDescriptor{};
277

278
    //! Array of overview datasets.
279
    std::vector<std::unique_ptr<GDALDataset>> m_apoOverviews{};
280

281
    //! Cache of buffers used by VRTComplexSource to avoid memory reallocation.
282
    VRTSource::WorkingState m_oWorkingState{};
283

284
    //! Used by IRasterIO() when using multi-threading
285
    struct QueueWorkingStates
286
    {
287
        std::mutex oMutex{};
288
        std::vector<std::unique_ptr<VRTSource::WorkingState>> oStates{};
289
    };
290

291
    //! Used by IRasterIO() when using multi-threading
292
    QueueWorkingStates m_oQueueWorkingStates{};
293

294
    //! Structure describing one of the source raster in the tile index.
295
    struct SourceDesc
296
    {
297
        //! Source dataset name.
298
        std::string osName{};
299

300
        //! Source dataset handle.
301
        std::shared_ptr<GDALDataset> poDS{};
302

303
        //! VRTSimpleSource or VRTComplexSource for the source.
304
        std::unique_ptr<VRTSimpleSource> poSource{};
305

306
        //! OGRFeature corresponding to the source in the tile index.
307
        std::unique_ptr<OGRFeature> poFeature{};
308

309
        //! Work buffer containing the value of the mask band for the current pixel query.
310
        mutable std::vector<GByte> abyMask{};
311

312
        //! Whether the source covers the whole area of interest of the current pixel query.
313
        bool bCoversWholeAOI = false;
314

315
        //! Whether the source has a nodata value at least in one of its band.
316
        bool bHasNoData = false;
317

318
        //! Whether all bands of the source have the same nodata value.
319
        bool bSameNoData = false;
320

321
        //! Nodata value of all bands (when bSameNoData == true).
322
        double dfSameNoData = 0;
323

324
        //! Mask band of the source.
325
        GDALRasterBand *poMaskBand = nullptr;
326
    };
327

328
    //! Array of sources participating to the current pixel query.
329
    std::vector<SourceDesc> m_aoSourceDesc{};
330

331
    //! Maximum number of threads. Updated by CollectSources().
332
    int m_nNumThreads = -1;
333

334
    //! Whereas the multi-threading rendering code path must be used. Updated by CollectSources().
335
    bool m_bLastMustUseMultiThreading = false;
336

337
    //! From a source dataset name, return its SourceDesc description structure.
338
    bool GetSourceDesc(const std::string &osTileName, SourceDesc &oSourceDesc,
339
                       std::mutex *pMutex);
340

341
    //! Collect sources corresponding to the georeferenced window of interest,
342
    //! and store them in m_aoSourceDesc[].
343
    bool CollectSources(double dfXOff, double dfYOff, double dfXSize,
344
                        double dfYSize, bool bMultiThreadAllowed);
345

346
    //! Sort sources according to m_nSortFieldIndex.
347
    void SortSourceDesc();
348

349
    //! Whether the output buffer needs to be nodata initialized, or if
350
    //! sources are fully covering it.
351
    bool NeedInitBuffer(int nBandCount, const int *panBandMap) const;
352

353
    //! Nodata initialize the output buffer.
354
    void InitBuffer(void *pData, int nBufXSize, int nBufYSize,
355
                    GDALDataType eBufType, int nBandCount,
356
                    const int *panBandMap, GSpacing nPixelSpace,
357
                    GSpacing nLineSpace, GSpacing nBandSpace) const;
358

359
    //! Render one source. Used by IRasterIO()
360
    CPLErr RenderSource(const SourceDesc &oSourceDesc, bool bNeedInitBuffer,
361
                        int nBandNrMax, int nXOff, int nYOff, int nXSize,
362
                        int nYSize, double dfXOff, double dfYOff,
363
                        double dfXSize, double dfYSize, int nBufXSize,
364
                        int nBufYSize, void *pData, GDALDataType eBufType,
365
                        int nBandCount, BANDMAP_TYPE panBandMap,
366
                        GSpacing nPixelSpace, GSpacing nLineSpace,
367
                        GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg,
368
                        VRTSource::WorkingState &oWorkingState) const;
369

370
    //! Whether m_poVectorDS supports SetMetadata()/SetMetadataItem()
371
    bool TileIndexSupportsEditingLayerMetadata() const;
372

373
    //! Return number of threads that can be used
374
    int GetNumThreads() const;
375

376
    /** Structure used to declare a threaded job to satisfy IRasterIO()
377
     * on a given source.
378
     */
379
    struct RasterIOJob
380
    {
381
        std::atomic<int> *pnCompletedJobs = nullptr;
382
        std::atomic<bool> *pbSuccess = nullptr;
383
        CPLErrorAccumulator *poErrorAccumulator = nullptr;
384
        GDALTileIndexDataset *poDS = nullptr;
385
        GDALTileIndexDataset::QueueWorkingStates *poQueueWorkingStates =
386
            nullptr;
387
        int nBandNrMax = 0;
388

389
        int nXOff = 0;
390
        int nYOff = 0;
391
        int nXSize = 0;
392
        int nYSize = 0;
393
        void *pData = nullptr;
394
        int nBufXSize = 0;
395
        int nBufYSize = 0;
396
        int nBandCount = 0;
397
        BANDMAP_TYPE panBandMap = nullptr;
398
        GDALDataType eBufType = GDT_Unknown;
399
        GSpacing nPixelSpace = 0;
400
        GSpacing nLineSpace = 0;
401
        GSpacing nBandSpace = 0;
402
        GDALRasterIOExtraArg *psExtraArg = nullptr;
403

404
        std::string osTileName{};
405

406
        static void Func(void *pData);
407
    };
408

409
    CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexDataset)
410
};
411

412
/************************************************************************/
413
/*                            GDALTileIndexBand                          */
414
/************************************************************************/
415

416
class GDALTileIndexBand final : public GDALPamRasterBand
417
{
418
  public:
419
    GDALTileIndexBand(GDALTileIndexDataset *poDSIn, int nBandIn,
420
                      GDALDataType eDT, int nBlockXSizeIn, int nBlockYSizeIn);
421

422
    double GetNoDataValue(int *pbHasNoData) override
70✔
423
    {
424
        if (pbHasNoData)
70✔
425
            *pbHasNoData = m_bNoDataValueSet;
70✔
426
        return m_dfNoDataValue;
70✔
427
    }
428

429
    GDALColorInterp GetColorInterpretation() override
55✔
430
    {
431
        return m_eColorInterp;
55✔
432
    }
433

434
    CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
435

436
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
437
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
438
                     GDALDataType eBufType, GSpacing nPixelSpace,
439
                     GSpacing nLineSpace,
440
                     GDALRasterIOExtraArg *psExtraArg) override;
441

442
    int IGetDataCoverageStatus(int nXOff, int nYOff, int nXSize, int nYSize,
443
                               int nMaskFlagStop, double *pdfDataPct) override;
444

445
    int GetMaskFlags() override
15✔
446
    {
447
        if (m_poDS->m_poMaskBand && m_poDS->m_poMaskBand.get() != this)
15✔
448
            return GMF_PER_DATASET;
4✔
449
        return GDALPamRasterBand::GetMaskFlags();
11✔
450
    }
451

452
    GDALRasterBand *GetMaskBand() override
21✔
453
    {
454
        if (m_poDS->m_poMaskBand && m_poDS->m_poMaskBand.get() != this)
21✔
455
            return m_poDS->m_poMaskBand.get();
7✔
456
        return GDALPamRasterBand::GetMaskBand();
14✔
457
    }
458

459
    double GetOffset(int *pbHasValue) override
10✔
460
    {
461
        int bHasValue = FALSE;
10✔
462
        double dfVal = GDALPamRasterBand::GetOffset(&bHasValue);
10✔
463
        if (bHasValue)
10✔
464
        {
465
            if (pbHasValue)
×
466
                *pbHasValue = true;
×
467
            return dfVal;
×
468
        }
469
        if (pbHasValue)
10✔
470
            *pbHasValue = !std::isnan(m_dfOffset);
10✔
471
        return std::isnan(m_dfOffset) ? 0.0 : m_dfOffset;
10✔
472
    }
473

474
    double GetScale(int *pbHasValue) override
10✔
475
    {
476
        int bHasValue = FALSE;
10✔
477
        double dfVal = GDALPamRasterBand::GetScale(&bHasValue);
10✔
478
        if (bHasValue)
10✔
479
        {
480
            if (pbHasValue)
×
481
                *pbHasValue = true;
×
482
            return dfVal;
×
483
        }
484
        if (pbHasValue)
10✔
485
            *pbHasValue = !std::isnan(m_dfScale);
10✔
486
        return std::isnan(m_dfScale) ? 1.0 : m_dfScale;
10✔
487
    }
488

489
    const char *GetUnitType() override
6✔
490
    {
491
        const char *pszVal = GDALPamRasterBand::GetUnitType();
6✔
492
        if (pszVal && *pszVal)
6✔
493
            return pszVal;
×
494
        return m_osUnit.c_str();
6✔
495
    }
496

497
    char **GetCategoryNames() override
2✔
498
    {
499
        return m_aosCategoryNames.List();
2✔
500
    }
501

502
    GDALColorTable *GetColorTable() override
8✔
503
    {
504
        return m_poColorTable.get();
8✔
505
    }
506

507
    GDALRasterAttributeTable *GetDefaultRAT() override
2✔
508
    {
509
        return m_poRAT.get();
2✔
510
    }
511

512
    int GetOverviewCount() override;
513
    GDALRasterBand *GetOverview(int iOvr) override;
514

515
    char **GetMetadataDomainList() override;
516
    const char *GetMetadataItem(const char *pszName,
517
                                const char *pszDomain) override;
518
    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
519
                           const char *pszDomain) override;
520
    CPLErr SetMetadata(char **papszMD, const char *pszDomain) override;
521

522
  private:
523
    friend class GDALTileIndexDataset;
524

525
    //! Dataset that owns this band.
526
    GDALTileIndexDataset *m_poDS = nullptr;
527

528
    //! Whether a nodata value is set to this band.
529
    bool m_bNoDataValueSet = false;
530

531
    //! Nodata value.
532
    double m_dfNoDataValue = 0;
533

534
    //! Color interpretation.
535
    GDALColorInterp m_eColorInterp = GCI_Undefined;
536

537
    //! Cached value for GetMetadataItem("Pixel_X_Y", "LocationInfo").
538
    std::string m_osLastLocationInfo{};
539

540
    //! Scale value (returned by GetScale())
541
    double m_dfScale = std::numeric_limits<double>::quiet_NaN();
542

543
    //! Offset value (returned by GetOffset())
544
    double m_dfOffset = std::numeric_limits<double>::quiet_NaN();
545

546
    //! Unit type (returned by GetUnitType()).
547
    std::string m_osUnit{};
548

549
    //! Category names (returned by GetCategoryNames()).
550
    CPLStringList m_aosCategoryNames{};
551

552
    //! Color table (returned by GetColorTable()).
553
    std::unique_ptr<GDALColorTable> m_poColorTable{};
554

555
    //! Raster attribute table (returned by GetDefaultRAT()).
556
    std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
557

558
    CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexBand)
559
};
560

561
/************************************************************************/
562
/*                        IsSameNaNAware()                              */
563
/************************************************************************/
564

565
static inline bool IsSameNaNAware(double a, double b)
262✔
566
{
567
    return a == b || (std::isnan(a) && std::isnan(b));
262✔
568
}
569

570
/************************************************************************/
571
/*                         GDALTileIndexDataset()                        */
572
/************************************************************************/
573

574
GDALTileIndexDataset::GDALTileIndexDataset()
269✔
575
    : m_osUniqueHandle(CPLSPrintf("%p", this))
269✔
576
{
577
}
269✔
578

579
/************************************************************************/
580
/*                        GetAbsoluteFileName()                         */
581
/************************************************************************/
582

583
static std::string GetAbsoluteFileName(const char *pszTileName,
579✔
584
                                       const char *pszVRTName)
585
{
586
    if (STARTS_WITH(pszTileName, "https://"))
579✔
587
        return std::string("/vsicurl/").append(pszTileName);
10✔
588

589
    if (CPLIsFilenameRelative(pszTileName) &&
574✔
590
        !STARTS_WITH(pszTileName, "<VRTDataset") &&
578✔
591
        !STARTS_WITH(pszVRTName, "<GDALTileIndexDataset"))
4✔
592
    {
593
        const auto oSubDSInfo(GDALGetSubdatasetInfo(pszTileName));
4✔
594
        if (oSubDSInfo && !oSubDSInfo->GetPathComponent().empty())
4✔
595
        {
596
            const std::string osPath(oSubDSInfo->GetPathComponent());
4✔
597
            const std::string osRet =
598
                CPLIsFilenameRelative(osPath.c_str())
2✔
599
                    ? oSubDSInfo->ModifyPathComponent(
600
                          CPLProjectRelativeFilenameSafe(
2✔
601
                              CPLGetPathSafe(pszVRTName).c_str(),
3✔
602
                              osPath.c_str()))
603
                    : std::string(pszTileName);
6✔
604
            GDALDestroySubdatasetInfo(oSubDSInfo);
2✔
605
            return osRet;
2✔
606
        }
607

608
        const std::string osRelativeMadeAbsolute =
609
            CPLProjectRelativeFilenameSafe(CPLGetPathSafe(pszVRTName).c_str(),
×
610
                                           pszTileName);
2✔
611
        VSIStatBufL sStat;
612
        if (VSIStatL(osRelativeMadeAbsolute.c_str(), &sStat) == 0)
2✔
613
            return osRelativeMadeAbsolute;
2✔
614
    }
615
    return pszTileName;
570✔
616
}
617

618
/************************************************************************/
619
/*                    GTIDoPaletteExpansionIfNeeded()                   */
620
/************************************************************************/
621

622
//! Do palette -> RGB(A) expansion
623
static bool
624
GTIDoPaletteExpansionIfNeeded(std::shared_ptr<GDALDataset> &poTileDS,
448✔
625
                              int nBandCount)
626
{
627
    if (poTileDS->GetRasterCount() == 1 &&
720✔
628
        (nBandCount == 3 || nBandCount == 4) &&
722✔
629
        poTileDS->GetRasterBand(1)->GetColorTable() != nullptr)
4✔
630
    {
631

632
        CPLStringList aosOptions;
4✔
633
        aosOptions.AddString("-of");
4✔
634
        aosOptions.AddString("VRT");
4✔
635

636
        aosOptions.AddString("-expand");
4✔
637
        aosOptions.AddString(nBandCount == 3 ? "rgb" : "rgba");
4✔
638

639
        GDALTranslateOptions *psOptions =
640
            GDALTranslateOptionsNew(aosOptions.List(), nullptr);
4✔
641
        int bUsageError = false;
4✔
642
        auto poRGBDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
643
            GDALTranslate("", GDALDataset::ToHandle(poTileDS.get()), psOptions,
644
                          &bUsageError)));
4✔
645
        GDALTranslateOptionsFree(psOptions);
4✔
646
        if (!poRGBDS)
4✔
647
        {
648
            return false;
×
649
        }
650

651
        poTileDS.reset(poRGBDS.release());
4✔
652
    }
653
    return true;
448✔
654
}
655

656
/************************************************************************/
657
/*                                Open()                                */
658
/************************************************************************/
659

660
bool GDALTileIndexDataset::Open(GDALOpenInfo *poOpenInfo)
269✔
661
{
662
    eAccess = poOpenInfo->eAccess;
269✔
663

664
    CPLXMLNode *psRoot = nullptr;
269✔
665
    const char *pszIndexDataset = poOpenInfo->pszFilename;
269✔
666

667
    if (STARTS_WITH(poOpenInfo->pszFilename, GTI_PREFIX))
269✔
668
    {
669
        pszIndexDataset = poOpenInfo->pszFilename + strlen(GTI_PREFIX);
8✔
670
    }
671
    else if (STARTS_WITH(poOpenInfo->pszFilename, "<GDALTileIndexDataset"))
261✔
672
    {
673
        // CPLParseXMLString() emits an error in case of failure
674
        m_psXMLTree.reset(CPLParseXMLString(poOpenInfo->pszFilename));
23✔
675
        if (m_psXMLTree == nullptr)
23✔
676
            return false;
1✔
677
    }
678
    else if (poOpenInfo->nHeaderBytes > 0 &&
238✔
679
             strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
238✔
680
                    "<GDALTileIndexDataset"))
681
    {
682
        // CPLParseXMLFile() emits an error in case of failure
683
        m_psXMLTree.reset(CPLParseXMLFile(poOpenInfo->pszFilename));
6✔
684
        if (m_psXMLTree == nullptr)
6✔
685
            return false;
1✔
686
        m_bXMLUpdatable = (poOpenInfo->eAccess == GA_Update);
5✔
687
    }
688

689
    if (m_psXMLTree)
267✔
690
    {
691
        psRoot = CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
27✔
692
        if (psRoot == nullptr)
27✔
693
        {
694
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
695
                     "Missing GDALTileIndexDataset root element.");
696
            return false;
1✔
697
        }
698

699
        pszIndexDataset = CPLGetXMLValue(psRoot, "IndexDataset", nullptr);
26✔
700
        if (!pszIndexDataset)
26✔
701
        {
702
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
703
                     "Missing IndexDataset element.");
704
            return false;
1✔
705
        }
706
    }
707

708
    if (ENDS_WITH_CI(pszIndexDataset, ".gti.gpkg") &&
265✔
709
        poOpenInfo->nHeaderBytes >= 100 &&
500✔
710
        STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
235✔
711
                    "SQLite format 3"))
712
    {
713
        const char *const apszAllowedDrivers[] = {"GPKG", nullptr};
231✔
714
        m_poVectorDS.reset(GDALDataset::Open(
231✔
715
            std::string("GPKG:\"").append(pszIndexDataset).append("\"").c_str(),
462✔
716
            GDAL_OF_VECTOR | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR |
231✔
717
                ((poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) ? GDAL_OF_UPDATE
231✔
718
                                                           : GDAL_OF_READONLY),
719
            apszAllowedDrivers));
720
        if (!m_poVectorDS)
231✔
721
        {
722
            return false;
1✔
723
        }
724
        if (m_poVectorDS->GetLayerCount() == 0 &&
233✔
725
            (m_poVectorDS->GetRasterCount() != 0 ||
2✔
726
             m_poVectorDS->GetMetadata("SUBDATASETS") != nullptr))
1✔
727
        {
728
            return false;
1✔
729
        }
730
    }
731
    else
732
    {
733
        m_poVectorDS.reset(GDALDataset::Open(
34✔
734
            pszIndexDataset, GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR |
34✔
735
                                 ((poOpenInfo->nOpenFlags & GDAL_OF_UPDATE)
34✔
736
                                      ? GDAL_OF_UPDATE
34✔
737
                                      : GDAL_OF_READONLY)));
738
        if (!m_poVectorDS)
34✔
739
        {
740
            return false;
1✔
741
        }
742
    }
743

744
    if (m_poVectorDS->GetLayerCount() == 0)
263✔
745
    {
746
        CPLError(CE_Failure, CPLE_AppDefined, "%s has no vector layer",
1✔
747
                 poOpenInfo->pszFilename);
748
        return false;
1✔
749
    }
750

751
    double dfOvrFactor = 1.0;
262✔
752
    if (const char *pszFactor =
262✔
753
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "FACTOR"))
262✔
754
    {
755
        dfOvrFactor = CPLAtof(pszFactor);
5✔
756
        if (!(dfOvrFactor > 1.0))
5✔
757
        {
758
            CPLError(CE_Failure, CPLE_AppDefined, "Wrong overview factor");
1✔
759
            return false;
1✔
760
        }
761
    }
762

763
    const char *pszLayerName;
764

765
    if ((pszLayerName = CSLFetchNameValue(poOpenInfo->papszOpenOptions,
261✔
766
                                          "LAYER")) != nullptr)
261✔
767
    {
768
        m_poLayer = m_poVectorDS->GetLayerByName(pszLayerName);
6✔
769
        if (!m_poLayer)
6✔
770
        {
771
            CPLError(CE_Failure, CPLE_AppDefined, "Layer %s does not exist",
2✔
772
                     pszLayerName);
773
            return false;
2✔
774
        }
775
    }
776
    else if (psRoot && (pszLayerName = CPLGetXMLValue(psRoot, "IndexLayer",
255✔
777
                                                      nullptr)) != nullptr)
778
    {
779
        m_poLayer = m_poVectorDS->GetLayerByName(pszLayerName);
8✔
780
        if (!m_poLayer)
8✔
781
        {
782
            CPLError(CE_Failure, CPLE_AppDefined, "Layer %s does not exist",
1✔
783
                     pszLayerName);
784
            return false;
1✔
785
        }
786
    }
787
    else if (!psRoot && (pszLayerName = m_poVectorDS->GetMetadataItem(
482✔
788
                             MD_DS_TILE_INDEX_LAYER)) != nullptr)
235✔
789
    {
790
        m_poLayer = m_poVectorDS->GetLayerByName(pszLayerName);
2✔
791
        if (!m_poLayer)
2✔
792
        {
793
            CPLError(CE_Failure, CPLE_AppDefined, "Layer %s does not exist",
1✔
794
                     pszLayerName);
795
            return false;
1✔
796
        }
797
    }
798
    else if (m_poVectorDS->GetLayerCount() == 1)
245✔
799
    {
800
        m_poLayer = m_poVectorDS->GetLayer(0);
243✔
801
        if (!m_poLayer)
243✔
802
        {
803
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot open layer 0");
×
804
            return false;
×
805
        }
806
    }
807
    else
808
    {
809
        if (STARTS_WITH(poOpenInfo->pszFilename, GTI_PREFIX))
2✔
810
        {
811
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
812
                     "%s has more than one layer. LAYER open option "
813
                     "must be defined to specify which one to "
814
                     "use as the tile index",
815
                     pszIndexDataset);
816
        }
817
        else if (psRoot)
1✔
818
        {
819
            CPLError(CE_Failure, CPLE_AppDefined,
×
820
                     "%s has more than one layer. IndexLayer element must be "
821
                     "defined to specify which one to "
822
                     "use as the tile index",
823
                     pszIndexDataset);
824
        }
825
        else
826
        {
827
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
828
                     "%s has more than one layer. %s "
829
                     "metadata item must be defined to specify which one to "
830
                     "use as the tile index",
831
                     pszIndexDataset, MD_DS_TILE_INDEX_LAYER);
832
        }
833
        return false;
2✔
834
    }
835

836
    // Try to get the metadata from an embedded xml:GTI domain
837
    if (!m_psXMLTree)
255✔
838
    {
839
        char **papszMD = m_poLayer->GetMetadata("xml:GTI");
233✔
840
        if (papszMD && papszMD[0])
233✔
841
        {
842
            m_psXMLTree.reset(CPLParseXMLString(papszMD[0]));
1✔
843
            if (m_psXMLTree == nullptr)
1✔
844
                return false;
×
845

846
            psRoot = CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
1✔
847
            if (psRoot == nullptr)
1✔
848
            {
849
                CPLError(CE_Failure, CPLE_AppDefined,
×
850
                         "Missing GDALTileIndexDataset root element.");
851
                return false;
×
852
            }
853
        }
854
    }
855

856
    // Get the value of an option.
857
    // The order of lookup is the following one (first to last):
858
    // - open options
859
    // - XML file
860
    // - Layer metadata items.
861
    const auto GetOption = [poOpenInfo, psRoot, this](const char *pszItem)
22,952✔
862
    {
863
        const char *pszVal =
864
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszItem);
7,323✔
865
        if (pszVal)
7,323✔
866
            return pszVal;
2✔
867

868
        if (psRoot)
7,321✔
869
        {
870
            pszVal = CPLGetXMLValue(psRoot, pszItem, nullptr);
516✔
871
            if (pszVal)
516✔
872
                return pszVal;
19✔
873

874
            if (EQUAL(pszItem, MD_BAND_COUNT))
497✔
875
                pszItem = GTI_XML_BANDCOUNT;
20✔
876
            else if (EQUAL(pszItem, MD_DATA_TYPE))
477✔
877
                pszItem = GTI_XML_DATATYPE;
23✔
878
            else if (EQUAL(pszItem, MD_NODATA))
454✔
879
                pszItem = GTI_XML_NODATAVALUE;
18✔
880
            else if (EQUAL(pszItem, MD_COLOR_INTERPRETATION))
436✔
881
                pszItem = GTI_XML_COLORINTERP;
23✔
882
            else if (EQUAL(pszItem, MD_LOCATION_FIELD))
413✔
883
                pszItem = GTI_XML_LOCATIONFIELD;
23✔
884
            else if (EQUAL(pszItem, MD_SORT_FIELD))
390✔
885
                pszItem = GTI_XML_SORTFIELD;
23✔
886
            else if (EQUAL(pszItem, MD_SORT_FIELD_ASC))
367✔
887
                pszItem = GTI_XML_SORTFIELDASC;
2✔
888
            else if (EQUAL(pszItem, MD_MASK_BAND))
365✔
889
                pszItem = GTI_XML_MASKBAND;
18✔
890
            pszVal = CPLGetXMLValue(psRoot, pszItem, nullptr);
497✔
891
            if (pszVal)
497✔
892
                return pszVal;
7✔
893
        }
894

895
        return m_poLayer->GetMetadataItem(pszItem);
7,295✔
896
    };
255✔
897

898
    const char *pszFilter = GetOption("Filter");
255✔
899
    if (pszFilter)
255✔
900
    {
901
        if (m_poLayer->SetAttributeFilter(pszFilter) != OGRERR_NONE)
1✔
902
            return false;
×
903
    }
904

905
    const OGRFeatureDefn *poLayerDefn = m_poLayer->GetLayerDefn();
255✔
906

907
    std::string osLocationFieldName;
510✔
908
    {
909
        const char *pszLocationFieldName = GetOption(MD_LOCATION_FIELD);
255✔
910
        if (pszLocationFieldName)
255✔
911
        {
912
            osLocationFieldName = pszLocationFieldName;
3✔
913
        }
914
        else
915
        {
916
            // Is this a https://stac-utils.github.io/stac-geoparquet/latest/spec/stac-geoparquet-spec ?
917
            if (poLayerDefn->GetFieldIndex("assets.data.href") >= 0)
252✔
918
            {
919
                osLocationFieldName = "assets.data.href";
×
920
                CPLDebug("GTI", "Using %s as location field",
×
921
                         osLocationFieldName.c_str());
922
            }
923
            else if (poLayerDefn->GetFieldIndex("assets.image.href") >= 0)
252✔
924
            {
925
                osLocationFieldName = "assets.image.href";
1✔
926
                CPLDebug("GTI", "Using %s as location field",
1✔
927
                         osLocationFieldName.c_str());
928
            }
929
            else if (poLayerDefn->GetFieldIndex("stac_version") >= 0)
251✔
930
            {
931
                const int nFieldCount = poLayerDefn->GetFieldCount();
4✔
932
                // Look for "assets.xxxxx.href" fields
933
                int nAssetCount = 0;
4✔
934
                for (int i = 0; i < nFieldCount; ++i)
60✔
935
                {
936
                    const auto poFDefn = poLayerDefn->GetFieldDefn(i);
56✔
937
                    const char *pszFieldName = poFDefn->GetNameRef();
56✔
938
                    if (STARTS_WITH(pszFieldName, "assets.") &&
56✔
939
                        EQUAL(pszFieldName + strlen(pszFieldName) -
44✔
940
                                  strlen(".href"),
941
                              ".href") &&
4✔
942
                        // Assets with "metadata" in them are very much likely
943
                        // not rasters... We could potentially confirm that by
944
                        // inspecting the value of the assets.XXX.type or
945
                        // assets.XXX.roles fields of one feature
946
                        !strstr(pszFieldName, "metadata"))
4✔
947
                    {
948
                        ++nAssetCount;
4✔
949
                        if (!osLocationFieldName.empty())
4✔
950
                        {
951
                            osLocationFieldName += ", ";
×
952
                        }
953
                        osLocationFieldName += pszFieldName;
4✔
954
                    }
955
                }
956
                if (nAssetCount > 1)
4✔
957
                {
958
                    CPLError(CE_Failure, CPLE_AppDefined,
×
959
                             "Several potential STAC assets. Please select one "
960
                             "among %s with the LOCATION_FIELD open option",
961
                             osLocationFieldName.c_str());
962
                    return false;
×
963
                }
964
                else if (nAssetCount == 0)
4✔
965
                {
966
                    CPLError(CE_Failure, CPLE_AppDefined,
×
967
                             "File has stac_version property but lacks assets");
968
                    return false;
×
969
                }
970
            }
971
            else
972
            {
973
                constexpr const char *DEFAULT_LOCATION_FIELD_NAME = "location";
247✔
974
                osLocationFieldName = DEFAULT_LOCATION_FIELD_NAME;
247✔
975
            }
976
        }
977
    }
978

979
    m_nLocationFieldIndex =
255✔
980
        poLayerDefn->GetFieldIndex(osLocationFieldName.c_str());
255✔
981
    if (m_nLocationFieldIndex < 0)
255✔
982
    {
983
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
1✔
984
                 osLocationFieldName.c_str());
985
        return false;
1✔
986
    }
987
    if (poLayerDefn->GetFieldDefn(m_nLocationFieldIndex)->GetType() !=
254✔
988
        OFTString)
989
    {
990
        CPLError(CE_Failure, CPLE_AppDefined, "Field %s is not of type string",
1✔
991
                 osLocationFieldName.c_str());
992
        return false;
1✔
993
    }
994

995
    const char *pszSortFieldName = GetOption(MD_SORT_FIELD);
253✔
996
    if (pszSortFieldName)
253✔
997
    {
998
        m_nSortFieldIndex = poLayerDefn->GetFieldIndex(pszSortFieldName);
96✔
999
        if (m_nSortFieldIndex < 0)
96✔
1000
        {
1001
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
1✔
1002
                     pszSortFieldName);
1003
            return false;
1✔
1004
        }
1005

1006
        const auto eFieldType =
1007
            poLayerDefn->GetFieldDefn(m_nSortFieldIndex)->GetType();
95✔
1008
        if (eFieldType != OFTString && eFieldType != OFTInteger &&
95✔
1009
            eFieldType != OFTInteger64 && eFieldType != OFTReal &&
61✔
1010
            eFieldType != OFTDate && eFieldType != OFTDateTime)
19✔
1011
        {
1012
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1013
                     "Unsupported type for field %s", pszSortFieldName);
1014
            return false;
1✔
1015
        }
1016

1017
        const char *pszSortFieldAsc = GetOption(MD_SORT_FIELD_ASC);
94✔
1018
        if (pszSortFieldAsc)
94✔
1019
        {
1020
            m_bSortFieldAsc = CPLTestBool(pszSortFieldAsc);
3✔
1021
        }
1022
    }
1023

1024
    const char *pszResX = GetOption(MD_RESX);
251✔
1025
    const char *pszResY = GetOption(MD_RESY);
251✔
1026
    if (pszResX && !pszResY)
251✔
1027
    {
1028
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1029
                 "%s metadata item defined, but not %s", MD_RESX, MD_RESY);
1030
        return false;
1✔
1031
    }
1032
    if (!pszResX && pszResY)
250✔
1033
    {
1034
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1035
                 "%s metadata item defined, but not %s", MD_RESY, MD_RESX);
1036
        return false;
1✔
1037
    }
1038

1039
    const char *pszResampling = GetOption(MD_RESAMPLING);
249✔
1040
    if (pszResampling)
249✔
1041
    {
1042
        const auto nErrorCountBefore = CPLGetErrorCounter();
8✔
1043
        m_eResampling = GDALRasterIOGetResampleAlg(pszResampling);
8✔
1044
        if (nErrorCountBefore != CPLGetErrorCounter())
8✔
1045
        {
1046
            return false;
×
1047
        }
1048
        m_osResampling = pszResampling;
8✔
1049
    }
1050

1051
    const char *pszMinX = GetOption(MD_MINX);
249✔
1052
    const char *pszMinY = GetOption(MD_MINY);
249✔
1053
    const char *pszMaxX = GetOption(MD_MAXX);
249✔
1054
    const char *pszMaxY = GetOption(MD_MAXY);
249✔
1055
    int nCountMinMaxXY = (pszMinX ? 1 : 0) + (pszMinY ? 1 : 0) +
249✔
1056
                         (pszMaxX ? 1 : 0) + (pszMaxY ? 1 : 0);
249✔
1057
    if (nCountMinMaxXY != 0 && nCountMinMaxXY != 4)
249✔
1058
    {
1059
        CPLError(CE_Failure, CPLE_AppDefined,
4✔
1060
                 "None or all of %s, %s, %s and %s must be specified", MD_MINX,
1061
                 MD_MINY, MD_MAXX, MD_MAXY);
1062
        return false;
4✔
1063
    }
1064

1065
    const char *pszXSize = GetOption(MD_XSIZE);
245✔
1066
    const char *pszYSize = GetOption(MD_YSIZE);
245✔
1067
    const char *pszGeoTransform = GetOption(MD_GEOTRANSFORM);
245✔
1068
    const int nCountXSizeYSizeGT =
245✔
1069
        (pszXSize ? 1 : 0) + (pszYSize ? 1 : 0) + (pszGeoTransform ? 1 : 0);
245✔
1070
    if (nCountXSizeYSizeGT != 0 && nCountXSizeYSizeGT != 3)
245✔
1071
    {
1072
        CPLError(CE_Failure, CPLE_AppDefined,
3✔
1073
                 "None or all of %s, %s, %s must be specified", MD_XSIZE,
1074
                 MD_YSIZE, MD_GEOTRANSFORM);
1075
        return false;
3✔
1076
    }
1077

1078
    const char *pszDataType = GetOption(MD_DATA_TYPE);
242✔
1079
    const char *pszColorInterp = GetOption(MD_COLOR_INTERPRETATION);
242✔
1080
    int nBandCount = 0;
242✔
1081
    std::vector<GDALDataType> aeDataTypes;
484✔
1082
    std::vector<std::pair<bool, double>> aNoData;
484✔
1083
    std::vector<GDALColorInterp> aeColorInterp;
484✔
1084

1085
    const char *pszSRS = GetOption(MD_SRS);
242✔
1086
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
242✔
1087
    if (pszSRS)
242✔
1088
    {
1089
        if (m_oSRS.SetFromUserInput(
2✔
1090
                pszSRS,
1091
                OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
2✔
1092
            OGRERR_NONE)
1093
        {
1094
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s", MD_SRS);
1✔
1095
            return false;
1✔
1096
        }
1097
    }
1098
    else if (const auto poSRS = m_poLayer->GetSpatialRef())
240✔
1099
    {
1100
        // Ignore GPKG "Undefined geographic SRS" and "Undefined Cartesian SRS"
1101
        if (!STARTS_WITH(poSRS->GetName(), "Undefined "))
122✔
1102
            m_oSRS = *poSRS;
122✔
1103
    }
1104

1105
    std::vector<const CPLXMLNode *> apoXMLNodeBands;
482✔
1106
    if (psRoot)
241✔
1107
    {
1108
        int nExpectedBandNumber = 1;
23✔
1109
        for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
98✔
1110
             psIter = psIter->psNext)
75✔
1111
        {
1112
            if (psIter->eType == CXT_Element &&
78✔
1113
                strcmp(psIter->pszValue, GTI_XML_BAND_ELEMENT) == 0)
78✔
1114
            {
1115
                const char *pszBand =
1116
                    CPLGetXMLValue(psIter, GTI_XML_BAND_NUMBER, nullptr);
8✔
1117
                if (!pszBand)
8✔
1118
                {
1119
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
1120
                             "%s attribute missing on %s element",
1121
                             GTI_XML_BAND_NUMBER, GTI_XML_BAND_ELEMENT);
1122
                    return false;
3✔
1123
                }
1124
                const int nBand = atoi(pszBand);
7✔
1125
                if (nBand <= 0)
7✔
1126
                {
1127
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
1128
                             "Invalid band number");
1129
                    return false;
1✔
1130
                }
1131
                if (nBand != nExpectedBandNumber)
6✔
1132
                {
1133
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
1134
                             "Invalid band number: found %d, expected %d",
1135
                             nBand, nExpectedBandNumber);
1136
                    return false;
1✔
1137
                }
1138
                apoXMLNodeBands.push_back(psIter);
5✔
1139
                ++nExpectedBandNumber;
5✔
1140
            }
1141
        }
1142
    }
1143

1144
    const char *pszBandCount = GetOption(MD_BAND_COUNT);
238✔
1145
    if (pszBandCount)
238✔
1146
        nBandCount = atoi(pszBandCount);
22✔
1147

1148
    if (!apoXMLNodeBands.empty())
238✔
1149
    {
1150
        if (!pszBandCount)
5✔
1151
            nBandCount = static_cast<int>(apoXMLNodeBands.size());
4✔
1152
        else if (nBandCount != static_cast<int>(apoXMLNodeBands.size()))
1✔
1153
        {
1154
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1155
                     "Inconsistent %s with actual number of %s elements",
1156
                     GTI_XML_BANDCOUNT, GTI_XML_BAND_ELEMENT);
1157
            return false;
1✔
1158
        }
1159
    }
1160

1161
    // Take into STAC GeoParquet proj:code / proj:epsg / proj:wkt2 / proj:projjson
1162
    // and proj:transform fields
1163
    std::unique_ptr<OGRFeature> poFeature;
237✔
1164
    std::string osResX, osResY, osMinX, osMinY, osMaxX, osMaxY;
474✔
1165
    int iProjCode = -1;
237✔
1166
    int iProjEPSG = -1;
237✔
1167
    int iProjWKT2 = -1;
237✔
1168
    int iProjPROJSON = -1;
237✔
1169
    int iProjTransform = -1;
237✔
1170

1171
    const bool bIsStacGeoParquet =
1172
        STARTS_WITH(osLocationFieldName.c_str(), "assets.") &&
242✔
1173
        EQUAL(osLocationFieldName.c_str() + osLocationFieldName.size() -
5✔
1174
                  strlen(".href"),
1175
              ".href");
1176
    std::string osAssetName;
474✔
1177
    if (bIsStacGeoParquet)
237✔
1178
    {
1179
        osAssetName = osLocationFieldName.substr(
10✔
1180
            strlen("assets."),
1181
            osLocationFieldName.size() - strlen("assets.") - strlen(".href"));
10✔
1182
    }
1183

1184
    const auto GetAssetFieldIndex =
1185
        [poLayerDefn, &osAssetName](const char *pszFieldName)
30✔
1186
    {
1187
        const int idx = poLayerDefn->GetFieldIndex(
17✔
1188
            CPLSPrintf("assets.%s.%s", osAssetName.c_str(), pszFieldName));
17✔
1189
        if (idx >= 0)
17✔
1190
            return idx;
4✔
1191
        return poLayerDefn->GetFieldIndex(pszFieldName);
13✔
1192
    };
237✔
1193

1194
    if (bIsStacGeoParquet && !pszSRS && !pszResX && !pszResY && !pszMinX &&
5✔
1195
        !pszMinY && !pszMaxX && !pszMaxY &&
5✔
1196
        ((iProjCode = GetAssetFieldIndex("proj:code")) >= 0 ||
5✔
1197
         (iProjEPSG = GetAssetFieldIndex("proj:epsg")) >= 0 ||
4✔
1198
         (iProjWKT2 = GetAssetFieldIndex("proj:wkt2")) >= 0 ||
2✔
1199
         (iProjPROJSON = GetAssetFieldIndex("proj:projjson")) >= 0) &&
243✔
1200
        ((iProjTransform = GetAssetFieldIndex("proj:transform")) >= 0))
5✔
1201
    {
1202
        poFeature.reset(m_poLayer->GetNextFeature());
5✔
1203
        const auto poProjTransformField =
1204
            poLayerDefn->GetFieldDefn(iProjTransform);
5✔
1205
        if (poFeature &&
5✔
1206
            ((iProjCode >= 0 && poFeature->IsFieldSet(iProjCode)) ||
5✔
1207
             (iProjEPSG >= 0 && poFeature->IsFieldSet(iProjEPSG)) ||
4✔
1208
             (iProjWKT2 >= 0 && poFeature->IsFieldSet(iProjWKT2)) ||
2✔
1209
             (iProjPROJSON >= 0 && poFeature->IsFieldSet(iProjPROJSON))) &&
6✔
1210
            poFeature->IsFieldSet(iProjTransform) &&
19✔
1211
            (poProjTransformField->GetType() == OFTRealList ||
9✔
1212
             poProjTransformField->GetType() == OFTIntegerList ||
4✔
1213
             poProjTransformField->GetType() == OFTInteger64List))
×
1214
        {
1215
            OGRSpatialReference oSTACSRS;
10✔
1216
            oSTACSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
5✔
1217

1218
            if (iProjCode >= 0 && poFeature->IsFieldSet(iProjCode))
5✔
1219
                oSTACSRS.SetFromUserInput(
1✔
1220
                    poFeature->GetFieldAsString(iProjCode),
1221
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1222

1223
            else if (iProjEPSG >= 0 && poFeature->IsFieldSet(iProjEPSG))
4✔
1224
                oSTACSRS.importFromEPSG(
2✔
1225
                    poFeature->GetFieldAsInteger(iProjEPSG));
1226

1227
            else if (iProjWKT2 >= 0 && poFeature->IsFieldSet(iProjWKT2))
2✔
1228
                oSTACSRS.SetFromUserInput(
1✔
1229
                    poFeature->GetFieldAsString(iProjWKT2),
1230
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1231

1232
            else if (iProjPROJSON >= 0 && poFeature->IsFieldSet(iProjPROJSON))
1✔
1233
                oSTACSRS.SetFromUserInput(
1✔
1234
                    poFeature->GetFieldAsString(iProjPROJSON),
1235
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1236

1237
            if (!oSTACSRS.IsEmpty())
5✔
1238
            {
1239
                int nTransformCount = 0;
5✔
1240
                double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
5✔
1241
                if (poProjTransformField->GetType() == OFTRealList)
5✔
1242
                {
1243
                    const auto padfFeatureTransform =
1244
                        poFeature->GetFieldAsDoubleList(iProjTransform,
1✔
1245
                                                        &nTransformCount);
1246
                    if (nTransformCount >= 6)
1✔
1247
                        memcpy(adfGeoTransform, padfFeatureTransform,
1✔
1248
                               6 * sizeof(double));
1249
                }
1250
                else if (poProjTransformField->GetType() == OFTInteger64List)
4✔
1251
                {
1252
                    const auto paFeatureTransform =
1253
                        poFeature->GetFieldAsInteger64List(iProjTransform,
×
1254
                                                           &nTransformCount);
1255
                    if (nTransformCount >= 6)
×
1256
                    {
1257
                        for (int i = 0; i < 6; ++i)
×
1258
                            adfGeoTransform[i] =
×
1259
                                static_cast<double>(paFeatureTransform[i]);
×
1260
                    }
1261
                }
1262
                else if (poProjTransformField->GetType() == OFTIntegerList)
4✔
1263
                {
1264
                    const auto paFeatureTransform =
1265
                        poFeature->GetFieldAsIntegerList(iProjTransform,
4✔
1266
                                                         &nTransformCount);
1267
                    if (nTransformCount >= 6)
4✔
1268
                    {
1269
                        for (int i = 0; i < 6; ++i)
28✔
1270
                            adfGeoTransform[i] = paFeatureTransform[i];
24✔
1271
                    }
1272
                }
1273
                OGREnvelope sEnvelope;
5✔
1274
                if (nTransformCount >= 6 && m_poLayer->GetSpatialRef() &&
10✔
1275
                    m_poLayer->GetExtent(&sEnvelope, /* bForce = */ true) ==
5✔
1276
                        OGRERR_NONE)
1277
                {
1278
                    const double dfResX = adfGeoTransform[0];
5✔
1279
                    osResX = CPLSPrintf("%.17g", dfResX);
5✔
1280
                    const double dfResY = std::fabs(adfGeoTransform[4]);
5✔
1281
                    osResY = CPLSPrintf("%.17g", dfResY);
5✔
1282

1283
                    auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
1284
                        OGRCreateCoordinateTransformation(
1285
                            m_poLayer->GetSpatialRef(), &oSTACSRS));
10✔
1286
                    auto poInvCT = std::unique_ptr<OGRCoordinateTransformation>(
1287
                        poCT ? poCT->GetInverse() : nullptr);
10✔
1288
                    double dfOutMinX = 0;
5✔
1289
                    double dfOutMinY = 0;
5✔
1290
                    double dfOutMaxX = 0;
5✔
1291
                    double dfOutMaxY = 0;
5✔
1292
                    if (dfResX > 0 && dfResY > 0 && poCT && poInvCT &&
10✔
1293
                        poCT->TransformBounds(sEnvelope.MinX, sEnvelope.MinY,
10✔
1294
                                              sEnvelope.MaxX, sEnvelope.MaxY,
1295
                                              &dfOutMinX, &dfOutMinY,
1296
                                              &dfOutMaxX, &dfOutMaxY, 21))
5✔
1297
                    {
1298
                        constexpr double EPSILON = 1e-3;
5✔
1299
                        const bool bTileAlignedOnRes =
1300
                            (fmod(std::fabs(adfGeoTransform[3]), dfResX) <=
5✔
1301
                                 EPSILON * dfResX &&
10✔
1302
                             fmod(std::fabs(adfGeoTransform[5]), dfResY) <=
5✔
1303
                                 EPSILON * dfResY);
5✔
1304

1305
                        osMinX = CPLSPrintf(
1306
                            "%.17g",
1307
                            !bTileAlignedOnRes
1308
                                ? dfOutMinX
1309
                                : std::floor(dfOutMinX / dfResX) * dfResX);
5✔
1310
                        osMinY = CPLSPrintf(
1311
                            "%.17g",
1312
                            !bTileAlignedOnRes
1313
                                ? dfOutMinY
1314
                                : std::floor(dfOutMinY / dfResY) * dfResY);
5✔
1315
                        osMaxX = CPLSPrintf(
1316
                            "%.17g",
1317
                            !bTileAlignedOnRes
1318
                                ? dfOutMaxX
1319
                                : std::ceil(dfOutMaxX / dfResX) * dfResX);
5✔
1320
                        osMaxY = CPLSPrintf(
1321
                            "%.17g",
1322
                            !bTileAlignedOnRes
1323
                                ? dfOutMaxY
1324
                                : std::ceil(dfOutMaxY / dfResY) * dfResY);
5✔
1325

1326
                        m_oSRS = std::move(oSTACSRS);
5✔
1327
                        pszResX = osResX.c_str();
5✔
1328
                        pszResY = osResY.c_str();
5✔
1329
                        pszMinX = osMinX.c_str();
5✔
1330
                        pszMinY = osMinY.c_str();
5✔
1331
                        pszMaxX = osMaxX.c_str();
5✔
1332
                        pszMaxY = osMaxY.c_str();
5✔
1333
                        nCountMinMaxXY = 4;
5✔
1334

1335
                        poFeature.reset();
5✔
1336
                        m_poLayer->ResetReading();
5✔
1337

1338
                        m_poWarpedLayerKeeper =
1339
                            std::make_unique<OGRWarpedLayer>(
5✔
1340
                                m_poLayer, /* iGeomField = */ 0,
×
1341
                                /* bTakeOwnership = */ false, poCT.release(),
5✔
1342
                                poInvCT.release());
10✔
1343
                        m_poLayer = m_poWarpedLayerKeeper.get();
5✔
1344
                        poLayerDefn = m_poLayer->GetLayerDefn();
5✔
1345
                    }
1346
                }
1347
            }
1348
        }
1349
    }
1350

1351
    bool bHasMaskBand = false;
237✔
1352
    std::unique_ptr<GDALColorTable> poSingleColorTable;
237✔
1353
    if ((!pszBandCount && apoXMLNodeBands.empty()) ||
262✔
1354
        (!(pszResX && pszResY) && nCountXSizeYSizeGT == 0))
25✔
1355
    {
1356
        CPLDebug("GTI", "Inspecting one feature due to missing metadata items");
226✔
1357
        m_bScannedOneFeatureAtOpening = true;
226✔
1358

1359
        if (!poFeature)
226✔
1360
            poFeature.reset(m_poLayer->GetNextFeature());
226✔
1361
        if (!poFeature ||
450✔
1362
            !poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
224✔
1363
        {
1364
            CPLError(
2✔
1365
                CE_Failure, CPLE_AppDefined,
1366
                "BAND_COUNT(+DATA_TYPE+COLOR_INTERPRETATION)+ (RESX+RESY or "
1367
                "XSIZE+YSIZE+GEOTRANSFORM) metadata items "
1368
                "missing");
1369
            return false;
10✔
1370
        }
1371

1372
        const char *pszTileName =
1373
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
224✔
1374
        const std::string osTileName(
1375
            GetAbsoluteFileName(pszTileName, poOpenInfo->pszFilename));
224✔
1376
        pszTileName = osTileName.c_str();
224✔
1377

1378
        auto poTileDS = std::shared_ptr<GDALDataset>(
1379
            GDALDataset::Open(pszTileName,
1380
                              GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR),
1381
            GDALDatasetUniquePtrReleaser());
224✔
1382
        if (!poTileDS)
224✔
1383
        {
1384
            return false;
1✔
1385
        }
1386

1387
        // do palette -> RGB(A) expansion if needed
1388
        if (!GTIDoPaletteExpansionIfNeeded(poTileDS, nBandCount))
223✔
1389
            return false;
×
1390

1391
        const int nTileBandCount = poTileDS->GetRasterCount();
223✔
1392
        for (int i = 0; i < nTileBandCount; ++i)
496✔
1393
        {
1394
            auto poTileBand = poTileDS->GetRasterBand(i + 1);
273✔
1395
            aeDataTypes.push_back(poTileBand->GetRasterDataType());
273✔
1396
            int bHasNoData = FALSE;
273✔
1397
            const double dfNoData = poTileBand->GetNoDataValue(&bHasNoData);
273✔
1398
            aNoData.emplace_back(CPL_TO_BOOL(bHasNoData), dfNoData);
273✔
1399
            aeColorInterp.push_back(poTileBand->GetColorInterpretation());
273✔
1400
            if (nTileBandCount == 1)
273✔
1401
            {
1402
                if (auto poCT = poTileBand->GetColorTable())
199✔
1403
                {
1404
                    // We assume that this will apply to all tiles...
1405
                    // TODO: detect if that it is really the case, and warn
1406
                    // if not, or do approximate palette matching like
1407
                    // done in GDALRasterBand::GetIndexColorTranslationTo()
1408
                    poSingleColorTable.reset(poCT->Clone());
×
1409
                }
1410
            }
1411

1412
            if (poTileBand->GetMaskFlags() == GMF_PER_DATASET)
273✔
1413
                bHasMaskBand = true;
1✔
1414
        }
1415
        if (!pszBandCount && nBandCount == 0)
223✔
1416
            nBandCount = nTileBandCount;
209✔
1417

1418
        auto poTileSRS = poTileDS->GetSpatialRef();
223✔
1419
        if (!m_oSRS.IsEmpty() && poTileSRS && !m_oSRS.IsSame(poTileSRS))
223✔
1420
        {
1421
            CPLStringList aosOptions;
7✔
1422
            aosOptions.AddString("-of");
7✔
1423
            aosOptions.AddString("VRT");
7✔
1424

1425
            char *pszWKT = nullptr;
7✔
1426
            const char *const apszWKTOptions[] = {"FORMAT=WKT2_2019", nullptr};
7✔
1427
            m_oSRS.exportToWkt(&pszWKT, apszWKTOptions);
7✔
1428
            if (pszWKT)
7✔
1429
                m_osWKT = pszWKT;
7✔
1430
            CPLFree(pszWKT);
7✔
1431

1432
            if (m_osWKT.empty())
7✔
1433
            {
1434
                CPLError(CE_Failure, CPLE_AppDefined,
×
1435
                         "Cannot export VRT SRS to WKT2");
1436
                return false;
×
1437
            }
1438

1439
            aosOptions.AddString("-t_srs");
7✔
1440
            aosOptions.AddString(m_osWKT.c_str());
7✔
1441

1442
            GDALWarpAppOptions *psWarpOptions =
1443
                GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
7✔
1444
            GDALDatasetH ahSrcDS[] = {GDALDataset::ToHandle(poTileDS.get())};
7✔
1445
            int bUsageError = false;
7✔
1446
            auto poWarpDS =
1447
                std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALWarp(
1448
                    "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
7✔
1449
            GDALWarpAppOptionsFree(psWarpOptions);
7✔
1450
            if (!poWarpDS)
7✔
1451
            {
1452
                return false;
×
1453
            }
1454

1455
            poTileDS.reset(poWarpDS.release());
7✔
1456
            poTileSRS = poTileDS->GetSpatialRef();
7✔
1457
            CPL_IGNORE_RET_VAL(poTileSRS);
7✔
1458
        }
1459

1460
        double adfGeoTransformTile[6];
1461
        if (poTileDS->GetGeoTransform(adfGeoTransformTile) != CE_None)
223✔
1462
        {
1463
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1464
                     "Cannot find geotransform on %s", pszTileName);
1465
            return false;
1✔
1466
        }
1467
        if (!(adfGeoTransformTile[GT_ROTATION_PARAM1] == 0))
222✔
1468
        {
1469
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1470
                     "3rd value of GeoTransform of %s must be 0", pszTileName);
1471
            return false;
1✔
1472
        }
1473
        if (!(adfGeoTransformTile[GT_ROTATION_PARAM2] == 0))
221✔
1474
        {
1475
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1476
                     "5th value of GeoTransform of %s must be 0", pszTileName);
1477
            return false;
1✔
1478
        }
1479
        if (!(adfGeoTransformTile[GT_NS_RES] < 0))
220✔
1480
        {
1481
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1482
                     "6th value of GeoTransform of %s must be < 0",
1483
                     pszTileName);
1484
            return false;
1✔
1485
        }
1486

1487
        const double dfResX = adfGeoTransformTile[GT_WE_RES];
219✔
1488
        const double dfResY = -adfGeoTransformTile[GT_NS_RES];
219✔
1489

1490
        OGREnvelope sEnvelope;
219✔
1491
        if (m_poLayer->GetExtent(&sEnvelope, /* bForce = */ false) ==
219✔
1492
            OGRERR_FAILURE)
1493
        {
1494
            if (m_poLayer->GetExtent(&sEnvelope, /* bForce = */ true) ==
1✔
1495
                OGRERR_FAILURE)
1496
            {
1497
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
1498
                         "Cannot get layer extent");
1499
                return false;
1✔
1500
            }
1501
            CPLError(CE_Warning, CPLE_AppDefined,
×
1502
                     "Could get layer extent, but using a slower method");
1503
        }
1504

1505
        const double dfXSize = (sEnvelope.MaxX - sEnvelope.MinX) / dfResX;
218✔
1506
        if (!(dfXSize >= 0 && dfXSize < INT_MAX))
218✔
1507
        {
1508
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1509
                     "Too small %s, or wrong layer extent", MD_RESX);
1510
            return false;
1✔
1511
        }
1512

1513
        const double dfYSize = (sEnvelope.MaxY - sEnvelope.MinY) / dfResY;
217✔
1514
        if (!(dfYSize >= 0 && dfYSize < INT_MAX))
217✔
1515
        {
1516
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1517
                     "Too small %s, or wrong layer extent", MD_RESY);
1518
            return false;
1✔
1519
        }
1520

1521
        m_adfGeoTransform[GT_TOPLEFT_X] = sEnvelope.MinX;
216✔
1522
        m_adfGeoTransform[GT_WE_RES] = dfResX;
216✔
1523
        m_adfGeoTransform[GT_ROTATION_PARAM1] = 0;
216✔
1524
        m_adfGeoTransform[GT_TOPLEFT_Y] = sEnvelope.MaxY;
216✔
1525
        m_adfGeoTransform[GT_ROTATION_PARAM2] = 0;
216✔
1526
        m_adfGeoTransform[GT_NS_RES] = -dfResY;
216✔
1527
        nRasterXSize = static_cast<int>(std::ceil(dfXSize));
216✔
1528
        nRasterYSize = static_cast<int>(std::ceil(dfYSize));
216✔
1529
    }
1530

1531
    if (pszXSize && pszYSize && pszGeoTransform)
227✔
1532
    {
1533
        const int nXSize = atoi(pszXSize);
12✔
1534
        if (nXSize <= 0)
12✔
1535
        {
1536
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1537
                     "%s metadata item must be > 0", MD_XSIZE);
1538
            return false;
6✔
1539
        }
1540

1541
        const int nYSize = atoi(pszYSize);
11✔
1542
        if (nYSize <= 0)
11✔
1543
        {
1544
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1545
                     "%s metadata item must be > 0", MD_YSIZE);
1546
            return false;
1✔
1547
        }
1548

1549
        const CPLStringList aosTokens(
1550
            CSLTokenizeString2(pszGeoTransform, ",", 0));
10✔
1551
        if (aosTokens.size() != 6)
10✔
1552
        {
1553
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1554
                     "%s metadata item must be 6 numeric values "
1555
                     "separated with comma",
1556
                     MD_GEOTRANSFORM);
1557
            return false;
1✔
1558
        }
1559
        for (int i = 0; i < 6; ++i)
63✔
1560
        {
1561
            m_adfGeoTransform[i] = CPLAtof(aosTokens[i]);
54✔
1562
        }
1563
        if (!(m_adfGeoTransform[GT_ROTATION_PARAM1] == 0))
9✔
1564
        {
1565
            CPLError(CE_Failure, CPLE_AppDefined, "3rd value of %s must be 0",
1✔
1566
                     MD_GEOTRANSFORM);
1567
            return false;
1✔
1568
        }
1569
        if (!(m_adfGeoTransform[GT_ROTATION_PARAM2] == 0))
8✔
1570
        {
1571
            CPLError(CE_Failure, CPLE_AppDefined, "5th value of %s must be 0",
1✔
1572
                     MD_GEOTRANSFORM);
1573
            return false;
1✔
1574
        }
1575
        if (!(m_adfGeoTransform[GT_NS_RES] < 0))
7✔
1576
        {
1577
            CPLError(CE_Failure, CPLE_AppDefined, "6th value of %s must be < 0",
1✔
1578
                     MD_GEOTRANSFORM);
1579
            return false;
1✔
1580
        }
1581

1582
        nRasterXSize = nXSize;
6✔
1583
        nRasterYSize = nYSize;
12✔
1584
    }
1585
    else if (pszResX && pszResY)
215✔
1586
    {
1587
        const double dfResX = CPLAtof(pszResX);
21✔
1588
        if (!(dfResX > 0))
21✔
1589
        {
1590
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1591
                     "RESX metadata item must be > 0");
1592
            return false;
6✔
1593
        }
1594
        const double dfResY = CPLAtof(pszResY);
20✔
1595
        if (!(dfResY > 0))
20✔
1596
        {
1597
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1598
                     "RESY metadata item must be > 0");
1599
            return false;
1✔
1600
        }
1601

1602
        OGREnvelope sEnvelope;
19✔
1603

1604
        if (nCountMinMaxXY == 4)
19✔
1605
        {
1606
            if (pszXSize || pszYSize || pszGeoTransform)
12✔
1607
            {
1608
                CPLError(CE_Warning, CPLE_AppDefined,
×
1609
                         "Ignoring %s, %s and %s when %s, "
1610
                         "%s, %s and %s are specified",
1611
                         MD_XSIZE, MD_YSIZE, MD_GEOTRANSFORM, MD_MINX, MD_MINY,
1612
                         MD_MAXX, MD_MAXY);
1613
            }
1614
            const double dfMinX = CPLAtof(pszMinX);
12✔
1615
            const double dfMinY = CPLAtof(pszMinY);
12✔
1616
            const double dfMaxX = CPLAtof(pszMaxX);
12✔
1617
            const double dfMaxY = CPLAtof(pszMaxY);
12✔
1618
            if (!(dfMaxX > dfMinX))
12✔
1619
            {
1620
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
1621
                         "%s metadata item must be > %s", MD_MAXX, MD_MINX);
1622
                return false;
1✔
1623
            }
1624
            if (!(dfMaxY > dfMinY))
11✔
1625
            {
1626
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
1627
                         "%s metadata item must be > %s", MD_MAXY, MD_MINY);
1628
                return false;
1✔
1629
            }
1630
            sEnvelope.MinX = dfMinX;
10✔
1631
            sEnvelope.MinY = dfMinY;
10✔
1632
            sEnvelope.MaxX = dfMaxX;
10✔
1633
            sEnvelope.MaxY = dfMaxY;
10✔
1634
        }
1635
        else if (m_poLayer->GetExtent(&sEnvelope, /* bForce = */ false) ==
7✔
1636
                 OGRERR_FAILURE)
1637
        {
1638
            if (m_poLayer->GetExtent(&sEnvelope, /* bForce = */ true) ==
×
1639
                OGRERR_FAILURE)
1640
            {
1641
                CPLError(CE_Failure, CPLE_AppDefined,
×
1642
                         "Cannot get layer extent");
1643
                return false;
×
1644
            }
1645
            CPLError(CE_Warning, CPLE_AppDefined,
×
1646
                     "Could get layer extent, but using a slower method");
1647
        }
1648

1649
        const double dfXSize = (sEnvelope.MaxX - sEnvelope.MinX) / dfResX;
17✔
1650
        if (!(dfXSize >= 0 && dfXSize < INT_MAX))
17✔
1651
        {
1652
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1653
                     "Too small %s, or wrong layer extent", MD_RESX);
1654
            return false;
1✔
1655
        }
1656

1657
        const double dfYSize = (sEnvelope.MaxY - sEnvelope.MinY) / dfResY;
16✔
1658
        if (!(dfYSize >= 0 && dfYSize < INT_MAX))
16✔
1659
        {
1660
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1661
                     "Too small %s, or wrong layer extent", MD_RESY);
1662
            return false;
1✔
1663
        }
1664

1665
        m_adfGeoTransform[GT_TOPLEFT_X] = sEnvelope.MinX;
15✔
1666
        m_adfGeoTransform[GT_WE_RES] = dfResX;
15✔
1667
        m_adfGeoTransform[GT_ROTATION_PARAM1] = 0;
15✔
1668
        m_adfGeoTransform[GT_TOPLEFT_Y] = sEnvelope.MaxY;
15✔
1669
        m_adfGeoTransform[GT_ROTATION_PARAM2] = 0;
15✔
1670
        m_adfGeoTransform[GT_NS_RES] = -dfResY;
15✔
1671
        nRasterXSize = static_cast<int>(std::ceil(dfXSize));
15✔
1672
        nRasterYSize = static_cast<int>(std::ceil(dfYSize));
15✔
1673
    }
1674

1675
    if (nBandCount == 0 && !pszBandCount)
215✔
1676
    {
1677
        CPLError(CE_Failure, CPLE_AppDefined, "%s metadata item missing",
×
1678
                 MD_BAND_COUNT);
1679
        return false;
×
1680
    }
1681

1682
    if (!GDALCheckBandCount(nBandCount, false))
215✔
1683
        return false;
1✔
1684

1685
    if (aeDataTypes.empty() && !pszDataType)
214✔
1686
    {
1687
        aeDataTypes.resize(nBandCount, GDT_Byte);
9✔
1688
    }
1689
    else if (pszDataType)
205✔
1690
    {
1691
        aeDataTypes.clear();
8✔
1692
        const CPLStringList aosTokens(CSLTokenizeString2(pszDataType, ", ", 0));
8✔
1693
        if (aosTokens.size() == 1)
8✔
1694
        {
1695
            const auto eDataType = GDALGetDataTypeByName(aosTokens[0]);
6✔
1696
            if (eDataType == GDT_Unknown)
6✔
1697
            {
1698
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for %s",
1✔
1699
                         MD_DATA_TYPE);
1700
                return false;
1✔
1701
            }
1702
            aeDataTypes.resize(nBandCount, eDataType);
5✔
1703
        }
1704
        else if (aosTokens.size() == nBandCount)
2✔
1705
        {
1706
            for (int i = 0; i < nBandCount; ++i)
2✔
1707
            {
1708
                const auto eDataType = GDALGetDataTypeByName(aosTokens[i]);
2✔
1709
                if (eDataType == GDT_Unknown)
2✔
1710
                {
1711
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
1712
                             "Invalid value for %s", MD_DATA_TYPE);
1713
                    return false;
1✔
1714
                }
1715
                aeDataTypes.push_back(eDataType);
1✔
1716
            }
1717
        }
1718
        else
1719
        {
1720
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1721
                     "Number of values in %s must be 1 or %s", MD_DATA_TYPE,
1722
                     MD_BAND_COUNT);
1723
            return false;
1✔
1724
        }
1725
    }
1726

1727
    const char *pszNoData = GetOption(MD_NODATA);
211✔
1728
    if (pszNoData)
211✔
1729
    {
1730
        const auto IsValidNoDataStr = [](const char *pszStr)
20✔
1731
        {
1732
            if (EQUAL(pszStr, "inf") || EQUAL(pszStr, "-inf") ||
20✔
1733
                EQUAL(pszStr, "nan"))
16✔
1734
                return true;
6✔
1735
            const auto eType = CPLGetValueType(pszStr);
14✔
1736
            return eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL;
14✔
1737
        };
1738

1739
        aNoData.clear();
18✔
1740
        const CPLStringList aosTokens(CSLTokenizeString2(pszNoData, ", ", 0));
18✔
1741
        if (aosTokens.size() == 1)
18✔
1742
        {
1743
            if (!EQUAL(aosTokens[0], "NONE"))
14✔
1744
            {
1745
                if (!IsValidNoDataStr(aosTokens[0]))
11✔
1746
                {
1747
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
1748
                             "Invalid value for %s", MD_NODATA);
1749
                    return false;
1✔
1750
                }
1751
                aNoData.resize(nBandCount,
10✔
1752
                               std::pair(true, CPLAtof(aosTokens[0])));
20✔
1753
            }
1754
        }
1755
        else if (aosTokens.size() == nBandCount)
4✔
1756
        {
1757
            for (int i = 0; i < nBandCount; ++i)
12✔
1758
            {
1759
                if (EQUAL(aosTokens[i], "NONE"))
10✔
1760
                {
1761
                    aNoData.emplace_back(false, 0);
1✔
1762
                }
1763
                else if (IsValidNoDataStr(aosTokens[i]))
9✔
1764
                {
1765
                    aNoData.emplace_back(true, CPLAtof(aosTokens[i]));
8✔
1766
                }
1767
                else
1768
                {
1769
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
1770
                             "Invalid value for %s", MD_NODATA);
1771
                    return false;
1✔
1772
                }
1773
            }
1774
        }
1775
        else
1776
        {
1777
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1778
                     "Number of values in %s must be 1 or %s", MD_NODATA,
1779
                     MD_BAND_COUNT);
1780
            return false;
1✔
1781
        }
1782
    }
1783

1784
    if (pszColorInterp)
208✔
1785
    {
1786
        aeColorInterp.clear();
11✔
1787
        const CPLStringList aosTokens(
1788
            CSLTokenizeString2(pszColorInterp, ", ", 0));
11✔
1789
        if (aosTokens.size() == 1)
11✔
1790
        {
1791
            const auto eInterp = GDALGetColorInterpretationByName(aosTokens[0]);
7✔
1792
            if (eInterp == GCI_Undefined &&
12✔
1793
                !EQUAL(aosTokens[0],
5✔
1794
                       GDALGetColorInterpretationName(GCI_Undefined)))
1795
            {
1796
                CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for %s",
1✔
1797
                         MD_COLOR_INTERPRETATION);
1798
                return false;
1✔
1799
            }
1800
            aeColorInterp.resize(nBandCount, eInterp);
6✔
1801
        }
1802
        else if (aosTokens.size() == nBandCount)
4✔
1803
        {
1804
            for (int i = 0; i < nBandCount; ++i)
11✔
1805
            {
1806
                const auto eInterp =
1807
                    GDALGetColorInterpretationByName(aosTokens[i]);
9✔
1808
                if (eInterp == GCI_Undefined &&
11✔
1809
                    !EQUAL(aosTokens[i],
2✔
1810
                           GDALGetColorInterpretationName(GCI_Undefined)))
1811
                {
1812
                    CPLError(CE_Failure, CPLE_AppDefined,
1✔
1813
                             "Invalid value for %s", MD_COLOR_INTERPRETATION);
1814
                    return false;
1✔
1815
                }
1816
                aeColorInterp.emplace_back(eInterp);
8✔
1817
            }
1818
        }
1819
        else
1820
        {
1821
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1822
                     "Number of values in %s must be 1 or "
1823
                     "%s",
1824
                     MD_COLOR_INTERPRETATION, MD_BAND_COUNT);
1825
            return false;
1✔
1826
        }
1827
    }
1828

1829
    /* -------------------------------------------------------------------- */
1830
    /*      Create bands.                                                   */
1831
    /* -------------------------------------------------------------------- */
1832
    if (aeDataTypes.size() != static_cast<size_t>(nBandCount))
205✔
1833
    {
1834
        CPLError(
1✔
1835
            CE_Failure, CPLE_AppDefined,
1836
            "Number of data types values found not matching number of bands");
1837
        return false;
1✔
1838
    }
1839
    if (!aNoData.empty() && aNoData.size() != static_cast<size_t>(nBandCount))
204✔
1840
    {
1841
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1842
                 "Number of nodata values found not matching number of bands");
1843
        return false;
1✔
1844
    }
1845
    if (!aeColorInterp.empty() &&
398✔
1846
        aeColorInterp.size() != static_cast<size_t>(nBandCount))
195✔
1847
    {
1848
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1849
                 "Number of color interpretation values found not matching "
1850
                 "number of bands");
1851
        return false;
1✔
1852
    }
1853

1854
    int nBlockXSize = 256;
202✔
1855
    const char *pszBlockXSize = GetOption(MD_BLOCK_X_SIZE);
202✔
1856
    if (pszBlockXSize)
202✔
1857
    {
1858
        nBlockXSize = atoi(pszBlockXSize);
3✔
1859
        if (nBlockXSize <= 0)
3✔
1860
        {
1861
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s",
1✔
1862
                     MD_BLOCK_X_SIZE);
1863
            return false;
1✔
1864
        }
1865
    }
1866

1867
    int nBlockYSize = 256;
201✔
1868
    const char *pszBlockYSize = GetOption(MD_BLOCK_Y_SIZE);
201✔
1869
    if (pszBlockYSize)
201✔
1870
    {
1871
        nBlockYSize = atoi(pszBlockYSize);
3✔
1872
        if (nBlockYSize <= 0)
3✔
1873
        {
1874
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s",
1✔
1875
                     MD_BLOCK_Y_SIZE);
1876
            return false;
1✔
1877
        }
1878
    }
1879

1880
    if (nBlockXSize > INT_MAX / nBlockYSize)
200✔
1881
    {
1882
        CPLError(CE_Failure, CPLE_AppDefined, "Too big %s * %s",
1✔
1883
                 MD_BLOCK_X_SIZE, MD_BLOCK_Y_SIZE);
1884
        return false;
1✔
1885
    }
1886

1887
    if (dfOvrFactor > 1.0)
199✔
1888
    {
1889
        m_adfGeoTransform[GT_WE_RES] *= dfOvrFactor;
4✔
1890
        m_adfGeoTransform[GT_NS_RES] *= dfOvrFactor;
4✔
1891
        nRasterXSize = static_cast<int>(std::ceil(nRasterXSize / dfOvrFactor));
4✔
1892
        nRasterYSize = static_cast<int>(std::ceil(nRasterYSize / dfOvrFactor));
4✔
1893
    }
1894

1895
    std::vector<std::string> aosDescriptions;
398✔
1896
    std::vector<double> adfCenterWavelength;
398✔
1897
    std::vector<double> adfFullWidthHalfMax;
398✔
1898
    std::vector<double> adfScale;
398✔
1899
    std::vector<double> adfOffset;
398✔
1900
    if (bIsStacGeoParquet && poFeature)
199✔
1901
    {
1902
        const int nEOBandsIdx = poLayerDefn->GetFieldIndex(
5✔
1903
            CPLSPrintf("assets.%s.eo:bands", osAssetName.c_str()));
5✔
1904
        if (nEOBandsIdx >= 0 &&
5✔
1905
            poLayerDefn->GetFieldDefn(nEOBandsIdx)->GetSubType() == OFSTJSON &&
10✔
1906
            poFeature->IsFieldSet(nEOBandsIdx))
5✔
1907
        {
1908
            const char *pszStr = poFeature->GetFieldAsString(nEOBandsIdx);
5✔
1909
            CPLJSONDocument oDoc;
10✔
1910
            if (oDoc.LoadMemory(pszStr) &&
15✔
1911
                oDoc.GetRoot().GetType() == CPLJSONObject::Type::Array)
10✔
1912
            {
1913
                const auto oArray = oDoc.GetRoot().ToArray();
10✔
1914
                if (oArray.Size() == nBandCount)
5✔
1915
                {
1916
                    int i = 0;
5✔
1917
                    aosDescriptions.resize(nBandCount);
5✔
1918
                    adfCenterWavelength.resize(nBandCount);
5✔
1919
                    adfFullWidthHalfMax.resize(nBandCount);
5✔
1920
                    for (const auto &oObj : oArray)
13✔
1921
                    {
1922
                        if (oObj.GetType() == CPLJSONObject::Type::Object)
8✔
1923
                        {
1924
                            const auto osCommonName =
1925
                                oObj.GetString("common_name");
24✔
1926
                            const auto eInterp =
1927
                                GDALGetColorInterpFromSTACCommonName(
8✔
1928
                                    osCommonName.c_str());
1929
                            if (eInterp != GCI_Undefined)
8✔
1930
                                aeColorInterp[i] = eInterp;
8✔
1931

1932
                            const auto osName = oObj.GetString("name");
24✔
1933
                            aosDescriptions[i] = osName;
8✔
1934

1935
                            const auto osDescription =
1936
                                oObj.GetString("description");
16✔
1937
                            if (!osDescription.empty())
8✔
1938
                            {
1939
                                if (aosDescriptions[i].empty())
1✔
1940
                                    aosDescriptions[i] = osDescription;
×
1941
                                else
1942
                                    aosDescriptions[i]
1✔
1943
                                        .append(" (")
1✔
1944
                                        .append(osDescription)
1✔
1945
                                        .append(")");
1✔
1946
                            }
1947

1948
                            adfCenterWavelength[i] =
16✔
1949
                                oObj.GetDouble("center_wavelength");
8✔
1950
                            adfFullWidthHalfMax[i] =
16✔
1951
                                oObj.GetDouble("full_width_half_max");
8✔
1952
                        }
1953
                        ++i;
8✔
1954
                    }
1955
                }
1956
            }
1957
        }
1958

1959
        const int nRasterBandsIdx = poLayerDefn->GetFieldIndex(
5✔
1960
            CPLSPrintf("assets.%s.raster:bands", osAssetName.c_str()));
5✔
1961
        if (nRasterBandsIdx >= 0 &&
4✔
1962
            poLayerDefn->GetFieldDefn(nRasterBandsIdx)->GetSubType() ==
4✔
1963
                OFSTJSON &&
9✔
1964
            poFeature->IsFieldSet(nRasterBandsIdx))
4✔
1965
        {
1966
            const char *pszStr = poFeature->GetFieldAsString(nRasterBandsIdx);
4✔
1967
            CPLJSONDocument oDoc;
8✔
1968
            if (oDoc.LoadMemory(pszStr) &&
12✔
1969
                oDoc.GetRoot().GetType() == CPLJSONObject::Type::Array)
8✔
1970
            {
1971
                const auto oArray = oDoc.GetRoot().ToArray();
8✔
1972
                if (oArray.Size() == nBandCount)
4✔
1973
                {
1974
                    int i = 0;
4✔
1975
                    adfScale.resize(nBandCount,
4✔
1976
                                    std::numeric_limits<double>::quiet_NaN());
4✔
1977
                    adfOffset.resize(nBandCount,
4✔
1978
                                     std::numeric_limits<double>::quiet_NaN());
4✔
1979
                    for (const auto &oObj : oArray)
8✔
1980
                    {
1981
                        if (oObj.GetType() == CPLJSONObject::Type::Object)
4✔
1982
                        {
1983
                            adfScale[i] = oObj.GetDouble(
4✔
1984
                                "scale",
1985
                                std::numeric_limits<double>::quiet_NaN());
1986
                            adfOffset[i] = oObj.GetDouble(
4✔
1987
                                "offset",
1988
                                std::numeric_limits<double>::quiet_NaN());
1989
                        }
1990
                        ++i;
4✔
1991
                    }
1992
                }
1993
            }
1994
        }
1995
    }
1996

1997
    GDALTileIndexBand *poFirstBand = nullptr;
199✔
1998
    for (int i = 0; i < nBandCount; ++i)
456✔
1999
    {
2000
        GDALDataType eDataType = aeDataTypes[i];
257✔
2001
        if (!apoXMLNodeBands.empty())
257✔
2002
        {
2003
            const char *pszVal = CPLGetXMLValue(apoXMLNodeBands[i],
4✔
2004
                                                GTI_XML_BAND_DATATYPE, nullptr);
2005
            if (pszVal)
4✔
2006
            {
2007
                eDataType = GDALGetDataTypeByName(pszVal);
3✔
2008
                if (eDataType == GDT_Unknown)
3✔
2009
                    return false;
×
2010
            }
2011
        }
2012
        auto poBandUniquePtr = std::make_unique<GDALTileIndexBand>(
2013
            this, i + 1, eDataType, nBlockXSize, nBlockYSize);
514✔
2014
        auto poBand = poBandUniquePtr.get();
257✔
2015
        SetBand(i + 1, poBandUniquePtr.release());
257✔
2016
        if (!poFirstBand)
257✔
2017
            poFirstBand = poBand;
199✔
2018
        if (poBand->GetRasterDataType() != poFirstBand->GetRasterDataType())
257✔
2019
        {
2020
            m_bSameDataType = false;
×
2021
        }
2022

2023
        if (!aosDescriptions.empty() && !aosDescriptions[i].empty())
257✔
2024
        {
2025
            poBand->GDALRasterBand::SetDescription(aosDescriptions[i].c_str());
8✔
2026
        }
2027
        if (!apoXMLNodeBands.empty())
257✔
2028
        {
2029
            const char *pszVal = CPLGetXMLValue(
4✔
2030
                apoXMLNodeBands[i], GTI_XML_BAND_DESCRIPTION, nullptr);
4✔
2031
            if (pszVal)
4✔
2032
            {
2033
                poBand->GDALRasterBand::SetDescription(pszVal);
2✔
2034
            }
2035
        }
2036

2037
        if (!aNoData.empty() && aNoData[i].first)
257✔
2038
        {
2039
            poBand->m_bNoDataValueSet = true;
25✔
2040
            poBand->m_dfNoDataValue = aNoData[i].second;
25✔
2041
        }
2042
        if (!apoXMLNodeBands.empty())
257✔
2043
        {
2044
            const char *pszVal = CPLGetXMLValue(
4✔
2045
                apoXMLNodeBands[i], GTI_XML_BAND_NODATAVALUE, nullptr);
4✔
2046
            if (pszVal)
4✔
2047
            {
2048
                poBand->m_bNoDataValueSet = true;
3✔
2049
                poBand->m_dfNoDataValue = CPLAtof(pszVal);
3✔
2050
            }
2051
        }
2052
        if (poBand->m_bNoDataValueSet != poFirstBand->m_bNoDataValueSet ||
513✔
2053
            !IsSameNaNAware(poBand->m_dfNoDataValue,
256✔
2054
                            poFirstBand->m_dfNoDataValue))
2055
        {
2056
            m_bSameNoData = false;
6✔
2057
        }
2058

2059
        if (!aeColorInterp.empty())
257✔
2060
        {
2061
            poBand->m_eColorInterp = aeColorInterp[i];
246✔
2062
        }
2063
        if (!apoXMLNodeBands.empty())
257✔
2064
        {
2065
            const char *pszVal = CPLGetXMLValue(
4✔
2066
                apoXMLNodeBands[i], GTI_XML_BAND_COLORINTERP, nullptr);
4✔
2067
            if (pszVal)
4✔
2068
            {
2069
                poBand->m_eColorInterp =
4✔
2070
                    GDALGetColorInterpretationByName(pszVal);
4✔
2071
            }
2072
        }
2073

2074
        if (static_cast<int>(adfScale.size()) == nBandCount &&
261✔
2075
            !std::isnan(adfScale[i]))
4✔
2076
        {
2077
            poBand->m_dfScale = adfScale[i];
4✔
2078
        }
2079
        if (const char *pszScale =
257✔
2080
                GetOption(CPLSPrintf("BAND_%d_%s", i + 1, MD_BAND_SCALE)))
257✔
2081
        {
2082
            poBand->m_dfScale = CPLAtof(pszScale);
6✔
2083
        }
2084
        if (!apoXMLNodeBands.empty())
257✔
2085
        {
2086
            const char *pszVal =
2087
                CPLGetXMLValue(apoXMLNodeBands[i], GTI_XML_BAND_SCALE, nullptr);
4✔
2088
            if (pszVal)
4✔
2089
            {
2090
                poBand->m_dfScale = CPLAtof(pszVal);
2✔
2091
            }
2092
        }
2093

2094
        if (static_cast<int>(adfOffset.size()) == nBandCount &&
261✔
2095
            !std::isnan(adfOffset[i]))
4✔
2096
        {
2097
            poBand->m_dfOffset = adfOffset[i];
4✔
2098
        }
2099
        if (const char *pszOffset =
257✔
2100
                GetOption(CPLSPrintf("BAND_%d_%s", i + 1, MD_BAND_OFFSET)))
257✔
2101
        {
2102
            poBand->m_dfOffset = CPLAtof(pszOffset);
6✔
2103
        }
2104
        if (!apoXMLNodeBands.empty())
257✔
2105
        {
2106
            const char *pszVal = CPLGetXMLValue(apoXMLNodeBands[i],
4✔
2107
                                                GTI_XML_BAND_OFFSET, nullptr);
2108
            if (pszVal)
4✔
2109
            {
2110
                poBand->m_dfOffset = CPLAtof(pszVal);
2✔
2111
            }
2112
        }
2113

2114
        if (const char *pszUnit =
257✔
2115
                GetOption(CPLSPrintf("BAND_%d_%s", i + 1, MD_BAND_UNITTYPE)))
257✔
2116
        {
2117
            poBand->m_osUnit = pszUnit;
6✔
2118
        }
2119
        if (!apoXMLNodeBands.empty())
257✔
2120
        {
2121
            const char *pszVal = CPLGetXMLValue(apoXMLNodeBands[i],
4✔
2122
                                                GTI_XML_BAND_UNITTYPE, nullptr);
2123
            if (pszVal)
4✔
2124
            {
2125
                poBand->m_osUnit = pszVal;
2✔
2126
            }
2127
        }
2128

2129
        if (!apoXMLNodeBands.empty())
257✔
2130
        {
2131
            const CPLXMLNode *psBandNode = apoXMLNodeBands[i];
4✔
2132
            poBand->oMDMD.XMLInit(psBandNode, TRUE);
4✔
2133

2134
            if (const CPLXMLNode *psCategoryNames =
4✔
2135
                    CPLGetXMLNode(psBandNode, GTI_XML_CATEGORYNAMES))
4✔
2136
            {
2137
                poBand->m_aosCategoryNames =
2138
                    VRTParseCategoryNames(psCategoryNames);
2✔
2139
            }
2140

2141
            if (const CPLXMLNode *psColorTable =
4✔
2142
                    CPLGetXMLNode(psBandNode, GTI_XML_COLORTABLE))
4✔
2143
            {
2144
                poBand->m_poColorTable = VRTParseColorTable(psColorTable);
2✔
2145
            }
2146

2147
            if (const CPLXMLNode *psRAT =
4✔
2148
                    CPLGetXMLNode(psBandNode, GTI_XML_RAT))
4✔
2149
            {
2150
                poBand->m_poRAT =
2151
                    std::make_unique<GDALDefaultRasterAttributeTable>();
2✔
2152
                poBand->m_poRAT->XMLInit(psRAT, "");
2✔
2153
            }
2154
        }
2155

2156
        if (static_cast<int>(adfCenterWavelength.size()) == nBandCount &&
265✔
2157
            adfCenterWavelength[i] != 0)
8✔
2158
        {
2159
            poBand->GDALRasterBand::SetMetadataItem(
4✔
2160
                "CENTRAL_WAVELENGTH_UM",
2161
                CPLSPrintf("%g", adfCenterWavelength[i]), "IMAGERY");
4✔
2162
        }
2163

2164
        if (static_cast<int>(adfFullWidthHalfMax.size()) == nBandCount &&
265✔
2165
            adfFullWidthHalfMax[i] != 0)
8✔
2166
        {
2167
            poBand->GDALRasterBand::SetMetadataItem(
4✔
2168
                "FWHM_UM", CPLSPrintf("%g", adfFullWidthHalfMax[i]), "IMAGERY");
4✔
2169
        }
2170
    }
2171

2172
    if (nBandCount == 1 && poFirstBand && poSingleColorTable &&
199✔
2173
        !poFirstBand->m_poColorTable)
×
2174
        poFirstBand->m_poColorTable = std::move(poSingleColorTable);
×
2175

2176
    const char *pszMaskBand = GetOption(MD_MASK_BAND);
199✔
2177
    if (pszMaskBand)
199✔
2178
        bHasMaskBand = CPLTestBool(pszMaskBand);
7✔
2179
    if (bHasMaskBand)
199✔
2180
    {
2181
        m_poMaskBand = std::make_unique<GDALTileIndexBand>(
8✔
2182
            this, 0, GDT_Byte, nBlockXSize, nBlockYSize);
16✔
2183
    }
2184

2185
    if (dfOvrFactor == 1.0)
199✔
2186
    {
2187
        if (psRoot)
195✔
2188
        {
2189
            for (const CPLXMLNode *psIter = psRoot->psChild; psIter;
69✔
2190
                 psIter = psIter->psNext)
53✔
2191
            {
2192
                if (psIter->eType == CXT_Element &&
54✔
2193
                    strcmp(psIter->pszValue, GTI_XML_OVERVIEW_ELEMENT) == 0)
54✔
2194
                {
2195
                    const char *pszDataset = CPLGetXMLValue(
9✔
2196
                        psIter, GTI_XML_OVERVIEW_DATASET, nullptr);
2197
                    const char *pszLayer =
2198
                        CPLGetXMLValue(psIter, GTI_XML_OVERVIEW_LAYER, nullptr);
9✔
2199
                    const char *pszFactor = CPLGetXMLValue(
9✔
2200
                        psIter, GTI_XML_OVERVIEW_FACTOR, nullptr);
2201
                    if (!pszDataset && !pszLayer && !pszFactor)
9✔
2202
                    {
2203
                        CPLError(
1✔
2204
                            CE_Failure, CPLE_AppDefined,
2205
                            "At least one of %s, %s or %s element "
2206
                            "must be present as an %s child",
2207
                            GTI_XML_OVERVIEW_DATASET, GTI_XML_OVERVIEW_LAYER,
2208
                            GTI_XML_OVERVIEW_FACTOR, GTI_XML_OVERVIEW_ELEMENT);
2209
                        return false;
1✔
2210
                    }
2211
                    m_aoOverviewDescriptor.emplace_back(
2212
                        std::string(pszDataset ? pszDataset : ""),
16✔
2213
                        CPLStringList(
16✔
2214
                            GDALDeserializeOpenOptionsFromXML(psIter)),
2215
                        std::string(pszLayer ? pszLayer : ""),
16✔
2216
                        pszFactor ? CPLAtof(pszFactor) : 0.0);
24✔
2217
                }
2218
            }
2219
        }
2220
        else
2221
        {
2222
            for (int iOvr = 0;; ++iOvr)
179✔
2223
            {
2224
                const char *pszOvrDSName =
2225
                    GetOption(CPLSPrintf("OVERVIEW_%d_DATASET", iOvr));
359✔
2226
                const char *pszOpenOptions =
2227
                    GetOption(CPLSPrintf("OVERVIEW_%d_OPEN_OPTIONS", iOvr));
359✔
2228
                const char *pszOvrLayer =
2229
                    GetOption(CPLSPrintf("OVERVIEW_%d_LAYER", iOvr));
359✔
2230
                const char *pszOvrFactor =
2231
                    GetOption(CPLSPrintf("OVERVIEW_%d_FACTOR", iOvr));
359✔
2232
                if (!pszOvrDSName && !pszOvrLayer && !pszOvrFactor)
359✔
2233
                {
2234
                    // Before GDAL 3.9.2, we started the iteration at 1.
2235
                    if (iOvr == 0)
352✔
2236
                        continue;
173✔
2237
                    break;
179✔
2238
                }
2239
                m_aoOverviewDescriptor.emplace_back(
2240
                    std::string(pszOvrDSName ? pszOvrDSName : ""),
14✔
2241
                    pszOpenOptions ? CPLStringList(CSLTokenizeString2(
14✔
2242
                                         pszOpenOptions, ",", 0))
2243
                                   : CPLStringList(),
2244
                    std::string(pszOvrLayer ? pszOvrLayer : ""),
14✔
2245
                    pszOvrFactor ? CPLAtof(pszOvrFactor) : 0.0);
21✔
2246
            }
180✔
2247
        }
2248
    }
2249

2250
    if (psRoot)
198✔
2251
    {
2252
        oMDMD.XMLInit(psRoot, TRUE);
17✔
2253
    }
2254
    else
2255
    {
2256
        // Set on the dataset all metadata items from the index layer which are
2257
        // not "reserved" keywords.
2258
        CSLConstList papszLayerMD = m_poLayer->GetMetadata();
181✔
2259
        for (const auto &[pszKey, pszValue] :
498✔
2260
             cpl::IterateNameValue(papszLayerMD))
679✔
2261
        {
2262
            if (STARTS_WITH_CI(pszKey, "OVERVIEW_"))
249✔
2263
            {
2264
                continue;
10✔
2265
            }
2266

2267
            bool bIsVRTItem = false;
239✔
2268
            for (const char *pszTest : apszTIOptions)
3,558✔
2269
            {
2270
                if (EQUAL(pszKey, pszTest))
3,498✔
2271
                {
2272
                    bIsVRTItem = true;
179✔
2273
                    break;
179✔
2274
                }
2275
            }
2276
            if (!bIsVRTItem)
239✔
2277
            {
2278
                if (STARTS_WITH_CI(pszKey, "BAND_"))
60✔
2279
                {
2280
                    const int nBandNr = atoi(pszKey + strlen("BAND_"));
52✔
2281
                    const char *pszNextUnderscore =
2282
                        strchr(pszKey + strlen("BAND_"), '_');
52✔
2283
                    if (pszNextUnderscore && nBandNr >= 1 && nBandNr <= nBands)
52✔
2284
                    {
2285
                        const char *pszKeyWithoutBand = pszNextUnderscore + 1;
42✔
2286
                        bool bIsReservedBandItem = false;
42✔
2287
                        for (const char *pszItem : apszReservedBandItems)
132✔
2288
                        {
2289
                            if (EQUAL(pszKeyWithoutBand, pszItem))
108✔
2290
                            {
2291
                                bIsReservedBandItem = true;
18✔
2292
                                break;
18✔
2293
                            }
2294
                        }
2295
                        if (!bIsReservedBandItem)
42✔
2296
                        {
2297
                            GetRasterBand(nBandNr)
24✔
2298
                                ->GDALRasterBand::SetMetadataItem(
24✔
2299
                                    pszKeyWithoutBand, pszValue);
2300
                        }
2301
                    }
2302
                }
2303
                else
2304
                {
2305
                    GDALDataset::SetMetadataItem(pszKey, pszValue);
8✔
2306
                }
2307
            }
2308
        }
2309
    }
2310

2311
    if (nBandCount > 1 && !GetMetadata("IMAGE_STRUCTURE"))
198✔
2312
    {
2313
        GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
28✔
2314
    }
2315

2316
    /* -------------------------------------------------------------------- */
2317
    /*      Initialize any PAM information.                                 */
2318
    /* -------------------------------------------------------------------- */
2319
    SetDescription(poOpenInfo->pszFilename);
198✔
2320
    TryLoadXML();
198✔
2321

2322
    /* -------------------------------------------------------------------- */
2323
    /*      Check for overviews.                                            */
2324
    /* -------------------------------------------------------------------- */
2325
    oOvManager.Initialize(this, poOpenInfo->pszFilename);
198✔
2326

2327
    return true;
198✔
2328
}
2329

2330
/************************************************************************/
2331
/*                        GetMetadataItem()                             */
2332
/************************************************************************/
2333

2334
const char *GDALTileIndexDataset::GetMetadataItem(const char *pszName,
89✔
2335
                                                  const char *pszDomain)
2336
{
2337
    if (pszName && pszDomain && EQUAL(pszDomain, "__DEBUG__"))
89✔
2338
    {
2339
        if (EQUAL(pszName, "SCANNED_ONE_FEATURE_AT_OPENING"))
20✔
2340
        {
2341
            return m_bScannedOneFeatureAtOpening ? "YES" : "NO";
4✔
2342
        }
2343
        else if (EQUAL(pszName, "NUMBER_OF_CONTRIBUTING_SOURCES"))
16✔
2344
        {
2345
            return CPLSPrintf("%d", static_cast<int>(m_aoSourceDesc.size()));
5✔
2346
        }
2347
        else if (EQUAL(pszName, "MULTI_THREADED_RASTERIO_LAST_USED"))
11✔
2348
        {
2349
            return m_bLastMustUseMultiThreading ? "1" : "0";
11✔
2350
        }
2351
    }
2352
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
69✔
2353
}
2354

2355
/************************************************************************/
2356
/*                TileIndexSupportsEditingLayerMetadata()               */
2357
/************************************************************************/
2358

2359
bool GDALTileIndexDataset::TileIndexSupportsEditingLayerMetadata() const
17✔
2360
{
2361
    return eAccess == GA_Update && m_poVectorDS->GetDriver() &&
27✔
2362
           EQUAL(m_poVectorDS->GetDriver()->GetDescription(), "GPKG");
27✔
2363
}
2364

2365
/************************************************************************/
2366
/*                        SetMetadataItem()                             */
2367
/************************************************************************/
2368

2369
CPLErr GDALTileIndexDataset::SetMetadataItem(const char *pszName,
3✔
2370
                                             const char *pszValue,
2371
                                             const char *pszDomain)
2372
{
2373
    if (m_bXMLUpdatable)
3✔
2374
    {
2375
        m_bXMLModified = true;
1✔
2376
        return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1✔
2377
    }
2378
    else if (TileIndexSupportsEditingLayerMetadata())
2✔
2379
    {
2380
        m_poLayer->SetMetadataItem(pszName, pszValue, pszDomain);
1✔
2381
        return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1✔
2382
    }
2383
    else
2384
    {
2385
        return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1✔
2386
    }
2387
}
2388

2389
/************************************************************************/
2390
/*                           SetMetadata()                              */
2391
/************************************************************************/
2392

2393
CPLErr GDALTileIndexDataset::SetMetadata(char **papszMD, const char *pszDomain)
3✔
2394
{
2395
    if (m_bXMLUpdatable)
3✔
2396
    {
2397
        m_bXMLModified = true;
1✔
2398
        return GDALDataset::SetMetadata(papszMD, pszDomain);
1✔
2399
    }
2400
    else if (TileIndexSupportsEditingLayerMetadata())
2✔
2401
    {
2402
        if (!pszDomain || pszDomain[0] == 0)
2✔
2403
        {
2404
            CPLStringList aosMD(CSLDuplicate(papszMD));
4✔
2405

2406
            // Reinject dataset reserved items
2407
            for (const char *pszItem : apszTIOptions)
44✔
2408
            {
2409
                if (!aosMD.FetchNameValue(pszItem))
42✔
2410
                {
2411
                    const char *pszValue = m_poLayer->GetMetadataItem(pszItem);
42✔
2412
                    if (pszValue)
42✔
2413
                    {
2414
                        aosMD.SetNameValue(pszItem, pszValue);
2✔
2415
                    }
2416
                }
2417
            }
2418

2419
            // Reinject band metadata
2420
            char **papszExistingLayerMD = m_poLayer->GetMetadata();
2✔
2421
            for (int i = 0; papszExistingLayerMD && papszExistingLayerMD[i];
17✔
2422
                 ++i)
2423
            {
2424
                if (STARTS_WITH_CI(papszExistingLayerMD[i], "BAND_"))
15✔
2425
                {
2426
                    aosMD.AddString(papszExistingLayerMD[i]);
12✔
2427
                }
2428
            }
2429

2430
            m_poLayer->SetMetadata(aosMD.List(), pszDomain);
4✔
2431
        }
2432
        else
2433
        {
2434
            m_poLayer->SetMetadata(papszMD, pszDomain);
×
2435
        }
2436
        return GDALDataset::SetMetadata(papszMD, pszDomain);
2✔
2437
    }
2438
    else
2439
    {
2440
        return GDALPamDataset::SetMetadata(papszMD, pszDomain);
×
2441
    }
2442
}
2443

2444
/************************************************************************/
2445
/*                     GDALTileIndexDatasetIdentify()                   */
2446
/************************************************************************/
2447

2448
static int GDALTileIndexDatasetIdentify(GDALOpenInfo *poOpenInfo)
84,459✔
2449
{
2450
    if (STARTS_WITH(poOpenInfo->pszFilename, GTI_PREFIX))
84,459✔
2451
        return true;
16✔
2452

2453
    if (STARTS_WITH(poOpenInfo->pszFilename, "<GDALTileIndexDataset"))
84,443✔
2454
        return true;
46✔
2455

2456
    if (poOpenInfo->nHeaderBytes >= 100 &&
84,397✔
2457
        STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
29,697✔
2458
                    "SQLite format 3"))
2459
    {
2460
        if (ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.gpkg"))
917✔
2461
        {
2462
            // Most likely handled by GTI driver, but we can't be sure
2463
            return GDAL_IDENTIFY_UNKNOWN;
493✔
2464
        }
2465
        else if (poOpenInfo->IsSingleAllowedDriver("GTI") &&
426✔
2466
                 poOpenInfo->IsExtensionEqualToCI("gpkg"))
2✔
2467
        {
2468
            return true;
2✔
2469
        }
2470
    }
2471

2472
    if (poOpenInfo->nHeaderBytes > 0 &&
83,902✔
2473
        (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0)
29,796✔
2474
    {
2475
        if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
59,582✔
2476
                   "<GDALTileIndexDataset") ||
29,780✔
2477
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.fgb") ||
59,569✔
2478
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.parquet"))
29,785✔
2479
        {
2480
            return true;
12✔
2481
        }
2482
        else if (poOpenInfo->IsSingleAllowedDriver("GTI") &&
29,776✔
2483
                 (poOpenInfo->IsExtensionEqualToCI("fgb") ||
×
UNCOV
2484
                  poOpenInfo->IsExtensionEqualToCI("parquet")))
×
2485
        {
2486
            return true;
×
2487
        }
2488
    }
2489

2490
    return false;
83,882✔
2491
}
2492

2493
/************************************************************************/
2494
/*                      GDALTileIndexDatasetOpen()                       */
2495
/************************************************************************/
2496

2497
static GDALDataset *GDALTileIndexDatasetOpen(GDALOpenInfo *poOpenInfo)
269✔
2498
{
2499
    if (GDALTileIndexDatasetIdentify(poOpenInfo) == GDAL_IDENTIFY_FALSE)
269✔
2500
        return nullptr;
×
2501
    auto poDS = std::make_unique<GDALTileIndexDataset>();
538✔
2502
    if (!poDS->Open(poOpenInfo))
269✔
2503
        return nullptr;
71✔
2504
    return poDS.release();
198✔
2505
}
2506

2507
/************************************************************************/
2508
/*                          ~GDALTileIndexDataset()                      */
2509
/************************************************************************/
2510

2511
GDALTileIndexDataset::~GDALTileIndexDataset()
538✔
2512
{
2513
    GDALTileIndexDataset::FlushCache(true);
269✔
2514
}
538✔
2515

2516
/************************************************************************/
2517
/*                              FlushCache()                            */
2518
/************************************************************************/
2519

2520
CPLErr GDALTileIndexDataset::FlushCache(bool bAtClosing)
270✔
2521
{
2522
    CPLErr eErr = CE_None;
270✔
2523
    if (bAtClosing && m_bXMLModified)
270✔
2524
    {
2525
        CPLXMLNode *psRoot =
2526
            CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
1✔
2527

2528
        // Suppress existing dataset metadata
2529
        while (true)
2530
        {
2531
            CPLXMLNode *psExistingMetadata = CPLGetXMLNode(psRoot, "Metadata");
1✔
2532
            if (!psExistingMetadata)
1✔
2533
                break;
1✔
2534
            CPLRemoveXMLChild(psRoot, psExistingMetadata);
×
2535
        }
×
2536

2537
        // Serialize new dataset metadata
2538
        if (CPLXMLNode *psMD = oMDMD.Serialize())
1✔
2539
            CPLAddXMLChild(psRoot, psMD);
1✔
2540

2541
        // Update existing band metadata
2542
        if (CPLGetXMLNode(psRoot, GTI_XML_BAND_ELEMENT))
1✔
2543
        {
2544
            for (CPLXMLNode *psIter = psRoot->psChild; psIter;
×
2545
                 psIter = psIter->psNext)
×
2546
            {
2547
                if (psIter->eType == CXT_Element &&
×
2548
                    strcmp(psIter->pszValue, GTI_XML_BAND_ELEMENT))
×
2549
                {
2550
                    const char *pszBand =
2551
                        CPLGetXMLValue(psIter, GTI_XML_BAND_NUMBER, nullptr);
×
2552
                    if (pszBand)
×
2553
                    {
2554
                        const int nBand = atoi(pszBand);
×
2555
                        if (nBand >= 1 && nBand <= nBands)
×
2556
                        {
2557
                            while (true)
2558
                            {
2559
                                CPLXMLNode *psExistingMetadata =
2560
                                    CPLGetXMLNode(psIter, "Metadata");
×
2561
                                if (!psExistingMetadata)
×
2562
                                    break;
×
2563
                                CPLRemoveXMLChild(psIter, psExistingMetadata);
×
2564
                            }
×
2565

2566
                            auto poBand = cpl::down_cast<GDALTileIndexBand *>(
×
2567
                                papoBands[nBand - 1]);
×
2568
                            if (CPLXMLNode *psMD = poBand->oMDMD.Serialize())
×
2569
                                CPLAddXMLChild(psIter, psMD);
×
2570
                        }
2571
                    }
2572
                }
2573
            }
2574
        }
2575
        else
2576
        {
2577
            // Create new band objects if they have metadata
2578
            std::vector<CPLXMLTreeCloser> aoBandXML;
2✔
2579
            bool bHasBandMD = false;
1✔
2580
            for (int i = 1; i <= nBands; ++i)
2✔
2581
            {
2582
                auto poBand =
2583
                    cpl::down_cast<GDALTileIndexBand *>(papoBands[i - 1]);
1✔
2584
                auto psMD = poBand->oMDMD.Serialize();
1✔
2585
                if (psMD)
1✔
2586
                    bHasBandMD = true;
1✔
2587
                aoBandXML.emplace_back(CPLXMLTreeCloser(psMD));
1✔
2588
            }
2589
            if (bHasBandMD)
1✔
2590
            {
2591
                for (int i = 1; i <= nBands; ++i)
2✔
2592
                {
2593
                    auto poBand =
2594
                        cpl::down_cast<GDALTileIndexBand *>(papoBands[i - 1]);
1✔
2595

2596
                    CPLXMLNode *psBand = CPLCreateXMLNode(psRoot, CXT_Element,
1✔
2597
                                                          GTI_XML_BAND_ELEMENT);
2598
                    CPLAddXMLAttributeAndValue(psBand, GTI_XML_BAND_NUMBER,
1✔
2599
                                               CPLSPrintf("%d", i));
2600
                    CPLAddXMLAttributeAndValue(
1✔
2601
                        psBand, GTI_XML_BAND_DATATYPE,
2602
                        GDALGetDataTypeName(poBand->GetRasterDataType()));
2603

2604
                    const char *pszDescription = poBand->GetDescription();
1✔
2605
                    if (pszDescription && pszDescription[0])
1✔
2606
                        CPLSetXMLValue(psBand, GTI_XML_BAND_DESCRIPTION,
×
2607
                                       pszDescription);
2608

2609
                    const auto eColorInterp = poBand->GetColorInterpretation();
1✔
2610
                    if (eColorInterp != GCI_Undefined)
1✔
2611
                        CPLSetXMLValue(
1✔
2612
                            psBand, GTI_XML_BAND_COLORINTERP,
2613
                            GDALGetColorInterpretationName(eColorInterp));
2614

2615
                    if (!std::isnan(poBand->m_dfOffset))
1✔
2616
                        CPLSetXMLValue(psBand, GTI_XML_BAND_OFFSET,
×
2617
                                       CPLSPrintf("%.16g", poBand->m_dfOffset));
2618

2619
                    if (!std::isnan(poBand->m_dfScale))
1✔
2620
                        CPLSetXMLValue(psBand, GTI_XML_BAND_SCALE,
×
2621
                                       CPLSPrintf("%.16g", poBand->m_dfScale));
2622

2623
                    if (!poBand->m_osUnit.empty())
1✔
2624
                        CPLSetXMLValue(psBand, GTI_XML_BAND_UNITTYPE,
×
2625
                                       poBand->m_osUnit.c_str());
2626

2627
                    if (poBand->m_bNoDataValueSet)
1✔
2628
                    {
2629
                        CPLSetXMLValue(
×
2630
                            psBand, GTI_XML_BAND_NODATAVALUE,
2631
                            VRTSerializeNoData(poBand->m_dfNoDataValue,
×
2632
                                               poBand->GetRasterDataType(), 18)
2633
                                .c_str());
2634
                    }
2635
                    if (aoBandXML[i - 1])
1✔
2636
                    {
2637
                        CPLAddXMLChild(psBand, aoBandXML[i - 1].release());
1✔
2638
                    }
2639
                }
2640
            }
2641
        }
2642

2643
        if (!CPLSerializeXMLTreeToFile(m_psXMLTree.get(), GetDescription()))
1✔
2644
            eErr = CE_Failure;
×
2645
    }
2646

2647
    // We also clear the cache of opened sources, in case the user would
2648
    // change the content of a source and would want the GTI dataset to see
2649
    // the refreshed content.
2650
    m_oMapSharedSources.clear();
270✔
2651
    m_dfLastMinXFilter = std::numeric_limits<double>::quiet_NaN();
270✔
2652
    m_dfLastMinYFilter = std::numeric_limits<double>::quiet_NaN();
270✔
2653
    m_dfLastMaxXFilter = std::numeric_limits<double>::quiet_NaN();
270✔
2654
    m_dfLastMaxYFilter = std::numeric_limits<double>::quiet_NaN();
270✔
2655
    m_aoSourceDesc.clear();
270✔
2656
    if (GDALPamDataset::FlushCache(bAtClosing) != CE_None)
270✔
2657
        eErr = CE_Failure;
×
2658
    return eErr;
270✔
2659
}
2660

2661
/************************************************************************/
2662
/*                            LoadOverviews()                           */
2663
/************************************************************************/
2664

2665
void GDALTileIndexDataset::LoadOverviews()
41✔
2666
{
2667
    if (m_apoOverviews.empty() && !m_aoOverviewDescriptor.empty())
41✔
2668
    {
2669
        for (const auto &[osDSName, aosOpenOptions, osLyrName, dfFactor] :
28✔
2670
             m_aoOverviewDescriptor)
42✔
2671
        {
2672
            CPLStringList aosNewOpenOptions(aosOpenOptions);
28✔
2673
            if (dfFactor != 0)
14✔
2674
            {
2675
                aosNewOpenOptions.SetNameValue("@FACTOR",
2676
                                               CPLSPrintf("%.17g", dfFactor));
4✔
2677
            }
2678
            if (!osLyrName.empty())
14✔
2679
            {
2680
                aosNewOpenOptions.SetNameValue("@LAYER", osLyrName.c_str());
5✔
2681
            }
2682

2683
            std::unique_ptr<GDALDataset> poOvrDS(GDALDataset::Open(
2684
                !osDSName.empty() ? osDSName.c_str() : GetDescription(),
28✔
2685
                GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
2686
                aosNewOpenOptions.List(), nullptr));
56✔
2687

2688
            const auto IsSmaller =
2689
                [](const GDALDataset *a, const GDALDataset *b)
10✔
2690
            {
2691
                return (a->GetRasterXSize() < b->GetRasterXSize() &&
10✔
2692
                        a->GetRasterYSize() <= b->GetRasterYSize()) ||
11✔
2693
                       (a->GetRasterYSize() < b->GetRasterYSize() &&
1✔
2694
                        a->GetRasterXSize() <= b->GetRasterXSize());
10✔
2695
            };
2696

2697
            if (poOvrDS &&
32✔
2698
                ((m_apoOverviews.empty() && IsSmaller(poOvrDS.get(), this)) ||
18✔
2699
                 ((!m_apoOverviews.empty() &&
1✔
2700
                   IsSmaller(poOvrDS.get(), m_apoOverviews.back().get())))))
14✔
2701
            {
2702
                if (poOvrDS->GetRasterCount() == GetRasterCount())
8✔
2703
                {
2704
                    m_apoOverviews.emplace_back(std::move(poOvrDS));
8✔
2705
                    // Add the overviews of the overview, unless the OVERVIEW_LEVEL
2706
                    // option option is specified
2707
                    if (aosOpenOptions.FetchNameValue("OVERVIEW_LEVEL") ==
8✔
2708
                        nullptr)
2709
                    {
2710
                        const int nOverviewCount = m_apoOverviews.back()
7✔
2711
                                                       ->GetRasterBand(1)
7✔
2712
                                                       ->GetOverviewCount();
7✔
2713
                        for (int i = 0; i < nOverviewCount; ++i)
8✔
2714
                        {
2715
                            aosNewOpenOptions.SetNameValue("OVERVIEW_LEVEL",
2716
                                                           CPLSPrintf("%d", i));
1✔
2717
                            std::unique_ptr<GDALDataset> poOvrOfOvrDS(
2718
                                GDALDataset::Open(
2719
                                    !osDSName.empty() ? osDSName.c_str()
2✔
2720
                                                      : GetDescription(),
×
2721
                                    GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
2722
                                    nullptr, aosNewOpenOptions.List(),
1✔
2723
                                    nullptr));
3✔
2724
                            if (poOvrOfOvrDS &&
2✔
2725
                                poOvrOfOvrDS->GetRasterCount() ==
1✔
2726
                                    GetRasterCount() &&
3✔
2727
                                IsSmaller(poOvrOfOvrDS.get(),
1✔
2728
                                          m_apoOverviews.back().get()))
1✔
2729
                            {
2730
                                m_apoOverviews.emplace_back(
2731
                                    std::move(poOvrOfOvrDS));
1✔
2732
                            }
2733
                        }
2734
                    }
2735
                }
2736
                else
2737
                {
2738
                    CPLError(CE_Warning, CPLE_AppDefined,
×
2739
                             "%s has not the same number of bands as %s",
2740
                             poOvrDS->GetDescription(), GetDescription());
×
2741
                }
2742
            }
2743
        }
2744
    }
2745
}
41✔
2746

2747
/************************************************************************/
2748
/*                          GetOverviewCount()                          */
2749
/************************************************************************/
2750

2751
int GDALTileIndexBand::GetOverviewCount()
65✔
2752
{
2753
    const int nPAMOverviews = GDALPamRasterBand::GetOverviewCount();
65✔
2754
    if (nPAMOverviews)
65✔
2755
        return nPAMOverviews;
24✔
2756

2757
    m_poDS->LoadOverviews();
41✔
2758
    return static_cast<int>(m_poDS->m_apoOverviews.size());
41✔
2759
}
2760

2761
/************************************************************************/
2762
/*                             GetOverview()                            */
2763
/************************************************************************/
2764

2765
GDALRasterBand *GDALTileIndexBand::GetOverview(int iOvr)
35✔
2766
{
2767
    if (iOvr < 0 || iOvr >= GetOverviewCount())
35✔
2768
        return nullptr;
6✔
2769

2770
    const int nPAMOverviews = GDALPamRasterBand::GetOverviewCount();
29✔
2771
    if (nPAMOverviews)
29✔
2772
        return GDALPamRasterBand::GetOverview(iOvr);
16✔
2773

2774
    if (nBand == 0)
13✔
2775
    {
2776
        auto poBand = m_poDS->m_apoOverviews[iOvr]->GetRasterBand(1);
1✔
2777
        if (!poBand)
1✔
2778
            return nullptr;
×
2779
        return poBand->GetMaskBand();
1✔
2780
    }
2781
    else
2782
    {
2783
        return m_poDS->m_apoOverviews[iOvr]->GetRasterBand(nBand);
12✔
2784
    }
2785
}
2786

2787
/************************************************************************/
2788
/*                           GetGeoTransform()                          */
2789
/************************************************************************/
2790

2791
CPLErr GDALTileIndexDataset::GetGeoTransform(double *padfGeoTransform)
21✔
2792
{
2793
    memcpy(padfGeoTransform, m_adfGeoTransform.data(), 6 * sizeof(double));
21✔
2794
    return CE_None;
21✔
2795
}
2796

2797
/************************************************************************/
2798
/*                            GetSpatialRef()                           */
2799
/************************************************************************/
2800

2801
const OGRSpatialReference *GDALTileIndexDataset::GetSpatialRef() const
17✔
2802
{
2803
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
17✔
2804
}
2805

2806
/************************************************************************/
2807
/*                           GDALTileIndexBand()                         */
2808
/************************************************************************/
2809

2810
GDALTileIndexBand::GDALTileIndexBand(GDALTileIndexDataset *poDSIn, int nBandIn,
265✔
2811
                                     GDALDataType eDT, int nBlockXSizeIn,
2812
                                     int nBlockYSizeIn)
265✔
2813
{
2814
    m_poDS = poDSIn;
265✔
2815
    nBand = nBandIn;
265✔
2816
    eDataType = eDT;
265✔
2817
    nRasterXSize = poDSIn->GetRasterXSize();
265✔
2818
    nRasterYSize = poDSIn->GetRasterYSize();
265✔
2819
    nBlockXSize = nBlockXSizeIn;
265✔
2820
    nBlockYSize = nBlockYSizeIn;
265✔
2821
}
265✔
2822

2823
/************************************************************************/
2824
/*                             IReadBlock()                             */
2825
/************************************************************************/
2826

2827
CPLErr GDALTileIndexBand::IReadBlock(int nBlockXOff, int nBlockYOff,
3✔
2828
                                     void *pImage)
2829

2830
{
2831
    const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
3✔
2832

2833
    int nReadXSize = nBlockXSize;
3✔
2834
    int nReadYSize = nBlockYSize;
3✔
2835
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nReadXSize, &nReadYSize);
3✔
2836

2837
    GDALRasterIOExtraArg sExtraArg;
2838
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
3✔
2839

2840
    return IRasterIO(
6✔
2841
        GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
3✔
2842
        nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
2843
        static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
6✔
2844
}
2845

2846
/************************************************************************/
2847
/*                             IRasterIO()                              */
2848
/************************************************************************/
2849

2850
CPLErr GDALTileIndexBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
125✔
2851
                                    int nXSize, int nYSize, void *pData,
2852
                                    int nBufXSize, int nBufYSize,
2853
                                    GDALDataType eBufType, GSpacing nPixelSpace,
2854
                                    GSpacing nLineSpace,
2855
                                    GDALRasterIOExtraArg *psExtraArg)
2856
{
2857
    int anBand[] = {nBand};
125✔
2858

2859
    return m_poDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
125✔
2860
                             nBufXSize, nBufYSize, eBufType, 1, anBand,
2861
                             nPixelSpace, nLineSpace, 0, psExtraArg);
250✔
2862
}
2863

2864
/************************************************************************/
2865
/*                         IGetDataCoverageStatus()                     */
2866
/************************************************************************/
2867

2868
#ifndef HAVE_GEOS
2869
int GDALTileIndexBand::IGetDataCoverageStatus(int /* nXOff */, int /* nYOff */,
2870
                                              int /* nXSize */,
2871
                                              int /* nYSize */,
2872
                                              int /* nMaskFlagStop */,
2873
                                              double *pdfDataPct)
2874
{
2875
    if (pdfDataPct != nullptr)
2876
        *pdfDataPct = -1.0;
2877
    return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
2878
           GDAL_DATA_COVERAGE_STATUS_DATA;
2879
}
2880
#else
2881
int GDALTileIndexBand::IGetDataCoverageStatus(int nXOff, int nYOff, int nXSize,
6✔
2882
                                              int nYSize, int nMaskFlagStop,
2883
                                              double *pdfDataPct)
2884
{
2885
    if (pdfDataPct != nullptr)
6✔
2886
        *pdfDataPct = -1.0;
6✔
2887

2888
    const double dfMinX = m_poDS->m_adfGeoTransform[GT_TOPLEFT_X] +
6✔
2889
                          nXOff * m_poDS->m_adfGeoTransform[GT_WE_RES];
6✔
2890
    const double dfMaxX =
2891
        dfMinX + nXSize * m_poDS->m_adfGeoTransform[GT_WE_RES];
6✔
2892
    const double dfMaxY = m_poDS->m_adfGeoTransform[GT_TOPLEFT_Y] +
6✔
2893
                          nYOff * m_poDS->m_adfGeoTransform[GT_NS_RES];
6✔
2894
    const double dfMinY =
2895
        dfMaxY + nYSize * m_poDS->m_adfGeoTransform[GT_NS_RES];
6✔
2896
    m_poDS->m_poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
6✔
2897
    m_poDS->m_poLayer->ResetReading();
6✔
2898

2899
    int nStatus = 0;
6✔
2900

2901
    auto poPolyNonCoveredBySources = std::make_unique<OGRPolygon>();
12✔
2902
    {
2903
        auto poLR = std::make_unique<OGRLinearRing>();
12✔
2904
        poLR->addPoint(nXOff, nYOff);
6✔
2905
        poLR->addPoint(nXOff, nYOff + nYSize);
6✔
2906
        poLR->addPoint(nXOff + nXSize, nYOff + nYSize);
6✔
2907
        poLR->addPoint(nXOff + nXSize, nYOff);
6✔
2908
        poLR->addPoint(nXOff, nYOff);
6✔
2909
        poPolyNonCoveredBySources->addRingDirectly(poLR.release());
6✔
2910
    }
2911
    while (true)
2912
    {
2913
        auto poFeature =
2914
            std::unique_ptr<OGRFeature>(m_poDS->m_poLayer->GetNextFeature());
10✔
2915
        if (!poFeature)
10✔
2916
            break;
2✔
2917
        if (!poFeature->IsFieldSetAndNotNull(m_poDS->m_nLocationFieldIndex))
8✔
2918
        {
2919
            continue;
×
2920
        }
2921

2922
        const auto poGeom = poFeature->GetGeometryRef();
8✔
2923
        if (!poGeom || poGeom->IsEmpty())
8✔
2924
            continue;
×
2925

2926
        OGREnvelope sSourceEnvelope;
8✔
2927
        poGeom->getEnvelope(&sSourceEnvelope);
8✔
2928

2929
        const double dfDstXOff =
2930
            std::max<double>(nXOff, (sSourceEnvelope.MinX -
16✔
2931
                                     m_poDS->m_adfGeoTransform[GT_TOPLEFT_X]) /
8✔
2932
                                        m_poDS->m_adfGeoTransform[GT_WE_RES]);
8✔
2933
        const double dfDstXOff2 = std::min<double>(
2934
            nXOff + nXSize,
8✔
2935
            (sSourceEnvelope.MaxX - m_poDS->m_adfGeoTransform[GT_TOPLEFT_X]) /
8✔
2936
                m_poDS->m_adfGeoTransform[GT_WE_RES]);
8✔
2937
        const double dfDstYOff =
2938
            std::max<double>(nYOff, (sSourceEnvelope.MaxY -
16✔
2939
                                     m_poDS->m_adfGeoTransform[GT_TOPLEFT_Y]) /
8✔
2940
                                        m_poDS->m_adfGeoTransform[GT_NS_RES]);
8✔
2941
        const double dfDstYOff2 = std::min<double>(
2942
            nYOff + nYSize,
8✔
2943
            (sSourceEnvelope.MinY - m_poDS->m_adfGeoTransform[GT_TOPLEFT_Y]) /
8✔
2944
                m_poDS->m_adfGeoTransform[GT_NS_RES]);
8✔
2945

2946
        // CPLDebug("GTI", "dfDstXOff=%f, dfDstXOff2=%f, dfDstYOff=%f, dfDstYOff2=%f",
2947
        //         dfDstXOff, dfDstXOff2, dfDstYOff, dfDstXOff2);
2948

2949
        // Check if the AOI is fully inside the source
2950
        if (nXOff >= dfDstXOff && nYOff >= dfDstYOff &&
8✔
2951
            nXOff + nXSize <= dfDstXOff2 && nYOff + nYSize <= dfDstYOff2)
5✔
2952
        {
2953
            if (pdfDataPct)
2✔
2954
                *pdfDataPct = 100.0;
2✔
2955
            return GDAL_DATA_COVERAGE_STATUS_DATA;
2✔
2956
        }
2957

2958
        // Check intersection of bounding boxes.
2959
        if (dfDstXOff2 > nXOff && dfDstYOff2 > nYOff &&
6✔
2960
            dfDstXOff < nXOff + nXSize && dfDstYOff < nYOff + nYSize)
6✔
2961
        {
2962
            nStatus |= GDAL_DATA_COVERAGE_STATUS_DATA;
6✔
2963
            if (poPolyNonCoveredBySources)
6✔
2964
            {
2965
                OGRPolygon oPolySource;
6✔
2966
                auto poLR = std::make_unique<OGRLinearRing>();
6✔
2967
                poLR->addPoint(dfDstXOff, dfDstYOff);
6✔
2968
                poLR->addPoint(dfDstXOff, dfDstYOff2);
6✔
2969
                poLR->addPoint(dfDstXOff2, dfDstYOff2);
6✔
2970
                poLR->addPoint(dfDstXOff2, dfDstYOff);
6✔
2971
                poLR->addPoint(dfDstXOff, dfDstYOff);
6✔
2972
                oPolySource.addRingDirectly(poLR.release());
6✔
2973
                auto poRes = std::unique_ptr<OGRGeometry>(
2974
                    poPolyNonCoveredBySources->Difference(&oPolySource));
6✔
2975
                if (poRes && poRes->IsEmpty())
6✔
2976
                {
2977
                    if (pdfDataPct)
2✔
2978
                        *pdfDataPct = 100.0;
2✔
2979
                    return GDAL_DATA_COVERAGE_STATUS_DATA;
2✔
2980
                }
2981
                else if (poRes && poRes->getGeometryType() == wkbPolygon)
4✔
2982
                {
2983
                    poPolyNonCoveredBySources.reset(
4✔
2984
                        poRes.release()->toPolygon());
2985
                }
2986
                else
2987
                {
2988
                    poPolyNonCoveredBySources.reset();
×
2989
                }
2990
            }
2991
        }
2992
        if (nMaskFlagStop != 0 && (nStatus & nMaskFlagStop) != 0)
4✔
2993
        {
2994
            return nStatus;
×
2995
        }
2996
    }
4✔
2997
    if (poPolyNonCoveredBySources)
2✔
2998
    {
2999
        if (!poPolyNonCoveredBySources->IsEmpty())
2✔
3000
            nStatus |= GDAL_DATA_COVERAGE_STATUS_EMPTY;
2✔
3001
        if (pdfDataPct)
2✔
3002
            *pdfDataPct = 100.0 * (1.0 - poPolyNonCoveredBySources->get_Area() /
2✔
3003
                                             nXSize / nYSize);
2✔
3004
    }
3005
    return nStatus;
2✔
3006
}
3007
#endif  // HAVE_GEOS
3008

3009
/************************************************************************/
3010
/*                      GetMetadataDomainList()                         */
3011
/************************************************************************/
3012

3013
char **GDALTileIndexBand::GetMetadataDomainList()
1✔
3014
{
3015
    return CSLAddString(GDALRasterBand::GetMetadataDomainList(),
1✔
3016
                        "LocationInfo");
1✔
3017
}
3018

3019
/************************************************************************/
3020
/*                          GetMetadataItem()                           */
3021
/************************************************************************/
3022

3023
const char *GDALTileIndexBand::GetMetadataItem(const char *pszName,
28✔
3024
                                               const char *pszDomain)
3025

3026
{
3027
    /* ==================================================================== */
3028
    /*      LocationInfo handling.                                          */
3029
    /* ==================================================================== */
3030
    if (pszDomain != nullptr && EQUAL(pszDomain, "LocationInfo") &&
28✔
3031
        (STARTS_WITH_CI(pszName, "Pixel_") ||
19✔
3032
         STARTS_WITH_CI(pszName, "GeoPixel_")))
6✔
3033
    {
3034
        // What pixel are we aiming at?
3035
        int iPixel = 0;
18✔
3036
        int iLine = 0;
18✔
3037

3038
        if (STARTS_WITH_CI(pszName, "Pixel_"))
18✔
3039
        {
3040
            pszName += strlen("Pixel_");
13✔
3041
            iPixel = atoi(pszName);
13✔
3042
            const char *const pszUnderscore = strchr(pszName, '_');
13✔
3043
            if (!pszUnderscore)
13✔
3044
                return nullptr;
2✔
3045
            iLine = atoi(pszUnderscore + 1);
11✔
3046
        }
3047
        else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
5✔
3048
        {
3049
            pszName += strlen("GeoPixel_");
5✔
3050
            const double dfGeoX = CPLAtof(pszName);
5✔
3051
            const char *const pszUnderscore = strchr(pszName, '_');
5✔
3052
            if (!pszUnderscore)
5✔
3053
                return nullptr;
2✔
3054
            const double dfGeoY = CPLAtof(pszUnderscore + 1);
3✔
3055

3056
            double adfInvGeoTransform[6] = {0.0};
3✔
3057
            if (!GDALInvGeoTransform(m_poDS->m_adfGeoTransform.data(),
3✔
3058
                                     adfInvGeoTransform))
3059
                return nullptr;
×
3060

3061
            iPixel = static_cast<int>(floor(adfInvGeoTransform[0] +
3✔
3062
                                            adfInvGeoTransform[1] * dfGeoX +
3✔
3063
                                            adfInvGeoTransform[2] * dfGeoY));
3✔
3064
            iLine = static_cast<int>(floor(adfInvGeoTransform[3] +
3✔
3065
                                           adfInvGeoTransform[4] * dfGeoX +
3✔
3066
                                           adfInvGeoTransform[5] * dfGeoY));
3✔
3067
        }
3068
        else
3069
        {
3070
            return nullptr;
×
3071
        }
3072

3073
        if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
23✔
3074
            iLine >= GetYSize())
9✔
3075
            return nullptr;
6✔
3076

3077
        if (!m_poDS->CollectSources(iPixel, iLine, 1, 1,
8✔
3078
                                    /* bMultiThreadAllowed = */ false))
3079
            return nullptr;
×
3080

3081
        // Format into XML.
3082
        m_osLastLocationInfo = "<LocationInfo>";
8✔
3083

3084
        if (!m_poDS->m_aoSourceDesc.empty())
8✔
3085
        {
3086
            const auto AddSource =
3087
                [&](const GDALTileIndexDataset::SourceDesc &oSourceDesc)
6✔
3088
            {
3089
                m_osLastLocationInfo += "<File>";
6✔
3090
                char *const pszXMLEscaped =
3091
                    CPLEscapeString(oSourceDesc.osName.c_str(), -1, CPLES_XML);
6✔
3092
                m_osLastLocationInfo += pszXMLEscaped;
6✔
3093
                CPLFree(pszXMLEscaped);
6✔
3094
                m_osLastLocationInfo += "</File>";
6✔
3095
            };
11✔
3096

3097
            const int anBand[] = {nBand};
5✔
3098
            if (!m_poDS->NeedInitBuffer(1, anBand))
5✔
3099
            {
3100
                AddSource(m_poDS->m_aoSourceDesc.back());
4✔
3101
            }
3102
            else
3103
            {
3104
                for (const auto &oSourceDesc : m_poDS->m_aoSourceDesc)
3✔
3105
                {
3106
                    if (oSourceDesc.poDS)
2✔
3107
                        AddSource(oSourceDesc);
2✔
3108
                }
3109
            }
3110
        }
3111

3112
        m_osLastLocationInfo += "</LocationInfo>";
8✔
3113

3114
        return m_osLastLocationInfo.c_str();
8✔
3115
    }
3116

3117
    return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
10✔
3118
}
3119

3120
/************************************************************************/
3121
/*                        SetMetadataItem()                             */
3122
/************************************************************************/
3123

3124
CPLErr GDALTileIndexBand::SetMetadataItem(const char *pszName,
13✔
3125
                                          const char *pszValue,
3126
                                          const char *pszDomain)
3127
{
3128
    if (nBand > 0 && m_poDS->m_bXMLUpdatable)
13✔
3129
    {
3130
        m_poDS->m_bXMLModified = true;
1✔
3131
        return GDALRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
1✔
3132
    }
3133
    else if (nBand > 0 && m_poDS->TileIndexSupportsEditingLayerMetadata())
12✔
3134
    {
3135
        m_poDS->m_poLayer->SetMetadataItem(
6✔
3136
            CPLSPrintf("BAND_%d_%s", nBand, pszName), pszValue, pszDomain);
6✔
3137
        return GDALRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
6✔
3138
    }
3139
    else
3140
    {
3141
        return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
6✔
3142
    }
3143
}
3144

3145
/************************************************************************/
3146
/*                           SetMetadata()                              */
3147
/************************************************************************/
3148

3149
CPLErr GDALTileIndexBand::SetMetadata(char **papszMD, const char *pszDomain)
2✔
3150
{
3151
    if (nBand > 0 && m_poDS->m_bXMLUpdatable)
2✔
3152
    {
3153
        m_poDS->m_bXMLModified = true;
1✔
3154
        return GDALRasterBand::SetMetadata(papszMD, pszDomain);
1✔
3155
    }
3156
    else if (nBand > 0 && m_poDS->TileIndexSupportsEditingLayerMetadata())
1✔
3157
    {
3158
        CPLStringList aosMD;
2✔
3159

3160
        if (!pszDomain || pszDomain[0] == 0)
1✔
3161
        {
3162
            // Reinject dataset metadata
3163
            char **papszLayerMD = m_poDS->m_poLayer->GetMetadata(pszDomain);
1✔
3164
            for (const char *const *papszIter = papszLayerMD;
14✔
3165
                 papszIter && *papszIter; ++papszIter)
14✔
3166
            {
3167
                if (!STARTS_WITH(*papszIter, "BAND_") ||
13✔
3168
                    STARTS_WITH(*papszIter, MD_BAND_COUNT))
12✔
3169
                    aosMD.AddString(*papszIter);
1✔
3170
            }
3171
        }
3172

3173
        for (int i = 0; papszMD && papszMD[i]; ++i)
8✔
3174
        {
3175
            aosMD.AddString(CPLSPrintf("BAND_%d_%s", nBand, papszMD[i]));
7✔
3176
        }
3177

3178
        if (!pszDomain || pszDomain[0] == 0)
1✔
3179
        {
3180
            for (const char *pszItem : apszReservedBandItems)
4✔
3181
            {
3182
                const char *pszKey = CPLSPrintf("BAND_%d_%s", nBand, pszItem);
3✔
3183
                if (!aosMD.FetchNameValue(pszKey))
3✔
3184
                {
3185
                    if (const char *pszVal =
3✔
3186
                            m_poDS->m_poLayer->GetMetadataItem(pszKey))
3✔
3187
                    {
3188
                        aosMD.SetNameValue(pszKey, pszVal);
3✔
3189
                    }
3190
                }
3191
            }
3192
        }
3193

3194
        m_poDS->m_poLayer->SetMetadata(aosMD.List(), pszDomain);
1✔
3195
        return GDALRasterBand::SetMetadata(papszMD, pszDomain);
1✔
3196
    }
3197
    else
3198
    {
3199
        return GDALPamRasterBand::SetMetadata(papszMD, pszDomain);
×
3200
    }
3201
}
3202

3203
/************************************************************************/
3204
/*                         GetSrcDstWin()                               */
3205
/************************************************************************/
3206

3207
static bool GetSrcDstWin(const double adfTileGT[6], int nTileXSize,
349✔
3208
                         int nTileYSize, const double adfVRTGT[6],
3209
                         int nVRTXSize, int nVRTYSize, double *pdfSrcXOff,
3210
                         double *pdfSrcYOff, double *pdfSrcXSize,
3211
                         double *pdfSrcYSize, double *pdfDstXOff,
3212
                         double *pdfDstYOff, double *pdfDstXSize,
3213
                         double *pdfDstYSize)
3214
{
3215
    const double minX = adfVRTGT[GT_TOPLEFT_X];
349✔
3216
    const double we_res = adfVRTGT[GT_WE_RES];
349✔
3217
    const double maxX = minX + nVRTXSize * we_res;
349✔
3218
    const double maxY = adfVRTGT[GT_TOPLEFT_Y];
349✔
3219
    const double ns_res = adfVRTGT[GT_NS_RES];
349✔
3220
    const double minY = maxY + nVRTYSize * ns_res;
349✔
3221

3222
    /* Check that the destination bounding box intersects the source bounding
3223
     * box */
3224
    if (adfTileGT[GT_TOPLEFT_X] + nTileXSize * adfTileGT[GT_WE_RES] <= minX)
349✔
3225
        return false;
×
3226
    if (adfTileGT[GT_TOPLEFT_X] >= maxX)
349✔
3227
        return false;
1✔
3228
    if (adfTileGT[GT_TOPLEFT_Y] + nTileYSize * adfTileGT[GT_NS_RES] >= maxY)
348✔
3229
        return false;
×
3230
    if (adfTileGT[GT_TOPLEFT_Y] <= minY)
348✔
3231
        return false;
×
3232

3233
    if (adfTileGT[GT_TOPLEFT_X] < minX)
348✔
3234
    {
3235
        *pdfSrcXOff = (minX - adfTileGT[GT_TOPLEFT_X]) / adfTileGT[GT_WE_RES];
1✔
3236
        *pdfDstXOff = 0.0;
1✔
3237
    }
3238
    else
3239
    {
3240
        *pdfSrcXOff = 0.0;
347✔
3241
        *pdfDstXOff = ((adfTileGT[GT_TOPLEFT_X] - minX) / we_res);
347✔
3242
    }
3243
    if (maxY < adfTileGT[GT_TOPLEFT_Y])
348✔
3244
    {
3245
        *pdfSrcYOff = (adfTileGT[GT_TOPLEFT_Y] - maxY) / -adfTileGT[GT_NS_RES];
1✔
3246
        *pdfDstYOff = 0.0;
1✔
3247
    }
3248
    else
3249
    {
3250
        *pdfSrcYOff = 0.0;
347✔
3251
        *pdfDstYOff = ((maxY - adfTileGT[GT_TOPLEFT_Y]) / -ns_res);
347✔
3252
    }
3253

3254
    *pdfSrcXSize = nTileXSize;
348✔
3255
    *pdfSrcYSize = nTileYSize;
348✔
3256
    if (*pdfSrcXOff > 0)
348✔
3257
        *pdfSrcXSize -= *pdfSrcXOff;
1✔
3258
    if (*pdfSrcYOff > 0)
348✔
3259
        *pdfSrcYSize -= *pdfSrcYOff;
1✔
3260

3261
    const double dfSrcToDstXSize = adfTileGT[GT_WE_RES] / we_res;
348✔
3262
    *pdfDstXSize = *pdfSrcXSize * dfSrcToDstXSize;
348✔
3263
    const double dfSrcToDstYSize = adfTileGT[GT_NS_RES] / ns_res;
348✔
3264
    *pdfDstYSize = *pdfSrcYSize * dfSrcToDstYSize;
348✔
3265

3266
    if (*pdfDstXOff + *pdfDstXSize > nVRTXSize)
348✔
3267
    {
3268
        *pdfDstXSize = nVRTXSize - *pdfDstXOff;
3✔
3269
        *pdfSrcXSize = *pdfDstXSize / dfSrcToDstXSize;
3✔
3270
    }
3271

3272
    if (*pdfDstYOff + *pdfDstYSize > nVRTYSize)
348✔
3273
    {
3274
        *pdfDstYSize = nVRTYSize - *pdfDstYOff;
1✔
3275
        *pdfSrcYSize = *pdfDstYSize / dfSrcToDstYSize;
1✔
3276
    }
3277

3278
    return *pdfSrcXSize > 0 && *pdfDstXSize > 0 && *pdfSrcYSize > 0 &&
696✔
3279
           *pdfDstYSize > 0;
696✔
3280
}
3281

3282
/************************************************************************/
3283
/*                   GDALDatasetCastToGTIDataset()                    */
3284
/************************************************************************/
3285

3286
GDALTileIndexDataset *GDALDatasetCastToGTIDataset(GDALDataset *poDS)
3✔
3287
{
3288
    return dynamic_cast<GDALTileIndexDataset *>(poDS);
3✔
3289
}
3290

3291
/************************************************************************/
3292
/*                   GTIGetSourcesMoreRecentThan()                    */
3293
/************************************************************************/
3294

3295
std::vector<GTISourceDesc>
3296
GTIGetSourcesMoreRecentThan(GDALTileIndexDataset *poDS, int64_t mTime)
2✔
3297
{
3298
    return poDS->GetSourcesMoreRecentThan(mTime);
2✔
3299
}
3300

3301
/************************************************************************/
3302
/*                       GetSourcesMoreRecentThan()                     */
3303
/************************************************************************/
3304

3305
std::vector<GTISourceDesc>
3306
GDALTileIndexDataset::GetSourcesMoreRecentThan(int64_t mTime)
2✔
3307
{
3308
    std::vector<GTISourceDesc> oRes;
2✔
3309

3310
    m_poLayer->SetSpatialFilter(nullptr);
2✔
3311
    for (auto &&poFeature : m_poLayer)
6✔
3312
    {
3313
        if (!poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
4✔
3314
        {
3315
            continue;
2✔
3316
        }
3317

3318
        auto poGeom = poFeature->GetGeometryRef();
4✔
3319
        if (!poGeom || poGeom->IsEmpty())
4✔
3320
            continue;
×
3321

3322
        OGREnvelope sEnvelope;
4✔
3323
        poGeom->getEnvelope(&sEnvelope);
4✔
3324

3325
        double dfXOff = (sEnvelope.MinX - m_adfGeoTransform[GT_TOPLEFT_X]) /
4✔
3326
                        m_adfGeoTransform[GT_WE_RES];
4✔
3327
        if (dfXOff >= nRasterXSize)
4✔
3328
            continue;
×
3329

3330
        double dfYOff = (sEnvelope.MaxY - m_adfGeoTransform[GT_TOPLEFT_Y]) /
4✔
3331
                        m_adfGeoTransform[GT_NS_RES];
4✔
3332
        if (dfYOff >= nRasterYSize)
4✔
3333
            continue;
×
3334

3335
        double dfXSize =
3336
            (sEnvelope.MaxX - sEnvelope.MinX) / m_adfGeoTransform[GT_WE_RES];
4✔
3337
        if (dfXOff < 0)
4✔
3338
        {
3339
            dfXSize += dfXOff;
×
3340
            dfXOff = 0;
×
3341
            if (dfXSize <= 0)
×
3342
                continue;
×
3343
        }
3344

3345
        double dfYSize = (sEnvelope.MaxY - sEnvelope.MinY) /
4✔
3346
                         std::fabs(m_adfGeoTransform[GT_NS_RES]);
4✔
3347
        if (dfYOff < 0)
4✔
3348
        {
3349
            dfYSize += dfYOff;
×
3350
            dfYOff = 0;
×
3351
            if (dfYSize <= 0)
×
3352
                continue;
×
3353
        }
3354

3355
        const char *pszTileName =
3356
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
4✔
3357
        const std::string osTileName(
3358
            GetAbsoluteFileName(pszTileName, GetDescription()));
4✔
3359
        VSIStatBufL sStatSource;
3360
        if (VSIStatL(osTileName.c_str(), &sStatSource) != 0 ||
8✔
3361
            sStatSource.st_mtime <= mTime)
4✔
3362
        {
3363
            continue;
2✔
3364
        }
3365

3366
        constexpr double EPS = 1e-8;
2✔
3367
        GTISourceDesc oSourceDesc;
4✔
3368
        oSourceDesc.osFilename = osTileName;
2✔
3369
        oSourceDesc.nDstXOff = static_cast<int>(dfXOff + EPS);
2✔
3370
        oSourceDesc.nDstYOff = static_cast<int>(dfYOff + EPS);
2✔
3371
        oSourceDesc.nDstXSize = static_cast<int>(dfXSize + 0.5);
2✔
3372
        oSourceDesc.nDstYSize = static_cast<int>(dfYSize + 0.5);
2✔
3373
        oRes.emplace_back(std::move(oSourceDesc));
2✔
3374
    }
3375

3376
    return oRes;
2✔
3377
}
3378

3379
/************************************************************************/
3380
/*                         GetSourceDesc()                              */
3381
/************************************************************************/
3382

3383
bool GDALTileIndexDataset::GetSourceDesc(const std::string &osTileName,
351✔
3384
                                         SourceDesc &oSourceDesc,
3385
                                         std::mutex *pMutex)
3386
{
3387
    std::shared_ptr<GDALDataset> poTileDS;
351✔
3388

3389
    if (pMutex)
351✔
3390
        pMutex->lock();
137✔
3391
    const bool bTileKnown = m_oMapSharedSources.tryGet(osTileName, poTileDS);
351✔
3392
    if (pMutex)
351✔
3393
        pMutex->unlock();
137✔
3394

3395
    if (!bTileKnown)
351✔
3396
    {
3397
        poTileDS = std::shared_ptr<GDALDataset>(
454✔
3398
            GDALProxyPoolDataset::Create(
3399
                osTileName.c_str(), nullptr, GA_ReadOnly,
3400
                /* bShared = */ true, m_osUniqueHandle.c_str()),
3401
            GDALDatasetUniquePtrReleaser());
227✔
3402
        if (!poTileDS || poTileDS->GetRasterCount() == 0)
227✔
3403
        {
3404
            return false;
2✔
3405
        }
3406

3407
        // do palette -> RGB(A) expansion if needed
3408
        if (!GTIDoPaletteExpansionIfNeeded(poTileDS, nBands))
225✔
3409
            return false;
×
3410

3411
        const OGRSpatialReference *poTileSRS;
3412
        if (!m_oSRS.IsEmpty() &&
225✔
3413
            (poTileSRS = poTileDS->GetSpatialRef()) != nullptr &&
393✔
3414
            !m_oSRS.IsSame(poTileSRS))
168✔
3415
        {
3416
            CPLDebug("VRT",
2✔
3417
                     "Tile %s has not the same SRS as the VRT. "
3418
                     "Proceed to on-the-fly warping",
3419
                     osTileName.c_str());
3420

3421
            CPLStringList aosOptions;
2✔
3422
            aosOptions.AddString("-of");
2✔
3423
            aosOptions.AddString("VRT");
2✔
3424

3425
            if ((poTileDS->GetRasterBand(1)->GetColorTable() == nullptr &&
2✔
3426
                 poTileDS->GetRasterBand(1)->GetCategoryNames() == nullptr) ||
2✔
3427
                m_eResampling == GRIORA_Mode)
×
3428
            {
3429
                aosOptions.AddString("-r");
2✔
3430
                aosOptions.AddString(m_osResampling.c_str());
2✔
3431
            }
3432

3433
            if (m_osWKT.empty())
2✔
3434
            {
3435
                char *pszWKT = nullptr;
×
3436
                const char *const apszWKTOptions[] = {"FORMAT=WKT2_2019",
×
3437
                                                      nullptr};
3438
                m_oSRS.exportToWkt(&pszWKT, apszWKTOptions);
×
3439
                if (pszWKT)
×
3440
                    m_osWKT = pszWKT;
×
3441
                CPLFree(pszWKT);
×
3442
            }
3443
            if (m_osWKT.empty())
2✔
3444
            {
3445
                CPLError(CE_Failure, CPLE_AppDefined,
×
3446
                         "Cannot export VRT SRS to WKT2");
3447
                return false;
×
3448
            }
3449

3450
            aosOptions.AddString("-t_srs");
2✔
3451
            aosOptions.AddString(m_osWKT.c_str());
2✔
3452

3453
            // First pass to get the extent of the tile in the
3454
            // target VRT SRS
3455
            GDALWarpAppOptions *psWarpOptions =
3456
                GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
2✔
3457
            GDALDatasetH ahSrcDS[] = {GDALDataset::ToHandle(poTileDS.get())};
2✔
3458
            int bUsageError = false;
2✔
3459
            auto poWarpDS =
3460
                std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALWarp(
3461
                    "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
2✔
3462
            GDALWarpAppOptionsFree(psWarpOptions);
2✔
3463
            if (!poWarpDS)
2✔
3464
            {
3465
                return false;
×
3466
            }
3467

3468
            // Second pass to create a warped source VRT whose
3469
            // extent is aligned on the one of the target VRT
3470
            double adfWarpDSGeoTransform[6];
3471
            const auto eErr = poWarpDS->GetGeoTransform(adfWarpDSGeoTransform);
2✔
3472
            CPL_IGNORE_RET_VAL(eErr);
2✔
3473
            CPLAssert(eErr == CE_None);
2✔
3474
            const double dfVRTMinX = m_adfGeoTransform[GT_TOPLEFT_X];
2✔
3475
            const double dfVRTResX = m_adfGeoTransform[GT_WE_RES];
2✔
3476
            const double dfVRTMaxY = m_adfGeoTransform[GT_TOPLEFT_Y];
2✔
3477
            const double dfVRTResYAbs = -m_adfGeoTransform[GT_NS_RES];
2✔
3478
            const double dfWarpMinX =
2✔
3479
                std::floor((adfWarpDSGeoTransform[GT_TOPLEFT_X] - dfVRTMinX) /
2✔
3480
                           dfVRTResX) *
2✔
3481
                    dfVRTResX +
3482
                dfVRTMinX;
3483
            const double dfWarpMaxX =
3484
                std::ceil((adfWarpDSGeoTransform[GT_TOPLEFT_X] +
4✔
3485
                           adfWarpDSGeoTransform[GT_WE_RES] *
4✔
3486
                               poWarpDS->GetRasterXSize() -
2✔
3487
                           dfVRTMinX) /
3488
                          dfVRTResX) *
2✔
3489
                    dfVRTResX +
3490
                dfVRTMinX;
2✔
3491
            const double dfWarpMaxY =
2✔
3492
                dfVRTMaxY -
3493
                std::floor((dfVRTMaxY - adfWarpDSGeoTransform[GT_TOPLEFT_Y]) /
2✔
3494
                           dfVRTResYAbs) *
2✔
3495
                    dfVRTResYAbs;
3496
            const double dfWarpMinY =
3497
                dfVRTMaxY -
3498
                std::ceil((dfVRTMaxY - (adfWarpDSGeoTransform[GT_TOPLEFT_Y] +
4✔
3499
                                        adfWarpDSGeoTransform[GT_NS_RES] *
4✔
3500
                                            poWarpDS->GetRasterYSize())) /
2✔
3501
                          dfVRTResYAbs) *
2✔
3502
                    dfVRTResYAbs;
2✔
3503

3504
            aosOptions.AddString("-te");
2✔
3505
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinX));
2✔
3506
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinY));
2✔
3507
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxX));
2✔
3508
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxY));
2✔
3509

3510
            aosOptions.AddString("-tr");
2✔
3511
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResX));
2✔
3512
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResYAbs));
2✔
3513

3514
            aosOptions.AddString("-dstalpha");
2✔
3515

3516
            psWarpOptions = GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
2✔
3517
            poWarpDS.reset(GDALDataset::FromHandle(GDALWarp(
2✔
3518
                "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
3519
            GDALWarpAppOptionsFree(psWarpOptions);
2✔
3520
            if (!poWarpDS)
2✔
3521
            {
3522
                return false;
×
3523
            }
3524

3525
            poTileDS.reset(poWarpDS.release());
2✔
3526
        }
3527

3528
        if (pMutex)
225✔
3529
            pMutex->lock();
69✔
3530
        m_oMapSharedSources.insert(osTileName, poTileDS);
225✔
3531
        if (pMutex)
225✔
3532
            pMutex->unlock();
69✔
3533
    }
3534

3535
    double adfGeoTransformTile[6];
3536
    if (poTileDS->GetGeoTransform(adfGeoTransformTile) != CE_None)
349✔
3537
    {
3538
        CPLError(CE_Failure, CPLE_AppDefined, "%s lacks geotransform",
×
3539
                 osTileName.c_str());
3540
        return false;
×
3541
    }
3542

3543
    bool bHasNoData = false;
349✔
3544
    bool bSameNoData = true;
349✔
3545
    double dfNoDataValue = 0;
349✔
3546
    GDALRasterBand *poMaskBand = nullptr;
349✔
3547
    const int nBandCount = poTileDS->GetRasterCount();
349✔
3548
    for (int iBand = 0; iBand < nBandCount; ++iBand)
1,199✔
3549
    {
3550
        auto poTileBand = poTileDS->GetRasterBand(iBand + 1);
850✔
3551
        int bThisBandHasNoData = false;
850✔
3552
        const double dfThisBandNoDataValue =
3553
            poTileBand->GetNoDataValue(&bThisBandHasNoData);
850✔
3554
        if (bThisBandHasNoData)
850✔
3555
        {
3556
            bHasNoData = true;
10✔
3557
            dfNoDataValue = dfThisBandNoDataValue;
10✔
3558
        }
3559
        if (iBand > 0 &&
1,351✔
3560
            (static_cast<int>(bThisBandHasNoData) !=
501✔
3561
                 static_cast<int>(bHasNoData) ||
501✔
3562
             (bHasNoData &&
4✔
3563
              !IsSameNaNAware(dfNoDataValue, dfThisBandNoDataValue))))
4✔
3564
        {
3565
            bSameNoData = false;
×
3566
        }
3567

3568
        if (poTileBand->GetMaskFlags() == GMF_PER_DATASET)
850✔
3569
            poMaskBand = poTileBand->GetMaskBand();
2✔
3570
        else if (poTileBand->GetColorInterpretation() == GCI_AlphaBand)
848✔
3571
            poMaskBand = poTileBand;
31✔
3572
    }
3573

3574
    std::unique_ptr<VRTSimpleSource> poSource;
×
3575
    if (!bHasNoData)
349✔
3576
    {
3577
        poSource = std::make_unique<VRTSimpleSource>();
343✔
3578
    }
3579
    else
3580
    {
3581
        auto poComplexSource = std::make_unique<VRTComplexSource>();
12✔
3582
        poComplexSource->SetNoDataValue(dfNoDataValue);
6✔
3583
        poSource = std::move(poComplexSource);
6✔
3584
    }
3585

3586
    GetSrcDstWin(adfGeoTransformTile, poTileDS->GetRasterXSize(),
698✔
3587
                 poTileDS->GetRasterYSize(), m_adfGeoTransform.data(),
349✔
3588
                 GetRasterXSize(), GetRasterYSize(), &poSource->m_dfSrcXOff,
349✔
3589
                 &poSource->m_dfSrcYOff, &poSource->m_dfSrcXSize,
349✔
3590
                 &poSource->m_dfSrcYSize, &poSource->m_dfDstXOff,
349✔
3591
                 &poSource->m_dfDstYOff, &poSource->m_dfDstXSize,
349✔
3592
                 &poSource->m_dfDstYSize);
349✔
3593

3594
    oSourceDesc.osName = osTileName;
349✔
3595
    oSourceDesc.poDS = std::move(poTileDS);
349✔
3596
    oSourceDesc.poSource = std::move(poSource);
349✔
3597
    oSourceDesc.bHasNoData = bHasNoData;
349✔
3598
    oSourceDesc.bSameNoData = bSameNoData;
349✔
3599
    if (bSameNoData)
349✔
3600
        oSourceDesc.dfSameNoData = dfNoDataValue;
349✔
3601
    oSourceDesc.poMaskBand = poMaskBand;
349✔
3602
    return true;
349✔
3603
}
3604

3605
/************************************************************************/
3606
/*                           GetNumThreads()                            */
3607
/************************************************************************/
3608

3609
int GDALTileIndexDataset::GetNumThreads() const
7✔
3610
{
3611
    const char *pszNumThreads =
3612
        CSLFetchNameValueDef(GetOpenOptions(), "NUM_THREADS", nullptr);
7✔
3613
    if (!pszNumThreads)
7✔
3614
        pszNumThreads = CPLGetConfigOption("GTI_NUM_THREADS", nullptr);
7✔
3615
    if (!pszNumThreads)
7✔
3616
        pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS");
5✔
3617
    if (EQUAL(pszNumThreads, "0") || EQUAL(pszNumThreads, "1"))
7✔
3618
        return atoi(pszNumThreads);
2✔
3619
    const int nMaxPoolSize = GDALGetMaxDatasetPoolSize();
5✔
3620
    const int nLimit = std::min(CPLGetNumCPUs(), nMaxPoolSize);
5✔
3621
    if (EQUAL(pszNumThreads, "ALL_CPUS"))
5✔
3622
        return nLimit;
5✔
3623
    return std::min(atoi(pszNumThreads), nLimit);
×
3624
}
3625

3626
/************************************************************************/
3627
/*                        CollectSources()                              */
3628
/************************************************************************/
3629

3630
bool GDALTileIndexDataset::CollectSources(double dfXOff, double dfYOff,
171✔
3631
                                          double dfXSize, double dfYSize,
3632
                                          bool bMultiThreadAllowed)
3633
{
3634
    const double dfMinX =
3635
        m_adfGeoTransform[GT_TOPLEFT_X] + dfXOff * m_adfGeoTransform[GT_WE_RES];
171✔
3636
    const double dfMaxX = dfMinX + dfXSize * m_adfGeoTransform[GT_WE_RES];
171✔
3637
    const double dfMaxY =
3638
        m_adfGeoTransform[GT_TOPLEFT_Y] + dfYOff * m_adfGeoTransform[GT_NS_RES];
171✔
3639
    const double dfMinY = dfMaxY + dfYSize * m_adfGeoTransform[GT_NS_RES];
171✔
3640

3641
    if (dfMinX == m_dfLastMinXFilter && dfMinY == m_dfLastMinYFilter &&
171✔
3642
        dfMaxX == m_dfLastMaxXFilter && dfMaxY == m_dfLastMaxYFilter)
47✔
3643
    {
3644
        return true;
43✔
3645
    }
3646

3647
    m_dfLastMinXFilter = dfMinX;
128✔
3648
    m_dfLastMinYFilter = dfMinY;
128✔
3649
    m_dfLastMaxXFilter = dfMaxX;
128✔
3650
    m_dfLastMaxYFilter = dfMaxY;
128✔
3651
    m_bLastMustUseMultiThreading = false;
128✔
3652

3653
    m_poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
128✔
3654
    m_poLayer->ResetReading();
128✔
3655

3656
    m_aoSourceDesc.clear();
128✔
3657
    while (true)
3658
    {
3659
        auto poFeature =
3660
            std::unique_ptr<OGRFeature>(m_poLayer->GetNextFeature());
446✔
3661
        if (!poFeature)
446✔
3662
            break;
128✔
3663
        if (!poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
318✔
3664
        {
3665
            continue;
1✔
3666
        }
3667

3668
        SourceDesc oSourceDesc;
317✔
3669
        oSourceDesc.poFeature = std::move(poFeature);
317✔
3670
        m_aoSourceDesc.emplace_back(std::move(oSourceDesc));
317✔
3671

3672
        if (m_aoSourceDesc.size() > 10 * 1000 * 1000)
317✔
3673
        {
3674
            // Safety belt...
3675
            CPLError(CE_Failure, CPLE_AppDefined,
×
3676
                     "More than 10 million contributing sources to a "
3677
                     "single RasterIO() request is not supported");
3678
            return false;
×
3679
        }
3680
    }
318✔
3681

3682
    constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
128✔
3683
    if (bMultiThreadAllowed && m_aoSourceDesc.size() > 1 &&
194✔
3684
        dfXSize * dfYSize > MINIMUM_PIXEL_COUNT_FOR_THREADED_IO)
66✔
3685
    {
3686
        if (m_nNumThreads < 0)
7✔
3687
            m_nNumThreads = GetNumThreads();
7✔
3688
        bMultiThreadAllowed = m_nNumThreads > 1;
7✔
3689
    }
3690
    else
3691
    {
3692
        bMultiThreadAllowed = false;
121✔
3693
    }
3694

3695
    if (bMultiThreadAllowed)
128✔
3696
    {
3697
        CPLRectObj sGlobalBounds;
3698
        sGlobalBounds.minx = dfXOff;
5✔
3699
        sGlobalBounds.miny = dfYOff;
5✔
3700
        sGlobalBounds.maxx = dfXOff + dfXSize;
5✔
3701
        sGlobalBounds.maxy = dfYOff + dfYSize;
5✔
3702
        CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
5✔
3703

3704
        bool bCompatibleOfMultiThread = true;
5✔
3705
        std::set<std::string> oSetTileNames;
5✔
3706
        for (const auto &oSourceDesc : m_aoSourceDesc)
77✔
3707
        {
3708
            const char *pszTileName =
3709
                oSourceDesc.poFeature->GetFieldAsString(m_nLocationFieldIndex);
73✔
3710
            if (oSetTileNames.find(pszTileName) != oSetTileNames.end())
73✔
3711
            {
3712
                bCompatibleOfMultiThread = false;
×
3713
                break;
1✔
3714
            }
3715
            oSetTileNames.insert(pszTileName);
73✔
3716

3717
            const auto poGeom = oSourceDesc.poFeature->GetGeometryRef();
73✔
3718
            if (!poGeom || poGeom->IsEmpty())
73✔
3719
                continue;
×
3720

3721
            OGREnvelope sEnvelope;
73✔
3722
            poGeom->getEnvelope(&sEnvelope);
73✔
3723

3724
            CPLRectObj sSourceBounds;
3725
            sSourceBounds.minx =
73✔
3726
                (sEnvelope.MinX - m_adfGeoTransform[GT_TOPLEFT_X]) /
73✔
3727
                m_adfGeoTransform[GT_WE_RES];
73✔
3728
            sSourceBounds.maxx =
73✔
3729
                (sEnvelope.MaxX - m_adfGeoTransform[GT_TOPLEFT_X]) /
73✔
3730
                m_adfGeoTransform[GT_WE_RES];
73✔
3731
            // Yes use of MaxY to compute miny is intended given that MaxY is
3732
            // in georeferenced space whereas miny is in pixel space.
3733
            sSourceBounds.miny =
73✔
3734
                (sEnvelope.MaxY - m_adfGeoTransform[GT_TOPLEFT_Y]) /
73✔
3735
                m_adfGeoTransform[GT_NS_RES];
73✔
3736
            // Same here for maxy vs Miny
3737
            sSourceBounds.maxy =
73✔
3738
                (sEnvelope.MinY - m_adfGeoTransform[GT_TOPLEFT_Y]) /
73✔
3739
                m_adfGeoTransform[GT_NS_RES];
73✔
3740

3741
            // Clamp to global bounds and some epsilon to avoid adjacent tiles
3742
            // to be considered as overlapping
3743
            constexpr double EPSILON = 0.1;
73✔
3744
            sSourceBounds.minx =
73✔
3745
                std::max(sGlobalBounds.minx, sSourceBounds.minx) + EPSILON;
73✔
3746
            sSourceBounds.maxx =
73✔
3747
                std::min(sGlobalBounds.maxx, sSourceBounds.maxx) - EPSILON;
73✔
3748
            sSourceBounds.miny =
73✔
3749
                std::max(sGlobalBounds.miny, sSourceBounds.miny) + EPSILON;
73✔
3750
            sSourceBounds.maxy =
73✔
3751
                std::min(sGlobalBounds.maxy, sSourceBounds.maxy) - EPSILON;
73✔
3752

3753
            // Check that the new source doesn't overlap an existing one.
3754
            if (CPLQuadTreeHasMatch(hQuadTree, &sSourceBounds))
73✔
3755
            {
3756
                bCompatibleOfMultiThread = false;
1✔
3757
                break;
1✔
3758
            }
3759

3760
            CPLQuadTreeInsertWithBounds(
72✔
3761
                hQuadTree,
3762
                const_cast<void *>(static_cast<const void *>(&oSourceDesc)),
3763
                &sSourceBounds);
3764
        }
3765

3766
        CPLQuadTreeDestroy(hQuadTree);
5✔
3767

3768
        if (bCompatibleOfMultiThread)
5✔
3769
        {
3770
            m_bLastMustUseMultiThreading = true;
4✔
3771
            return true;
4✔
3772
        }
3773
    }
3774

3775
    if (m_aoSourceDesc.size() > 1)
124✔
3776
    {
3777
        SortSourceDesc();
63✔
3778
    }
3779

3780
    // Try to find the last (most prioritary) fully opaque source covering
3781
    // the whole AOI. We only need to start rendering from it.
3782
    size_t i = m_aoSourceDesc.size();
124✔
3783
    while (i > 0)
256✔
3784
    {
3785
        --i;
214✔
3786
        auto &poFeature = m_aoSourceDesc[i].poFeature;
214✔
3787
        const char *pszTileName =
3788
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
214✔
3789
        const std::string osTileName(
3790
            GetAbsoluteFileName(pszTileName, GetDescription()));
214✔
3791

3792
        SourceDesc oSourceDesc;
214✔
3793
        if (!GetSourceDesc(osTileName, oSourceDesc, nullptr))
214✔
3794
            continue;
1✔
3795

3796
        // Check consistency of bounding box in tile index vs actual
3797
        // extent of the tile.
3798
        double adfTileGT[6];
3799
        if (oSourceDesc.poDS->GetGeoTransform(adfTileGT) == CE_None &&
213✔
3800
            adfTileGT[GT_ROTATION_PARAM1] == 0 &&
426✔
3801
            adfTileGT[GT_ROTATION_PARAM2] == 0)
213✔
3802
        {
3803
            OGREnvelope sActualTileExtent;
213✔
3804
            sActualTileExtent.MinX = adfTileGT[GT_TOPLEFT_X];
213✔
3805
            sActualTileExtent.MaxX =
213✔
3806
                sActualTileExtent.MinX +
426✔
3807
                oSourceDesc.poDS->GetRasterXSize() * adfTileGT[GT_WE_RES];
213✔
3808
            sActualTileExtent.MaxY = adfTileGT[GT_TOPLEFT_Y];
213✔
3809
            sActualTileExtent.MinY =
213✔
3810
                sActualTileExtent.MaxY +
426✔
3811
                oSourceDesc.poDS->GetRasterYSize() * adfTileGT[GT_NS_RES];
213✔
3812
            const auto poGeom = poFeature->GetGeometryRef();
213✔
3813
            if (poGeom && !poGeom->IsEmpty())
213✔
3814
            {
3815
                OGREnvelope sGeomTileExtent;
213✔
3816
                poGeom->getEnvelope(&sGeomTileExtent);
213✔
3817
                sGeomTileExtent.MinX -= m_adfGeoTransform[GT_WE_RES];
213✔
3818
                sGeomTileExtent.MaxX += m_adfGeoTransform[GT_WE_RES];
213✔
3819
                sGeomTileExtent.MinY -= std::fabs(m_adfGeoTransform[GT_NS_RES]);
213✔
3820
                sGeomTileExtent.MaxY += std::fabs(m_adfGeoTransform[GT_NS_RES]);
213✔
3821
                if (!sGeomTileExtent.Contains(sActualTileExtent))
213✔
3822
                {
3823
                    if (!sGeomTileExtent.Intersects(sActualTileExtent))
2✔
3824
                    {
3825
                        CPLError(CE_Warning, CPLE_AppDefined,
1✔
3826
                                 "Tile index is out of sync with actual "
3827
                                 "extent of %s. Bounding box from tile index "
3828
                                 "is (%g, %g, %g, %g) does not intersect at "
3829
                                 "all bounding box from tile (%g, %g, %g, %g)",
3830
                                 osTileName.c_str(), sGeomTileExtent.MinX,
3831
                                 sGeomTileExtent.MinY, sGeomTileExtent.MaxX,
3832
                                 sGeomTileExtent.MaxY, sActualTileExtent.MinX,
3833
                                 sActualTileExtent.MinY, sActualTileExtent.MaxX,
3834
                                 sActualTileExtent.MaxY);
3835
                        continue;
1✔
3836
                    }
3837
                    CPLError(CE_Warning, CPLE_AppDefined,
1✔
3838
                             "Tile index is out of sync with actual extent "
3839
                             "of %s. Bounding box from tile index is (%g, %g, "
3840
                             "%g, %g) does not fully contain bounding box from "
3841
                             "tile (%g, %g, %g, %g)",
3842
                             osTileName.c_str(), sGeomTileExtent.MinX,
3843
                             sGeomTileExtent.MinY, sGeomTileExtent.MaxX,
3844
                             sGeomTileExtent.MaxY, sActualTileExtent.MinX,
3845
                             sActualTileExtent.MinY, sActualTileExtent.MaxX,
3846
                             sActualTileExtent.MaxY);
3847
                }
3848
            }
3849
        }
3850

3851
        const auto &poSource = oSourceDesc.poSource;
212✔
3852
        if (dfXOff >= poSource->m_dfDstXOff + poSource->m_dfDstXSize ||
212✔
3853
            dfYOff >= poSource->m_dfDstYOff + poSource->m_dfDstYSize ||
212✔
3854
            poSource->m_dfDstXOff >= dfXOff + dfXSize ||
634✔
3855
            poSource->m_dfDstYOff >= dfYOff + dfYSize)
210✔
3856
        {
3857
            // Can happen as some spatial filters select slightly more features
3858
            // than strictly needed.
3859
            continue;
2✔
3860
        }
3861

3862
        const bool bCoversWholeAOI =
3863
            (poSource->m_dfDstXOff <= dfXOff &&
210✔
3864
             poSource->m_dfDstYOff <= dfYOff &&
133✔
3865
             poSource->m_dfDstXOff + poSource->m_dfDstXSize >=
133✔
3866
                 dfXOff + dfXSize &&
463✔
3867
             poSource->m_dfDstYOff + poSource->m_dfDstYSize >=
120✔
3868
                 dfYOff + dfYSize);
120✔
3869
        oSourceDesc.bCoversWholeAOI = bCoversWholeAOI;
210✔
3870

3871
        m_aoSourceDesc[i] = std::move(oSourceDesc);
210✔
3872

3873
        if (m_aoSourceDesc[i].bCoversWholeAOI &&
210✔
3874
            !m_aoSourceDesc[i].bHasNoData && !m_aoSourceDesc[i].poMaskBand)
210✔
3875
        {
3876
            break;
82✔
3877
        }
3878
    }
3879

3880
    if (i > 0)
124✔
3881
    {
3882
        // Remove sources that will not be rendered
3883
        m_aoSourceDesc.erase(m_aoSourceDesc.begin(),
32✔
3884
                             m_aoSourceDesc.begin() + i);
64✔
3885
    }
3886

3887
    // Truncate the array when its last elements have no dataset
3888
    i = m_aoSourceDesc.size();
124✔
3889
    while (i > 0)
333✔
3890
    {
3891
        --i;
213✔
3892
        if (!m_aoSourceDesc[i].poDS)
213✔
3893
        {
3894
            m_aoSourceDesc.resize(i);
4✔
3895
            break;
4✔
3896
        }
3897
    }
3898

3899
    return true;
124✔
3900
}
3901

3902
/************************************************************************/
3903
/*                          SortSourceDesc()                            */
3904
/************************************************************************/
3905

3906
void GDALTileIndexDataset::SortSourceDesc()
63✔
3907
{
3908
    const auto eFieldType = m_nSortFieldIndex >= 0
63✔
3909
                                ? m_poLayer->GetLayerDefn()
63✔
3910
                                      ->GetFieldDefn(m_nSortFieldIndex)
47✔
3911
                                      ->GetType()
47✔
3912
                                : OFTMaxType;
63✔
3913
    std::sort(
63✔
3914
        m_aoSourceDesc.begin(), m_aoSourceDesc.end(),
3915
        [this, eFieldType](const SourceDesc &a, const SourceDesc &b)
1,810✔
3916
        {
3917
            const auto &poFeatureA = (m_bSortFieldAsc ? a : b).poFeature;
413✔
3918
            const auto &poFeatureB = (m_bSortFieldAsc ? b : a).poFeature;
413✔
3919
            if (m_nSortFieldIndex >= 0 &&
906✔
3920
                poFeatureA->IsFieldSetAndNotNull(m_nSortFieldIndex) &&
493✔
3921
                poFeatureB->IsFieldSetAndNotNull(m_nSortFieldIndex))
80✔
3922
            {
3923
                if (eFieldType == OFTString)
80✔
3924
                {
3925
                    const int nCmp =
3926
                        strcmp(poFeatureA->GetFieldAsString(m_nSortFieldIndex),
5✔
3927
                               poFeatureB->GetFieldAsString(m_nSortFieldIndex));
3928
                    if (nCmp < 0)
5✔
3929
                        return true;
1✔
3930
                    if (nCmp > 0)
4✔
3931
                        return false;
2✔
3932
                }
3933
                else if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
75✔
3934
                {
3935
                    const auto nA =
3936
                        poFeatureA->GetFieldAsInteger64(m_nSortFieldIndex);
45✔
3937
                    const auto nB =
3938
                        poFeatureB->GetFieldAsInteger64(m_nSortFieldIndex);
45✔
3939
                    if (nA < nB)
45✔
3940
                        return true;
3✔
3941
                    if (nA > nB)
42✔
3942
                        return false;
42✔
3943
                }
3944
                else if (eFieldType == OFTReal)
30✔
3945
                {
3946
                    const auto dfA =
3947
                        poFeatureA->GetFieldAsDouble(m_nSortFieldIndex);
3✔
3948
                    const auto dfB =
3949
                        poFeatureB->GetFieldAsDouble(m_nSortFieldIndex);
3✔
3950
                    if (dfA < dfB)
3✔
3951
                        return true;
1✔
3952
                    if (dfA > dfB)
2✔
3953
                        return false;
2✔
3954
                }
3955
                else if (eFieldType == OFTDate || eFieldType == OFTDateTime)
27✔
3956
                {
3957
                    const auto poFieldA =
3958
                        poFeatureA->GetRawFieldRef(m_nSortFieldIndex);
27✔
3959
                    const auto poFieldB =
3960
                        poFeatureB->GetRawFieldRef(m_nSortFieldIndex);
27✔
3961

3962
#define COMPARE_DATE_COMPONENT(comp)                                           \
3963
    do                                                                         \
3964
    {                                                                          \
3965
        if (poFieldA->Date.comp < poFieldB->Date.comp)                         \
3966
            return true;                                                       \
3967
        if (poFieldA->Date.comp > poFieldB->Date.comp)                         \
3968
            return false;                                                      \
3969
    } while (0)
3970

3971
                    COMPARE_DATE_COMPONENT(Year);
27✔
3972
                    COMPARE_DATE_COMPONENT(Month);
21✔
3973
                    COMPARE_DATE_COMPONENT(Day);
15✔
3974
                    COMPARE_DATE_COMPONENT(Hour);
9✔
3975
                    COMPARE_DATE_COMPONENT(Minute);
8✔
3976
                    COMPARE_DATE_COMPONENT(Second);
7✔
3977
                }
3978
                else
3979
                {
3980
                    CPLAssert(false);
×
3981
                }
3982
            }
3983
            return poFeatureA->GetFID() < poFeatureB->GetFID();
341✔
3984
        });
3985
}
63✔
3986

3987
/************************************************************************/
3988
/*                   CompositeSrcWithMaskIntoDest()                     */
3989
/************************************************************************/
3990

3991
static void
3992
CompositeSrcWithMaskIntoDest(const int nOutXSize, const int nOutYSize,
66✔
3993
                             const GDALDataType eBufType,
3994
                             const int nBufTypeSize, const GSpacing nPixelSpace,
3995
                             const GSpacing nLineSpace, const GByte *pabySrc,
3996
                             const GByte *const pabyMask, GByte *const pabyDest)
3997
{
3998
    size_t iMaskIdx = 0;
66✔
3999
    if (eBufType == GDT_Byte)
66✔
4000
    {
4001
        // Optimization for byte case
4002
        for (int iY = 0; iY < nOutYSize; iY++)
136✔
4003
        {
4004
            GByte *pabyDestLine =
86✔
4005
                pabyDest + static_cast<GPtrDiff_t>(iY * nLineSpace);
86✔
4006
            int iX = 0;
86✔
4007
#ifdef USE_SSE2_OPTIM
4008
            if (nPixelSpace == 1)
86✔
4009
            {
4010
                // SSE2 version up to 6 times faster than portable version
4011
                const auto xmm_zero = _mm_setzero_si128();
86✔
4012
                constexpr int SIZEOF_REG = static_cast<int>(sizeof(xmm_zero));
86✔
4013
                for (; iX + SIZEOF_REG <= nOutXSize; iX += SIZEOF_REG)
110✔
4014
                {
4015
                    auto xmm_mask = _mm_loadu_si128(
48✔
4016
                        reinterpret_cast<__m128i const *>(pabyMask + iMaskIdx));
4017
                    const auto xmm_src = _mm_loadu_si128(
24✔
4018
                        reinterpret_cast<__m128i const *>(pabySrc));
4019
                    auto xmm_dst = _mm_loadu_si128(
24✔
4020
                        reinterpret_cast<__m128i const *>(pabyDestLine));
4021
#ifdef USE_SSE41_OPTIM
4022
                    xmm_dst = _mm_blendv_epi8(xmm_dst, xmm_src, xmm_mask);
4023
#else
4024
                    // mask[i] = 0 becomes 255, and mask[i] != 0 becomes 0
4025
                    xmm_mask = _mm_cmpeq_epi8(xmm_mask, xmm_zero);
24✔
4026
                    // dst_data[i] = (mask[i] & dst_data[i]) |
4027
                    //               (~mask[i] & src_data[i])
4028
                    // That is:
4029
                    // dst_data[i] = dst_data[i] when mask[i] = 255
4030
                    // dst_data[i] = src_data[i] when mask[i] = 0
4031
                    xmm_dst = _mm_or_si128(_mm_and_si128(xmm_mask, xmm_dst),
72✔
4032
                                           _mm_andnot_si128(xmm_mask, xmm_src));
4033
#endif
4034
                    _mm_storeu_si128(reinterpret_cast<__m128i *>(pabyDestLine),
4035
                                     xmm_dst);
4036
                    pabyDestLine += SIZEOF_REG;
24✔
4037
                    pabySrc += SIZEOF_REG;
24✔
4038
                    iMaskIdx += SIZEOF_REG;
24✔
4039
                }
4040
            }
4041
#endif
4042
            for (; iX < nOutXSize; iX++)
342✔
4043
            {
4044
                if (pabyMask[iMaskIdx])
256✔
4045
                {
4046
                    *pabyDestLine = *pabySrc;
218✔
4047
                }
4048
                pabyDestLine += static_cast<GPtrDiff_t>(nPixelSpace);
256✔
4049
                pabySrc++;
256✔
4050
                iMaskIdx++;
256✔
4051
            }
4052
        }
4053
    }
4054
    else
4055
    {
4056
        for (int iY = 0; iY < nOutYSize; iY++)
38✔
4057
        {
4058
            GByte *pabyDestLine =
22✔
4059
                pabyDest + static_cast<GPtrDiff_t>(iY * nLineSpace);
22✔
4060
            for (int iX = 0; iX < nOutXSize; iX++)
54✔
4061
            {
4062
                if (pabyMask[iMaskIdx])
32✔
4063
                {
4064
                    memcpy(pabyDestLine, pabySrc, nBufTypeSize);
16✔
4065
                }
4066
                pabyDestLine += static_cast<GPtrDiff_t>(nPixelSpace);
32✔
4067
                pabySrc += nBufTypeSize;
32✔
4068
                iMaskIdx++;
32✔
4069
            }
4070
        }
4071
    }
4072
}
66✔
4073

4074
/************************************************************************/
4075
/*                         NeedInitBuffer()                             */
4076
/************************************************************************/
4077

4078
// Must be called after CollectSources()
4079
bool GDALTileIndexDataset::NeedInitBuffer(int nBandCount,
162✔
4080
                                          const int *panBandMap) const
4081
{
4082
    bool bNeedInitBuffer = true;
162✔
4083
    // If the last source (that is the most prioritary one) covers at least
4084
    // the window of interest and is fully opaque, then we don't need to
4085
    // initialize the buffer, and can directly render that source.
4086
    int bHasNoData = false;
162✔
4087
    if (!m_aoSourceDesc.empty() && m_aoSourceDesc.back().bCoversWholeAOI &&
320✔
4088
        (!m_aoSourceDesc.back().bHasNoData ||
146✔
4089
         // Also, if there's a single source and that the VRT bands and the
4090
         // source bands have the same nodata value, we can skip initialization.
4091
         (m_aoSourceDesc.size() == 1 && m_aoSourceDesc.back().bSameNoData &&
6✔
4092
          m_bSameNoData && m_bSameDataType &&
4✔
4093
          IsSameNaNAware(papoBands[0]->GetNoDataValue(&bHasNoData),
2✔
4094
                         m_aoSourceDesc.back().dfSameNoData) &&
2✔
4095
          bHasNoData)) &&
322✔
4096
        (!m_aoSourceDesc.back().poMaskBand ||
176✔
4097
         // Also, if there's a single source that has a mask band, and the VRT
4098
         // bands have no-nodata or a 0-nodata value, we can skip
4099
         // initialization.
4100
         (m_aoSourceDesc.size() == 1 && m_bSameDataType &&
43✔
4101
          !(nBandCount == 1 && panBandMap[0] == 0) && m_bSameNoData &&
7✔
4102
          papoBands[0]->GetNoDataValue(&bHasNoData) == 0)))
7✔
4103
    {
4104
        bNeedInitBuffer = false;
111✔
4105
    }
4106
    return bNeedInitBuffer;
162✔
4107
}
4108

4109
/************************************************************************/
4110
/*                            InitBuffer()                              */
4111
/************************************************************************/
4112

4113
void GDALTileIndexDataset::InitBuffer(void *pData, int nBufXSize, int nBufYSize,
56✔
4114
                                      GDALDataType eBufType, int nBandCount,
4115
                                      const int *panBandMap,
4116
                                      GSpacing nPixelSpace, GSpacing nLineSpace,
4117
                                      GSpacing nBandSpace) const
4118
{
4119
    const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
56✔
4120
    if (m_bSameNoData && nBandCount > 1 &&
56✔
4121
        ((nPixelSpace == nBufTypeSize &&
18✔
4122
          nLineSpace == nBufXSize * nPixelSpace &&
18✔
4123
          nBandSpace == nBufYSize * nLineSpace) ||
18✔
4124
         (nBandSpace == nBufTypeSize &&
×
4125
          nPixelSpace == nBandCount * nBandSpace &&
×
4126
          nLineSpace == nBufXSize * nPixelSpace)))
×
4127
    {
4128
        const int nBandNr = panBandMap[0];
18✔
4129
        auto poVRTBand =
4130
            nBandNr == 0
4131
                ? m_poMaskBand.get()
18✔
4132
                : cpl::down_cast<GDALTileIndexBand *>(papoBands[nBandNr - 1]);
18✔
4133
        CPLAssert(poVRTBand);
18✔
4134
        const double dfNoData = poVRTBand->m_dfNoDataValue;
18✔
4135
        if (dfNoData == 0.0)
18✔
4136
        {
4137
            memset(pData, 0,
16✔
4138
                   static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
16✔
4139
                       nBufTypeSize);
16✔
4140
        }
4141
        else
4142
        {
4143
            GDALCopyWords64(
2✔
4144
                &dfNoData, GDT_Float64, 0, pData, eBufType, nBufTypeSize,
4145
                static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount);
2✔
4146
        }
18✔
4147
    }
4148
    else
4149
    {
4150
        for (int i = 0; i < nBandCount; ++i)
77✔
4151
        {
4152
            const int nBandNr = panBandMap[i];
39✔
4153
            auto poVRTBand = nBandNr == 0 ? m_poMaskBand.get()
39✔
4154
                                          : cpl::down_cast<GDALTileIndexBand *>(
37✔
4155
                                                papoBands[nBandNr - 1]);
37✔
4156
            GByte *pabyBandData = static_cast<GByte *>(pData) + i * nBandSpace;
39✔
4157
            if (nPixelSpace == nBufTypeSize &&
39✔
4158
                poVRTBand->m_dfNoDataValue == 0.0)
39✔
4159
            {
4160
                if (nLineSpace == nBufXSize * nPixelSpace)
35✔
4161
                {
4162
                    memset(pabyBandData, 0,
35✔
4163
                           static_cast<size_t>(nBufYSize * nLineSpace));
35✔
4164
                }
4165
                else
4166
                {
4167
                    for (int iLine = 0; iLine < nBufYSize; iLine++)
×
4168
                    {
4169
                        memset(static_cast<GByte *>(pabyBandData) +
×
4170
                                   static_cast<GIntBig>(iLine) * nLineSpace,
×
4171
                               0, static_cast<size_t>(nBufXSize * nPixelSpace));
×
4172
                    }
4173
                }
35✔
4174
            }
4175
            else
4176
            {
4177
                double dfWriteValue = poVRTBand->m_dfNoDataValue;
4✔
4178

4179
                for (int iLine = 0; iLine < nBufYSize; iLine++)
12✔
4180
                {
4181
                    GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
8✔
4182
                                  static_cast<GByte *>(pabyBandData) +
8✔
4183
                                      static_cast<GIntBig>(nLineSpace) * iLine,
8✔
4184
                                  eBufType, static_cast<int>(nPixelSpace),
4185
                                  nBufXSize);
4186
                }
4187
            }
4188
        }
4189
    }
4190
}
56✔
4191

4192
/************************************************************************/
4193
/*                            RenderSource()                            */
4194
/************************************************************************/
4195

4196
CPLErr GDALTileIndexDataset::RenderSource(
460✔
4197
    const SourceDesc &oSourceDesc, bool bNeedInitBuffer, int nBandNrMax,
4198
    int nXOff, int nYOff, int nXSize, int nYSize, double dfXOff, double dfYOff,
4199
    double dfXSize, double dfYSize, int nBufXSize, int nBufYSize, void *pData,
4200
    GDALDataType eBufType, int nBandCount, BANDMAP_TYPE panBandMap,
4201
    GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace,
4202
    GDALRasterIOExtraArg *psExtraArg,
4203
    VRTSource::WorkingState &oWorkingState) const
4204
{
4205
    auto &poTileDS = oSourceDesc.poDS;
460✔
4206
    auto &poSource = oSourceDesc.poSource;
460✔
4207
    auto poComplexSource = dynamic_cast<VRTComplexSource *>(poSource.get());
460✔
4208
    CPLErr eErr = CE_None;
460✔
4209

4210
    if (poTileDS->GetRasterCount() + 1 == nBandNrMax &&
460✔
4211
        papoBands[nBandNrMax - 1]->GetColorInterpretation() == GCI_AlphaBand &&
464✔
4212
        papoBands[nBandNrMax - 1]->GetRasterDataType() == GDT_Byte)
4✔
4213
    {
4214
        // Special case when there's typically a mix of RGB and RGBA source
4215
        // datasets and we read a RGB one.
4216
        for (int iBand = 0; iBand < nBandCount && eErr == CE_None; ++iBand)
14✔
4217
        {
4218
            const int nBandNr = panBandMap[iBand];
10✔
4219
            if (nBandNr == nBandNrMax)
10✔
4220
            {
4221
                // The window we will actually request from the source raster band.
4222
                double dfReqXOff = 0.0;
4✔
4223
                double dfReqYOff = 0.0;
4✔
4224
                double dfReqXSize = 0.0;
4✔
4225
                double dfReqYSize = 0.0;
4✔
4226
                int nReqXOff = 0;
4✔
4227
                int nReqYOff = 0;
4✔
4228
                int nReqXSize = 0;
4✔
4229
                int nReqYSize = 0;
4✔
4230

4231
                // The window we will actual set _within_ the pData buffer.
4232
                int nOutXOff = 0;
4✔
4233
                int nOutYOff = 0;
4✔
4234
                int nOutXSize = 0;
4✔
4235
                int nOutYSize = 0;
4✔
4236

4237
                bool bError = false;
4✔
4238

4239
                auto poTileBand = poTileDS->GetRasterBand(1);
4✔
4240
                poSource->SetRasterBand(poTileBand, false);
4✔
4241
                if (poSource->GetSrcDstWindow(
4✔
4242
                        dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
4243
                        &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
4244
                        &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
4245
                        &nOutYOff, &nOutXSize, &nOutYSize, bError))
4✔
4246
                {
4247
                    GByte *pabyOut =
4✔
4248
                        static_cast<GByte *>(pData) +
4249
                        static_cast<GPtrDiff_t>(iBand * nBandSpace +
4✔
4250
                                                nOutXOff * nPixelSpace +
4✔
4251
                                                nOutYOff * nLineSpace);
4✔
4252

4253
                    constexpr GByte n255 = 255;
4✔
4254
                    for (int iY = 0; iY < nOutYSize; iY++)
8✔
4255
                    {
4256
                        GDALCopyWords(
4✔
4257
                            &n255, GDT_Byte, 0,
4258
                            pabyOut + static_cast<GPtrDiff_t>(iY * nLineSpace),
4✔
4259
                            eBufType, static_cast<int>(nPixelSpace), nOutXSize);
4260
                    }
4261
                }
4262
            }
4263
            else
4264
            {
4265
                auto poTileBand = poTileDS->GetRasterBand(nBandNr);
6✔
4266
                if (poComplexSource)
6✔
4267
                {
4268
                    int bHasNoData = false;
×
4269
                    const double dfNoDataValue =
4270
                        poTileBand->GetNoDataValue(&bHasNoData);
×
4271
                    poComplexSource->SetNoDataValue(
×
4272
                        bHasNoData ? dfNoDataValue : VRT_NODATA_UNSET);
×
4273
                }
4274
                poSource->SetRasterBand(poTileBand, false);
6✔
4275

4276
                GDALRasterIOExtraArg sExtraArg;
4277
                INIT_RASTERIO_EXTRA_ARG(sExtraArg);
6✔
4278
                if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
6✔
4279
                {
4280
                    // cppcheck-suppress redundantAssignment
4281
                    sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4282
                }
4283
                else
4284
                {
4285
                    // cppcheck-suppress redundantAssignment
4286
                    sExtraArg.eResampleAlg = m_eResampling;
6✔
4287
                }
4288

4289
                GByte *pabyBandData =
6✔
4290
                    static_cast<GByte *>(pData) + iBand * nBandSpace;
6✔
4291
                eErr = poSource->RasterIO(
12✔
4292
                    poTileBand->GetRasterDataType(), nXOff, nYOff, nXSize,
4293
                    nYSize, pabyBandData, nBufXSize, nBufYSize, eBufType,
4294
                    nPixelSpace, nLineSpace, &sExtraArg, oWorkingState);
6✔
4295
            }
4296
        }
4297
        return eErr;
4✔
4298
    }
4299
    else if (poTileDS->GetRasterCount() < nBandNrMax)
456✔
4300
    {
4301
        CPLError(CE_Failure, CPLE_AppDefined, "%s has not enough bands.",
2✔
4302
                 oSourceDesc.osName.c_str());
4303
        return CE_Failure;
2✔
4304
    }
4305

4306
    if ((oSourceDesc.poMaskBand && bNeedInitBuffer) || nBandNrMax == 0)
454✔
4307
    {
4308
        // The window we will actually request from the source raster band.
4309
        double dfReqXOff = 0.0;
55✔
4310
        double dfReqYOff = 0.0;
55✔
4311
        double dfReqXSize = 0.0;
55✔
4312
        double dfReqYSize = 0.0;
55✔
4313
        int nReqXOff = 0;
55✔
4314
        int nReqYOff = 0;
55✔
4315
        int nReqXSize = 0;
55✔
4316
        int nReqYSize = 0;
55✔
4317

4318
        // The window we will actual set _within_ the pData buffer.
4319
        int nOutXOff = 0;
55✔
4320
        int nOutYOff = 0;
55✔
4321
        int nOutXSize = 0;
55✔
4322
        int nOutYSize = 0;
55✔
4323

4324
        bool bError = false;
55✔
4325

4326
        auto poFirstTileBand = poTileDS->GetRasterBand(1);
55✔
4327
        poSource->SetRasterBand(poFirstTileBand, false);
55✔
4328
        if (poSource->GetSrcDstWindow(
55✔
4329
                dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
4330
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
4331
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
4332
                &nOutXSize, &nOutYSize, bError))
55✔
4333
        {
4334
            int iMaskBandIdx = -1;
55✔
4335
            if (eBufType == GDT_Byte && nBandNrMax == 0)
55✔
4336
            {
4337
                // when called from m_poMaskBand
4338
                iMaskBandIdx = 0;
4✔
4339
            }
4340
            else if (oSourceDesc.poMaskBand)
51✔
4341
            {
4342
                // If we request a Byte buffer and the mask band is actually
4343
                // one of the queried bands of this request, we can save
4344
                // requesting it separately.
4345
                const int nMaskBandNr = oSourceDesc.poMaskBand->GetBand();
51✔
4346
                if (eBufType == GDT_Byte && nMaskBandNr >= 1 &&
39✔
4347
                    nMaskBandNr <= poTileDS->GetRasterCount() &&
129✔
4348
                    poTileDS->GetRasterBand(nMaskBandNr) ==
39✔
4349
                        oSourceDesc.poMaskBand)
39✔
4350
                {
4351
                    for (int iBand = 0; iBand < nBandCount; ++iBand)
61✔
4352
                    {
4353
                        if (panBandMap[iBand] == nMaskBandNr)
44✔
4354
                        {
4355
                            iMaskBandIdx = iBand;
20✔
4356
                            break;
20✔
4357
                        }
4358
                    }
4359
                }
4360
            }
4361

4362
            GDALRasterIOExtraArg sExtraArg;
4363
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
55✔
4364
            if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
55✔
4365
            {
4366
                // cppcheck-suppress redundantAssignment
4367
                sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4368
            }
4369
            else
4370
            {
4371
                // cppcheck-suppress redundantAssignment
4372
                sExtraArg.eResampleAlg = m_eResampling;
55✔
4373
            }
4374
            sExtraArg.bFloatingPointWindowValidity = TRUE;
55✔
4375
            sExtraArg.dfXOff = dfReqXOff;
55✔
4376
            sExtraArg.dfYOff = dfReqYOff;
55✔
4377
            sExtraArg.dfXSize = dfReqXSize;
55✔
4378
            sExtraArg.dfYSize = dfReqYSize;
55✔
4379

4380
            if (iMaskBandIdx < 0 && oSourceDesc.abyMask.empty() &&
76✔
4381
                oSourceDesc.poMaskBand)
21✔
4382
            {
4383
                // Fetch the mask band
4384
                try
4385
                {
4386
                    oSourceDesc.abyMask.resize(static_cast<size_t>(nOutXSize) *
21✔
4387
                                               nOutYSize);
21✔
4388
                }
4389
                catch (const std::bad_alloc &)
×
4390
                {
4391
                    CPLError(CE_Failure, CPLE_OutOfMemory,
×
4392
                             "Cannot allocate working buffer for mask");
4393
                    return CE_Failure;
×
4394
                }
4395

4396
                if (oSourceDesc.poMaskBand->RasterIO(
21✔
4397
                        GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
4398
                        oSourceDesc.abyMask.data(), nOutXSize, nOutYSize,
21✔
4399
                        GDT_Byte, 0, 0, &sExtraArg) != CE_None)
21✔
4400
                {
4401
                    oSourceDesc.abyMask.clear();
×
4402
                    return CE_Failure;
×
4403
                }
4404
            }
4405

4406
            // Allocate a temporary contiguous buffer to receive pixel data
4407
            const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
55✔
4408
            const size_t nWorkBufferBandSize =
55✔
4409
                static_cast<size_t>(nOutXSize) * nOutYSize * nBufTypeSize;
55✔
4410
            std::vector<GByte> abyWorkBuffer;
55✔
4411
            try
4412
            {
4413
                abyWorkBuffer.resize(nBandCount * nWorkBufferBandSize);
55✔
4414
            }
4415
            catch (const std::bad_alloc &)
×
4416
            {
4417
                CPLError(CE_Failure, CPLE_OutOfMemory,
×
4418
                         "Cannot allocate working buffer");
4419
                return CE_Failure;
×
4420
            }
4421

4422
            const GByte *const pabyMask =
4423
                iMaskBandIdx >= 0
4424
                    ? abyWorkBuffer.data() + iMaskBandIdx * nWorkBufferBandSize
24✔
4425
                    : oSourceDesc.abyMask.data();
79✔
4426

4427
            if (nBandNrMax == 0)
55✔
4428
            {
4429
                // Special case when called from m_poMaskBand
4430
                if (poTileDS->GetRasterBand(1)->GetMaskBand()->RasterIO(
12✔
4431
                        GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
4432
                        abyWorkBuffer.data(), nOutXSize, nOutYSize, eBufType, 0,
6✔
4433
                        0, &sExtraArg) != CE_None)
6✔
4434
                {
4435
                    return CE_Failure;
×
4436
                }
4437
            }
4438
            else if (poTileDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
98✔
4439
                                        nReqYSize, abyWorkBuffer.data(),
49✔
4440
                                        nOutXSize, nOutYSize, eBufType,
4441
                                        nBandCount, panBandMap, 0, 0, 0,
4442
                                        &sExtraArg) != CE_None)
49✔
4443
            {
4444
                return CE_Failure;
×
4445
            }
4446

4447
            // Compose the temporary contiguous buffer into the target
4448
            // buffer, taking into account the mask
4449
            GByte *pabyOut = static_cast<GByte *>(pData) +
55✔
4450
                             static_cast<GPtrDiff_t>(nOutXOff * nPixelSpace +
55✔
4451
                                                     nOutYOff * nLineSpace);
55✔
4452

4453
            for (int iBand = 0; iBand < nBandCount && eErr == CE_None; ++iBand)
121✔
4454
            {
4455
                GByte *pabyDestBand =
66✔
4456
                    pabyOut + static_cast<GPtrDiff_t>(iBand * nBandSpace);
66✔
4457
                const GByte *pabySrc =
4458
                    abyWorkBuffer.data() + iBand * nWorkBufferBandSize;
66✔
4459

4460
                CompositeSrcWithMaskIntoDest(
66✔
4461
                    nOutXSize, nOutYSize, eBufType, nBufTypeSize, nPixelSpace,
4462
                    nLineSpace, pabySrc, pabyMask, pabyDestBand);
4463
            }
4464
        }
55✔
4465
    }
4466
    else if (m_bSameDataType && !bNeedInitBuffer && oSourceDesc.bHasNoData)
399✔
4467
    {
4468
        // We create a non-VRTComplexSource SimpleSource copy of poSource
4469
        // to be able to call DatasetRasterIO()
4470
        VRTSimpleSource oSimpleSource(poSource.get(), 1.0, 1.0);
1✔
4471

4472
        GDALRasterIOExtraArg sExtraArg;
4473
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1✔
4474
        if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
1✔
4475
        {
4476
            // cppcheck-suppress redundantAssignment
4477
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4478
        }
4479
        else
4480
        {
4481
            // cppcheck-suppress redundantAssignment
4482
            sExtraArg.eResampleAlg = m_eResampling;
1✔
4483
        }
4484

4485
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
1✔
4486
        oSimpleSource.SetRasterBand(poTileBand, false);
1✔
4487
        eErr = oSimpleSource.DatasetRasterIO(
1✔
4488
            papoBands[0]->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
1✔
4489
            pData, nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
4490
            nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
1✔
4491
    }
4492
    else if (m_bSameDataType && !poComplexSource)
398✔
4493
    {
4494
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
396✔
4495
        poSource->SetRasterBand(poTileBand, false);
396✔
4496

4497
        GDALRasterIOExtraArg sExtraArg;
4498
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
396✔
4499
        if (poTileBand->GetColorTable())
396✔
4500
        {
4501
            // cppcheck-suppress redundantAssignment
4502
            sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
×
4503
        }
4504
        else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
396✔
4505
        {
4506
            // cppcheck-suppress redundantAssignment
4507
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4508
        }
4509
        else
4510
        {
4511
            // cppcheck-suppress redundantAssignment
4512
            sExtraArg.eResampleAlg = m_eResampling;
396✔
4513
        }
4514

4515
        eErr = poSource->DatasetRasterIO(
792✔
4516
            papoBands[0]->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
396✔
4517
            pData, nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
4518
            nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
396✔
4519
    }
4520
    else
4521
    {
4522
        for (int i = 0; i < nBandCount && eErr == CE_None; ++i)
4✔
4523
        {
4524
            const int nBandNr = panBandMap[i];
2✔
4525
            GByte *pabyBandData = static_cast<GByte *>(pData) + i * nBandSpace;
2✔
4526
            auto poTileBand = poTileDS->GetRasterBand(nBandNr);
2✔
4527
            if (poComplexSource)
2✔
4528
            {
4529
                int bHasNoData = false;
2✔
4530
                const double dfNoDataValue =
4531
                    poTileBand->GetNoDataValue(&bHasNoData);
2✔
4532
                poComplexSource->SetNoDataValue(bHasNoData ? dfNoDataValue
2✔
4533
                                                           : VRT_NODATA_UNSET);
2✔
4534
            }
4535
            poSource->SetRasterBand(poTileBand, false);
2✔
4536

4537
            GDALRasterIOExtraArg sExtraArg;
4538
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2✔
4539
            if (poTileBand->GetColorTable())
2✔
4540
            {
4541
                // cppcheck-suppress redundantAssignment
4542
                sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
×
4543
            }
4544
            else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2✔
4545
            {
4546
                // cppcheck-suppress redundantAssignment
4547
                sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4548
            }
4549
            else
4550
            {
4551
                // cppcheck-suppress redundantAssignment
4552
                sExtraArg.eResampleAlg = m_eResampling;
2✔
4553
            }
4554

4555
            eErr = poSource->RasterIO(
4✔
4556
                papoBands[nBandNr - 1]->GetRasterDataType(), nXOff, nYOff,
2✔
4557
                nXSize, nYSize, pabyBandData, nBufXSize, nBufYSize, eBufType,
4558
                nPixelSpace, nLineSpace, &sExtraArg, oWorkingState);
2✔
4559
        }
4560
    }
4561
    return eErr;
454✔
4562
}
4563

4564
/************************************************************************/
4565
/*                             IRasterIO()                              */
4566
/************************************************************************/
4567

4568
CPLErr GDALTileIndexDataset::IRasterIO(
165✔
4569
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
4570
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
4571
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
4572
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
4573
{
4574
    if (eRWFlag != GF_Read)
165✔
4575
        return CE_Failure;
×
4576

4577
    if (nBufXSize < nXSize && nBufYSize < nYSize && AreOverviewsEnabled())
165✔
4578
    {
4579
        int bTried = FALSE;
2✔
4580
        const CPLErr eErr = TryOverviewRasterIO(
2✔
4581
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
4582
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
4583
            nBandSpace, psExtraArg, &bTried);
4584
        if (bTried)
2✔
4585
            return eErr;
2✔
4586
    }
4587

4588
    double dfXOff = nXOff;
163✔
4589
    double dfYOff = nYOff;
163✔
4590
    double dfXSize = nXSize;
163✔
4591
    double dfYSize = nYSize;
163✔
4592
    if (psExtraArg->bFloatingPointWindowValidity)
163✔
4593
    {
4594
        dfXOff = psExtraArg->dfXOff;
×
4595
        dfYOff = psExtraArg->dfYOff;
×
4596
        dfXSize = psExtraArg->dfXSize;
×
4597
        dfYSize = psExtraArg->dfYSize;
×
4598
    }
4599

4600
    if (!CollectSources(dfXOff, dfYOff, dfXSize, dfYSize,
163✔
4601
                        /* bMultiThreadAllowed = */ true))
4602
    {
4603
        return CE_Failure;
×
4604
    }
4605

4606
    // We might be called with nBandCount == 1 && panBandMap[0] == 0
4607
    // to mean m_poMaskBand
4608
    int nBandNrMax = 0;
163✔
4609
    for (int i = 0; i < nBandCount; ++i)
375✔
4610
    {
4611
        const int nBandNr = panBandMap[i];
212✔
4612
        nBandNrMax = std::max(nBandNrMax, nBandNr);
212✔
4613
    }
4614

4615
    const bool bNeedInitBuffer =
4616
        m_bLastMustUseMultiThreading || NeedInitBuffer(nBandCount, panBandMap);
163✔
4617

4618
    if (!bNeedInitBuffer)
163✔
4619
    {
4620
        return RenderSource(
107✔
4621
            m_aoSourceDesc.back(), bNeedInitBuffer, nBandNrMax, nXOff, nYOff,
107✔
4622
            nXSize, nYSize, dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize,
4623
            nBufYSize, pData, eBufType, nBandCount, panBandMap, nPixelSpace,
4624
            nLineSpace, nBandSpace, psExtraArg, m_oWorkingState);
214✔
4625
    }
4626
    else
4627
    {
4628
        InitBuffer(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
56✔
4629
                   panBandMap, nPixelSpace, nLineSpace, nBandSpace);
4630

4631
        if (m_bLastMustUseMultiThreading)
56✔
4632
        {
4633
            CPLErrorAccumulator oErrorAccumulator;
12✔
4634
            std::atomic<bool> bSuccess = true;
6✔
4635
            const int nContributingSources =
4636
                static_cast<int>(m_aoSourceDesc.size());
6✔
4637
            CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
6✔
4638
                std::min(nContributingSources, m_nNumThreads));
6✔
4639
            const int nThreads =
4640
                std::min(nContributingSources, psThreadPool->GetThreadCount());
6✔
4641
            CPLDebugOnly("GTI",
6✔
4642
                         "IRasterIO(): use optimized "
4643
                         "multi-threaded code path. "
4644
                         "Using %d threads",
4645
                         nThreads);
4646

4647
            {
4648
                std::lock_guard oLock(m_oQueueWorkingStates.oMutex);
12✔
4649
                if (m_oQueueWorkingStates.oStates.size() <
6✔
4650
                    static_cast<size_t>(nThreads))
6✔
4651
                {
4652
                    m_oQueueWorkingStates.oStates.resize(nThreads);
4✔
4653
                }
4654
                for (int i = 0; i < nThreads; ++i)
22✔
4655
                {
4656
                    if (!m_oQueueWorkingStates.oStates[i])
16✔
4657
                        m_oQueueWorkingStates.oStates[i] =
10✔
4658
                            std::make_unique<VRTSource::WorkingState>();
20✔
4659
                }
4660
            }
4661

4662
            auto oQueue = psThreadPool->CreateJobQueue();
6✔
4663
            std::atomic<int> nCompletedJobs = 0;
6✔
4664
            for (auto &oSourceDesc : m_aoSourceDesc)
144✔
4665
            {
4666
                auto psJob = new RasterIOJob();
138✔
4667
                psJob->poDS = this;
138✔
4668
                psJob->pbSuccess = &bSuccess;
138✔
4669
                psJob->poErrorAccumulator = &oErrorAccumulator;
138✔
4670
                psJob->pnCompletedJobs = &nCompletedJobs;
138✔
4671
                psJob->poQueueWorkingStates = &m_oQueueWorkingStates;
138✔
4672
                psJob->nBandNrMax = nBandNrMax;
138✔
4673
                psJob->nXOff = nXOff;
138✔
4674
                psJob->nYOff = nYOff;
138✔
4675
                psJob->nXSize = nXSize;
138✔
4676
                psJob->nYSize = nYSize;
138✔
4677
                psJob->pData = pData;
138✔
4678
                psJob->nBufXSize = nBufXSize;
138✔
4679
                psJob->nBufYSize = nBufYSize;
138✔
4680
                psJob->eBufType = eBufType;
138✔
4681
                psJob->nBandCount = nBandCount;
138✔
4682
                psJob->panBandMap = panBandMap;
138✔
4683
                psJob->nPixelSpace = nPixelSpace;
138✔
4684
                psJob->nLineSpace = nLineSpace;
138✔
4685
                psJob->nBandSpace = nBandSpace;
138✔
4686
                psJob->psExtraArg = psExtraArg;
138✔
4687

4688
                psJob->osTileName = oSourceDesc.poFeature->GetFieldAsString(
4689
                    m_nLocationFieldIndex);
138✔
4690

4691
                if (!oQueue->SubmitJob(RasterIOJob::Func, psJob))
138✔
4692
                {
4693
                    delete psJob;
×
4694
                    bSuccess = false;
×
4695
                    break;
×
4696
                }
4697
            }
4698

4699
            while (oQueue->WaitEvent())
63✔
4700
            {
4701
                // Quite rough progress callback. We could do better by counting
4702
                // the number of contributing pixels.
4703
                if (psExtraArg->pfnProgress)
57✔
4704
                {
4705
                    psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
112✔
4706
                                                nContributingSources,
4707
                                            "", psExtraArg->pProgressData);
4708
                }
4709
            }
4710

4711
            oErrorAccumulator.ReplayErrors();
6✔
4712

4713
            if (bSuccess && psExtraArg->pfnProgress)
6✔
4714
            {
4715
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4✔
4716
            }
4717

4718
            return bSuccess ? CE_None : CE_Failure;
6✔
4719
        }
4720
        else
4721
        {
4722
            // Now render from bottom of the stack to top.
4723
            for (auto &oSourceDesc : m_aoSourceDesc)
267✔
4724
            {
4725
                if (oSourceDesc.poDS &&
434✔
4726
                    RenderSource(oSourceDesc, bNeedInitBuffer, nBandNrMax,
217✔
4727
                                 nXOff, nYOff, nXSize, nYSize, dfXOff, dfYOff,
4728
                                 dfXSize, dfYSize, nBufXSize, nBufYSize, pData,
4729
                                 eBufType, nBandCount, panBandMap, nPixelSpace,
4730
                                 nLineSpace, nBandSpace, psExtraArg,
4731
                                 m_oWorkingState) != CE_None)
434✔
4732
                    return CE_Failure;
×
4733
            }
4734

4735
            if (psExtraArg->pfnProgress)
50✔
4736
            {
4737
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4✔
4738
            }
4739

4740
            return CE_None;
50✔
4741
        }
4742
    }
4743
}
4744

4745
/************************************************************************/
4746
/*                 GDALTileIndexDataset::RasterIOJob::Func()            */
4747
/************************************************************************/
4748

4749
void GDALTileIndexDataset::RasterIOJob::Func(void *pData)
138✔
4750
{
4751
    auto psJob =
4752
        std::unique_ptr<RasterIOJob>(static_cast<RasterIOJob *>(pData));
276✔
4753
    if (*psJob->pbSuccess)
138✔
4754
    {
4755
        const std::string osTileName(GetAbsoluteFileName(
4756
            psJob->osTileName.c_str(), psJob->poDS->GetDescription()));
274✔
4757

4758
        SourceDesc oSourceDesc;
274✔
4759

4760
        auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
274✔
4761
        CPL_IGNORE_RET_VAL(oAccumulator);
137✔
4762

4763
        const bool bCanOpenSource =
4764
            psJob->poDS->GetSourceDesc(osTileName, oSourceDesc,
137✔
4765
                                       &psJob->poQueueWorkingStates->oMutex) &&
273✔
4766
            oSourceDesc.poDS;
136✔
4767

4768
        if (!bCanOpenSource)
137✔
4769
        {
4770
            *psJob->pbSuccess = false;
1✔
4771
        }
4772
        else
4773
        {
4774
            GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
136✔
4775
            sArg.pfnProgress = nullptr;
136✔
4776
            sArg.pProgressData = nullptr;
136✔
4777

4778
            std::unique_ptr<VRTSource::WorkingState> poWorkingState;
136✔
4779
            {
4780
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
272✔
4781
                poWorkingState =
4782
                    std::move(psJob->poQueueWorkingStates->oStates.back());
136✔
4783
                psJob->poQueueWorkingStates->oStates.pop_back();
136✔
4784
                CPLAssert(poWorkingState.get());
136✔
4785
            }
4786

4787
            double dfXOff = psJob->nXOff;
136✔
4788
            double dfYOff = psJob->nYOff;
136✔
4789
            double dfXSize = psJob->nXSize;
136✔
4790
            double dfYSize = psJob->nYSize;
136✔
4791
            if (psJob->psExtraArg->bFloatingPointWindowValidity)
136✔
4792
            {
4793
                dfXOff = psJob->psExtraArg->dfXOff;
×
4794
                dfYOff = psJob->psExtraArg->dfYOff;
×
4795
                dfXSize = psJob->psExtraArg->dfXSize;
×
4796
                dfYSize = psJob->psExtraArg->dfYSize;
×
4797
            }
4798

4799
            const bool bRenderOK =
4800
                psJob->poDS->RenderSource(
272✔
4801
                    oSourceDesc, /*bNeedInitBuffer = */ true, psJob->nBandNrMax,
136✔
4802
                    psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
136✔
4803
                    dfXOff, dfYOff, dfXSize, dfYSize, psJob->nBufXSize,
136✔
4804
                    psJob->nBufYSize, psJob->pData, psJob->eBufType,
136✔
4805
                    psJob->nBandCount, psJob->panBandMap, psJob->nPixelSpace,
136✔
4806
                    psJob->nLineSpace, psJob->nBandSpace, &sArg,
136✔
4807
                    *(poWorkingState.get())) == CE_None;
136✔
4808

4809
            if (!bRenderOK)
136✔
4810
            {
4811
                *psJob->pbSuccess = false;
1✔
4812
            }
4813

4814
            {
4815
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
272✔
4816
                psJob->poQueueWorkingStates->oStates.push_back(
272✔
4817
                    std::move(poWorkingState));
136✔
4818
            }
4819
        }
4820
    }
4821

4822
    ++(*psJob->pnCompletedJobs);
138✔
4823
}
138✔
4824

4825
/************************************************************************/
4826
/*                     GDALGTICreateAlgorithm                           */
4827
/************************************************************************/
4828

4829
class GDALGTICreateAlgorithm final : public GDALRasterIndexAlgorithm
4830
{
4831
  public:
4832
    static constexpr const char *NAME = "create";
4833
    static constexpr const char *DESCRIPTION =
4834
        "Create an index of raster datasets compatible of the GDAL Tile Index "
4835
        "(GTI) driver.";
4836
    static constexpr const char *HELP_URL =
4837
        "/programs/gdal_driver_gti_create.html";
4838

4839
    GDALGTICreateAlgorithm();
4840

4841
  protected:
4842
    bool AddExtraOptions(CPLStringList &aosOptions) override;
4843

4844
  private:
4845
    std::string m_xmlFilename{};
4846
    std::vector<double> m_resolution{};
4847
    std::vector<double> m_bbox{};
4848
    std::string m_dataType{};
4849
    int m_bandCount = 0;
4850
    std::vector<double> m_nodata{};
4851
    std::vector<std::string> m_colorInterpretation{};
4852
    bool m_mask = false;
4853
    std::vector<std::string> m_fetchedMetadata{};
4854
};
4855

4856
/************************************************************************/
4857
/*          GDALGTICreateAlgorithm::GDALGTICreateAlgorithm()            */
4858
/************************************************************************/
4859

4860
GDALGTICreateAlgorithm::GDALGTICreateAlgorithm()
19✔
4861
    : GDALRasterIndexAlgorithm(NAME, DESCRIPTION, HELP_URL)
19✔
4862
{
4863
    AddProgressArg();
19✔
4864
    AddInputDatasetArg(&m_inputDatasets, GDAL_OF_RASTER)
19✔
4865
        .SetAutoOpenDataset(false);
19✔
4866
    GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs();
19✔
4867

4868
    AddCommonOptions();
19✔
4869

4870
    AddArg("xml-filename", 0,
4871
           _("Filename of the XML Virtual Tile Index file to generate, that "
4872
             "can be used as an input for the GDAL GTI / Virtual Raster Tile "
4873
             "Index driver"),
4874
           &m_xmlFilename)
38✔
4875
        .SetMinCharCount(1);
19✔
4876

4877
    AddArg("resolution", 0,
4878
           _("Resolution (in destination CRS units) of the virtual mosaic"),
4879
           &m_resolution)
38✔
4880
        .SetMinCount(2)
19✔
4881
        .SetMaxCount(2)
19✔
4882
        .SetMinValueExcluded(0)
19✔
4883
        .SetRepeatedArgAllowed(false)
19✔
4884
        .SetDisplayHintAboutRepetition(false)
19✔
4885
        .SetMetaVar("<xres>,<yres>");
19✔
4886

4887
    AddBBOXArg(
4888
        &m_bbox,
4889
        _("Bounding box (in destination CRS units) of the virtual mosaic"));
19✔
4890
    AddOutputDataTypeArg(&m_dataType, _("Datatype of the virtual mosaic"));
19✔
4891
    AddArg("band-count", 0, _("Number of bands of the virtual mosaic"),
4892
           &m_bandCount)
38✔
4893
        .SetMinValueIncluded(1);
19✔
4894
    AddArg("nodata", 0, _("Nodata value(s) of the bands of the virtual mosaic"),
4895
           &m_nodata);
19✔
4896
    AddArg("color-interpretation", 0,
4897
           _("Color interpretation(s) of the bands of the virtual mosaic"),
4898
           &m_colorInterpretation)
38✔
4899
        .SetChoices("red", "green", "blue", "alpha", "gray", "undefined");
19✔
4900
    AddArg("mask", 0, _("Defines that the virtual mosaic has a mask band"),
4901
           &m_mask);
19✔
4902
    AddArg("fetch-metadata", 0,
4903
           _("Fetch a metadata item from source rasters and write it as a "
4904
             "field in the index."),
4905
           &m_fetchedMetadata)
38✔
4906
        .SetMetaVar("<gdal-metadata-name>,<field-name>,<field-type>")
38✔
4907
        .SetPackedValuesAllowed(false)
19✔
4908
        .AddValidationAction(
4909
            [this]()
5✔
4910
            {
4911
                for (const std::string &s : m_fetchedMetadata)
4✔
4912
                {
4913
                    const CPLStringList aosTokens(
4914
                        CSLTokenizeString2(s.c_str(), ",", 0));
3✔
4915
                    if (aosTokens.size() != 3)
3✔
4916
                    {
4917
                        ReportError(
1✔
4918
                            CE_Failure, CPLE_IllegalArg,
4919
                            "'%s' is not of the form "
4920
                            "<gdal-metadata-name>,<field-name>,<field-type>",
4921
                            s.c_str());
4922
                        return false;
1✔
4923
                    }
4924
                    bool ok = false;
2✔
4925
                    for (const char *type : {"String", "Integer", "Integer64",
12✔
4926
                                             "Real", "Date", "DateTime"})
14✔
4927
                    {
4928
                        if (EQUAL(aosTokens[2], type))
12✔
4929
                            ok = true;
1✔
4930
                    }
4931
                    if (!ok)
2✔
4932
                    {
4933
                        ReportError(CE_Failure, CPLE_IllegalArg,
1✔
4934
                                    "'%s' has an invalid field type '%s'. It "
4935
                                    "should be one of 'String', 'Integer', "
4936
                                    "'Integer64', 'Real', 'Date', 'DateTime'.",
4937
                                    s.c_str(), aosTokens[2]);
4938
                        return false;
1✔
4939
                    }
4940
                }
4941
                return true;
1✔
4942
            });
19✔
4943
}
19✔
4944

4945
/************************************************************************/
4946
/*            GDALGTICreateAlgorithm::AddExtraOptions()                 */
4947
/************************************************************************/
4948

4949
bool GDALGTICreateAlgorithm::AddExtraOptions(CPLStringList &aosOptions)
4✔
4950
{
4951
    if (!m_xmlFilename.empty())
4✔
4952
    {
4953
        aosOptions.push_back("-gti_filename");
1✔
4954
        aosOptions.push_back(m_xmlFilename);
1✔
4955
    }
4956
    if (!m_resolution.empty())
4✔
4957
    {
4958
        aosOptions.push_back("-tr");
1✔
4959
        aosOptions.push_back(CPLSPrintf("%.17g", m_resolution[0]));
1✔
4960
        aosOptions.push_back(CPLSPrintf("%.17g", m_resolution[1]));
1✔
4961
    }
4962
    if (!m_bbox.empty())
4✔
4963
    {
4964
        aosOptions.push_back("-te");
1✔
4965
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
1✔
4966
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
1✔
4967
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
1✔
4968
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
1✔
4969
    }
4970
    if (!m_dataType.empty())
4✔
4971
    {
4972
        aosOptions.push_back("-ot");
1✔
4973
        aosOptions.push_back(m_dataType);
1✔
4974
    }
4975
    if (m_bandCount > 0)
4✔
4976
    {
4977
        aosOptions.push_back("-bandcount");
3✔
4978
        aosOptions.push_back(CPLSPrintf("%d", m_bandCount));
3✔
4979

4980
        if (!m_nodata.empty() && m_nodata.size() != 1 &&
5✔
4981
            static_cast<int>(m_nodata.size()) != m_bandCount)
2✔
4982
        {
4983
            ReportError(CE_Failure, CPLE_IllegalArg,
1✔
4984
                        "%d nodata values whereas one or %d were expected",
4985
                        static_cast<int>(m_nodata.size()), m_bandCount);
1✔
4986
            return false;
1✔
4987
        }
4988

4989
        if (!m_colorInterpretation.empty() &&
4✔
4990
            m_colorInterpretation.size() != 1 &&
4✔
4991
            static_cast<int>(m_colorInterpretation.size()) != m_bandCount)
2✔
4992
        {
4993
            ReportError(
1✔
4994
                CE_Failure, CPLE_IllegalArg,
4995
                "%d color interpretations whereas one or %d were expected",
4996
                static_cast<int>(m_colorInterpretation.size()), m_bandCount);
1✔
4997
            return false;
1✔
4998
        }
4999
    }
5000
    if (!m_nodata.empty())
2✔
5001
    {
5002
        std::string val;
2✔
5003
        for (double v : m_nodata)
3✔
5004
        {
5005
            if (!val.empty())
2✔
5006
                val += ',';
1✔
5007
            val += CPLSPrintf("%.17g", v);
2✔
5008
        }
5009
        aosOptions.push_back("-nodata");
1✔
5010
        aosOptions.push_back(val);
1✔
5011
    }
5012
    if (!m_colorInterpretation.empty())
2✔
5013
    {
5014
        std::string val;
2✔
5015
        for (const std::string &s : m_colorInterpretation)
3✔
5016
        {
5017
            if (!val.empty())
2✔
5018
                val += ',';
1✔
5019
            val += s;
2✔
5020
        }
5021
        aosOptions.push_back("-colorinterp");
1✔
5022
        aosOptions.push_back(val);
1✔
5023
    }
5024
    if (m_mask)
2✔
5025
        aosOptions.push_back("-mask");
1✔
5026
    for (const std::string &s : m_fetchedMetadata)
3✔
5027
    {
5028
        aosOptions.push_back("-fetch_md");
1✔
5029
        const CPLStringList aosTokens(CSLTokenizeString2(s.c_str(), ",", 0));
2✔
5030
        for (const char *token : aosTokens)
4✔
5031
        {
5032
            aosOptions.push_back(token);
3✔
5033
        }
5034
    }
5035
    return true;
2✔
5036
}
5037

5038
/************************************************************************/
5039
/*                 GDALTileIndexInstantiateAlgorithm()                  */
5040
/************************************************************************/
5041

5042
static GDALAlgorithm *
5043
GDALTileIndexInstantiateAlgorithm(const std::vector<std::string> &aosPath)
19✔
5044
{
5045
    if (aosPath.size() == 1 && aosPath[0] == "create")
19✔
5046
    {
5047
        return std::make_unique<GDALGTICreateAlgorithm>().release();
19✔
5048
    }
5049
    else
5050
    {
5051
        return nullptr;
×
5052
    }
5053
}
5054

5055
/************************************************************************/
5056
/*                         GDALRegister_GTI()                           */
5057
/************************************************************************/
5058

5059
void GDALRegister_GTI()
1,898✔
5060
{
5061
    if (GDALGetDriverByName("GTI") != nullptr)
1,898✔
5062
        return;
282✔
5063

5064
    auto poDriver = std::make_unique<GDALDriver>();
3,232✔
5065

5066
    poDriver->SetDescription("GTI");
1,616✔
5067
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,616✔
5068
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GDAL Raster Tile Index");
1,616✔
5069
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gti.gpkg gti.fgb gti");
1,616✔
5070
    poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, GTI_PREFIX);
1,616✔
5071
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gti.html");
1,616✔
5072

5073
    poDriver->pfnOpen = GDALTileIndexDatasetOpen;
1,616✔
5074
    poDriver->pfnIdentify = GDALTileIndexDatasetIdentify;
1,616✔
5075

5076
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,616✔
5077

5078
    poDriver->SetMetadataItem(
1,616✔
5079
        GDAL_DMD_OPENOPTIONLIST,
5080
        "<OpenOptionList>"
5081
        "  <Option name='LAYER' type='string'/>"
5082
        "  <Option name='LOCATION_FIELD' type='string'/>"
5083
        "  <Option name='SORT_FIELD' type='string'/>"
5084
        "  <Option name='SORT_FIELD_ASC' type='boolean'/>"
5085
        "  <Option name='FILTER' type='string'/>"
5086
        "  <Option name='SRS' type='string'/>"
5087
        "  <Option name='RESX' type='float'/>"
5088
        "  <Option name='RESY' type='float'/>"
5089
        "  <Option name='MINX' type='float'/>"
5090
        "  <Option name='MINY' type='float'/>"
5091
        "  <Option name='MAXX' type='float'/>"
5092
        "  <Option name='MAXY' type='float'/>"
5093
        "<Option name='NUM_THREADS' type='string' description="
5094
        "'Number of worker threads for reading. Can be set to ALL_CPUS' "
5095
        "default='ALL_CPUS'/>"
5096
        "</OpenOptionList>");
1,616✔
5097

5098
    poDriver->DeclareAlgorithm({"create"});
3,232✔
5099
    poDriver->pfnInstantiateAlgorithm = GDALTileIndexInstantiateAlgorithm;
1,616✔
5100

5101
#ifdef BUILT_AS_PLUGIN
5102
    // Used by gdaladdo and test_gdaladdo.py
5103
    poDriver->SetMetadataItem("IS_PLUGIN", "YES");
5104
#endif
5105

5106
    GetGDALDriverManager()->RegisterDriver(poDriver.release());
1,616✔
5107
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc