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

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

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

92.48
/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)
61,619✔
153
{
154
    return strlen(a) >= strlen(b) && EQUAL(a + strlen(a) - strlen(b), b);
61,619✔
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(GDALGeoTransform &gt) const 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
    GDALGeoTransform m_gt{};
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,
580✔
584
                                       const char *pszVRTName)
585
{
586
    if (STARTS_WITH(pszTileName, "https://"))
580✔
587
        return std::string("/vsicurl/").append(pszTileName);
10✔
588

589
    if (CPLIsFilenameRelative(pszTileName) &&
575✔
590
        !STARTS_WITH(pszTileName, "<VRTDataset") &&
579✔
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
            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
        std::string osRelativeMadeAbsolute = CPLProjectRelativeFilenameSafe(
609
            CPLGetPathSafe(pszVRTName).c_str(), pszTileName);
2✔
610
        VSIStatBufL sStat;
611
        if (VSIStatL(osRelativeMadeAbsolute.c_str(), &sStat) == 0)
2✔
612
            return osRelativeMadeAbsolute;
2✔
613
    }
614
    return pszTileName;
571✔
615
}
616

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

762
    const char *pszLayerName;
763

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1486
        const double dfResX = gtTile[GT_WE_RES];
219✔
1487
        const double dfResY = -gtTile[GT_NS_RES];
219✔
1488

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

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

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

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

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

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

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

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

1601
        OGREnvelope sEnvelope;
19✔
1602

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1931
                            aosDescriptions[i] = oObj.GetString("name");
8✔
1932

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2326
    return true;
198✔
2327
}
2328

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

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

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

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

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

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

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

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

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

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

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

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

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

2452
    if (STARTS_WITH(poOpenInfo->pszFilename, "<GDALTileIndexDataset"))
85,386✔
2453
        return true;
46✔
2454

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

2471
    if (poOpenInfo->nHeaderBytes > 0 &&
84,845✔
2472
        (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0)
30,229✔
2473
    {
2474
        if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
60,458✔
2475
                   "<GDALTileIndexDataset") ||
30,217✔
2476
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.fgb") ||
60,446✔
2477
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.parquet"))
30,217✔
2478
        {
2479
            return true;
12✔
2480
        }
2481
        else if (poOpenInfo->IsSingleAllowedDriver("GTI") &&
30,217✔
2482
                 (poOpenInfo->IsExtensionEqualToCI("fgb") ||
×
UNCOV
2483
                  poOpenInfo->IsExtensionEqualToCI("parquet")))
×
2484
        {
2485
            return true;
×
2486
        }
2487
    }
2488

2489
    return false;
84,833✔
2490
}
2491

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2790
CPLErr GDALTileIndexDataset::GetGeoTransform(GDALGeoTransform &gt) const
21✔
2791
{
2792
    gt = m_gt;
21✔
2793
    return CE_None;
21✔
2794
}
2795

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2896
    int nStatus = 0;
6✔
2897

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

2919
        const auto poGeom = poFeature->GetGeometryRef();
8✔
2920
        if (!poGeom || poGeom->IsEmpty())
8✔
2921
            continue;
×
2922

2923
        OGREnvelope sSourceEnvelope;
8✔
2924
        poGeom->getEnvelope(&sSourceEnvelope);
8✔
2925

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

2941
        // CPLDebug("GTI", "dfDstXOff=%f, dfDstXOff2=%f, dfDstYOff=%f, dfDstYOff2=%f",
2942
        //         dfDstXOff, dfDstXOff2, dfDstYOff, dfDstXOff2);
2943

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

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

3004
/************************************************************************/
3005
/*                      GetMetadataDomainList()                         */
3006
/************************************************************************/
3007

3008
char **GDALTileIndexBand::GetMetadataDomainList()
1✔
3009
{
3010
    return CSLAddString(GDALRasterBand::GetMetadataDomainList(),
1✔
3011
                        "LocationInfo");
1✔
3012
}
3013

3014
/************************************************************************/
3015
/*                          GetMetadataItem()                           */
3016
/************************************************************************/
3017

3018
const char *GDALTileIndexBand::GetMetadataItem(const char *pszName,
28✔
3019
                                               const char *pszDomain)
3020

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

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

3051
            double adfInvGeoTransform[6] = {0.0};
3✔
3052
            if (!GDALInvGeoTransform(m_poDS->m_gt.data(), adfInvGeoTransform))
3✔
3053
                return nullptr;
×
3054

3055
            iPixel = static_cast<int>(floor(adfInvGeoTransform[0] +
3✔
3056
                                            adfInvGeoTransform[1] * dfGeoX +
3✔
3057
                                            adfInvGeoTransform[2] * dfGeoY));
3✔
3058
            iLine = static_cast<int>(floor(adfInvGeoTransform[3] +
3✔
3059
                                           adfInvGeoTransform[4] * dfGeoX +
3✔
3060
                                           adfInvGeoTransform[5] * dfGeoY));
3✔
3061
        }
3062
        else
3063
        {
3064
            return nullptr;
×
3065
        }
3066

3067
        if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
23✔
3068
            iLine >= GetYSize())
9✔
3069
            return nullptr;
6✔
3070

3071
        if (!m_poDS->CollectSources(iPixel, iLine, 1, 1,
8✔
3072
                                    /* bMultiThreadAllowed = */ false))
3073
            return nullptr;
×
3074

3075
        // Format into XML.
3076
        m_osLastLocationInfo = "<LocationInfo>";
8✔
3077

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

3091
            const int anBand[] = {nBand};
5✔
3092
            if (!m_poDS->NeedInitBuffer(1, anBand))
5✔
3093
            {
3094
                AddSource(m_poDS->m_aoSourceDesc.back());
4✔
3095
            }
3096
            else
3097
            {
3098
                for (const auto &oSourceDesc : m_poDS->m_aoSourceDesc)
3✔
3099
                {
3100
                    if (oSourceDesc.poDS)
2✔
3101
                        AddSource(oSourceDesc);
2✔
3102
                }
3103
            }
3104
        }
3105

3106
        m_osLastLocationInfo += "</LocationInfo>";
8✔
3107

3108
        return m_osLastLocationInfo.c_str();
8✔
3109
    }
3110

3111
    return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
10✔
3112
}
3113

3114
/************************************************************************/
3115
/*                        SetMetadataItem()                             */
3116
/************************************************************************/
3117

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

3139
/************************************************************************/
3140
/*                           SetMetadata()                              */
3141
/************************************************************************/
3142

3143
CPLErr GDALTileIndexBand::SetMetadata(char **papszMD, const char *pszDomain)
2✔
3144
{
3145
    if (nBand > 0 && m_poDS->m_bXMLUpdatable)
2✔
3146
    {
3147
        m_poDS->m_bXMLModified = true;
1✔
3148
        return GDALRasterBand::SetMetadata(papszMD, pszDomain);
1✔
3149
    }
3150
    else if (nBand > 0 && m_poDS->TileIndexSupportsEditingLayerMetadata())
1✔
3151
    {
3152
        CPLStringList aosMD;
2✔
3153

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

3167
        for (int i = 0; papszMD && papszMD[i]; ++i)
8✔
3168
        {
3169
            aosMD.AddString(CPLSPrintf("BAND_%d_%s", nBand, papszMD[i]));
7✔
3170
        }
3171

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

3188
        m_poDS->m_poLayer->SetMetadata(aosMD.List(), pszDomain);
1✔
3189
        return GDALRasterBand::SetMetadata(papszMD, pszDomain);
1✔
3190
    }
3191
    else
3192
    {
3193
        return GDALPamRasterBand::SetMetadata(papszMD, pszDomain);
×
3194
    }
3195
}
3196

3197
/************************************************************************/
3198
/*                         GetSrcDstWin()                               */
3199
/************************************************************************/
3200

3201
static bool GetSrcDstWin(const GDALGeoTransform &tileGT, int nTileXSize,
350✔
3202
                         int nTileYSize, const GDALGeoTransform &vrtGT,
3203
                         int nVRTXSize, int nVRTYSize, double *pdfSrcXOff,
3204
                         double *pdfSrcYOff, double *pdfSrcXSize,
3205
                         double *pdfSrcYSize, double *pdfDstXOff,
3206
                         double *pdfDstYOff, double *pdfDstXSize,
3207
                         double *pdfDstYSize)
3208
{
3209
    const double minX = vrtGT[GT_TOPLEFT_X];
350✔
3210
    const double we_res = vrtGT[GT_WE_RES];
350✔
3211
    const double maxX = minX + nVRTXSize * we_res;
350✔
3212
    const double maxY = vrtGT[GT_TOPLEFT_Y];
350✔
3213
    const double ns_res = vrtGT[GT_NS_RES];
350✔
3214
    const double minY = maxY + nVRTYSize * ns_res;
350✔
3215

3216
    /* Check that the destination bounding box intersects the source bounding
3217
     * box */
3218
    if (tileGT[GT_TOPLEFT_X] + nTileXSize * tileGT[GT_WE_RES] <= minX)
350✔
3219
        return false;
×
3220
    if (tileGT[GT_TOPLEFT_X] >= maxX)
350✔
3221
        return false;
1✔
3222
    if (tileGT[GT_TOPLEFT_Y] + nTileYSize * tileGT[GT_NS_RES] >= maxY)
349✔
3223
        return false;
×
3224
    if (tileGT[GT_TOPLEFT_Y] <= minY)
349✔
3225
        return false;
×
3226

3227
    if (tileGT[GT_TOPLEFT_X] < minX)
349✔
3228
    {
3229
        *pdfSrcXOff = (minX - tileGT[GT_TOPLEFT_X]) / tileGT[GT_WE_RES];
1✔
3230
        *pdfDstXOff = 0.0;
1✔
3231
    }
3232
    else
3233
    {
3234
        *pdfSrcXOff = 0.0;
348✔
3235
        *pdfDstXOff = ((tileGT[GT_TOPLEFT_X] - minX) / we_res);
348✔
3236
    }
3237
    if (maxY < tileGT[GT_TOPLEFT_Y])
349✔
3238
    {
3239
        *pdfSrcYOff = (tileGT[GT_TOPLEFT_Y] - maxY) / -tileGT[GT_NS_RES];
1✔
3240
        *pdfDstYOff = 0.0;
1✔
3241
    }
3242
    else
3243
    {
3244
        *pdfSrcYOff = 0.0;
348✔
3245
        *pdfDstYOff = ((maxY - tileGT[GT_TOPLEFT_Y]) / -ns_res);
348✔
3246
    }
3247

3248
    *pdfSrcXSize = nTileXSize;
349✔
3249
    *pdfSrcYSize = nTileYSize;
349✔
3250
    if (*pdfSrcXOff > 0)
349✔
3251
        *pdfSrcXSize -= *pdfSrcXOff;
1✔
3252
    if (*pdfSrcYOff > 0)
349✔
3253
        *pdfSrcYSize -= *pdfSrcYOff;
1✔
3254

3255
    const double dfSrcToDstXSize = tileGT[GT_WE_RES] / we_res;
349✔
3256
    *pdfDstXSize = *pdfSrcXSize * dfSrcToDstXSize;
349✔
3257
    const double dfSrcToDstYSize = tileGT[GT_NS_RES] / ns_res;
349✔
3258
    *pdfDstYSize = *pdfSrcYSize * dfSrcToDstYSize;
349✔
3259

3260
    if (*pdfDstXOff + *pdfDstXSize > nVRTXSize)
349✔
3261
    {
3262
        *pdfDstXSize = nVRTXSize - *pdfDstXOff;
3✔
3263
        *pdfSrcXSize = *pdfDstXSize / dfSrcToDstXSize;
3✔
3264
    }
3265

3266
    if (*pdfDstYOff + *pdfDstYSize > nVRTYSize)
349✔
3267
    {
3268
        *pdfDstYSize = nVRTYSize - *pdfDstYOff;
1✔
3269
        *pdfSrcYSize = *pdfDstYSize / dfSrcToDstYSize;
1✔
3270
    }
3271

3272
    return *pdfSrcXSize > 0 && *pdfDstXSize > 0 && *pdfSrcYSize > 0 &&
698✔
3273
           *pdfDstYSize > 0;
698✔
3274
}
3275

3276
/************************************************************************/
3277
/*                   GDALDatasetCastToGTIDataset()                    */
3278
/************************************************************************/
3279

3280
GDALTileIndexDataset *GDALDatasetCastToGTIDataset(GDALDataset *poDS)
3✔
3281
{
3282
    return dynamic_cast<GDALTileIndexDataset *>(poDS);
3✔
3283
}
3284

3285
/************************************************************************/
3286
/*                   GTIGetSourcesMoreRecentThan()                    */
3287
/************************************************************************/
3288

3289
std::vector<GTISourceDesc>
3290
GTIGetSourcesMoreRecentThan(GDALTileIndexDataset *poDS, int64_t mTime)
2✔
3291
{
3292
    return poDS->GetSourcesMoreRecentThan(mTime);
2✔
3293
}
3294

3295
/************************************************************************/
3296
/*                       GetSourcesMoreRecentThan()                     */
3297
/************************************************************************/
3298

3299
std::vector<GTISourceDesc>
3300
GDALTileIndexDataset::GetSourcesMoreRecentThan(int64_t mTime)
2✔
3301
{
3302
    std::vector<GTISourceDesc> oRes;
2✔
3303

3304
    m_poLayer->SetSpatialFilter(nullptr);
2✔
3305
    for (auto &&poFeature : m_poLayer)
6✔
3306
    {
3307
        if (!poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
4✔
3308
        {
3309
            continue;
2✔
3310
        }
3311

3312
        auto poGeom = poFeature->GetGeometryRef();
4✔
3313
        if (!poGeom || poGeom->IsEmpty())
4✔
3314
            continue;
×
3315

3316
        OGREnvelope sEnvelope;
4✔
3317
        poGeom->getEnvelope(&sEnvelope);
4✔
3318

3319
        double dfXOff = (sEnvelope.MinX - m_gt[GT_TOPLEFT_X]) / m_gt[GT_WE_RES];
4✔
3320
        if (dfXOff >= nRasterXSize)
4✔
3321
            continue;
×
3322

3323
        double dfYOff = (sEnvelope.MaxY - m_gt[GT_TOPLEFT_Y]) / m_gt[GT_NS_RES];
4✔
3324
        if (dfYOff >= nRasterYSize)
4✔
3325
            continue;
×
3326

3327
        double dfXSize = (sEnvelope.MaxX - sEnvelope.MinX) / m_gt[GT_WE_RES];
4✔
3328
        if (dfXOff < 0)
4✔
3329
        {
3330
            dfXSize += dfXOff;
×
3331
            dfXOff = 0;
×
3332
            if (dfXSize <= 0)
×
3333
                continue;
×
3334
        }
3335

3336
        double dfYSize =
3337
            (sEnvelope.MaxY - sEnvelope.MinY) / std::fabs(m_gt[GT_NS_RES]);
4✔
3338
        if (dfYOff < 0)
4✔
3339
        {
3340
            dfYSize += dfYOff;
×
3341
            dfYOff = 0;
×
3342
            if (dfYSize <= 0)
×
3343
                continue;
×
3344
        }
3345

3346
        const char *pszTileName =
3347
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
4✔
3348
        std::string osTileName(
3349
            GetAbsoluteFileName(pszTileName, GetDescription()));
4✔
3350
        VSIStatBufL sStatSource;
3351
        if (VSIStatL(osTileName.c_str(), &sStatSource) != 0 ||
8✔
3352
            sStatSource.st_mtime <= mTime)
4✔
3353
        {
3354
            continue;
2✔
3355
        }
3356

3357
        constexpr double EPS = 1e-8;
2✔
3358
        GTISourceDesc oSourceDesc;
4✔
3359
        oSourceDesc.osFilename = std::move(osTileName);
2✔
3360
        oSourceDesc.nDstXOff = static_cast<int>(dfXOff + EPS);
2✔
3361
        oSourceDesc.nDstYOff = static_cast<int>(dfYOff + EPS);
2✔
3362
        oSourceDesc.nDstXSize = static_cast<int>(dfXSize + 0.5);
2✔
3363
        oSourceDesc.nDstYSize = static_cast<int>(dfYSize + 0.5);
2✔
3364
        oRes.emplace_back(std::move(oSourceDesc));
2✔
3365
    }
3366

3367
    return oRes;
2✔
3368
}
3369

3370
/************************************************************************/
3371
/*                         GetSourceDesc()                              */
3372
/************************************************************************/
3373

3374
bool GDALTileIndexDataset::GetSourceDesc(const std::string &osTileName,
352✔
3375
                                         SourceDesc &oSourceDesc,
3376
                                         std::mutex *pMutex)
3377
{
3378
    std::shared_ptr<GDALDataset> poTileDS;
352✔
3379

3380
    if (pMutex)
352✔
3381
        pMutex->lock();
138✔
3382
    const bool bTileKnown = m_oMapSharedSources.tryGet(osTileName, poTileDS);
352✔
3383
    if (pMutex)
352✔
3384
        pMutex->unlock();
138✔
3385

3386
    if (!bTileKnown)
352✔
3387
    {
3388
        poTileDS = std::shared_ptr<GDALDataset>(
456✔
3389
            GDALProxyPoolDataset::Create(
3390
                osTileName.c_str(), nullptr, GA_ReadOnly,
3391
                /* bShared = */ true, m_osUniqueHandle.c_str()),
3392
            GDALDatasetUniquePtrReleaser());
228✔
3393
        if (!poTileDS || poTileDS->GetRasterCount() == 0)
228✔
3394
        {
3395
            return false;
2✔
3396
        }
3397

3398
        // do palette -> RGB(A) expansion if needed
3399
        if (!GTIDoPaletteExpansionIfNeeded(poTileDS, nBands))
226✔
3400
            return false;
×
3401

3402
        const OGRSpatialReference *poTileSRS;
3403
        if (!m_oSRS.IsEmpty() &&
226✔
3404
            (poTileSRS = poTileDS->GetSpatialRef()) != nullptr &&
395✔
3405
            !m_oSRS.IsSame(poTileSRS))
169✔
3406
        {
3407
            CPLDebug("VRT",
2✔
3408
                     "Tile %s has not the same SRS as the VRT. "
3409
                     "Proceed to on-the-fly warping",
3410
                     osTileName.c_str());
3411

3412
            CPLStringList aosOptions;
2✔
3413
            aosOptions.AddString("-of");
2✔
3414
            aosOptions.AddString("VRT");
2✔
3415

3416
            if ((poTileDS->GetRasterBand(1)->GetColorTable() == nullptr &&
2✔
3417
                 poTileDS->GetRasterBand(1)->GetCategoryNames() == nullptr) ||
2✔
3418
                m_eResampling == GRIORA_Mode)
×
3419
            {
3420
                aosOptions.AddString("-r");
2✔
3421
                aosOptions.AddString(m_osResampling.c_str());
2✔
3422
            }
3423

3424
            if (m_osWKT.empty())
2✔
3425
            {
3426
                char *pszWKT = nullptr;
×
3427
                const char *const apszWKTOptions[] = {"FORMAT=WKT2_2019",
×
3428
                                                      nullptr};
3429
                m_oSRS.exportToWkt(&pszWKT, apszWKTOptions);
×
3430
                if (pszWKT)
×
3431
                    m_osWKT = pszWKT;
×
3432
                CPLFree(pszWKT);
×
3433
            }
3434
            if (m_osWKT.empty())
2✔
3435
            {
3436
                CPLError(CE_Failure, CPLE_AppDefined,
×
3437
                         "Cannot export VRT SRS to WKT2");
3438
                return false;
×
3439
            }
3440

3441
            aosOptions.AddString("-t_srs");
2✔
3442
            aosOptions.AddString(m_osWKT.c_str());
2✔
3443

3444
            // First pass to get the extent of the tile in the
3445
            // target VRT SRS
3446
            GDALWarpAppOptions *psWarpOptions =
3447
                GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
2✔
3448
            GDALDatasetH ahSrcDS[] = {GDALDataset::ToHandle(poTileDS.get())};
2✔
3449
            int bUsageError = false;
2✔
3450
            auto poWarpDS =
3451
                std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALWarp(
3452
                    "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
2✔
3453
            GDALWarpAppOptionsFree(psWarpOptions);
2✔
3454
            if (!poWarpDS)
2✔
3455
            {
3456
                return false;
×
3457
            }
3458

3459
            // Second pass to create a warped source VRT whose
3460
            // extent is aligned on the one of the target VRT
3461
            GDALGeoTransform warpDSGT;
2✔
3462
            const auto eErr = poWarpDS->GetGeoTransform(warpDSGT);
2✔
3463
            CPL_IGNORE_RET_VAL(eErr);
2✔
3464
            CPLAssert(eErr == CE_None);
2✔
3465
            const double dfVRTMinX = m_gt[GT_TOPLEFT_X];
2✔
3466
            const double dfVRTResX = m_gt[GT_WE_RES];
2✔
3467
            const double dfVRTMaxY = m_gt[GT_TOPLEFT_Y];
2✔
3468
            const double dfVRTResYAbs = -m_gt[GT_NS_RES];
2✔
3469
            const double dfWarpMinX =
3470
                std::floor((warpDSGT[GT_TOPLEFT_X] - dfVRTMinX) / dfVRTResX) *
2✔
3471
                    dfVRTResX +
3472
                dfVRTMinX;
2✔
3473
            const double dfWarpMaxX =
3474
                std::ceil((warpDSGT[GT_TOPLEFT_X] +
2✔
3475
                           warpDSGT[GT_WE_RES] * poWarpDS->GetRasterXSize() -
2✔
3476
                           dfVRTMinX) /
3477
                          dfVRTResX) *
2✔
3478
                    dfVRTResX +
3479
                dfVRTMinX;
2✔
3480
            const double dfWarpMaxY =
3481
                dfVRTMaxY - std::floor((dfVRTMaxY - warpDSGT[GT_TOPLEFT_Y]) /
2✔
3482
                                       dfVRTResYAbs) *
2✔
3483
                                dfVRTResYAbs;
2✔
3484
            const double dfWarpMinY =
3485
                dfVRTMaxY -
3486
                std::ceil((dfVRTMaxY -
2✔
3487
                           (warpDSGT[GT_TOPLEFT_Y] +
2✔
3488
                            warpDSGT[GT_NS_RES] * poWarpDS->GetRasterYSize())) /
2✔
3489
                          dfVRTResYAbs) *
2✔
3490
                    dfVRTResYAbs;
2✔
3491

3492
            aosOptions.AddString("-te");
2✔
3493
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinX));
2✔
3494
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinY));
2✔
3495
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxX));
2✔
3496
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxY));
2✔
3497

3498
            aosOptions.AddString("-tr");
2✔
3499
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResX));
2✔
3500
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResYAbs));
2✔
3501

3502
            aosOptions.AddString("-dstalpha");
2✔
3503

3504
            psWarpOptions = GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
2✔
3505
            poWarpDS.reset(GDALDataset::FromHandle(GDALWarp(
2✔
3506
                "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
3507
            GDALWarpAppOptionsFree(psWarpOptions);
2✔
3508
            if (!poWarpDS)
2✔
3509
            {
3510
                return false;
×
3511
            }
3512

3513
            poTileDS.reset(poWarpDS.release());
2✔
3514
        }
3515

3516
        if (pMutex)
226✔
3517
            pMutex->lock();
70✔
3518
        m_oMapSharedSources.insert(osTileName, poTileDS);
226✔
3519
        if (pMutex)
226✔
3520
            pMutex->unlock();
70✔
3521
    }
3522

3523
    GDALGeoTransform gtTile;
350✔
3524
    if (poTileDS->GetGeoTransform(gtTile) != CE_None)
350✔
3525
    {
3526
        CPLError(CE_Failure, CPLE_AppDefined, "%s lacks geotransform",
×
3527
                 osTileName.c_str());
3528
        return false;
×
3529
    }
3530

3531
    bool bHasNoData = false;
350✔
3532
    bool bSameNoData = true;
350✔
3533
    double dfNoDataValue = 0;
350✔
3534
    GDALRasterBand *poMaskBand = nullptr;
350✔
3535
    const int nBandCount = poTileDS->GetRasterCount();
350✔
3536
    for (int iBand = 0; iBand < nBandCount; ++iBand)
1,203✔
3537
    {
3538
        auto poTileBand = poTileDS->GetRasterBand(iBand + 1);
853✔
3539
        int bThisBandHasNoData = false;
853✔
3540
        const double dfThisBandNoDataValue =
3541
            poTileBand->GetNoDataValue(&bThisBandHasNoData);
853✔
3542
        if (bThisBandHasNoData)
853✔
3543
        {
3544
            bHasNoData = true;
10✔
3545
            dfNoDataValue = dfThisBandNoDataValue;
10✔
3546
        }
3547
        if (iBand > 0 &&
1,356✔
3548
            (static_cast<int>(bThisBandHasNoData) !=
503✔
3549
                 static_cast<int>(bHasNoData) ||
503✔
3550
             (bHasNoData &&
4✔
3551
              !IsSameNaNAware(dfNoDataValue, dfThisBandNoDataValue))))
4✔
3552
        {
3553
            bSameNoData = false;
×
3554
        }
3555

3556
        if (poTileBand->GetMaskFlags() == GMF_PER_DATASET)
853✔
3557
            poMaskBand = poTileBand->GetMaskBand();
2✔
3558
        else if (poTileBand->GetColorInterpretation() == GCI_AlphaBand)
851✔
3559
            poMaskBand = poTileBand;
31✔
3560
    }
3561

3562
    std::unique_ptr<VRTSimpleSource> poSource;
×
3563
    if (!bHasNoData)
350✔
3564
    {
3565
        poSource = std::make_unique<VRTSimpleSource>();
344✔
3566
    }
3567
    else
3568
    {
3569
        auto poComplexSource = std::make_unique<VRTComplexSource>();
12✔
3570
        poComplexSource->SetNoDataValue(dfNoDataValue);
6✔
3571
        poSource = std::move(poComplexSource);
6✔
3572
    }
3573

3574
    GetSrcDstWin(gtTile, poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
700✔
3575
                 m_gt, GetRasterXSize(), GetRasterYSize(),
350✔
3576
                 &poSource->m_dfSrcXOff, &poSource->m_dfSrcYOff,
350✔
3577
                 &poSource->m_dfSrcXSize, &poSource->m_dfSrcYSize,
350✔
3578
                 &poSource->m_dfDstXOff, &poSource->m_dfDstYOff,
350✔
3579
                 &poSource->m_dfDstXSize, &poSource->m_dfDstYSize);
350✔
3580

3581
    oSourceDesc.osName = osTileName;
350✔
3582
    oSourceDesc.poDS = std::move(poTileDS);
350✔
3583
    oSourceDesc.poSource = std::move(poSource);
350✔
3584
    oSourceDesc.bHasNoData = bHasNoData;
350✔
3585
    oSourceDesc.bSameNoData = bSameNoData;
350✔
3586
    if (bSameNoData)
350✔
3587
        oSourceDesc.dfSameNoData = dfNoDataValue;
350✔
3588
    oSourceDesc.poMaskBand = poMaskBand;
350✔
3589
    return true;
350✔
3590
}
3591

3592
/************************************************************************/
3593
/*                           GetNumThreads()                            */
3594
/************************************************************************/
3595

3596
int GDALTileIndexDataset::GetNumThreads() const
7✔
3597
{
3598
    const char *pszNumThreads =
3599
        CSLFetchNameValueDef(GetOpenOptions(), "NUM_THREADS", nullptr);
7✔
3600
    if (!pszNumThreads)
7✔
3601
        pszNumThreads = CPLGetConfigOption("GTI_NUM_THREADS", nullptr);
7✔
3602
    if (!pszNumThreads)
7✔
3603
        pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS");
5✔
3604
    if (EQUAL(pszNumThreads, "0") || EQUAL(pszNumThreads, "1"))
7✔
3605
        return atoi(pszNumThreads);
2✔
3606
    const int nMaxPoolSize = GDALGetMaxDatasetPoolSize();
5✔
3607
    const int nLimit = std::min(CPLGetNumCPUs(), nMaxPoolSize);
5✔
3608
    if (EQUAL(pszNumThreads, "ALL_CPUS"))
5✔
3609
        return nLimit;
5✔
3610
    return std::min(atoi(pszNumThreads), nLimit);
×
3611
}
3612

3613
/************************************************************************/
3614
/*                        CollectSources()                              */
3615
/************************************************************************/
3616

3617
bool GDALTileIndexDataset::CollectSources(double dfXOff, double dfYOff,
171✔
3618
                                          double dfXSize, double dfYSize,
3619
                                          bool bMultiThreadAllowed)
3620
{
3621
    const double dfMinX = m_gt[GT_TOPLEFT_X] + dfXOff * m_gt[GT_WE_RES];
171✔
3622
    const double dfMaxX = dfMinX + dfXSize * m_gt[GT_WE_RES];
171✔
3623
    const double dfMaxY = m_gt[GT_TOPLEFT_Y] + dfYOff * m_gt[GT_NS_RES];
171✔
3624
    const double dfMinY = dfMaxY + dfYSize * m_gt[GT_NS_RES];
171✔
3625

3626
    if (dfMinX == m_dfLastMinXFilter && dfMinY == m_dfLastMinYFilter &&
171✔
3627
        dfMaxX == m_dfLastMaxXFilter && dfMaxY == m_dfLastMaxYFilter)
47✔
3628
    {
3629
        return true;
43✔
3630
    }
3631

3632
    m_dfLastMinXFilter = dfMinX;
128✔
3633
    m_dfLastMinYFilter = dfMinY;
128✔
3634
    m_dfLastMaxXFilter = dfMaxX;
128✔
3635
    m_dfLastMaxYFilter = dfMaxY;
128✔
3636
    m_bLastMustUseMultiThreading = false;
128✔
3637

3638
    m_poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
128✔
3639
    m_poLayer->ResetReading();
128✔
3640

3641
    m_aoSourceDesc.clear();
128✔
3642
    while (true)
3643
    {
3644
        auto poFeature =
3645
            std::unique_ptr<OGRFeature>(m_poLayer->GetNextFeature());
446✔
3646
        if (!poFeature)
446✔
3647
            break;
128✔
3648
        if (!poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
318✔
3649
        {
3650
            continue;
1✔
3651
        }
3652

3653
        SourceDesc oSourceDesc;
317✔
3654
        oSourceDesc.poFeature = std::move(poFeature);
317✔
3655
        m_aoSourceDesc.emplace_back(std::move(oSourceDesc));
317✔
3656

3657
        if (m_aoSourceDesc.size() > 10 * 1000 * 1000)
317✔
3658
        {
3659
            // Safety belt...
3660
            CPLError(CE_Failure, CPLE_AppDefined,
×
3661
                     "More than 10 million contributing sources to a "
3662
                     "single RasterIO() request is not supported");
3663
            return false;
×
3664
        }
3665
    }
318✔
3666

3667
    constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
128✔
3668
    if (bMultiThreadAllowed && m_aoSourceDesc.size() > 1 &&
194✔
3669
        dfXSize * dfYSize > MINIMUM_PIXEL_COUNT_FOR_THREADED_IO)
66✔
3670
    {
3671
        if (m_nNumThreads < 0)
7✔
3672
            m_nNumThreads = GetNumThreads();
7✔
3673
        bMultiThreadAllowed = m_nNumThreads > 1;
7✔
3674
    }
3675
    else
3676
    {
3677
        bMultiThreadAllowed = false;
121✔
3678
    }
3679

3680
    if (bMultiThreadAllowed)
128✔
3681
    {
3682
        CPLRectObj sGlobalBounds;
3683
        sGlobalBounds.minx = dfXOff;
5✔
3684
        sGlobalBounds.miny = dfYOff;
5✔
3685
        sGlobalBounds.maxx = dfXOff + dfXSize;
5✔
3686
        sGlobalBounds.maxy = dfYOff + dfYSize;
5✔
3687
        CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
5✔
3688

3689
        bool bCompatibleOfMultiThread = true;
5✔
3690
        std::set<std::string> oSetTileNames;
5✔
3691
        for (const auto &oSourceDesc : m_aoSourceDesc)
77✔
3692
        {
3693
            const char *pszTileName =
3694
                oSourceDesc.poFeature->GetFieldAsString(m_nLocationFieldIndex);
73✔
3695
            if (oSetTileNames.find(pszTileName) != oSetTileNames.end())
73✔
3696
            {
3697
                bCompatibleOfMultiThread = false;
×
3698
                break;
1✔
3699
            }
3700
            oSetTileNames.insert(pszTileName);
73✔
3701

3702
            const auto poGeom = oSourceDesc.poFeature->GetGeometryRef();
73✔
3703
            if (!poGeom || poGeom->IsEmpty())
73✔
3704
                continue;
×
3705

3706
            OGREnvelope sEnvelope;
73✔
3707
            poGeom->getEnvelope(&sEnvelope);
73✔
3708

3709
            CPLRectObj sSourceBounds;
3710
            sSourceBounds.minx =
73✔
3711
                (sEnvelope.MinX - m_gt[GT_TOPLEFT_X]) / m_gt[GT_WE_RES];
73✔
3712
            sSourceBounds.maxx =
73✔
3713
                (sEnvelope.MaxX - m_gt[GT_TOPLEFT_X]) / m_gt[GT_WE_RES];
73✔
3714
            // Yes use of MaxY to compute miny is intended given that MaxY is
3715
            // in georeferenced space whereas miny is in pixel space.
3716
            sSourceBounds.miny =
73✔
3717
                (sEnvelope.MaxY - m_gt[GT_TOPLEFT_Y]) / m_gt[GT_NS_RES];
73✔
3718
            // Same here for maxy vs Miny
3719
            sSourceBounds.maxy =
73✔
3720
                (sEnvelope.MinY - m_gt[GT_TOPLEFT_Y]) / m_gt[GT_NS_RES];
73✔
3721

3722
            // Clamp to global bounds and some epsilon to avoid adjacent tiles
3723
            // to be considered as overlapping
3724
            constexpr double EPSILON = 0.1;
73✔
3725
            sSourceBounds.minx =
73✔
3726
                std::max(sGlobalBounds.minx, sSourceBounds.minx) + EPSILON;
73✔
3727
            sSourceBounds.maxx =
73✔
3728
                std::min(sGlobalBounds.maxx, sSourceBounds.maxx) - EPSILON;
73✔
3729
            sSourceBounds.miny =
73✔
3730
                std::max(sGlobalBounds.miny, sSourceBounds.miny) + EPSILON;
73✔
3731
            sSourceBounds.maxy =
73✔
3732
                std::min(sGlobalBounds.maxy, sSourceBounds.maxy) - EPSILON;
73✔
3733

3734
            // Check that the new source doesn't overlap an existing one.
3735
            if (CPLQuadTreeHasMatch(hQuadTree, &sSourceBounds))
73✔
3736
            {
3737
                bCompatibleOfMultiThread = false;
1✔
3738
                break;
1✔
3739
            }
3740

3741
            CPLQuadTreeInsertWithBounds(
72✔
3742
                hQuadTree,
3743
                const_cast<void *>(static_cast<const void *>(&oSourceDesc)),
3744
                &sSourceBounds);
3745
        }
3746

3747
        CPLQuadTreeDestroy(hQuadTree);
5✔
3748

3749
        if (bCompatibleOfMultiThread)
5✔
3750
        {
3751
            m_bLastMustUseMultiThreading = true;
4✔
3752
            return true;
4✔
3753
        }
3754
    }
3755

3756
    if (m_aoSourceDesc.size() > 1)
124✔
3757
    {
3758
        SortSourceDesc();
63✔
3759
    }
3760

3761
    // Try to find the last (most prioritary) fully opaque source covering
3762
    // the whole AOI. We only need to start rendering from it.
3763
    size_t i = m_aoSourceDesc.size();
124✔
3764
    while (i > 0)
256✔
3765
    {
3766
        --i;
214✔
3767
        auto &poFeature = m_aoSourceDesc[i].poFeature;
214✔
3768
        const char *pszTileName =
3769
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
214✔
3770
        const std::string osTileName(
3771
            GetAbsoluteFileName(pszTileName, GetDescription()));
214✔
3772

3773
        SourceDesc oSourceDesc;
214✔
3774
        if (!GetSourceDesc(osTileName, oSourceDesc, nullptr))
214✔
3775
            continue;
1✔
3776

3777
        // Check consistency of bounding box in tile index vs actual
3778
        // extent of the tile.
3779
        GDALGeoTransform tileGT;
213✔
3780
        if (oSourceDesc.poDS->GetGeoTransform(tileGT) == CE_None &&
213✔
3781
            tileGT[GT_ROTATION_PARAM1] == 0 && tileGT[GT_ROTATION_PARAM2] == 0)
213✔
3782
        {
3783
            OGREnvelope sActualTileExtent;
213✔
3784
            sActualTileExtent.MinX = tileGT[GT_TOPLEFT_X];
213✔
3785
            sActualTileExtent.MaxX =
213✔
3786
                sActualTileExtent.MinX +
426✔
3787
                oSourceDesc.poDS->GetRasterXSize() * tileGT[GT_WE_RES];
213✔
3788
            sActualTileExtent.MaxY = tileGT[GT_TOPLEFT_Y];
213✔
3789
            sActualTileExtent.MinY =
213✔
3790
                sActualTileExtent.MaxY +
426✔
3791
                oSourceDesc.poDS->GetRasterYSize() * tileGT[GT_NS_RES];
213✔
3792
            const auto poGeom = poFeature->GetGeometryRef();
213✔
3793
            if (poGeom && !poGeom->IsEmpty())
213✔
3794
            {
3795
                OGREnvelope sGeomTileExtent;
213✔
3796
                poGeom->getEnvelope(&sGeomTileExtent);
213✔
3797
                sGeomTileExtent.MinX -= m_gt[GT_WE_RES];
213✔
3798
                sGeomTileExtent.MaxX += m_gt[GT_WE_RES];
213✔
3799
                sGeomTileExtent.MinY -= std::fabs(m_gt[GT_NS_RES]);
213✔
3800
                sGeomTileExtent.MaxY += std::fabs(m_gt[GT_NS_RES]);
213✔
3801
                if (!sGeomTileExtent.Contains(sActualTileExtent))
213✔
3802
                {
3803
                    if (!sGeomTileExtent.Intersects(sActualTileExtent))
2✔
3804
                    {
3805
                        CPLError(CE_Warning, CPLE_AppDefined,
1✔
3806
                                 "Tile index is out of sync with actual "
3807
                                 "extent of %s. Bounding box from tile index "
3808
                                 "is (%g, %g, %g, %g) does not intersect at "
3809
                                 "all bounding box from tile (%g, %g, %g, %g)",
3810
                                 osTileName.c_str(), sGeomTileExtent.MinX,
3811
                                 sGeomTileExtent.MinY, sGeomTileExtent.MaxX,
3812
                                 sGeomTileExtent.MaxY, sActualTileExtent.MinX,
3813
                                 sActualTileExtent.MinY, sActualTileExtent.MaxX,
3814
                                 sActualTileExtent.MaxY);
3815
                        continue;
1✔
3816
                    }
3817
                    CPLError(CE_Warning, CPLE_AppDefined,
1✔
3818
                             "Tile index is out of sync with actual extent "
3819
                             "of %s. Bounding box from tile index is (%g, %g, "
3820
                             "%g, %g) does not fully contain bounding box from "
3821
                             "tile (%g, %g, %g, %g)",
3822
                             osTileName.c_str(), sGeomTileExtent.MinX,
3823
                             sGeomTileExtent.MinY, sGeomTileExtent.MaxX,
3824
                             sGeomTileExtent.MaxY, sActualTileExtent.MinX,
3825
                             sActualTileExtent.MinY, sActualTileExtent.MaxX,
3826
                             sActualTileExtent.MaxY);
3827
                }
3828
            }
3829
        }
3830

3831
        const auto &poSource = oSourceDesc.poSource;
212✔
3832
        if (dfXOff >= poSource->m_dfDstXOff + poSource->m_dfDstXSize ||
212✔
3833
            dfYOff >= poSource->m_dfDstYOff + poSource->m_dfDstYSize ||
212✔
3834
            poSource->m_dfDstXOff >= dfXOff + dfXSize ||
634✔
3835
            poSource->m_dfDstYOff >= dfYOff + dfYSize)
210✔
3836
        {
3837
            // Can happen as some spatial filters select slightly more features
3838
            // than strictly needed.
3839
            continue;
2✔
3840
        }
3841

3842
        const bool bCoversWholeAOI =
3843
            (poSource->m_dfDstXOff <= dfXOff &&
210✔
3844
             poSource->m_dfDstYOff <= dfYOff &&
133✔
3845
             poSource->m_dfDstXOff + poSource->m_dfDstXSize >=
133✔
3846
                 dfXOff + dfXSize &&
463✔
3847
             poSource->m_dfDstYOff + poSource->m_dfDstYSize >=
120✔
3848
                 dfYOff + dfYSize);
120✔
3849
        oSourceDesc.bCoversWholeAOI = bCoversWholeAOI;
210✔
3850

3851
        m_aoSourceDesc[i] = std::move(oSourceDesc);
210✔
3852

3853
        if (m_aoSourceDesc[i].bCoversWholeAOI &&
210✔
3854
            !m_aoSourceDesc[i].bHasNoData && !m_aoSourceDesc[i].poMaskBand)
210✔
3855
        {
3856
            break;
82✔
3857
        }
3858
    }
3859

3860
    if (i > 0)
124✔
3861
    {
3862
        // Remove sources that will not be rendered
3863
        m_aoSourceDesc.erase(m_aoSourceDesc.begin(),
32✔
3864
                             m_aoSourceDesc.begin() + i);
64✔
3865
    }
3866

3867
    // Truncate the array when its last elements have no dataset
3868
    i = m_aoSourceDesc.size();
124✔
3869
    while (i > 0)
333✔
3870
    {
3871
        --i;
213✔
3872
        if (!m_aoSourceDesc[i].poDS)
213✔
3873
        {
3874
            m_aoSourceDesc.resize(i);
4✔
3875
            break;
4✔
3876
        }
3877
    }
3878

3879
    return true;
124✔
3880
}
3881

3882
/************************************************************************/
3883
/*                          SortSourceDesc()                            */
3884
/************************************************************************/
3885

3886
void GDALTileIndexDataset::SortSourceDesc()
63✔
3887
{
3888
    const auto eFieldType = m_nSortFieldIndex >= 0
63✔
3889
                                ? m_poLayer->GetLayerDefn()
63✔
3890
                                      ->GetFieldDefn(m_nSortFieldIndex)
47✔
3891
                                      ->GetType()
47✔
3892
                                : OFTMaxType;
63✔
3893
    std::sort(
63✔
3894
        m_aoSourceDesc.begin(), m_aoSourceDesc.end(),
3895
        [this, eFieldType](const SourceDesc &a, const SourceDesc &b)
1,810✔
3896
        {
3897
            const auto &poFeatureA = (m_bSortFieldAsc ? a : b).poFeature;
413✔
3898
            const auto &poFeatureB = (m_bSortFieldAsc ? b : a).poFeature;
413✔
3899
            if (m_nSortFieldIndex >= 0 &&
906✔
3900
                poFeatureA->IsFieldSetAndNotNull(m_nSortFieldIndex) &&
493✔
3901
                poFeatureB->IsFieldSetAndNotNull(m_nSortFieldIndex))
80✔
3902
            {
3903
                if (eFieldType == OFTString)
80✔
3904
                {
3905
                    const int nCmp =
3906
                        strcmp(poFeatureA->GetFieldAsString(m_nSortFieldIndex),
5✔
3907
                               poFeatureB->GetFieldAsString(m_nSortFieldIndex));
3908
                    if (nCmp < 0)
5✔
3909
                        return true;
1✔
3910
                    if (nCmp > 0)
4✔
3911
                        return false;
2✔
3912
                }
3913
                else if (eFieldType == OFTInteger || eFieldType == OFTInteger64)
75✔
3914
                {
3915
                    const auto nA =
3916
                        poFeatureA->GetFieldAsInteger64(m_nSortFieldIndex);
45✔
3917
                    const auto nB =
3918
                        poFeatureB->GetFieldAsInteger64(m_nSortFieldIndex);
45✔
3919
                    if (nA < nB)
45✔
3920
                        return true;
3✔
3921
                    if (nA > nB)
42✔
3922
                        return false;
42✔
3923
                }
3924
                else if (eFieldType == OFTReal)
30✔
3925
                {
3926
                    const auto dfA =
3927
                        poFeatureA->GetFieldAsDouble(m_nSortFieldIndex);
3✔
3928
                    const auto dfB =
3929
                        poFeatureB->GetFieldAsDouble(m_nSortFieldIndex);
3✔
3930
                    if (dfA < dfB)
3✔
3931
                        return true;
1✔
3932
                    if (dfA > dfB)
2✔
3933
                        return false;
2✔
3934
                }
3935
                else if (eFieldType == OFTDate || eFieldType == OFTDateTime)
27✔
3936
                {
3937
                    const auto poFieldA =
3938
                        poFeatureA->GetRawFieldRef(m_nSortFieldIndex);
27✔
3939
                    const auto poFieldB =
3940
                        poFeatureB->GetRawFieldRef(m_nSortFieldIndex);
27✔
3941

3942
#define COMPARE_DATE_COMPONENT(comp)                                           \
3943
    do                                                                         \
3944
    {                                                                          \
3945
        if (poFieldA->Date.comp < poFieldB->Date.comp)                         \
3946
            return true;                                                       \
3947
        if (poFieldA->Date.comp > poFieldB->Date.comp)                         \
3948
            return false;                                                      \
3949
    } while (0)
3950

3951
                    COMPARE_DATE_COMPONENT(Year);
27✔
3952
                    COMPARE_DATE_COMPONENT(Month);
21✔
3953
                    COMPARE_DATE_COMPONENT(Day);
15✔
3954
                    COMPARE_DATE_COMPONENT(Hour);
9✔
3955
                    COMPARE_DATE_COMPONENT(Minute);
8✔
3956
                    COMPARE_DATE_COMPONENT(Second);
7✔
3957
                }
3958
                else
3959
                {
3960
                    CPLAssert(false);
×
3961
                }
3962
            }
3963
            return poFeatureA->GetFID() < poFeatureB->GetFID();
341✔
3964
        });
3965
}
63✔
3966

3967
/************************************************************************/
3968
/*                   CompositeSrcWithMaskIntoDest()                     */
3969
/************************************************************************/
3970

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

4054
/************************************************************************/
4055
/*                         NeedInitBuffer()                             */
4056
/************************************************************************/
4057

4058
// Must be called after CollectSources()
4059
bool GDALTileIndexDataset::NeedInitBuffer(int nBandCount,
162✔
4060
                                          const int *panBandMap) const
4061
{
4062
    bool bNeedInitBuffer = true;
162✔
4063
    // If the last source (that is the most prioritary one) covers at least
4064
    // the window of interest and is fully opaque, then we don't need to
4065
    // initialize the buffer, and can directly render that source.
4066
    int bHasNoData = false;
162✔
4067
    if (!m_aoSourceDesc.empty() && m_aoSourceDesc.back().bCoversWholeAOI &&
320✔
4068
        (!m_aoSourceDesc.back().bHasNoData ||
146✔
4069
         // Also, if there's a single source and that the VRT bands and the
4070
         // source bands have the same nodata value, we can skip initialization.
4071
         (m_aoSourceDesc.size() == 1 && m_aoSourceDesc.back().bSameNoData &&
6✔
4072
          m_bSameNoData && m_bSameDataType &&
4✔
4073
          IsSameNaNAware(papoBands[0]->GetNoDataValue(&bHasNoData),
2✔
4074
                         m_aoSourceDesc.back().dfSameNoData) &&
2✔
4075
          bHasNoData)) &&
322✔
4076
        (!m_aoSourceDesc.back().poMaskBand ||
176✔
4077
         // Also, if there's a single source that has a mask band, and the VRT
4078
         // bands have no-nodata or a 0-nodata value, we can skip
4079
         // initialization.
4080
         (m_aoSourceDesc.size() == 1 && m_bSameDataType &&
43✔
4081
          !(nBandCount == 1 && panBandMap[0] == 0) && m_bSameNoData &&
7✔
4082
          papoBands[0]->GetNoDataValue(&bHasNoData) == 0)))
7✔
4083
    {
4084
        bNeedInitBuffer = false;
111✔
4085
    }
4086
    return bNeedInitBuffer;
162✔
4087
}
4088

4089
/************************************************************************/
4090
/*                            InitBuffer()                              */
4091
/************************************************************************/
4092

4093
void GDALTileIndexDataset::InitBuffer(void *pData, int nBufXSize, int nBufYSize,
56✔
4094
                                      GDALDataType eBufType, int nBandCount,
4095
                                      const int *panBandMap,
4096
                                      GSpacing nPixelSpace, GSpacing nLineSpace,
4097
                                      GSpacing nBandSpace) const
4098
{
4099
    const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
56✔
4100
    if (m_bSameNoData && nBandCount > 1 &&
56✔
4101
        ((nPixelSpace == nBufTypeSize &&
18✔
4102
          nLineSpace == nBufXSize * nPixelSpace &&
18✔
4103
          nBandSpace == nBufYSize * nLineSpace) ||
18✔
4104
         (nBandSpace == nBufTypeSize &&
×
4105
          nPixelSpace == nBandCount * nBandSpace &&
×
4106
          nLineSpace == nBufXSize * nPixelSpace)))
×
4107
    {
4108
        const int nBandNr = panBandMap[0];
18✔
4109
        auto poVRTBand =
4110
            nBandNr == 0
4111
                ? m_poMaskBand.get()
18✔
4112
                : cpl::down_cast<GDALTileIndexBand *>(papoBands[nBandNr - 1]);
18✔
4113
        CPLAssert(poVRTBand);
18✔
4114
        const double dfNoData = poVRTBand->m_dfNoDataValue;
18✔
4115
        if (dfNoData == 0.0)
18✔
4116
        {
4117
            memset(pData, 0,
16✔
4118
                   static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
16✔
4119
                       nBufTypeSize);
16✔
4120
        }
4121
        else
4122
        {
4123
            GDALCopyWords64(
2✔
4124
                &dfNoData, GDT_Float64, 0, pData, eBufType, nBufTypeSize,
4125
                static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount);
2✔
4126
        }
18✔
4127
    }
4128
    else
4129
    {
4130
        for (int i = 0; i < nBandCount; ++i)
77✔
4131
        {
4132
            const int nBandNr = panBandMap[i];
39✔
4133
            auto poVRTBand = nBandNr == 0 ? m_poMaskBand.get()
39✔
4134
                                          : cpl::down_cast<GDALTileIndexBand *>(
37✔
4135
                                                papoBands[nBandNr - 1]);
37✔
4136
            GByte *pabyBandData = static_cast<GByte *>(pData) + i * nBandSpace;
39✔
4137
            if (nPixelSpace == nBufTypeSize &&
39✔
4138
                poVRTBand->m_dfNoDataValue == 0.0)
39✔
4139
            {
4140
                if (nLineSpace == nBufXSize * nPixelSpace)
35✔
4141
                {
4142
                    memset(pabyBandData, 0,
35✔
4143
                           static_cast<size_t>(nBufYSize * nLineSpace));
35✔
4144
                }
4145
                else
4146
                {
4147
                    for (int iLine = 0; iLine < nBufYSize; iLine++)
×
4148
                    {
4149
                        memset(static_cast<GByte *>(pabyBandData) +
×
4150
                                   static_cast<GIntBig>(iLine) * nLineSpace,
×
4151
                               0, static_cast<size_t>(nBufXSize * nPixelSpace));
×
4152
                    }
4153
                }
35✔
4154
            }
4155
            else
4156
            {
4157
                double dfWriteValue = poVRTBand->m_dfNoDataValue;
4✔
4158

4159
                for (int iLine = 0; iLine < nBufYSize; iLine++)
12✔
4160
                {
4161
                    GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
8✔
4162
                                  static_cast<GByte *>(pabyBandData) +
8✔
4163
                                      static_cast<GIntBig>(nLineSpace) * iLine,
8✔
4164
                                  eBufType, static_cast<int>(nPixelSpace),
4165
                                  nBufXSize);
4166
                }
4167
            }
4168
        }
4169
    }
4170
}
56✔
4171

4172
/************************************************************************/
4173
/*                            RenderSource()                            */
4174
/************************************************************************/
4175

4176
CPLErr GDALTileIndexDataset::RenderSource(
461✔
4177
    const SourceDesc &oSourceDesc, bool bNeedInitBuffer, int nBandNrMax,
4178
    int nXOff, int nYOff, int nXSize, int nYSize, double dfXOff, double dfYOff,
4179
    double dfXSize, double dfYSize, int nBufXSize, int nBufYSize, void *pData,
4180
    GDALDataType eBufType, int nBandCount, BANDMAP_TYPE panBandMap,
4181
    GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace,
4182
    GDALRasterIOExtraArg *psExtraArg,
4183
    VRTSource::WorkingState &oWorkingState) const
4184
{
4185
    auto &poTileDS = oSourceDesc.poDS;
461✔
4186
    auto &poSource = oSourceDesc.poSource;
461✔
4187
    auto poComplexSource = dynamic_cast<VRTComplexSource *>(poSource.get());
461✔
4188
    CPLErr eErr = CE_None;
461✔
4189

4190
    if (poTileDS->GetRasterCount() + 1 == nBandNrMax &&
461✔
4191
        papoBands[nBandNrMax - 1]->GetColorInterpretation() == GCI_AlphaBand &&
465✔
4192
        papoBands[nBandNrMax - 1]->GetRasterDataType() == GDT_Byte)
4✔
4193
    {
4194
        // Special case when there's typically a mix of RGB and RGBA source
4195
        // datasets and we read a RGB one.
4196
        for (int iBand = 0; iBand < nBandCount && eErr == CE_None; ++iBand)
14✔
4197
        {
4198
            const int nBandNr = panBandMap[iBand];
10✔
4199
            if (nBandNr == nBandNrMax)
10✔
4200
            {
4201
                // The window we will actually request from the source raster band.
4202
                double dfReqXOff = 0.0;
4✔
4203
                double dfReqYOff = 0.0;
4✔
4204
                double dfReqXSize = 0.0;
4✔
4205
                double dfReqYSize = 0.0;
4✔
4206
                int nReqXOff = 0;
4✔
4207
                int nReqYOff = 0;
4✔
4208
                int nReqXSize = 0;
4✔
4209
                int nReqYSize = 0;
4✔
4210

4211
                // The window we will actual set _within_ the pData buffer.
4212
                int nOutXOff = 0;
4✔
4213
                int nOutYOff = 0;
4✔
4214
                int nOutXSize = 0;
4✔
4215
                int nOutYSize = 0;
4✔
4216

4217
                bool bError = false;
4✔
4218

4219
                auto poTileBand = poTileDS->GetRasterBand(1);
4✔
4220
                poSource->SetRasterBand(poTileBand, false);
4✔
4221
                if (poSource->GetSrcDstWindow(
4✔
4222
                        dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
4223
                        &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
4224
                        &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
4225
                        &nOutYOff, &nOutXSize, &nOutYSize, bError))
4✔
4226
                {
4227
                    GByte *pabyOut =
4✔
4228
                        static_cast<GByte *>(pData) +
4229
                        static_cast<GPtrDiff_t>(iBand * nBandSpace +
4✔
4230
                                                nOutXOff * nPixelSpace +
4✔
4231
                                                nOutYOff * nLineSpace);
4✔
4232

4233
                    constexpr GByte n255 = 255;
4✔
4234
                    for (int iY = 0; iY < nOutYSize; iY++)
8✔
4235
                    {
4236
                        GDALCopyWords(
4✔
4237
                            &n255, GDT_Byte, 0,
4238
                            pabyOut + static_cast<GPtrDiff_t>(iY * nLineSpace),
4✔
4239
                            eBufType, static_cast<int>(nPixelSpace), nOutXSize);
4240
                    }
4241
                }
4242
            }
4243
            else
4244
            {
4245
                auto poTileBand = poTileDS->GetRasterBand(nBandNr);
6✔
4246
                if (poComplexSource)
6✔
4247
                {
4248
                    int bHasNoData = false;
×
4249
                    const double dfNoDataValue =
4250
                        poTileBand->GetNoDataValue(&bHasNoData);
×
4251
                    poComplexSource->SetNoDataValue(
×
4252
                        bHasNoData ? dfNoDataValue : VRT_NODATA_UNSET);
×
4253
                }
4254
                poSource->SetRasterBand(poTileBand, false);
6✔
4255

4256
                GDALRasterIOExtraArg sExtraArg;
4257
                INIT_RASTERIO_EXTRA_ARG(sExtraArg);
6✔
4258
                if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
6✔
4259
                {
4260
                    // cppcheck-suppress redundantAssignment
4261
                    sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4262
                }
4263
                else
4264
                {
4265
                    // cppcheck-suppress redundantAssignment
4266
                    sExtraArg.eResampleAlg = m_eResampling;
6✔
4267
                }
4268

4269
                GByte *pabyBandData =
6✔
4270
                    static_cast<GByte *>(pData) + iBand * nBandSpace;
6✔
4271
                eErr = poSource->RasterIO(
12✔
4272
                    poTileBand->GetRasterDataType(), nXOff, nYOff, nXSize,
4273
                    nYSize, pabyBandData, nBufXSize, nBufYSize, eBufType,
4274
                    nPixelSpace, nLineSpace, &sExtraArg, oWorkingState);
6✔
4275
            }
4276
        }
4277
        return eErr;
4✔
4278
    }
4279
    else if (poTileDS->GetRasterCount() < nBandNrMax)
457✔
4280
    {
4281
        CPLError(CE_Failure, CPLE_AppDefined, "%s has not enough bands.",
2✔
4282
                 oSourceDesc.osName.c_str());
4283
        return CE_Failure;
2✔
4284
    }
4285

4286
    if ((oSourceDesc.poMaskBand && bNeedInitBuffer) || nBandNrMax == 0)
455✔
4287
    {
4288
        // The window we will actually request from the source raster band.
4289
        double dfReqXOff = 0.0;
55✔
4290
        double dfReqYOff = 0.0;
55✔
4291
        double dfReqXSize = 0.0;
55✔
4292
        double dfReqYSize = 0.0;
55✔
4293
        int nReqXOff = 0;
55✔
4294
        int nReqYOff = 0;
55✔
4295
        int nReqXSize = 0;
55✔
4296
        int nReqYSize = 0;
55✔
4297

4298
        // The window we will actual set _within_ the pData buffer.
4299
        int nOutXOff = 0;
55✔
4300
        int nOutYOff = 0;
55✔
4301
        int nOutXSize = 0;
55✔
4302
        int nOutYSize = 0;
55✔
4303

4304
        bool bError = false;
55✔
4305

4306
        auto poFirstTileBand = poTileDS->GetRasterBand(1);
55✔
4307
        poSource->SetRasterBand(poFirstTileBand, false);
55✔
4308
        if (poSource->GetSrcDstWindow(
55✔
4309
                dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
4310
                &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
4311
                &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff, &nOutYOff,
4312
                &nOutXSize, &nOutYSize, bError))
55✔
4313
        {
4314
            int iMaskBandIdx = -1;
55✔
4315
            if (eBufType == GDT_Byte && nBandNrMax == 0)
55✔
4316
            {
4317
                // when called from m_poMaskBand
4318
                iMaskBandIdx = 0;
4✔
4319
            }
4320
            else if (oSourceDesc.poMaskBand)
51✔
4321
            {
4322
                // If we request a Byte buffer and the mask band is actually
4323
                // one of the queried bands of this request, we can save
4324
                // requesting it separately.
4325
                const int nMaskBandNr = oSourceDesc.poMaskBand->GetBand();
51✔
4326
                if (eBufType == GDT_Byte && nMaskBandNr >= 1 &&
39✔
4327
                    nMaskBandNr <= poTileDS->GetRasterCount() &&
129✔
4328
                    poTileDS->GetRasterBand(nMaskBandNr) ==
39✔
4329
                        oSourceDesc.poMaskBand)
39✔
4330
                {
4331
                    for (int iBand = 0; iBand < nBandCount; ++iBand)
61✔
4332
                    {
4333
                        if (panBandMap[iBand] == nMaskBandNr)
44✔
4334
                        {
4335
                            iMaskBandIdx = iBand;
20✔
4336
                            break;
20✔
4337
                        }
4338
                    }
4339
                }
4340
            }
4341

4342
            GDALRasterIOExtraArg sExtraArg;
4343
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
55✔
4344
            if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
55✔
4345
            {
4346
                // cppcheck-suppress redundantAssignment
4347
                sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4348
            }
4349
            else
4350
            {
4351
                // cppcheck-suppress redundantAssignment
4352
                sExtraArg.eResampleAlg = m_eResampling;
55✔
4353
            }
4354
            sExtraArg.bFloatingPointWindowValidity = TRUE;
55✔
4355
            sExtraArg.dfXOff = dfReqXOff;
55✔
4356
            sExtraArg.dfYOff = dfReqYOff;
55✔
4357
            sExtraArg.dfXSize = dfReqXSize;
55✔
4358
            sExtraArg.dfYSize = dfReqYSize;
55✔
4359

4360
            if (iMaskBandIdx < 0 && oSourceDesc.abyMask.empty() &&
76✔
4361
                oSourceDesc.poMaskBand)
21✔
4362
            {
4363
                // Fetch the mask band
4364
                try
4365
                {
4366
                    oSourceDesc.abyMask.resize(static_cast<size_t>(nOutXSize) *
21✔
4367
                                               nOutYSize);
21✔
4368
                }
4369
                catch (const std::bad_alloc &)
×
4370
                {
4371
                    CPLError(CE_Failure, CPLE_OutOfMemory,
×
4372
                             "Cannot allocate working buffer for mask");
4373
                    return CE_Failure;
×
4374
                }
4375

4376
                if (oSourceDesc.poMaskBand->RasterIO(
21✔
4377
                        GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
4378
                        oSourceDesc.abyMask.data(), nOutXSize, nOutYSize,
21✔
4379
                        GDT_Byte, 0, 0, &sExtraArg) != CE_None)
21✔
4380
                {
4381
                    oSourceDesc.abyMask.clear();
×
4382
                    return CE_Failure;
×
4383
                }
4384
            }
4385

4386
            // Allocate a temporary contiguous buffer to receive pixel data
4387
            const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
55✔
4388
            const size_t nWorkBufferBandSize =
55✔
4389
                static_cast<size_t>(nOutXSize) * nOutYSize * nBufTypeSize;
55✔
4390
            std::vector<GByte> abyWorkBuffer;
55✔
4391
            try
4392
            {
4393
                abyWorkBuffer.resize(nBandCount * nWorkBufferBandSize);
55✔
4394
            }
4395
            catch (const std::bad_alloc &)
×
4396
            {
4397
                CPLError(CE_Failure, CPLE_OutOfMemory,
×
4398
                         "Cannot allocate working buffer");
4399
                return CE_Failure;
×
4400
            }
4401

4402
            const GByte *const pabyMask =
4403
                iMaskBandIdx >= 0
4404
                    ? abyWorkBuffer.data() + iMaskBandIdx * nWorkBufferBandSize
24✔
4405
                    : oSourceDesc.abyMask.data();
79✔
4406

4407
            if (nBandNrMax == 0)
55✔
4408
            {
4409
                // Special case when called from m_poMaskBand
4410
                if (poTileDS->GetRasterBand(1)->GetMaskBand()->RasterIO(
12✔
4411
                        GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
4412
                        abyWorkBuffer.data(), nOutXSize, nOutYSize, eBufType, 0,
6✔
4413
                        0, &sExtraArg) != CE_None)
6✔
4414
                {
4415
                    return CE_Failure;
×
4416
                }
4417
            }
4418
            else if (poTileDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
98✔
4419
                                        nReqYSize, abyWorkBuffer.data(),
49✔
4420
                                        nOutXSize, nOutYSize, eBufType,
4421
                                        nBandCount, panBandMap, 0, 0, 0,
4422
                                        &sExtraArg) != CE_None)
49✔
4423
            {
4424
                return CE_Failure;
×
4425
            }
4426

4427
            // Compose the temporary contiguous buffer into the target
4428
            // buffer, taking into account the mask
4429
            GByte *pabyOut = static_cast<GByte *>(pData) +
55✔
4430
                             static_cast<GPtrDiff_t>(nOutXOff * nPixelSpace +
55✔
4431
                                                     nOutYOff * nLineSpace);
55✔
4432

4433
            for (int iBand = 0; iBand < nBandCount && eErr == CE_None; ++iBand)
121✔
4434
            {
4435
                GByte *pabyDestBand =
66✔
4436
                    pabyOut + static_cast<GPtrDiff_t>(iBand * nBandSpace);
66✔
4437
                const GByte *pabySrc =
4438
                    abyWorkBuffer.data() + iBand * nWorkBufferBandSize;
66✔
4439

4440
                CompositeSrcWithMaskIntoDest(
66✔
4441
                    nOutXSize, nOutYSize, eBufType, nBufTypeSize, nPixelSpace,
4442
                    nLineSpace, pabySrc, pabyMask, pabyDestBand);
4443
            }
4444
        }
55✔
4445
    }
4446
    else if (m_bSameDataType && !bNeedInitBuffer && oSourceDesc.bHasNoData)
400✔
4447
    {
4448
        // We create a non-VRTComplexSource SimpleSource copy of poSource
4449
        // to be able to call DatasetRasterIO()
4450
        VRTSimpleSource oSimpleSource(poSource.get(), 1.0, 1.0);
1✔
4451

4452
        GDALRasterIOExtraArg sExtraArg;
4453
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1✔
4454
        if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
1✔
4455
        {
4456
            // cppcheck-suppress redundantAssignment
4457
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4458
        }
4459
        else
4460
        {
4461
            // cppcheck-suppress redundantAssignment
4462
            sExtraArg.eResampleAlg = m_eResampling;
1✔
4463
        }
4464

4465
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
1✔
4466
        oSimpleSource.SetRasterBand(poTileBand, false);
1✔
4467
        eErr = oSimpleSource.DatasetRasterIO(
1✔
4468
            papoBands[0]->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
1✔
4469
            pData, nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
4470
            nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
1✔
4471
    }
4472
    else if (m_bSameDataType && !poComplexSource)
399✔
4473
    {
4474
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
397✔
4475
        poSource->SetRasterBand(poTileBand, false);
397✔
4476

4477
        GDALRasterIOExtraArg sExtraArg;
4478
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
397✔
4479
        if (poTileBand->GetColorTable())
397✔
4480
        {
4481
            // cppcheck-suppress redundantAssignment
4482
            sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
×
4483
        }
4484
        else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
397✔
4485
        {
4486
            // cppcheck-suppress redundantAssignment
4487
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4488
        }
4489
        else
4490
        {
4491
            // cppcheck-suppress redundantAssignment
4492
            sExtraArg.eResampleAlg = m_eResampling;
397✔
4493
        }
4494

4495
        eErr = poSource->DatasetRasterIO(
794✔
4496
            papoBands[0]->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
397✔
4497
            pData, nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
4498
            nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
397✔
4499
    }
4500
    else
4501
    {
4502
        for (int i = 0; i < nBandCount && eErr == CE_None; ++i)
4✔
4503
        {
4504
            const int nBandNr = panBandMap[i];
2✔
4505
            GByte *pabyBandData = static_cast<GByte *>(pData) + i * nBandSpace;
2✔
4506
            auto poTileBand = poTileDS->GetRasterBand(nBandNr);
2✔
4507
            if (poComplexSource)
2✔
4508
            {
4509
                int bHasNoData = false;
2✔
4510
                const double dfNoDataValue =
4511
                    poTileBand->GetNoDataValue(&bHasNoData);
2✔
4512
                poComplexSource->SetNoDataValue(bHasNoData ? dfNoDataValue
2✔
4513
                                                           : VRT_NODATA_UNSET);
2✔
4514
            }
4515
            poSource->SetRasterBand(poTileBand, false);
2✔
4516

4517
            GDALRasterIOExtraArg sExtraArg;
4518
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2✔
4519
            if (poTileBand->GetColorTable())
2✔
4520
            {
4521
                // cppcheck-suppress redundantAssignment
4522
                sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
×
4523
            }
4524
            else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2✔
4525
            {
4526
                // cppcheck-suppress redundantAssignment
4527
                sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4528
            }
4529
            else
4530
            {
4531
                // cppcheck-suppress redundantAssignment
4532
                sExtraArg.eResampleAlg = m_eResampling;
2✔
4533
            }
4534

4535
            eErr = poSource->RasterIO(
4✔
4536
                papoBands[nBandNr - 1]->GetRasterDataType(), nXOff, nYOff,
2✔
4537
                nXSize, nYSize, pabyBandData, nBufXSize, nBufYSize, eBufType,
4538
                nPixelSpace, nLineSpace, &sExtraArg, oWorkingState);
2✔
4539
        }
4540
    }
4541
    return eErr;
455✔
4542
}
4543

4544
/************************************************************************/
4545
/*                             IRasterIO()                              */
4546
/************************************************************************/
4547

4548
CPLErr GDALTileIndexDataset::IRasterIO(
165✔
4549
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
4550
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
4551
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
4552
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
4553
{
4554
    if (eRWFlag != GF_Read)
165✔
4555
        return CE_Failure;
×
4556

4557
    if (nBufXSize < nXSize && nBufYSize < nYSize && AreOverviewsEnabled())
165✔
4558
    {
4559
        int bTried = FALSE;
2✔
4560
        const CPLErr eErr = TryOverviewRasterIO(
2✔
4561
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
4562
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
4563
            nBandSpace, psExtraArg, &bTried);
4564
        if (bTried)
2✔
4565
            return eErr;
2✔
4566
    }
4567

4568
    double dfXOff = nXOff;
163✔
4569
    double dfYOff = nYOff;
163✔
4570
    double dfXSize = nXSize;
163✔
4571
    double dfYSize = nYSize;
163✔
4572
    if (psExtraArg->bFloatingPointWindowValidity)
163✔
4573
    {
4574
        dfXOff = psExtraArg->dfXOff;
×
4575
        dfYOff = psExtraArg->dfYOff;
×
4576
        dfXSize = psExtraArg->dfXSize;
×
4577
        dfYSize = psExtraArg->dfYSize;
×
4578
    }
4579

4580
    if (!CollectSources(dfXOff, dfYOff, dfXSize, dfYSize,
163✔
4581
                        /* bMultiThreadAllowed = */ true))
4582
    {
4583
        return CE_Failure;
×
4584
    }
4585

4586
    // We might be called with nBandCount == 1 && panBandMap[0] == 0
4587
    // to mean m_poMaskBand
4588
    int nBandNrMax = 0;
163✔
4589
    for (int i = 0; i < nBandCount; ++i)
375✔
4590
    {
4591
        const int nBandNr = panBandMap[i];
212✔
4592
        nBandNrMax = std::max(nBandNrMax, nBandNr);
212✔
4593
    }
4594

4595
    const bool bNeedInitBuffer =
4596
        m_bLastMustUseMultiThreading || NeedInitBuffer(nBandCount, panBandMap);
163✔
4597

4598
    if (!bNeedInitBuffer)
163✔
4599
    {
4600
        return RenderSource(
107✔
4601
            m_aoSourceDesc.back(), bNeedInitBuffer, nBandNrMax, nXOff, nYOff,
107✔
4602
            nXSize, nYSize, dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize,
4603
            nBufYSize, pData, eBufType, nBandCount, panBandMap, nPixelSpace,
4604
            nLineSpace, nBandSpace, psExtraArg, m_oWorkingState);
214✔
4605
    }
4606
    else
4607
    {
4608
        InitBuffer(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
56✔
4609
                   panBandMap, nPixelSpace, nLineSpace, nBandSpace);
4610

4611
        if (m_bLastMustUseMultiThreading)
56✔
4612
        {
4613
            CPLErrorAccumulator oErrorAccumulator;
12✔
4614
            std::atomic<bool> bSuccess = true;
6✔
4615
            const int nContributingSources =
4616
                static_cast<int>(m_aoSourceDesc.size());
6✔
4617
            CPLWorkerThreadPool *psThreadPool = GDALGetGlobalThreadPool(
6✔
4618
                std::min(nContributingSources, m_nNumThreads));
6✔
4619
            const int nThreads =
4620
                std::min(nContributingSources, psThreadPool->GetThreadCount());
6✔
4621
            CPLDebugOnly("GTI",
6✔
4622
                         "IRasterIO(): use optimized "
4623
                         "multi-threaded code path. "
4624
                         "Using %d threads",
4625
                         nThreads);
4626

4627
            {
4628
                std::lock_guard oLock(m_oQueueWorkingStates.oMutex);
12✔
4629
                if (m_oQueueWorkingStates.oStates.size() <
6✔
4630
                    static_cast<size_t>(nThreads))
6✔
4631
                {
4632
                    m_oQueueWorkingStates.oStates.resize(nThreads);
4✔
4633
                }
4634
                for (int i = 0; i < nThreads; ++i)
22✔
4635
                {
4636
                    if (!m_oQueueWorkingStates.oStates[i])
16✔
4637
                        m_oQueueWorkingStates.oStates[i] =
10✔
4638
                            std::make_unique<VRTSource::WorkingState>();
20✔
4639
                }
4640
            }
4641

4642
            auto oQueue = psThreadPool->CreateJobQueue();
6✔
4643
            std::atomic<int> nCompletedJobs = 0;
6✔
4644
            for (auto &oSourceDesc : m_aoSourceDesc)
144✔
4645
            {
4646
                auto psJob = new RasterIOJob();
138✔
4647
                psJob->poDS = this;
138✔
4648
                psJob->pbSuccess = &bSuccess;
138✔
4649
                psJob->poErrorAccumulator = &oErrorAccumulator;
138✔
4650
                psJob->pnCompletedJobs = &nCompletedJobs;
138✔
4651
                psJob->poQueueWorkingStates = &m_oQueueWorkingStates;
138✔
4652
                psJob->nBandNrMax = nBandNrMax;
138✔
4653
                psJob->nXOff = nXOff;
138✔
4654
                psJob->nYOff = nYOff;
138✔
4655
                psJob->nXSize = nXSize;
138✔
4656
                psJob->nYSize = nYSize;
138✔
4657
                psJob->pData = pData;
138✔
4658
                psJob->nBufXSize = nBufXSize;
138✔
4659
                psJob->nBufYSize = nBufYSize;
138✔
4660
                psJob->eBufType = eBufType;
138✔
4661
                psJob->nBandCount = nBandCount;
138✔
4662
                psJob->panBandMap = panBandMap;
138✔
4663
                psJob->nPixelSpace = nPixelSpace;
138✔
4664
                psJob->nLineSpace = nLineSpace;
138✔
4665
                psJob->nBandSpace = nBandSpace;
138✔
4666
                psJob->psExtraArg = psExtraArg;
138✔
4667

4668
                psJob->osTileName = oSourceDesc.poFeature->GetFieldAsString(
4669
                    m_nLocationFieldIndex);
138✔
4670

4671
                if (!oQueue->SubmitJob(RasterIOJob::Func, psJob))
138✔
4672
                {
4673
                    delete psJob;
×
4674
                    bSuccess = false;
×
4675
                    break;
×
4676
                }
4677
            }
4678

4679
            while (oQueue->WaitEvent())
92✔
4680
            {
4681
                // Quite rough progress callback. We could do better by counting
4682
                // the number of contributing pixels.
4683
                if (psExtraArg->pfnProgress)
86✔
4684
                {
4685
                    psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
170✔
4686
                                                nContributingSources,
4687
                                            "", psExtraArg->pProgressData);
4688
                }
4689
            }
4690

4691
            oErrorAccumulator.ReplayErrors();
6✔
4692

4693
            if (bSuccess && psExtraArg->pfnProgress)
6✔
4694
            {
4695
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4✔
4696
            }
4697

4698
            return bSuccess ? CE_None : CE_Failure;
6✔
4699
        }
4700
        else
4701
        {
4702
            // Now render from bottom of the stack to top.
4703
            for (auto &oSourceDesc : m_aoSourceDesc)
267✔
4704
            {
4705
                if (oSourceDesc.poDS &&
434✔
4706
                    RenderSource(oSourceDesc, bNeedInitBuffer, nBandNrMax,
217✔
4707
                                 nXOff, nYOff, nXSize, nYSize, dfXOff, dfYOff,
4708
                                 dfXSize, dfYSize, nBufXSize, nBufYSize, pData,
4709
                                 eBufType, nBandCount, panBandMap, nPixelSpace,
4710
                                 nLineSpace, nBandSpace, psExtraArg,
4711
                                 m_oWorkingState) != CE_None)
434✔
4712
                    return CE_Failure;
×
4713
            }
4714

4715
            if (psExtraArg->pfnProgress)
50✔
4716
            {
4717
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4✔
4718
            }
4719

4720
            return CE_None;
50✔
4721
        }
4722
    }
4723
}
4724

4725
/************************************************************************/
4726
/*                 GDALTileIndexDataset::RasterIOJob::Func()            */
4727
/************************************************************************/
4728

4729
void GDALTileIndexDataset::RasterIOJob::Func(void *pData)
138✔
4730
{
4731
    auto psJob =
4732
        std::unique_ptr<RasterIOJob>(static_cast<RasterIOJob *>(pData));
276✔
4733
    if (*psJob->pbSuccess)
138✔
4734
    {
4735
        const std::string osTileName(GetAbsoluteFileName(
4736
            psJob->osTileName.c_str(), psJob->poDS->GetDescription()));
276✔
4737

4738
        SourceDesc oSourceDesc;
276✔
4739

4740
        auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
276✔
4741
        CPL_IGNORE_RET_VAL(oAccumulator);
138✔
4742

4743
        const bool bCanOpenSource =
4744
            psJob->poDS->GetSourceDesc(osTileName, oSourceDesc,
138✔
4745
                                       &psJob->poQueueWorkingStates->oMutex) &&
275✔
4746
            oSourceDesc.poDS;
137✔
4747

4748
        if (!bCanOpenSource)
138✔
4749
        {
4750
            *psJob->pbSuccess = false;
1✔
4751
        }
4752
        else
4753
        {
4754
            GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
137✔
4755
            sArg.pfnProgress = nullptr;
137✔
4756
            sArg.pProgressData = nullptr;
137✔
4757

4758
            std::unique_ptr<VRTSource::WorkingState> poWorkingState;
137✔
4759
            {
4760
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
274✔
4761
                poWorkingState =
4762
                    std::move(psJob->poQueueWorkingStates->oStates.back());
137✔
4763
                psJob->poQueueWorkingStates->oStates.pop_back();
137✔
4764
                CPLAssert(poWorkingState.get());
137✔
4765
            }
4766

4767
            double dfXOff = psJob->nXOff;
137✔
4768
            double dfYOff = psJob->nYOff;
137✔
4769
            double dfXSize = psJob->nXSize;
137✔
4770
            double dfYSize = psJob->nYSize;
137✔
4771
            if (psJob->psExtraArg->bFloatingPointWindowValidity)
137✔
4772
            {
4773
                dfXOff = psJob->psExtraArg->dfXOff;
×
4774
                dfYOff = psJob->psExtraArg->dfYOff;
×
4775
                dfXSize = psJob->psExtraArg->dfXSize;
×
4776
                dfYSize = psJob->psExtraArg->dfYSize;
×
4777
            }
4778

4779
            const bool bRenderOK =
4780
                psJob->poDS->RenderSource(
274✔
4781
                    oSourceDesc, /*bNeedInitBuffer = */ true, psJob->nBandNrMax,
137✔
4782
                    psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
137✔
4783
                    dfXOff, dfYOff, dfXSize, dfYSize, psJob->nBufXSize,
137✔
4784
                    psJob->nBufYSize, psJob->pData, psJob->eBufType,
137✔
4785
                    psJob->nBandCount, psJob->panBandMap, psJob->nPixelSpace,
137✔
4786
                    psJob->nLineSpace, psJob->nBandSpace, &sArg,
137✔
4787
                    *(poWorkingState.get())) == CE_None;
137✔
4788

4789
            if (!bRenderOK)
137✔
4790
            {
4791
                *psJob->pbSuccess = false;
1✔
4792
            }
4793

4794
            {
4795
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
274✔
4796
                psJob->poQueueWorkingStates->oStates.push_back(
274✔
4797
                    std::move(poWorkingState));
137✔
4798
            }
4799
        }
4800
    }
4801

4802
    ++(*psJob->pnCompletedJobs);
138✔
4803
}
138✔
4804

4805
/************************************************************************/
4806
/*                     GDALGTICreateAlgorithm                           */
4807
/************************************************************************/
4808

4809
class GDALGTICreateAlgorithm final : public GDALRasterIndexAlgorithm
4810
{
4811
  public:
4812
    static constexpr const char *NAME = "create";
4813
    static constexpr const char *DESCRIPTION =
4814
        "Create an index of raster datasets compatible of the GDAL Tile Index "
4815
        "(GTI) driver.";
4816
    static constexpr const char *HELP_URL =
4817
        "/programs/gdal_driver_gti_create.html";
4818

4819
    GDALGTICreateAlgorithm();
4820

4821
  protected:
4822
    bool AddExtraOptions(CPLStringList &aosOptions) override;
4823

4824
  private:
4825
    std::string m_xmlFilename{};
4826
    std::vector<double> m_resolution{};
4827
    std::vector<double> m_bbox{};
4828
    std::string m_dataType{};
4829
    int m_bandCount = 0;
4830
    std::vector<double> m_nodata{};
4831
    std::vector<std::string> m_colorInterpretation{};
4832
    bool m_mask = false;
4833
    std::vector<std::string> m_fetchedMetadata{};
4834
};
4835

4836
/************************************************************************/
4837
/*          GDALGTICreateAlgorithm::GDALGTICreateAlgorithm()            */
4838
/************************************************************************/
4839

4840
GDALGTICreateAlgorithm::GDALGTICreateAlgorithm()
19✔
4841
    : GDALRasterIndexAlgorithm(NAME, DESCRIPTION, HELP_URL)
19✔
4842
{
4843
    AddProgressArg();
19✔
4844
    AddInputDatasetArg(&m_inputDatasets, GDAL_OF_RASTER)
19✔
4845
        .SetAutoOpenDataset(false);
19✔
4846
    GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs();
19✔
4847

4848
    AddCommonOptions();
19✔
4849

4850
    AddArg("xml-filename", 0,
4851
           _("Filename of the XML Virtual Tile Index file to generate, that "
4852
             "can be used as an input for the GDAL GTI / Virtual Raster Tile "
4853
             "Index driver"),
4854
           &m_xmlFilename)
38✔
4855
        .SetMinCharCount(1);
19✔
4856

4857
    AddArg("resolution", 0,
4858
           _("Resolution (in destination CRS units) of the virtual mosaic"),
4859
           &m_resolution)
38✔
4860
        .SetMinCount(2)
19✔
4861
        .SetMaxCount(2)
19✔
4862
        .SetMinValueExcluded(0)
19✔
4863
        .SetRepeatedArgAllowed(false)
19✔
4864
        .SetDisplayHintAboutRepetition(false)
19✔
4865
        .SetMetaVar("<xres>,<yres>");
19✔
4866

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

4925
/************************************************************************/
4926
/*            GDALGTICreateAlgorithm::AddExtraOptions()                 */
4927
/************************************************************************/
4928

4929
bool GDALGTICreateAlgorithm::AddExtraOptions(CPLStringList &aosOptions)
4✔
4930
{
4931
    if (!m_xmlFilename.empty())
4✔
4932
    {
4933
        aosOptions.push_back("-gti_filename");
1✔
4934
        aosOptions.push_back(m_xmlFilename);
1✔
4935
    }
4936
    if (!m_resolution.empty())
4✔
4937
    {
4938
        aosOptions.push_back("-tr");
1✔
4939
        aosOptions.push_back(CPLSPrintf("%.17g", m_resolution[0]));
1✔
4940
        aosOptions.push_back(CPLSPrintf("%.17g", m_resolution[1]));
1✔
4941
    }
4942
    if (!m_bbox.empty())
4✔
4943
    {
4944
        aosOptions.push_back("-te");
1✔
4945
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
1✔
4946
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
1✔
4947
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
1✔
4948
        aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
1✔
4949
    }
4950
    if (!m_dataType.empty())
4✔
4951
    {
4952
        aosOptions.push_back("-ot");
1✔
4953
        aosOptions.push_back(m_dataType);
1✔
4954
    }
4955
    if (m_bandCount > 0)
4✔
4956
    {
4957
        aosOptions.push_back("-bandcount");
3✔
4958
        aosOptions.push_back(CPLSPrintf("%d", m_bandCount));
3✔
4959

4960
        if (!m_nodata.empty() && m_nodata.size() != 1 &&
5✔
4961
            static_cast<int>(m_nodata.size()) != m_bandCount)
2✔
4962
        {
4963
            ReportError(CE_Failure, CPLE_IllegalArg,
1✔
4964
                        "%d nodata values whereas one or %d were expected",
4965
                        static_cast<int>(m_nodata.size()), m_bandCount);
1✔
4966
            return false;
1✔
4967
        }
4968

4969
        if (!m_colorInterpretation.empty() &&
4✔
4970
            m_colorInterpretation.size() != 1 &&
4✔
4971
            static_cast<int>(m_colorInterpretation.size()) != m_bandCount)
2✔
4972
        {
4973
            ReportError(
1✔
4974
                CE_Failure, CPLE_IllegalArg,
4975
                "%d color interpretations whereas one or %d were expected",
4976
                static_cast<int>(m_colorInterpretation.size()), m_bandCount);
1✔
4977
            return false;
1✔
4978
        }
4979
    }
4980
    if (!m_nodata.empty())
2✔
4981
    {
4982
        std::string val;
2✔
4983
        for (double v : m_nodata)
3✔
4984
        {
4985
            if (!val.empty())
2✔
4986
                val += ',';
1✔
4987
            val += CPLSPrintf("%.17g", v);
2✔
4988
        }
4989
        aosOptions.push_back("-nodata");
1✔
4990
        aosOptions.push_back(val);
1✔
4991
    }
4992
    if (!m_colorInterpretation.empty())
2✔
4993
    {
4994
        std::string val;
2✔
4995
        for (const std::string &s : m_colorInterpretation)
3✔
4996
        {
4997
            if (!val.empty())
2✔
4998
                val += ',';
1✔
4999
            val += s;
2✔
5000
        }
5001
        aosOptions.push_back("-colorinterp");
1✔
5002
        aosOptions.push_back(val);
1✔
5003
    }
5004
    if (m_mask)
2✔
5005
        aosOptions.push_back("-mask");
1✔
5006
    for (const std::string &s : m_fetchedMetadata)
3✔
5007
    {
5008
        aosOptions.push_back("-fetch_md");
1✔
5009
        const CPLStringList aosTokens(CSLTokenizeString2(s.c_str(), ",", 0));
2✔
5010
        for (const char *token : aosTokens)
4✔
5011
        {
5012
            aosOptions.push_back(token);
3✔
5013
        }
5014
    }
5015
    return true;
2✔
5016
}
5017

5018
/************************************************************************/
5019
/*                 GDALTileIndexInstantiateAlgorithm()                  */
5020
/************************************************************************/
5021

5022
static GDALAlgorithm *
5023
GDALTileIndexInstantiateAlgorithm(const std::vector<std::string> &aosPath)
19✔
5024
{
5025
    if (aosPath.size() == 1 && aosPath[0] == "create")
19✔
5026
    {
5027
        return std::make_unique<GDALGTICreateAlgorithm>().release();
19✔
5028
    }
5029
    else
5030
    {
5031
        return nullptr;
×
5032
    }
5033
}
5034

5035
/************************************************************************/
5036
/*                         GDALRegister_GTI()                           */
5037
/************************************************************************/
5038

5039
void GDALRegister_GTI()
1,911✔
5040
{
5041
    if (GDALGetDriverByName("GTI") != nullptr)
1,911✔
5042
        return;
282✔
5043

5044
    auto poDriver = std::make_unique<GDALDriver>();
3,258✔
5045

5046
    poDriver->SetDescription("GTI");
1,629✔
5047
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
5048
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GDAL Raster Tile Index");
1,629✔
5049
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gti.gpkg gti.fgb gti");
1,629✔
5050
    poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, GTI_PREFIX);
1,629✔
5051
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gti.html");
1,629✔
5052

5053
    poDriver->pfnOpen = GDALTileIndexDatasetOpen;
1,629✔
5054
    poDriver->pfnIdentify = GDALTileIndexDatasetIdentify;
1,629✔
5055

5056
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
5057

5058
    poDriver->SetMetadataItem(
1,629✔
5059
        GDAL_DMD_OPENOPTIONLIST,
5060
        "<OpenOptionList>"
5061
        "  <Option name='LAYER' type='string'/>"
5062
        "  <Option name='LOCATION_FIELD' type='string'/>"
5063
        "  <Option name='SORT_FIELD' type='string'/>"
5064
        "  <Option name='SORT_FIELD_ASC' type='boolean'/>"
5065
        "  <Option name='FILTER' type='string'/>"
5066
        "  <Option name='SRS' type='string'/>"
5067
        "  <Option name='RESX' type='float'/>"
5068
        "  <Option name='RESY' type='float'/>"
5069
        "  <Option name='MINX' type='float'/>"
5070
        "  <Option name='MINY' type='float'/>"
5071
        "  <Option name='MAXX' type='float'/>"
5072
        "  <Option name='MAXY' type='float'/>"
5073
        "<Option name='NUM_THREADS' type='string' description="
5074
        "'Number of worker threads for reading. Can be set to ALL_CPUS' "
5075
        "default='ALL_CPUS'/>"
5076
        "</OpenOptionList>");
1,629✔
5077

5078
    poDriver->DeclareAlgorithm({"create"});
3,258✔
5079
    poDriver->pfnInstantiateAlgorithm = GDALTileIndexInstantiateAlgorithm;
1,629✔
5080

5081
#ifdef BUILT_AS_PLUGIN
5082
    // Used by gdaladdo and test_gdaladdo.py
5083
    poDriver->SetMetadataItem("IS_PLUGIN", "YES");
5084
#endif
5085

5086
    GetGDALDriverManager()->RegisterDriver(poDriver.release());
1,629✔
5087
}
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