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

OSGeo / gdal / 13700616263

06 Mar 2025 02:10PM UTC coverage: 70.331% (-0.02%) from 70.35%
13700616263

Pull #11923

github

web-flow
Merge 6a40f0624 into 6d20ab96f
Pull Request #11923: [cli][gdal_contour] Port gdal_contour to cli

355 of 433 new or added lines in 7 files covered. (81.99%)

20395 existing lines in 43 files now uncovered.

553123 of 786461 relevant lines covered (70.33%)

221121.31 hits per line

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

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

54
// Semantincs of indices of a GeoTransform (double[6]) matrix
55
constexpr int GT_TOPLEFT_X = 0;
56
constexpr int GT_WE_RES = 1;
57
constexpr int GT_ROTATION_PARAM1 = 2;
58
constexpr int GT_TOPLEFT_Y = 3;
59
constexpr int GT_ROTATION_PARAM2 = 4;
60
constexpr int GT_NS_RES = 5;
61

62
constexpr const char *GTI_PREFIX = "GTI:";
63

64
constexpr const char *MD_DS_TILE_INDEX_LAYER = "TILE_INDEX_LAYER";
65

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

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

110
constexpr const char *const MD_BAND_OFFSET = "OFFSET";
111
constexpr const char *const MD_BAND_SCALE = "SCALE";
112
constexpr const char *const MD_BAND_UNITTYPE = "UNITTYPE";
113
constexpr const char *const apszReservedBandItems[] = {
114
    MD_BAND_OFFSET, MD_BAND_SCALE, MD_BAND_UNITTYPE};
115

116
constexpr const char *GTI_XML_BANDCOUNT = "BandCount";
117
constexpr const char *GTI_XML_DATATYPE = "DataType";
118
constexpr const char *GTI_XML_NODATAVALUE = "NoDataValue";
119
constexpr const char *GTI_XML_COLORINTERP = "ColorInterp";
120
constexpr const char *GTI_XML_LOCATIONFIELD = "LocationField";
121
constexpr const char *GTI_XML_SORTFIELD = "SortField";
122
constexpr const char *GTI_XML_SORTFIELDASC = "SortFieldAsc";
123
constexpr const char *GTI_XML_MASKBAND = "MaskBand";
124
constexpr const char *GTI_XML_OVERVIEW_ELEMENT = "Overview";
125
constexpr const char *GTI_XML_OVERVIEW_DATASET = "Dataset";
126
constexpr const char *GTI_XML_OVERVIEW_LAYER = "Layer";
127
constexpr const char *GTI_XML_OVERVIEW_FACTOR = "Factor";
128

129
constexpr const char *GTI_XML_BAND_ELEMENT = "Band";
130
constexpr const char *GTI_XML_BAND_NUMBER = "band";
131
constexpr const char *GTI_XML_BAND_DATATYPE = "dataType";
132
constexpr const char *GTI_XML_BAND_DESCRIPTION = "Description";
133
constexpr const char *GTI_XML_BAND_OFFSET = "Offset";
134
constexpr const char *GTI_XML_BAND_SCALE = "Scale";
135
constexpr const char *GTI_XML_BAND_NODATAVALUE = "NoDataValue";
136
constexpr const char *GTI_XML_BAND_UNITTYPE = "UnitType";
137
constexpr const char *GTI_XML_BAND_COLORINTERP = "ColorInterp";
138
constexpr const char *GTI_XML_CATEGORYNAMES = "CategoryNames";
139
constexpr const char *GTI_XML_COLORTABLE = "ColorTable";
140
constexpr const char *GTI_XML_RAT = "GDALRasterAttributeTable";
141

142
/************************************************************************/
143
/*                           ENDS_WITH_CI()                             */
144
/************************************************************************/
145

146
static inline bool ENDS_WITH_CI(const char *a, const char *b)
54,480✔
147
{
148
    return strlen(a) >= strlen(b) && EQUAL(a + strlen(a) - strlen(b), b);
54,480✔
149
}
150

151
/************************************************************************/
152
/*                       GDALTileIndexDataset                           */
153
/************************************************************************/
154

155
class GDALTileIndexBand;
156

157
class GDALTileIndexDataset final : public GDALPamDataset
158
{
159
  public:
160
    GDALTileIndexDataset();
161
    ~GDALTileIndexDataset() override;
162

163
    bool Open(GDALOpenInfo *poOpenInfo);
164

165
    CPLErr FlushCache(bool bAtClosing) override;
166

167
    CPLErr GetGeoTransform(double *padfGeoTransform) override;
168
    const OGRSpatialReference *GetSpatialRef() const override;
169

170
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
171
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
172
                     GDALDataType eBufType, int nBandCount,
173
                     BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
174
                     GSpacing nLineSpace, GSpacing nBandSpace,
175
                     GDALRasterIOExtraArg *psExtraArg) override;
176

177
    const char *GetMetadataItem(const char *pszName,
178
                                const char *pszDomain) override;
179
    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
180
                           const char *pszDomain) override;
181
    CPLErr SetMetadata(char **papszMD, const char *pszDomain) override;
182

183
    void LoadOverviews();
184

185
    std::vector<GTISourceDesc> GetSourcesMoreRecentThan(int64_t mTime);
186

187
  private:
188
    friend class GDALTileIndexBand;
189

190
    //! Optional GTI XML
191
    CPLXMLTreeCloser m_psXMLTree{nullptr};
192

193
    //! Whether the GTI XML might be modified (by SetMetadata/SetMetadataItem)
194
    bool m_bXMLUpdatable = false;
195

196
    //! Whether the GTI XML has been modified (by SetMetadata/SetMetadataItem)
197
    bool m_bXMLModified = false;
198

199
    //! Unique string (without the process) for this tile index. Passed to
200
    //! GDALProxyPoolDataset to ensure that sources are unique for a given owner
201
    const std::string m_osUniqueHandle;
202

203
    //! Vector dataset with the sources
204
    std::unique_ptr<GDALDataset> m_poVectorDS{};
205

206
    //! Vector layer with the sources
207
    OGRLayer *m_poLayer = nullptr;
208

209
    //! When the SRS of m_poLayer is not the one we expose
210
    std::unique_ptr<OGRLayer> m_poWarpedLayerKeeper{};
211

212
    //! Geotransform matrix of the tile index
213
    std::array<double, 6> m_adfGeoTransform{0, 0, 0, 0, 0, 0};
214

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

219
    //! SRS of the tile index.
220
    OGRSpatialReference m_oSRS{};
221

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

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

232
    //! Whether all bands of the tile index have the same data type.
233
    bool m_bSameDataType = true;
234

235
    //! Whether all bands of the tile index have the same nodata value.
236
    bool m_bSameNoData = true;
237

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

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

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

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

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

253
    //! Whether sorting must be ascending (true) or descending (false).
254
    bool m_bSortFieldAsc = true;
255

256
    //! Resampling method by default for warping or when a source has not
257
    //! the same resolution as the tile index.
258
    std::string m_osResampling = "near";
259
    GDALRIOResampleAlg m_eResampling = GRIORA_NearestNeighbour;
260

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

264
    //! Whether we had to open of the sources at tile index opening.
265
    bool m_bScannedOneFeatureAtOpening = false;
266

267
    //! Array of overview descriptors.
268
    //! Each descriptor is a tuple (dataset_name, concatenated_open_options, layer_name, overview_factor).
269
    std::vector<std::tuple<std::string, CPLStringList, std::string, double>>
270
        m_aoOverviewDescriptor{};
271

272
    //! Array of overview datasets.
273
    std::vector<std::unique_ptr<GDALDataset>> m_apoOverviews{};
274

275
    //! Cache of buffers used by VRTComplexSource to avoid memory reallocation.
276
    VRTSource::WorkingState m_oWorkingState{};
277

278
    //! Used by IRasterIO() when using multi-threading
279
    struct QueueWorkingStates
280
    {
281
        std::mutex oMutex{};
282
        std::vector<std::unique_ptr<VRTSource::WorkingState>> oStates{};
283
    };
284

285
    //! Used by IRasterIO() when using multi-threading
286
    QueueWorkingStates m_oQueueWorkingStates{};
287

288
    //! Structure describing one of the source raster in the tile index.
289
    struct SourceDesc
290
    {
291
        //! Source dataset name.
292
        std::string osName{};
293

294
        //! Source dataset handle.
295
        std::shared_ptr<GDALDataset> poDS{};
296

297
        //! VRTSimpleSource or VRTComplexSource for the source.
298
        std::unique_ptr<VRTSimpleSource> poSource{};
299

300
        //! OGRFeature corresponding to the source in the tile index.
301
        std::unique_ptr<OGRFeature> poFeature{};
302

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

306
        //! Whether the source covers the whole area of interest of the current pixel query.
307
        bool bCoversWholeAOI = false;
308

309
        //! Whether the source has a nodata value at least in one of its band.
310
        bool bHasNoData = false;
311

312
        //! Whether all bands of the source have the same nodata value.
313
        bool bSameNoData = false;
314

315
        //! Nodata value of all bands (when bSameNoData == true).
316
        double dfSameNoData = 0;
317

318
        //! Mask band of the source.
319
        GDALRasterBand *poMaskBand = nullptr;
320
    };
321

322
    //! Array of sources participating to the current pixel query.
323
    std::vector<SourceDesc> m_aoSourceDesc{};
324

325
    //! Maximum number of threads. Updated by CollectSources().
326
    int m_nNumThreads = -1;
327

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

331
    //! From a source dataset name, return its SourceDesc description structure.
332
    bool GetSourceDesc(const std::string &osTileName, SourceDesc &oSourceDesc,
333
                       std::mutex *pMutex);
334

335
    //! Collect sources corresponding to the georeferenced window of interest,
336
    //! and store them in m_aoSourceDesc[].
337
    bool CollectSources(double dfXOff, double dfYOff, double dfXSize,
338
                        double dfYSize, bool bMultiThreadAllowed);
339

340
    //! Sort sources according to m_nSortFieldIndex.
341
    void SortSourceDesc();
342

343
    //! Whether the output buffer needs to be nodata initialized, or if
344
    //! sources are fully covering it.
345
    bool NeedInitBuffer(int nBandCount, const int *panBandMap) const;
346

347
    //! Nodata initialize the output buffer.
348
    void InitBuffer(void *pData, int nBufXSize, int nBufYSize,
349
                    GDALDataType eBufType, int nBandCount,
350
                    const int *panBandMap, GSpacing nPixelSpace,
351
                    GSpacing nLineSpace, GSpacing nBandSpace) const;
352

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

364
    //! Whether m_poVectorDS supports SetMetadata()/SetMetadataItem()
365
    bool TileIndexSupportsEditingLayerMetadata() const;
366

367
    //! Return number of threads that can be used
368
    int GetNumThreads() const;
369

370
    /** Structure used to declare a threaded job to satisfy IRasterIO()
371
     * on a given source.
372
     */
373
    struct RasterIOJob
374
    {
375
        std::atomic<int> *pnCompletedJobs = nullptr;
376
        std::atomic<bool> *pbSuccess = nullptr;
377
        CPLErrorAccumulator *poErrorAccumulator = nullptr;
378
        GDALTileIndexDataset *poDS = nullptr;
379
        GDALTileIndexDataset::QueueWorkingStates *poQueueWorkingStates =
380
            nullptr;
381
        int nBandNrMax = 0;
382

383
        int nXOff = 0;
384
        int nYOff = 0;
385
        int nXSize = 0;
386
        int nYSize = 0;
387
        void *pData = nullptr;
388
        int nBufXSize = 0;
389
        int nBufYSize = 0;
390
        int nBandCount = 0;
391
        BANDMAP_TYPE panBandMap = nullptr;
392
        GDALDataType eBufType = GDT_Unknown;
393
        GSpacing nPixelSpace = 0;
394
        GSpacing nLineSpace = 0;
395
        GSpacing nBandSpace = 0;
396
        GDALRasterIOExtraArg *psExtraArg = nullptr;
397

398
        std::string osTileName{};
399

400
        static void Func(void *pData);
401
    };
402

403
    CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexDataset)
404
};
405

406
/************************************************************************/
407
/*                            GDALTileIndexBand                          */
408
/************************************************************************/
409

410
class GDALTileIndexBand final : public GDALPamRasterBand
411
{
412
  public:
413
    GDALTileIndexBand(GDALTileIndexDataset *poDSIn, int nBandIn,
414
                      GDALDataType eDT, int nBlockXSizeIn, int nBlockYSizeIn);
415

416
    double GetNoDataValue(int *pbHasNoData) override
63✔
417
    {
418
        if (pbHasNoData)
63✔
419
            *pbHasNoData = m_bNoDataValueSet;
63✔
420
        return m_dfNoDataValue;
63✔
421
    }
422

423
    GDALColorInterp GetColorInterpretation() override
47✔
424
    {
425
        return m_eColorInterp;
47✔
426
    }
427

428
    CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
429

430
    CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
431
                     int nYSize, void *pData, int nBufXSize, int nBufYSize,
432
                     GDALDataType eBufType, GSpacing nPixelSpace,
433
                     GSpacing nLineSpace,
434
                     GDALRasterIOExtraArg *psExtraArg) override;
435

436
    int IGetDataCoverageStatus(int nXOff, int nYOff, int nXSize, int nYSize,
437
                               int nMaskFlagStop, double *pdfDataPct) override;
438

439
    int GetMaskFlags() override
12✔
440
    {
441
        if (m_poDS->m_poMaskBand && m_poDS->m_poMaskBand.get() != this)
12✔
442
            return GMF_PER_DATASET;
4✔
443
        return GDALPamRasterBand::GetMaskFlags();
8✔
444
    }
445

446
    GDALRasterBand *GetMaskBand() override
18✔
447
    {
448
        if (m_poDS->m_poMaskBand && m_poDS->m_poMaskBand.get() != this)
18✔
449
            return m_poDS->m_poMaskBand.get();
7✔
450
        return GDALPamRasterBand::GetMaskBand();
11✔
451
    }
452

453
    double GetOffset(int *pbHasValue) override
10✔
454
    {
455
        int bHasValue = FALSE;
10✔
456
        double dfVal = GDALPamRasterBand::GetOffset(&bHasValue);
10✔
457
        if (bHasValue)
10✔
458
        {
459
            if (pbHasValue)
×
460
                *pbHasValue = true;
×
461
            return dfVal;
×
462
        }
463
        if (pbHasValue)
10✔
464
            *pbHasValue = !std::isnan(m_dfOffset);
10✔
465
        return std::isnan(m_dfOffset) ? 0.0 : m_dfOffset;
10✔
466
    }
467

468
    double GetScale(int *pbHasValue) override
10✔
469
    {
470
        int bHasValue = FALSE;
10✔
471
        double dfVal = GDALPamRasterBand::GetScale(&bHasValue);
10✔
472
        if (bHasValue)
10✔
473
        {
474
            if (pbHasValue)
×
475
                *pbHasValue = true;
×
476
            return dfVal;
×
477
        }
478
        if (pbHasValue)
10✔
479
            *pbHasValue = !std::isnan(m_dfScale);
10✔
480
        return std::isnan(m_dfScale) ? 1.0 : m_dfScale;
10✔
481
    }
482

483
    const char *GetUnitType() override
6✔
484
    {
485
        const char *pszVal = GDALPamRasterBand::GetUnitType();
6✔
486
        if (pszVal && *pszVal)
6✔
487
            return pszVal;
×
488
        return m_osUnit.c_str();
6✔
489
    }
490

491
    char **GetCategoryNames() override
2✔
492
    {
493
        return m_aosCategoryNames.List();
2✔
494
    }
495

496
    GDALColorTable *GetColorTable() override
6✔
497
    {
498
        return m_poColorTable.get();
6✔
499
    }
500

501
    GDALRasterAttributeTable *GetDefaultRAT() override
2✔
502
    {
503
        return m_poRAT.get();
2✔
504
    }
505

506
    int GetOverviewCount() override;
507
    GDALRasterBand *GetOverview(int iOvr) override;
508

509
    char **GetMetadataDomainList() override;
510
    const char *GetMetadataItem(const char *pszName,
511
                                const char *pszDomain) override;
512
    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
513
                           const char *pszDomain) override;
514
    CPLErr SetMetadata(char **papszMD, const char *pszDomain) override;
515

516
  private:
517
    friend class GDALTileIndexDataset;
518

519
    //! Dataset that owns this band.
520
    GDALTileIndexDataset *m_poDS = nullptr;
521

522
    //! Whether a nodata value is set to this band.
523
    bool m_bNoDataValueSet = false;
524

525
    //! Nodata value.
526
    double m_dfNoDataValue = 0;
527

528
    //! Color interpretation.
529
    GDALColorInterp m_eColorInterp = GCI_Undefined;
530

531
    //! Cached value for GetMetadataItem("Pixel_X_Y", "LocationInfo").
532
    std::string m_osLastLocationInfo{};
533

534
    //! Scale value (returned by GetScale())
535
    double m_dfScale = std::numeric_limits<double>::quiet_NaN();
536

537
    //! Offset value (returned by GetOffset())
538
    double m_dfOffset = std::numeric_limits<double>::quiet_NaN();
539

540
    //! Unit type (returned by GetUnitType()).
541
    std::string m_osUnit{};
542

543
    //! Category names (returned by GetCategoryNames()).
544
    CPLStringList m_aosCategoryNames{};
545

546
    //! Color table (returned by GetColorTable()).
547
    std::unique_ptr<GDALColorTable> m_poColorTable{};
548

549
    //! Raster attribute table (returned by GetDefaultRAT()).
550
    std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
551

552
    CPL_DISALLOW_COPY_ASSIGN(GDALTileIndexBand)
553
};
554

555
/************************************************************************/
556
/*                        IsSameNaNAware()                              */
557
/************************************************************************/
558

559
static inline bool IsSameNaNAware(double a, double b)
259✔
560
{
561
    return a == b || (std::isnan(a) && std::isnan(b));
259✔
562
}
563

564
/************************************************************************/
565
/*                         GDALTileIndexDataset()                        */
566
/************************************************************************/
567

568
GDALTileIndexDataset::GDALTileIndexDataset()
266✔
569
    : m_osUniqueHandle(CPLSPrintf("%p", this))
266✔
570
{
571
}
266✔
572

573
/************************************************************************/
574
/*                        GetAbsoluteFileName()                         */
575
/************************************************************************/
576

577
static std::string GetAbsoluteFileName(const char *pszTileName,
571✔
578
                                       const char *pszVRTName)
579
{
580
    if (STARTS_WITH(pszTileName, "https://"))
571✔
581
        return std::string("/vsicurl/").append(pszTileName);
10✔
582

583
    if (CPLIsFilenameRelative(pszTileName) &&
566✔
584
        !STARTS_WITH(pszTileName, "<VRTDataset") &&
570✔
585
        !STARTS_WITH(pszVRTName, "<GDALTileIndexDataset"))
4✔
586
    {
587
        const auto oSubDSInfo(GDALGetSubdatasetInfo(pszTileName));
4✔
588
        if (oSubDSInfo && !oSubDSInfo->GetPathComponent().empty())
4✔
589
        {
590
            const std::string osPath(oSubDSInfo->GetPathComponent());
4✔
591
            const std::string osRet =
592
                CPLIsFilenameRelative(osPath.c_str())
2✔
593
                    ? oSubDSInfo->ModifyPathComponent(
594
                          CPLProjectRelativeFilenameSafe(
2✔
595
                              CPLGetPathSafe(pszVRTName).c_str(),
3✔
596
                              osPath.c_str()))
597
                    : std::string(pszTileName);
6✔
598
            GDALDestroySubdatasetInfo(oSubDSInfo);
2✔
599
            return osRet;
2✔
600
        }
601

602
        const std::string osRelativeMadeAbsolute =
603
            CPLProjectRelativeFilenameSafe(CPLGetPathSafe(pszVRTName).c_str(),
×
604
                                           pszTileName);
2✔
605
        VSIStatBufL sStat;
606
        if (VSIStatL(osRelativeMadeAbsolute.c_str(), &sStat) == 0)
2✔
607
            return osRelativeMadeAbsolute;
2✔
608
    }
609
    return pszTileName;
562✔
610
}
611

612
/************************************************************************/
613
/*                    GTIDoPaletteExpansionIfNeeded()                   */
614
/************************************************************************/
615

616
//! Do palette -> RGB(A) expansion
617
static bool
618
GTIDoPaletteExpansionIfNeeded(std::shared_ptr<GDALDataset> &poTileDS,
442✔
619
                              int nBandCount)
620
{
621
    if (poTileDS->GetRasterCount() == 1 &&
707✔
622
        (nBandCount == 3 || nBandCount == 4) &&
709✔
623
        poTileDS->GetRasterBand(1)->GetColorTable() != nullptr)
4✔
624
    {
625

626
        CPLStringList aosOptions;
4✔
627
        aosOptions.AddString("-of");
4✔
628
        aosOptions.AddString("VRT");
4✔
629

630
        aosOptions.AddString("-expand");
4✔
631
        aosOptions.AddString(nBandCount == 3 ? "rgb" : "rgba");
4✔
632

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

645
        poTileDS.reset(poRGBDS.release());
4✔
646
    }
647
    return true;
442✔
648
}
649

650
/************************************************************************/
651
/*                                Open()                                */
652
/************************************************************************/
653

654
bool GDALTileIndexDataset::Open(GDALOpenInfo *poOpenInfo)
266✔
655
{
656
    eAccess = poOpenInfo->eAccess;
266✔
657

658
    CPLXMLNode *psRoot = nullptr;
266✔
659
    const char *pszIndexDataset = poOpenInfo->pszFilename;
266✔
660

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

683
    if (m_psXMLTree)
264✔
684
    {
685
        psRoot = CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
27✔
686
        if (psRoot == nullptr)
27✔
687
        {
688
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
689
                     "Missing GDALTileIndexDataset root element.");
690
            return false;
1✔
691
        }
692

693
        pszIndexDataset = CPLGetXMLValue(psRoot, "IndexDataset", nullptr);
26✔
694
        if (!pszIndexDataset)
26✔
695
        {
696
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
697
                     "Missing IndexDataset element.");
698
            return false;
1✔
699
        }
700
    }
701

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

738
    if (m_poVectorDS->GetLayerCount() == 0)
260✔
739
    {
740
        CPLError(CE_Failure, CPLE_AppDefined, "%s has no vector layer",
1✔
741
                 poOpenInfo->pszFilename);
742
        return false;
1✔
743
    }
744

745
    double dfOvrFactor = 1.0;
259✔
746
    if (const char *pszFactor =
259✔
747
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "FACTOR"))
259✔
748
    {
749
        dfOvrFactor = CPLAtof(pszFactor);
5✔
750
        if (!(dfOvrFactor > 1.0))
5✔
751
        {
752
            CPLError(CE_Failure, CPLE_AppDefined, "Wrong overview factor");
1✔
753
            return false;
1✔
754
        }
755
    }
756

757
    const char *pszLayerName;
758

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

830
    // Try to get the metadata from an embedded xml:GTI domain
831
    if (!m_psXMLTree)
252✔
832
    {
833
        char **papszMD = m_poLayer->GetMetadata("xml:GTI");
230✔
834
        if (papszMD && papszMD[0])
230✔
835
        {
836
            m_psXMLTree.reset(CPLParseXMLString(papszMD[0]));
1✔
837
            if (m_psXMLTree == nullptr)
1✔
838
                return false;
×
839

840
            psRoot = CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
1✔
841
            if (psRoot == nullptr)
1✔
842
            {
843
                CPLError(CE_Failure, CPLE_AppDefined,
×
844
                         "Missing GDALTileIndexDataset root element.");
845
                return false;
×
846
            }
847
        }
848
    }
849

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

862
        if (psRoot)
7,225✔
863
        {
864
            pszVal = CPLGetXMLValue(psRoot, pszItem, nullptr);
516✔
865
            if (pszVal)
516✔
866
                return pszVal;
19✔
867

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

889
        return m_poLayer->GetMetadataItem(pszItem);
7,199✔
890
    };
252✔
891

892
    const char *pszFilter = GetOption("Filter");
252✔
893
    if (pszFilter)
252✔
894
    {
895
        if (m_poLayer->SetAttributeFilter(pszFilter) != OGRERR_NONE)
1✔
896
            return false;
×
897
    }
898

899
    const OGRFeatureDefn *poLayerDefn = m_poLayer->GetLayerDefn();
252✔
900

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

973
    m_nLocationFieldIndex =
252✔
974
        poLayerDefn->GetFieldIndex(osLocationFieldName.c_str());
252✔
975
    if (m_nLocationFieldIndex < 0)
252✔
976
    {
977
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
1✔
978
                 osLocationFieldName.c_str());
979
        return false;
1✔
980
    }
981
    if (poLayerDefn->GetFieldDefn(m_nLocationFieldIndex)->GetType() !=
251✔
982
        OFTString)
983
    {
984
        CPLError(CE_Failure, CPLE_AppDefined, "Field %s is not of type string",
1✔
985
                 osLocationFieldName.c_str());
986
        return false;
1✔
987
    }
988

989
    const char *pszSortFieldName = GetOption(MD_SORT_FIELD);
250✔
990
    if (pszSortFieldName)
250✔
991
    {
992
        m_nSortFieldIndex = poLayerDefn->GetFieldIndex(pszSortFieldName);
96✔
993
        if (m_nSortFieldIndex < 0)
96✔
994
        {
995
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
1✔
996
                     pszSortFieldName);
997
            return false;
1✔
998
        }
999

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

1011
        const char *pszSortFieldAsc = GetOption(MD_SORT_FIELD_ASC);
94✔
1012
        if (pszSortFieldAsc)
94✔
1013
        {
1014
            m_bSortFieldAsc = CPLTestBool(pszSortFieldAsc);
3✔
1015
        }
1016
    }
1017

1018
    const char *pszResX = GetOption(MD_RESX);
248✔
1019
    const char *pszResY = GetOption(MD_RESY);
248✔
1020
    if (pszResX && !pszResY)
248✔
1021
    {
1022
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1023
                 "%s metadata item defined, but not %s", MD_RESX, MD_RESY);
1024
        return false;
1✔
1025
    }
1026
    if (!pszResX && pszResY)
247✔
1027
    {
1028
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1029
                 "%s metadata item defined, but not %s", MD_RESY, MD_RESX);
1030
        return false;
1✔
1031
    }
1032

1033
    const char *pszResampling = GetOption(MD_RESAMPLING);
246✔
1034
    if (pszResampling)
246✔
1035
    {
1036
        const auto nErrorCountBefore = CPLGetErrorCounter();
8✔
1037
        m_eResampling = GDALRasterIOGetResampleAlg(pszResampling);
8✔
1038
        if (nErrorCountBefore != CPLGetErrorCounter())
8✔
1039
        {
1040
            return false;
×
1041
        }
1042
        m_osResampling = pszResampling;
8✔
1043
    }
1044

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

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

1072
    const char *pszDataType = GetOption(MD_DATA_TYPE);
239✔
1073
    const char *pszColorInterp = GetOption(MD_COLOR_INTERPRETATION);
239✔
1074
    int nBandCount = 0;
239✔
1075
    std::vector<GDALDataType> aeDataTypes;
478✔
1076
    std::vector<std::pair<bool, double>> aNoData;
478✔
1077
    std::vector<GDALColorInterp> aeColorInterp;
478✔
1078

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

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

1138
    const char *pszBandCount = GetOption(MD_BAND_COUNT);
235✔
1139
    if (pszBandCount)
235✔
1140
        nBandCount = atoi(pszBandCount);
22✔
1141

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

1155
    // Take into STAC GeoParquet proj:code / proj:epsg / proj:wkt2 / proj:projjson
1156
    // and proj:transform fields
1157
    std::unique_ptr<OGRFeature> poFeature;
234✔
1158
    std::string osResX, osResY, osMinX, osMinY, osMaxX, osMaxY;
468✔
1159
    int iProjCode = -1;
234✔
1160
    int iProjEPSG = -1;
234✔
1161
    int iProjWKT2 = -1;
234✔
1162
    int iProjPROJSON = -1;
234✔
1163
    int iProjTransform = -1;
234✔
1164

1165
    const bool bIsStacGeoParquet =
1166
        STARTS_WITH(osLocationFieldName.c_str(), "assets.") &&
239✔
1167
        EQUAL(osLocationFieldName.c_str() + osLocationFieldName.size() -
5✔
1168
                  strlen(".href"),
1169
              ".href");
1170
    std::string osAssetName;
468✔
1171
    if (bIsStacGeoParquet)
234✔
1172
    {
1173
        osAssetName = osLocationFieldName.substr(
10✔
1174
            strlen("assets."),
1175
            osLocationFieldName.size() - strlen("assets.") - strlen(".href"));
10✔
1176
    }
1177

1178
    const auto GetAssetFieldIndex =
1179
        [poLayerDefn, &osAssetName](const char *pszFieldName)
30✔
1180
    {
1181
        const int idx = poLayerDefn->GetFieldIndex(
17✔
1182
            CPLSPrintf("assets.%s.%s", osAssetName.c_str(), pszFieldName));
17✔
1183
        if (idx >= 0)
17✔
1184
            return idx;
4✔
1185
        return poLayerDefn->GetFieldIndex(pszFieldName);
13✔
1186
    };
234✔
1187

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

1212
            if (iProjCode >= 0 && poFeature->IsFieldSet(iProjCode))
5✔
1213
                oSTACSRS.SetFromUserInput(
1✔
1214
                    poFeature->GetFieldAsString(iProjCode),
1215
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1216

1217
            else if (iProjEPSG >= 0 && poFeature->IsFieldSet(iProjEPSG))
4✔
1218
                oSTACSRS.importFromEPSG(
2✔
1219
                    poFeature->GetFieldAsInteger(iProjEPSG));
1220

1221
            else if (iProjWKT2 >= 0 && poFeature->IsFieldSet(iProjWKT2))
2✔
1222
                oSTACSRS.SetFromUserInput(
1✔
1223
                    poFeature->GetFieldAsString(iProjWKT2),
1224
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1225

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

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

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

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

1320
                        m_oSRS = std::move(oSTACSRS);
5✔
1321
                        pszResX = osResX.c_str();
5✔
1322
                        pszResY = osResY.c_str();
5✔
1323
                        pszMinX = osMinX.c_str();
5✔
1324
                        pszMinY = osMinY.c_str();
5✔
1325
                        pszMaxX = osMaxX.c_str();
5✔
1326
                        pszMaxY = osMaxY.c_str();
5✔
1327
                        nCountMinMaxXY = 4;
5✔
1328

1329
                        poFeature.reset();
5✔
1330
                        m_poLayer->ResetReading();
5✔
1331

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

1345
    bool bHasMaskBand = false;
234✔
1346
    std::unique_ptr<GDALColorTable> poSingleColorTable;
234✔
1347
    if ((!pszBandCount && apoXMLNodeBands.empty()) ||
259✔
1348
        (!(pszResX && pszResY) && nCountXSizeYSizeGT == 0))
25✔
1349
    {
1350
        CPLDebug("GTI", "Inspecting one feature due to missing metadata items");
223✔
1351
        m_bScannedOneFeatureAtOpening = true;
223✔
1352

1353
        if (!poFeature)
223✔
1354
            poFeature.reset(m_poLayer->GetNextFeature());
223✔
1355
        if (!poFeature ||
444✔
1356
            !poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
221✔
1357
        {
1358
            CPLError(
2✔
1359
                CE_Failure, CPLE_AppDefined,
1360
                "BAND_COUNT(+DATA_TYPE+COLOR_INTERPRETATION)+ (RESX+RESY or "
1361
                "XSIZE+YSIZE+GEOTRANSFORM) metadata items "
1362
                "missing");
1363
            return false;
10✔
1364
        }
1365

1366
        const char *pszTileName =
1367
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
221✔
1368
        const std::string osTileName(
1369
            GetAbsoluteFileName(pszTileName, poOpenInfo->pszFilename));
221✔
1370
        pszTileName = osTileName.c_str();
221✔
1371

1372
        auto poTileDS = std::shared_ptr<GDALDataset>(
1373
            GDALDataset::Open(pszTileName,
1374
                              GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR),
1375
            GDALDatasetUniquePtrReleaser());
221✔
1376
        if (!poTileDS)
221✔
1377
        {
1378
            return false;
1✔
1379
        }
1380

1381
        // do palette -> RGB(A) expansion if needed
1382
        if (!GTIDoPaletteExpansionIfNeeded(poTileDS, nBandCount))
220✔
1383
            return false;
×
1384

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

1406
            if (poTileBand->GetMaskFlags() == GMF_PER_DATASET)
270✔
1407
                bHasMaskBand = true;
1✔
1408
        }
1409
        if (!pszBandCount && nBandCount == 0)
220✔
1410
            nBandCount = nTileBandCount;
206✔
1411

1412
        auto poTileSRS = poTileDS->GetSpatialRef();
220✔
1413
        if (!m_oSRS.IsEmpty() && poTileSRS && !m_oSRS.IsSame(poTileSRS))
220✔
1414
        {
1415
            CPLStringList aosOptions;
7✔
1416
            aosOptions.AddString("-of");
7✔
1417
            aosOptions.AddString("VRT");
7✔
1418

1419
            char *pszWKT = nullptr;
7✔
1420
            const char *const apszWKTOptions[] = {"FORMAT=WKT2_2019", nullptr};
7✔
1421
            m_oSRS.exportToWkt(&pszWKT, apszWKTOptions);
7✔
1422
            if (pszWKT)
7✔
1423
                m_osWKT = pszWKT;
7✔
1424
            CPLFree(pszWKT);
7✔
1425

1426
            if (m_osWKT.empty())
7✔
1427
            {
1428
                CPLError(CE_Failure, CPLE_AppDefined,
×
1429
                         "Cannot export VRT SRS to WKT2");
1430
                return false;
×
1431
            }
1432

1433
            aosOptions.AddString("-t_srs");
7✔
1434
            aosOptions.AddString(m_osWKT.c_str());
7✔
1435

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

1449
            poTileDS.reset(poWarpDS.release());
7✔
1450
            poTileSRS = poTileDS->GetSpatialRef();
7✔
1451
            CPL_IGNORE_RET_VAL(poTileSRS);
7✔
1452
        }
1453

1454
        double adfGeoTransformTile[6];
1455
        if (poTileDS->GetGeoTransform(adfGeoTransformTile) != CE_None)
220✔
1456
        {
1457
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1458
                     "Cannot find geotransform on %s", pszTileName);
1459
            return false;
1✔
1460
        }
1461
        if (!(adfGeoTransformTile[GT_ROTATION_PARAM1] == 0))
219✔
1462
        {
1463
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1464
                     "3rd value of GeoTransform of %s must be 0", pszTileName);
1465
            return false;
1✔
1466
        }
1467
        if (!(adfGeoTransformTile[GT_ROTATION_PARAM2] == 0))
218✔
1468
        {
1469
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1470
                     "5th value of GeoTransform of %s must be 0", pszTileName);
1471
            return false;
1✔
1472
        }
1473
        if (!(adfGeoTransformTile[GT_NS_RES] < 0))
217✔
1474
        {
1475
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1476
                     "6th value of GeoTransform of %s must be < 0",
1477
                     pszTileName);
1478
            return false;
1✔
1479
        }
1480

1481
        const double dfResX = adfGeoTransformTile[GT_WE_RES];
216✔
1482
        const double dfResY = -adfGeoTransformTile[GT_NS_RES];
216✔
1483

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

1499
        const double dfXSize = (sEnvelope.MaxX - sEnvelope.MinX) / dfResX;
215✔
1500
        if (!(dfXSize >= 0 && dfXSize < INT_MAX))
215✔
1501
        {
1502
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1503
                     "Too small %s, or wrong layer extent", MD_RESX);
1504
            return false;
1✔
1505
        }
1506

1507
        const double dfYSize = (sEnvelope.MaxY - sEnvelope.MinY) / dfResY;
214✔
1508
        if (!(dfYSize >= 0 && dfYSize < INT_MAX))
214✔
1509
        {
1510
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1511
                     "Too small %s, or wrong layer extent", MD_RESY);
1512
            return false;
1✔
1513
        }
1514

1515
        m_adfGeoTransform[GT_TOPLEFT_X] = sEnvelope.MinX;
213✔
1516
        m_adfGeoTransform[GT_WE_RES] = dfResX;
213✔
1517
        m_adfGeoTransform[GT_ROTATION_PARAM1] = 0;
213✔
1518
        m_adfGeoTransform[GT_TOPLEFT_Y] = sEnvelope.MaxY;
213✔
1519
        m_adfGeoTransform[GT_ROTATION_PARAM2] = 0;
213✔
1520
        m_adfGeoTransform[GT_NS_RES] = -dfResY;
213✔
1521
        nRasterXSize = static_cast<int>(std::ceil(dfXSize));
213✔
1522
        nRasterYSize = static_cast<int>(std::ceil(dfYSize));
213✔
1523
    }
1524

1525
    if (pszXSize && pszYSize && pszGeoTransform)
224✔
1526
    {
1527
        const int nXSize = atoi(pszXSize);
12✔
1528
        if (nXSize <= 0)
12✔
1529
        {
1530
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1531
                     "%s metadata item must be > 0", MD_XSIZE);
1532
            return false;
6✔
1533
        }
1534

1535
        const int nYSize = atoi(pszYSize);
11✔
1536
        if (nYSize <= 0)
11✔
1537
        {
1538
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1539
                     "%s metadata item must be > 0", MD_YSIZE);
1540
            return false;
1✔
1541
        }
1542

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

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

1596
        OGREnvelope sEnvelope;
19✔
1597

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

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

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

1659
        m_adfGeoTransform[GT_TOPLEFT_X] = sEnvelope.MinX;
15✔
1660
        m_adfGeoTransform[GT_WE_RES] = dfResX;
15✔
1661
        m_adfGeoTransform[GT_ROTATION_PARAM1] = 0;
15✔
1662
        m_adfGeoTransform[GT_TOPLEFT_Y] = sEnvelope.MaxY;
15✔
1663
        m_adfGeoTransform[GT_ROTATION_PARAM2] = 0;
15✔
1664
        m_adfGeoTransform[GT_NS_RES] = -dfResY;
15✔
1665
        nRasterXSize = static_cast<int>(std::ceil(dfXSize));
15✔
1666
        nRasterYSize = static_cast<int>(std::ceil(dfYSize));
15✔
1667
    }
1668

1669
    if (nBandCount == 0 && !pszBandCount)
212✔
1670
    {
1671
        CPLError(CE_Failure, CPLE_AppDefined, "%s metadata item missing",
×
1672
                 MD_BAND_COUNT);
1673
        return false;
×
1674
    }
1675

1676
    if (!GDALCheckBandCount(nBandCount, false))
212✔
1677
        return false;
1✔
1678

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

1721
    const char *pszNoData = GetOption(MD_NODATA);
208✔
1722
    if (pszNoData)
208✔
1723
    {
1724
        const auto IsValidNoDataStr = [](const char *pszStr)
20✔
1725
        {
1726
            if (EQUAL(pszStr, "inf") || EQUAL(pszStr, "-inf") ||
20✔
1727
                EQUAL(pszStr, "nan"))
16✔
1728
                return true;
6✔
1729
            const auto eType = CPLGetValueType(pszStr);
14✔
1730
            return eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL;
14✔
1731
        };
1732

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

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

1823
    /* -------------------------------------------------------------------- */
1824
    /*      Create bands.                                                   */
1825
    /* -------------------------------------------------------------------- */
1826
    if (aeDataTypes.size() != static_cast<size_t>(nBandCount))
202✔
1827
    {
1828
        CPLError(
1✔
1829
            CE_Failure, CPLE_AppDefined,
1830
            "Number of data types values found not matching number of bands");
1831
        return false;
1✔
1832
    }
1833
    if (!aNoData.empty() && aNoData.size() != static_cast<size_t>(nBandCount))
201✔
1834
    {
1835
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1836
                 "Number of nodata values found not matching number of bands");
1837
        return false;
1✔
1838
    }
1839
    if (!aeColorInterp.empty() &&
392✔
1840
        aeColorInterp.size() != static_cast<size_t>(nBandCount))
192✔
1841
    {
1842
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
1843
                 "Number of color interpretation values found not matching "
1844
                 "number of bands");
1845
        return false;
1✔
1846
    }
1847

1848
    int nBlockXSize = 256;
199✔
1849
    const char *pszBlockXSize = GetOption(MD_BLOCK_X_SIZE);
199✔
1850
    if (pszBlockXSize)
199✔
1851
    {
1852
        nBlockXSize = atoi(pszBlockXSize);
3✔
1853
        if (nBlockXSize <= 0)
3✔
1854
        {
1855
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s",
1✔
1856
                     MD_BLOCK_X_SIZE);
1857
            return false;
1✔
1858
        }
1859
    }
1860

1861
    int nBlockYSize = 256;
198✔
1862
    const char *pszBlockYSize = GetOption(MD_BLOCK_Y_SIZE);
198✔
1863
    if (pszBlockYSize)
198✔
1864
    {
1865
        nBlockYSize = atoi(pszBlockYSize);
3✔
1866
        if (nBlockYSize <= 0)
3✔
1867
        {
1868
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s",
1✔
1869
                     MD_BLOCK_Y_SIZE);
1870
            return false;
1✔
1871
        }
1872
    }
1873

1874
    if (nBlockXSize > INT_MAX / nBlockYSize)
197✔
1875
    {
1876
        CPLError(CE_Failure, CPLE_AppDefined, "Too big %s * %s",
1✔
1877
                 MD_BLOCK_X_SIZE, MD_BLOCK_Y_SIZE);
1878
        return false;
1✔
1879
    }
1880

1881
    if (dfOvrFactor > 1.0)
196✔
1882
    {
1883
        m_adfGeoTransform[GT_WE_RES] *= dfOvrFactor;
4✔
1884
        m_adfGeoTransform[GT_NS_RES] *= dfOvrFactor;
4✔
1885
        nRasterXSize = static_cast<int>(std::ceil(nRasterXSize / dfOvrFactor));
4✔
1886
        nRasterYSize = static_cast<int>(std::ceil(nRasterYSize / dfOvrFactor));
4✔
1887
    }
1888

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

1926
                            const auto osName = oObj.GetString("name");
24✔
1927
                            aosDescriptions[i] = osName;
8✔
1928

1929
                            const auto osDescription =
1930
                                oObj.GetString("description");
16✔
1931
                            if (!osDescription.empty())
8✔
1932
                            {
1933
                                if (aosDescriptions[i].empty())
1✔
1934
                                    aosDescriptions[i] = osDescription;
×
1935
                                else
1936
                                    aosDescriptions[i]
1✔
1937
                                        .append(" (")
1✔
1938
                                        .append(osDescription)
1✔
1939
                                        .append(")");
1✔
1940
                            }
1941

1942
                            adfCenterWavelength[i] =
16✔
1943
                                oObj.GetDouble("center_wavelength");
8✔
1944
                            adfFullWidthHalfMax[i] =
16✔
1945
                                oObj.GetDouble("full_width_half_max");
8✔
1946
                        }
1947
                        ++i;
8✔
1948
                    }
1949
                }
1950
            }
1951
        }
1952

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

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

2017
        if (!aosDescriptions.empty() && !aosDescriptions[i].empty())
254✔
2018
        {
2019
            poBand->GDALRasterBand::SetDescription(aosDescriptions[i].c_str());
8✔
2020
        }
2021
        if (!apoXMLNodeBands.empty())
254✔
2022
        {
2023
            const char *pszVal = CPLGetXMLValue(
4✔
2024
                apoXMLNodeBands[i], GTI_XML_BAND_DESCRIPTION, nullptr);
4✔
2025
            if (pszVal)
4✔
2026
            {
2027
                poBand->GDALRasterBand::SetDescription(pszVal);
2✔
2028
            }
2029
        }
2030

2031
        if (!aNoData.empty() && aNoData[i].first)
254✔
2032
        {
2033
            poBand->m_bNoDataValueSet = true;
25✔
2034
            poBand->m_dfNoDataValue = aNoData[i].second;
25✔
2035
        }
2036
        if (!apoXMLNodeBands.empty())
254✔
2037
        {
2038
            const char *pszVal = CPLGetXMLValue(
4✔
2039
                apoXMLNodeBands[i], GTI_XML_BAND_NODATAVALUE, nullptr);
4✔
2040
            if (pszVal)
4✔
2041
            {
2042
                poBand->m_bNoDataValueSet = true;
3✔
2043
                poBand->m_dfNoDataValue = CPLAtof(pszVal);
3✔
2044
            }
2045
        }
2046
        if (poBand->m_bNoDataValueSet != poFirstBand->m_bNoDataValueSet ||
507✔
2047
            !IsSameNaNAware(poBand->m_dfNoDataValue,
253✔
2048
                            poFirstBand->m_dfNoDataValue))
2049
        {
2050
            m_bSameNoData = false;
6✔
2051
        }
2052

2053
        if (!aeColorInterp.empty())
254✔
2054
        {
2055
            poBand->m_eColorInterp = aeColorInterp[i];
243✔
2056
        }
2057
        if (!apoXMLNodeBands.empty())
254✔
2058
        {
2059
            const char *pszVal = CPLGetXMLValue(
4✔
2060
                apoXMLNodeBands[i], GTI_XML_BAND_COLORINTERP, nullptr);
4✔
2061
            if (pszVal)
4✔
2062
            {
2063
                poBand->m_eColorInterp =
4✔
2064
                    GDALGetColorInterpretationByName(pszVal);
4✔
2065
            }
2066
        }
2067

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

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

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

2123
        if (!apoXMLNodeBands.empty())
254✔
2124
        {
2125
            const CPLXMLNode *psBandNode = apoXMLNodeBands[i];
4✔
2126
            poBand->oMDMD.XMLInit(psBandNode, TRUE);
4✔
2127

2128
            if (const CPLXMLNode *psCategoryNames =
4✔
2129
                    CPLGetXMLNode(psBandNode, GTI_XML_CATEGORYNAMES))
4✔
2130
            {
2131
                poBand->m_aosCategoryNames =
2132
                    VRTParseCategoryNames(psCategoryNames);
2✔
2133
            }
2134

2135
            if (const CPLXMLNode *psColorTable =
4✔
2136
                    CPLGetXMLNode(psBandNode, GTI_XML_COLORTABLE))
4✔
2137
            {
2138
                poBand->m_poColorTable = VRTParseColorTable(psColorTable);
2✔
2139
            }
2140

2141
            if (const CPLXMLNode *psRAT =
4✔
2142
                    CPLGetXMLNode(psBandNode, GTI_XML_RAT))
4✔
2143
            {
2144
                poBand->m_poRAT =
2145
                    std::make_unique<GDALDefaultRasterAttributeTable>();
2✔
2146
                poBand->m_poRAT->XMLInit(psRAT, "");
2✔
2147
            }
2148
        }
2149

2150
        if (static_cast<int>(adfCenterWavelength.size()) == nBandCount &&
262✔
2151
            adfCenterWavelength[i] != 0)
8✔
2152
        {
2153
            poBand->GDALRasterBand::SetMetadataItem(
4✔
2154
                "CENTRAL_WAVELENGTH_UM",
2155
                CPLSPrintf("%g", adfCenterWavelength[i]), "IMAGERY");
4✔
2156
        }
2157

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

2166
    if (nBandCount == 1 && poFirstBand && poSingleColorTable &&
196✔
2167
        !poFirstBand->m_poColorTable)
×
2168
        poFirstBand->m_poColorTable = std::move(poSingleColorTable);
×
2169

2170
    const char *pszMaskBand = GetOption(MD_MASK_BAND);
196✔
2171
    if (pszMaskBand)
196✔
2172
        bHasMaskBand = CPLTestBool(pszMaskBand);
7✔
2173
    if (bHasMaskBand)
196✔
2174
    {
2175
        m_poMaskBand = std::make_unique<GDALTileIndexBand>(
8✔
2176
            this, 0, GDT_Byte, nBlockXSize, nBlockYSize);
16✔
2177
    }
2178

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

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

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

2305
    if (nBandCount > 1 && !GetMetadata("IMAGE_STRUCTURE"))
195✔
2306
    {
2307
        GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
28✔
2308
    }
2309

2310
    /* -------------------------------------------------------------------- */
2311
    /*      Initialize any PAM information.                                 */
2312
    /* -------------------------------------------------------------------- */
2313
    SetDescription(poOpenInfo->pszFilename);
195✔
2314
    TryLoadXML();
195✔
2315

2316
    /* -------------------------------------------------------------------- */
2317
    /*      Check for overviews.                                            */
2318
    /* -------------------------------------------------------------------- */
2319
    oOvManager.Initialize(this, poOpenInfo->pszFilename);
195✔
2320

2321
    return true;
195✔
2322
}
2323

2324
/************************************************************************/
2325
/*                        GetMetadataItem()                             */
2326
/************************************************************************/
2327

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

2349
/************************************************************************/
2350
/*                TileIndexSupportsEditingLayerMetadata()               */
2351
/************************************************************************/
2352

2353
bool GDALTileIndexDataset::TileIndexSupportsEditingLayerMetadata() const
17✔
2354
{
2355
    return eAccess == GA_Update && m_poVectorDS->GetDriver() &&
27✔
2356
           EQUAL(m_poVectorDS->GetDriver()->GetDescription(), "GPKG");
27✔
2357
}
2358

2359
/************************************************************************/
2360
/*                        SetMetadataItem()                             */
2361
/************************************************************************/
2362

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

2383
/************************************************************************/
2384
/*                           SetMetadata()                              */
2385
/************************************************************************/
2386

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

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

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

2424
            m_poLayer->SetMetadata(aosMD.List(), pszDomain);
4✔
2425
        }
2426
        else
2427
        {
2428
            m_poLayer->SetMetadata(papszMD, pszDomain);
×
2429
        }
2430
        return GDALDataset::SetMetadata(papszMD, pszDomain);
2✔
2431
    }
2432
    else
2433
    {
2434
        return GDALPamDataset::SetMetadata(papszMD, pszDomain);
×
2435
    }
2436
}
2437

2438
/************************************************************************/
2439
/*                     GDALTileIndexDatasetIdentify()                   */
2440
/************************************************************************/
2441

2442
static int GDALTileIndexDatasetIdentify(GDALOpenInfo *poOpenInfo)
74,195✔
2443
{
2444
    if (STARTS_WITH(poOpenInfo->pszFilename, GTI_PREFIX))
74,195✔
2445
        return true;
16✔
2446

2447
    if (STARTS_WITH(poOpenInfo->pszFilename, "<GDALTileIndexDataset"))
74,179✔
2448
        return true;
46✔
2449

2450
    if (poOpenInfo->nHeaderBytes >= 100 &&
74,133✔
2451
        STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
26,600✔
2452
                    "SQLite format 3"))
2453
    {
2454
        if (ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.gpkg"))
901✔
2455
        {
2456
            // Most likely handled by GTI driver, but we can't be sure
2457
            return GDAL_IDENTIFY_UNKNOWN;
487✔
2458
        }
2459
        else if (poOpenInfo->IsSingleAllowedDriver("GTI") &&
416✔
2460
                 poOpenInfo->IsExtensionEqualToCI("gpkg"))
2✔
2461
        {
2462
            return true;
2✔
2463
        }
2464
    }
2465

2466
    if (poOpenInfo->nHeaderBytes > 0 &&
73,644✔
2467
        (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0)
26,670✔
2468
    {
2469
        if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
53,338✔
2470
                   "<GDALTileIndexDataset") ||
26,658✔
2471
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.fgb") ||
53,328✔
2472
            ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.parquet"))
26,658✔
2473
        {
2474
            return true;
12✔
2475
        }
2476
        else if (poOpenInfo->IsSingleAllowedDriver("GTI") &&
26,657✔
2477
                 (poOpenInfo->IsExtensionEqualToCI("fgb") ||
×
UNCOV
2478
                  poOpenInfo->IsExtensionEqualToCI("parquet")))
×
2479
        {
2480
            return true;
×
2481
        }
2482
    }
2483

2484
    return false;
73,633✔
2485
}
2486

2487
/************************************************************************/
2488
/*                      GDALTileIndexDatasetOpen()                       */
2489
/************************************************************************/
2490

2491
static GDALDataset *GDALTileIndexDatasetOpen(GDALOpenInfo *poOpenInfo)
266✔
2492
{
2493
    if (GDALTileIndexDatasetIdentify(poOpenInfo) == GDAL_IDENTIFY_FALSE)
266✔
2494
        return nullptr;
×
2495
    auto poDS = std::make_unique<GDALTileIndexDataset>();
532✔
2496
    if (!poDS->Open(poOpenInfo))
266✔
2497
        return nullptr;
71✔
2498
    return poDS.release();
195✔
2499
}
2500

2501
/************************************************************************/
2502
/*                          ~GDALTileIndexDataset()                      */
2503
/************************************************************************/
2504

2505
GDALTileIndexDataset::~GDALTileIndexDataset()
532✔
2506
{
2507
    GDALTileIndexDataset::FlushCache(true);
266✔
2508
}
532✔
2509

2510
/************************************************************************/
2511
/*                              FlushCache()                            */
2512
/************************************************************************/
2513

2514
CPLErr GDALTileIndexDataset::FlushCache(bool bAtClosing)
267✔
2515
{
2516
    CPLErr eErr = CE_None;
267✔
2517
    if (bAtClosing && m_bXMLModified)
267✔
2518
    {
2519
        CPLXMLNode *psRoot =
2520
            CPLGetXMLNode(m_psXMLTree.get(), "=GDALTileIndexDataset");
1✔
2521

2522
        // Suppress existing dataset metadata
2523
        while (true)
2524
        {
2525
            CPLXMLNode *psExistingMetadata = CPLGetXMLNode(psRoot, "Metadata");
1✔
2526
            if (!psExistingMetadata)
1✔
2527
                break;
1✔
2528
            CPLRemoveXMLChild(psRoot, psExistingMetadata);
×
2529
        }
×
2530

2531
        // Serialize new dataset metadata
2532
        if (CPLXMLNode *psMD = oMDMD.Serialize())
1✔
2533
            CPLAddXMLChild(psRoot, psMD);
1✔
2534

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

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

2590
                    CPLXMLNode *psBand = CPLCreateXMLNode(psRoot, CXT_Element,
1✔
2591
                                                          GTI_XML_BAND_ELEMENT);
2592
                    CPLAddXMLAttributeAndValue(psBand, GTI_XML_BAND_NUMBER,
1✔
2593
                                               CPLSPrintf("%d", i));
2594
                    CPLAddXMLAttributeAndValue(
1✔
2595
                        psBand, GTI_XML_BAND_DATATYPE,
2596
                        GDALGetDataTypeName(poBand->GetRasterDataType()));
2597

2598
                    const char *pszDescription = poBand->GetDescription();
1✔
2599
                    if (pszDescription && pszDescription[0])
1✔
2600
                        CPLSetXMLValue(psBand, GTI_XML_BAND_DESCRIPTION,
×
2601
                                       pszDescription);
2602

2603
                    const auto eColorInterp = poBand->GetColorInterpretation();
1✔
2604
                    if (eColorInterp != GCI_Undefined)
1✔
2605
                        CPLSetXMLValue(
1✔
2606
                            psBand, GTI_XML_BAND_COLORINTERP,
2607
                            GDALGetColorInterpretationName(eColorInterp));
2608

2609
                    if (!std::isnan(poBand->m_dfOffset))
1✔
2610
                        CPLSetXMLValue(psBand, GTI_XML_BAND_OFFSET,
×
2611
                                       CPLSPrintf("%.16g", poBand->m_dfOffset));
2612

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

2617
                    if (!poBand->m_osUnit.empty())
1✔
2618
                        CPLSetXMLValue(psBand, GTI_XML_BAND_UNITTYPE,
×
2619
                                       poBand->m_osUnit.c_str());
2620

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

2637
        if (!CPLSerializeXMLTreeToFile(m_psXMLTree.get(), GetDescription()))
1✔
2638
            eErr = CE_Failure;
×
2639
    }
2640

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

2655
/************************************************************************/
2656
/*                            LoadOverviews()                           */
2657
/************************************************************************/
2658

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

2677
            std::unique_ptr<GDALDataset> poOvrDS(GDALDataset::Open(
2678
                !osDSName.empty() ? osDSName.c_str() : GetDescription(),
28✔
2679
                GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
2680
                aosNewOpenOptions.List(), nullptr));
56✔
2681

2682
            const auto IsSmaller =
2683
                [](const GDALDataset *a, const GDALDataset *b)
10✔
2684
            {
2685
                return (a->GetRasterXSize() < b->GetRasterXSize() &&
10✔
2686
                        a->GetRasterYSize() <= b->GetRasterYSize()) ||
11✔
2687
                       (a->GetRasterYSize() < b->GetRasterYSize() &&
1✔
2688
                        a->GetRasterXSize() <= b->GetRasterXSize());
10✔
2689
            };
2690

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

2741
/************************************************************************/
2742
/*                          GetOverviewCount()                          */
2743
/************************************************************************/
2744

2745
int GDALTileIndexBand::GetOverviewCount()
51✔
2746
{
2747
    const int nPAMOverviews = GDALPamRasterBand::GetOverviewCount();
51✔
2748
    if (nPAMOverviews)
51✔
2749
        return nPAMOverviews;
12✔
2750

2751
    m_poDS->LoadOverviews();
39✔
2752
    return static_cast<int>(m_poDS->m_apoOverviews.size());
39✔
2753
}
2754

2755
/************************************************************************/
2756
/*                             GetOverview()                            */
2757
/************************************************************************/
2758

2759
GDALRasterBand *GDALTileIndexBand::GetOverview(int iOvr)
26✔
2760
{
2761
    if (iOvr < 0 || iOvr >= GetOverviewCount())
26✔
2762
        return nullptr;
6✔
2763

2764
    const int nPAMOverviews = GDALPamRasterBand::GetOverviewCount();
20✔
2765
    if (nPAMOverviews)
20✔
2766
        return GDALPamRasterBand::GetOverview(iOvr);
7✔
2767

2768
    if (nBand == 0)
13✔
2769
    {
2770
        auto poBand = m_poDS->m_apoOverviews[iOvr]->GetRasterBand(1);
1✔
2771
        if (!poBand)
1✔
2772
            return nullptr;
×
2773
        return poBand->GetMaskBand();
1✔
2774
    }
2775
    else
2776
    {
2777
        return m_poDS->m_apoOverviews[iOvr]->GetRasterBand(nBand);
12✔
2778
    }
2779
}
2780

2781
/************************************************************************/
2782
/*                           GetGeoTransform()                          */
2783
/************************************************************************/
2784

2785
CPLErr GDALTileIndexDataset::GetGeoTransform(double *padfGeoTransform)
21✔
2786
{
2787
    memcpy(padfGeoTransform, m_adfGeoTransform.data(), 6 * sizeof(double));
21✔
2788
    return CE_None;
21✔
2789
}
2790

2791
/************************************************************************/
2792
/*                            GetSpatialRef()                           */
2793
/************************************************************************/
2794

2795
const OGRSpatialReference *GDALTileIndexDataset::GetSpatialRef() const
17✔
2796
{
2797
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
17✔
2798
}
2799

2800
/************************************************************************/
2801
/*                           GDALTileIndexBand()                         */
2802
/************************************************************************/
2803

2804
GDALTileIndexBand::GDALTileIndexBand(GDALTileIndexDataset *poDSIn, int nBandIn,
262✔
2805
                                     GDALDataType eDT, int nBlockXSizeIn,
2806
                                     int nBlockYSizeIn)
262✔
2807
{
2808
    m_poDS = poDSIn;
262✔
2809
    nBand = nBandIn;
262✔
2810
    eDataType = eDT;
262✔
2811
    nRasterXSize = poDSIn->GetRasterXSize();
262✔
2812
    nRasterYSize = poDSIn->GetRasterYSize();
262✔
2813
    nBlockXSize = nBlockXSizeIn;
262✔
2814
    nBlockYSize = nBlockYSizeIn;
262✔
2815
}
262✔
2816

2817
/************************************************************************/
2818
/*                             IReadBlock()                             */
2819
/************************************************************************/
2820

2821
CPLErr GDALTileIndexBand::IReadBlock(int nBlockXOff, int nBlockYOff,
3✔
2822
                                     void *pImage)
2823

2824
{
2825
    const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
3✔
2826

2827
    int nReadXSize = nBlockXSize;
3✔
2828
    int nReadYSize = nBlockYSize;
3✔
2829
    GetActualBlockSize(nBlockXOff, nBlockYOff, &nReadXSize, &nReadYSize);
3✔
2830

2831
    GDALRasterIOExtraArg sExtraArg;
2832
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
3✔
2833

2834
    return IRasterIO(
6✔
2835
        GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nReadXSize,
3✔
2836
        nReadYSize, pImage, nReadXSize, nReadYSize, eDataType, nPixelSize,
2837
        static_cast<GSpacing>(nPixelSize) * nBlockXSize, &sExtraArg);
6✔
2838
}
2839

2840
/************************************************************************/
2841
/*                             IRasterIO()                              */
2842
/************************************************************************/
2843

2844
CPLErr GDALTileIndexBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
123✔
2845
                                    int nXSize, int nYSize, void *pData,
2846
                                    int nBufXSize, int nBufYSize,
2847
                                    GDALDataType eBufType, GSpacing nPixelSpace,
2848
                                    GSpacing nLineSpace,
2849
                                    GDALRasterIOExtraArg *psExtraArg)
2850
{
2851
    int anBand[] = {nBand};
123✔
2852

2853
    return m_poDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
123✔
2854
                             nBufXSize, nBufYSize, eBufType, 1, anBand,
2855
                             nPixelSpace, nLineSpace, 0, psExtraArg);
246✔
2856
}
2857

2858
/************************************************************************/
2859
/*                         IGetDataCoverageStatus()                     */
2860
/************************************************************************/
2861

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

2882
    const double dfMinX = m_poDS->m_adfGeoTransform[GT_TOPLEFT_X] +
6✔
2883
                          nXOff * m_poDS->m_adfGeoTransform[GT_WE_RES];
6✔
2884
    const double dfMaxX =
2885
        dfMinX + nXSize * m_poDS->m_adfGeoTransform[GT_WE_RES];
6✔
2886
    const double dfMaxY = m_poDS->m_adfGeoTransform[GT_TOPLEFT_Y] +
6✔
2887
                          nYOff * m_poDS->m_adfGeoTransform[GT_NS_RES];
6✔
2888
    const double dfMinY =
2889
        dfMaxY + nYSize * m_poDS->m_adfGeoTransform[GT_NS_RES];
6✔
2890
    m_poDS->m_poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
6✔
2891
    m_poDS->m_poLayer->ResetReading();
6✔
2892

2893
    int nStatus = 0;
6✔
2894

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

2916
        const auto poGeom = poFeature->GetGeometryRef();
8✔
2917
        if (!poGeom || poGeom->IsEmpty())
8✔
2918
            continue;
×
2919

2920
        OGREnvelope sSourceEnvelope;
8✔
2921
        poGeom->getEnvelope(&sSourceEnvelope);
8✔
2922

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

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

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

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

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

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

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

3017
const char *GDALTileIndexBand::GetMetadataItem(const char *pszName,
27✔
3018
                                               const char *pszDomain)
3019

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

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

3050
            double adfInvGeoTransform[6] = {0.0};
3✔
3051
            if (!GDALInvGeoTransform(m_poDS->m_adfGeoTransform.data(),
3✔
3052
                                     adfInvGeoTransform))
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);
9✔
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 double adfTileGT[6], int nTileXSize,
346✔
3202
                         int nTileYSize, const double adfVRTGT[6],
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 = adfVRTGT[GT_TOPLEFT_X];
346✔
3210
    const double we_res = adfVRTGT[GT_WE_RES];
346✔
3211
    const double maxX = minX + nVRTXSize * we_res;
346✔
3212
    const double maxY = adfVRTGT[GT_TOPLEFT_Y];
346✔
3213
    const double ns_res = adfVRTGT[GT_NS_RES];
346✔
3214
    const double minY = maxY + nVRTYSize * ns_res;
346✔
3215

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3324
        double dfYOff = (sEnvelope.MaxY - m_adfGeoTransform[GT_TOPLEFT_Y]) /
2✔
3325
                        m_adfGeoTransform[GT_NS_RES];
2✔
3326
        if (dfYOff >= nRasterYSize)
2✔
3327
            continue;
×
3328

3329
        double dfXSize =
3330
            (sEnvelope.MaxX - sEnvelope.MinX) / m_adfGeoTransform[GT_WE_RES];
2✔
3331
        if (dfXOff < 0)
2✔
3332
        {
3333
            dfXSize += dfXOff;
×
3334
            dfXOff = 0;
×
3335
            if (dfXSize <= 0)
×
3336
                continue;
×
3337
        }
3338

3339
        double dfYSize = (sEnvelope.MaxY - sEnvelope.MinY) /
2✔
3340
                         std::fabs(m_adfGeoTransform[GT_NS_RES]);
2✔
3341
        if (dfYOff < 0)
2✔
3342
        {
3343
            dfYSize += dfYOff;
×
3344
            dfYOff = 0;
×
3345
            if (dfYSize <= 0)
×
3346
                continue;
×
3347
        }
3348

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

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

3370
    return oRes;
1✔
3371
}
3372

3373
/************************************************************************/
3374
/*                         GetSourceDesc()                              */
3375
/************************************************************************/
3376

3377
bool GDALTileIndexDataset::GetSourceDesc(const std::string &osTileName,
347✔
3378
                                         SourceDesc &oSourceDesc,
3379
                                         std::mutex *pMutex)
3380
{
3381
    std::shared_ptr<GDALDataset> poTileDS;
348✔
3382

3383
    if (pMutex)
347✔
3384
        pMutex->lock();
137✔
3385
    const bool bTileKnown = m_oMapSharedSources.tryGet(osTileName, poTileDS);
348✔
3386
    if (pMutex)
348✔
3387
        pMutex->unlock();
138✔
3388

3389
    if (!bTileKnown)
348✔
3390
    {
3391
        poTileDS = std::shared_ptr<GDALDataset>(
448✔
3392
            GDALProxyPoolDataset::Create(
3393
                osTileName.c_str(), nullptr, GA_ReadOnly,
3394
                /* bShared = */ true, m_osUniqueHandle.c_str()),
3395
            GDALDatasetUniquePtrReleaser());
224✔
3396
        if (!poTileDS || poTileDS->GetRasterCount() == 0)
224✔
3397
        {
3398
            return false;
2✔
3399
        }
3400

3401
        // do palette -> RGB(A) expansion if needed
3402
        if (!GTIDoPaletteExpansionIfNeeded(poTileDS, nBands))
222✔
3403
            return false;
×
3404

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

3415
            CPLStringList aosOptions;
2✔
3416
            aosOptions.AddString("-of");
2✔
3417
            aosOptions.AddString("VRT");
2✔
3418

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

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

3444
            aosOptions.AddString("-t_srs");
2✔
3445
            aosOptions.AddString(m_osWKT.c_str());
2✔
3446

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

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

3498
            aosOptions.AddString("-te");
2✔
3499
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinX));
2✔
3500
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMinY));
2✔
3501
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxX));
2✔
3502
            aosOptions.AddString(CPLSPrintf("%.17g", dfWarpMaxY));
2✔
3503

3504
            aosOptions.AddString("-tr");
2✔
3505
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResX));
2✔
3506
            aosOptions.AddString(CPLSPrintf("%.17g", dfVRTResYAbs));
2✔
3507

3508
            aosOptions.AddString("-dstalpha");
2✔
3509

3510
            psWarpOptions = GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
2✔
3511
            poWarpDS.reset(GDALDataset::FromHandle(GDALWarp(
2✔
3512
                "", nullptr, 1, ahSrcDS, psWarpOptions, &bUsageError)));
3513
            GDALWarpAppOptionsFree(psWarpOptions);
2✔
3514
            if (!poWarpDS)
2✔
3515
            {
3516
                return false;
×
3517
            }
3518

3519
            poTileDS.reset(poWarpDS.release());
2✔
3520
        }
3521

3522
        if (pMutex)
222✔
3523
            pMutex->lock();
70✔
3524
        m_oMapSharedSources.insert(osTileName, poTileDS);
222✔
3525
        if (pMutex)
222✔
3526
            pMutex->unlock();
70✔
3527
    }
3528

3529
    double adfGeoTransformTile[6];
3530
    if (poTileDS->GetGeoTransform(adfGeoTransformTile) != CE_None)
346✔
3531
    {
3532
        CPLError(CE_Failure, CPLE_AppDefined, "%s lacks geotransform",
×
3533
                 osTileName.c_str());
3534
        return false;
×
3535
    }
3536

3537
    bool bHasNoData = false;
346✔
3538
    bool bSameNoData = true;
346✔
3539
    double dfNoDataValue = 0;
346✔
3540
    GDALRasterBand *poMaskBand = nullptr;
346✔
3541
    const int nBandCount = poTileDS->GetRasterCount();
346✔
3542
    for (int iBand = 0; iBand < nBandCount; ++iBand)
1,195✔
3543
    {
3544
        auto poTileBand = poTileDS->GetRasterBand(iBand + 1);
849✔
3545
        int bThisBandHasNoData = false;
849✔
3546
        const double dfThisBandNoDataValue =
3547
            poTileBand->GetNoDataValue(&bThisBandHasNoData);
849✔
3548
        if (bThisBandHasNoData)
849✔
3549
        {
3550
            bHasNoData = true;
10✔
3551
            dfNoDataValue = dfThisBandNoDataValue;
10✔
3552
        }
3553
        if (iBand > 0 &&
1,352✔
3554
            (static_cast<int>(bThisBandHasNoData) !=
503✔
3555
                 static_cast<int>(bHasNoData) ||
503✔
3556
             (bHasNoData &&
4✔
3557
              !IsSameNaNAware(dfNoDataValue, dfThisBandNoDataValue))))
4✔
3558
        {
3559
            bSameNoData = false;
×
3560
        }
3561

3562
        if (poTileBand->GetMaskFlags() == GMF_PER_DATASET)
849✔
3563
            poMaskBand = poTileBand->GetMaskBand();
2✔
3564
        else if (poTileBand->GetColorInterpretation() == GCI_AlphaBand)
847✔
3565
            poMaskBand = poTileBand;
31✔
3566
    }
3567

3568
    std::unique_ptr<VRTSimpleSource> poSource;
×
3569
    if (!bHasNoData)
346✔
3570
    {
3571
        poSource = std::make_unique<VRTSimpleSource>();
340✔
3572
    }
3573
    else
3574
    {
3575
        auto poComplexSource = std::make_unique<VRTComplexSource>();
12✔
3576
        poComplexSource->SetNoDataValue(dfNoDataValue);
6✔
3577
        poSource = std::move(poComplexSource);
6✔
3578
    }
3579

3580
    GetSrcDstWin(adfGeoTransformTile, poTileDS->GetRasterXSize(),
692✔
3581
                 poTileDS->GetRasterYSize(), m_adfGeoTransform.data(),
346✔
3582
                 GetRasterXSize(), GetRasterYSize(), &poSource->m_dfSrcXOff,
346✔
3583
                 &poSource->m_dfSrcYOff, &poSource->m_dfSrcXSize,
346✔
3584
                 &poSource->m_dfSrcYSize, &poSource->m_dfDstXOff,
346✔
3585
                 &poSource->m_dfDstYOff, &poSource->m_dfDstXSize,
346✔
3586
                 &poSource->m_dfDstYSize);
346✔
3587

3588
    oSourceDesc.osName = osTileName;
346✔
3589
    oSourceDesc.poDS = std::move(poTileDS);
346✔
3590
    oSourceDesc.poSource = std::move(poSource);
346✔
3591
    oSourceDesc.bHasNoData = bHasNoData;
346✔
3592
    oSourceDesc.bSameNoData = bSameNoData;
346✔
3593
    if (bSameNoData)
346✔
3594
        oSourceDesc.dfSameNoData = dfNoDataValue;
346✔
3595
    oSourceDesc.poMaskBand = poMaskBand;
346✔
3596
    return true;
346✔
3597
}
3598

3599
/************************************************************************/
3600
/*                           GetNumThreads()                            */
3601
/************************************************************************/
3602

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

3620
/************************************************************************/
3621
/*                        CollectSources()                              */
3622
/************************************************************************/
3623

3624
bool GDALTileIndexDataset::CollectSources(double dfXOff, double dfYOff,
169✔
3625
                                          double dfXSize, double dfYSize,
3626
                                          bool bMultiThreadAllowed)
3627
{
3628
    const double dfMinX =
3629
        m_adfGeoTransform[GT_TOPLEFT_X] + dfXOff * m_adfGeoTransform[GT_WE_RES];
169✔
3630
    const double dfMaxX = dfMinX + dfXSize * m_adfGeoTransform[GT_WE_RES];
169✔
3631
    const double dfMaxY =
3632
        m_adfGeoTransform[GT_TOPLEFT_Y] + dfYOff * m_adfGeoTransform[GT_NS_RES];
169✔
3633
    const double dfMinY = dfMaxY + dfYSize * m_adfGeoTransform[GT_NS_RES];
169✔
3634

3635
    if (dfMinX == m_dfLastMinXFilter && dfMinY == m_dfLastMinYFilter &&
169✔
3636
        dfMaxX == m_dfLastMaxXFilter && dfMaxY == m_dfLastMaxYFilter)
47✔
3637
    {
3638
        return true;
43✔
3639
    }
3640

3641
    m_dfLastMinXFilter = dfMinX;
126✔
3642
    m_dfLastMinYFilter = dfMinY;
126✔
3643
    m_dfLastMaxXFilter = dfMaxX;
126✔
3644
    m_dfLastMaxYFilter = dfMaxY;
126✔
3645
    m_bLastMustUseMultiThreading = false;
126✔
3646

3647
    m_poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
126✔
3648
    m_poLayer->ResetReading();
126✔
3649

3650
    m_aoSourceDesc.clear();
126✔
3651
    while (true)
3652
    {
3653
        auto poFeature =
3654
            std::unique_ptr<OGRFeature>(m_poLayer->GetNextFeature());
440✔
3655
        if (!poFeature)
440✔
3656
            break;
126✔
3657
        if (!poFeature->IsFieldSetAndNotNull(m_nLocationFieldIndex))
314✔
3658
        {
3659
            continue;
1✔
3660
        }
3661

3662
        SourceDesc oSourceDesc;
313✔
3663
        oSourceDesc.poFeature = std::move(poFeature);
313✔
3664
        m_aoSourceDesc.emplace_back(std::move(oSourceDesc));
313✔
3665

3666
        if (m_aoSourceDesc.size() > 10 * 1000 * 1000)
313✔
3667
        {
3668
            // Safety belt...
3669
            CPLError(CE_Failure, CPLE_AppDefined,
×
3670
                     "More than 10 million contributing sources to a "
3671
                     "single RasterIO() request is not supported");
3672
            return false;
×
3673
        }
3674
    }
314✔
3675

3676
    constexpr int MINIMUM_PIXEL_COUNT_FOR_THREADED_IO = 1000 * 1000;
126✔
3677
    if (bMultiThreadAllowed && m_aoSourceDesc.size() > 1 &&
190✔
3678
        dfXSize * dfYSize > MINIMUM_PIXEL_COUNT_FOR_THREADED_IO)
64✔
3679
    {
3680
        if (m_nNumThreads < 0)
7✔
3681
            m_nNumThreads = GetNumThreads();
7✔
3682
        bMultiThreadAllowed = m_nNumThreads > 1;
7✔
3683
    }
3684
    else
3685
    {
3686
        bMultiThreadAllowed = false;
119✔
3687
    }
3688

3689
    if (bMultiThreadAllowed)
126✔
3690
    {
3691
        CPLRectObj sGlobalBounds;
3692
        sGlobalBounds.minx = dfXOff;
5✔
3693
        sGlobalBounds.miny = dfYOff;
5✔
3694
        sGlobalBounds.maxx = dfXOff + dfXSize;
5✔
3695
        sGlobalBounds.maxy = dfYOff + dfYSize;
5✔
3696
        CPLQuadTree *hQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
5✔
3697

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

3711
            const auto poGeom = oSourceDesc.poFeature->GetGeometryRef();
73✔
3712
            if (!poGeom || poGeom->IsEmpty())
73✔
3713
                continue;
×
3714

3715
            OGREnvelope sEnvelope;
73✔
3716
            poGeom->getEnvelope(&sEnvelope);
73✔
3717

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

3735
            // Clamp to global bounds and some epsilon to avoid adjacent tiles
3736
            // to be considered as overlapping
3737
            constexpr double EPSILON = 0.1;
73✔
3738
            sSourceBounds.minx =
73✔
3739
                std::max(sGlobalBounds.minx, sSourceBounds.minx) + EPSILON;
73✔
3740
            sSourceBounds.maxx =
73✔
3741
                std::min(sGlobalBounds.maxx, sSourceBounds.maxx) - EPSILON;
73✔
3742
            sSourceBounds.miny =
73✔
3743
                std::max(sGlobalBounds.miny, sSourceBounds.miny) + EPSILON;
73✔
3744
            sSourceBounds.maxy =
73✔
3745
                std::min(sGlobalBounds.maxy, sSourceBounds.maxy) - EPSILON;
73✔
3746

3747
            // Check that the new source doesn't overlap an existing one.
3748
            if (CPLQuadTreeHasMatch(hQuadTree, &sSourceBounds))
73✔
3749
            {
3750
                bCompatibleOfMultiThread = false;
1✔
3751
                break;
1✔
3752
            }
3753

3754
            CPLQuadTreeInsertWithBounds(
72✔
3755
                hQuadTree,
3756
                const_cast<void *>(static_cast<const void *>(&oSourceDesc)),
3757
                &sSourceBounds);
3758
        }
3759

3760
        CPLQuadTreeDestroy(hQuadTree);
5✔
3761

3762
        if (bCompatibleOfMultiThread)
5✔
3763
        {
3764
            m_bLastMustUseMultiThreading = true;
4✔
3765
            return true;
4✔
3766
        }
3767
    }
3768

3769
    if (m_aoSourceDesc.size() > 1)
122✔
3770
    {
3771
        SortSourceDesc();
61✔
3772
    }
3773

3774
    // Try to find the last (most prioritary) fully opaque source covering
3775
    // the whole AOI. We only need to start rendering from it.
3776
    size_t i = m_aoSourceDesc.size();
122✔
3777
    while (i > 0)
250✔
3778
    {
3779
        --i;
210✔
3780
        auto &poFeature = m_aoSourceDesc[i].poFeature;
210✔
3781
        const char *pszTileName =
3782
            poFeature->GetFieldAsString(m_nLocationFieldIndex);
210✔
3783
        const std::string osTileName(
3784
            GetAbsoluteFileName(pszTileName, GetDescription()));
210✔
3785

3786
        SourceDesc oSourceDesc;
210✔
3787
        if (!GetSourceDesc(osTileName, oSourceDesc, nullptr))
210✔
3788
            continue;
1✔
3789

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

3845
        const auto &poSource = oSourceDesc.poSource;
208✔
3846
        if (dfXOff >= poSource->m_dfDstXOff + poSource->m_dfDstXSize ||
208✔
3847
            dfYOff >= poSource->m_dfDstYOff + poSource->m_dfDstYSize ||
208✔
3848
            poSource->m_dfDstXOff >= dfXOff + dfXSize ||
622✔
3849
            poSource->m_dfDstYOff >= dfYOff + dfYSize)
206✔
3850
        {
3851
            // Can happen as some spatial filters select slightly more features
3852
            // than strictly needed.
3853
            continue;
2✔
3854
        }
3855

3856
        const bool bCoversWholeAOI =
3857
            (poSource->m_dfDstXOff <= dfXOff &&
206✔
3858
             poSource->m_dfDstYOff <= dfYOff &&
131✔
3859
             poSource->m_dfDstXOff + poSource->m_dfDstXSize >=
131✔
3860
                 dfXOff + dfXSize &&
457✔
3861
             poSource->m_dfDstYOff + poSource->m_dfDstYSize >=
120✔
3862
                 dfYOff + dfYSize);
120✔
3863
        oSourceDesc.bCoversWholeAOI = bCoversWholeAOI;
206✔
3864

3865
        m_aoSourceDesc[i] = std::move(oSourceDesc);
206✔
3866

3867
        if (m_aoSourceDesc[i].bCoversWholeAOI &&
206✔
3868
            !m_aoSourceDesc[i].bHasNoData && !m_aoSourceDesc[i].poMaskBand)
206✔
3869
        {
3870
            break;
82✔
3871
        }
3872
    }
3873

3874
    if (i > 0)
122✔
3875
    {
3876
        // Remove sources that will not be rendered
3877
        m_aoSourceDesc.erase(m_aoSourceDesc.begin(),
32✔
3878
                             m_aoSourceDesc.begin() + i);
64✔
3879
    }
3880

3881
    // Truncate the array when its last elements have no dataset
3882
    i = m_aoSourceDesc.size();
122✔
3883
    while (i > 0)
327✔
3884
    {
3885
        --i;
209✔
3886
        if (!m_aoSourceDesc[i].poDS)
209✔
3887
        {
3888
            m_aoSourceDesc.resize(i);
4✔
3889
            break;
4✔
3890
        }
3891
    }
3892

3893
    return true;
122✔
3894
}
3895

3896
/************************************************************************/
3897
/*                          SortSourceDesc()                            */
3898
/************************************************************************/
3899

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

3956
#define COMPARE_DATE_COMPONENT(comp)                                           \
3957
    do                                                                         \
3958
    {                                                                          \
3959
        if (poFieldA->Date.comp < poFieldB->Date.comp)                         \
3960
            return true;                                                       \
3961
        if (poFieldA->Date.comp > poFieldB->Date.comp)                         \
3962
            return false;                                                      \
3963
    } while (0)
3964

3965
                    COMPARE_DATE_COMPONENT(Year);
27✔
3966
                    COMPARE_DATE_COMPONENT(Month);
21✔
3967
                    COMPARE_DATE_COMPONENT(Day);
15✔
3968
                    COMPARE_DATE_COMPONENT(Hour);
9✔
3969
                    COMPARE_DATE_COMPONENT(Minute);
8✔
3970
                    COMPARE_DATE_COMPONENT(Second);
7✔
3971
                }
3972
                else
3973
                {
3974
                    CPLAssert(false);
×
3975
                }
3976
            }
3977
            return poFeatureA->GetFID() < poFeatureB->GetFID();
337✔
3978
        });
3979
}
61✔
3980

3981
/************************************************************************/
3982
/*                   CompositeSrcWithMaskIntoDest()                     */
3983
/************************************************************************/
3984

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

4068
/************************************************************************/
4069
/*                         NeedInitBuffer()                             */
4070
/************************************************************************/
4071

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

4103
/************************************************************************/
4104
/*                            InitBuffer()                              */
4105
/************************************************************************/
4106

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

4173
                for (int iLine = 0; iLine < nBufYSize; iLine++)
12✔
4174
                {
4175
                    GDALCopyWords(&dfWriteValue, GDT_Float64, 0,
8✔
4176
                                  static_cast<GByte *>(pabyBandData) +
8✔
4177
                                      static_cast<GIntBig>(nLineSpace) * iLine,
8✔
4178
                                  eBufType, static_cast<int>(nPixelSpace),
4179
                                  nBufXSize);
4180
                }
4181
            }
4182
        }
4183
    }
4184
}
54✔
4185

4186
/************************************************************************/
4187
/*                            RenderSource()                            */
4188
/************************************************************************/
4189

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

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

4225
                // The window we will actual set _within_ the pData buffer.
4226
                int nOutXOff = 0;
4✔
4227
                int nOutYOff = 0;
4✔
4228
                int nOutXSize = 0;
4✔
4229
                int nOutYSize = 0;
4✔
4230

4231
                bool bError = false;
4✔
4232

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

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

4270
                GDALRasterIOExtraArg sExtraArg;
4271
                INIT_RASTERIO_EXTRA_ARG(sExtraArg);
6✔
4272
                if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
6✔
4273
                {
4274
                    // cppcheck-suppress redundantAssignment
4275
                    sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4276
                }
4277
                else
4278
                {
4279
                    // cppcheck-suppress redundantAssignment
4280
                    sExtraArg.eResampleAlg = m_eResampling;
6✔
4281
                }
4282

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

4300
    if ((oSourceDesc.poMaskBand && bNeedInitBuffer) || nBandNrMax == 0)
451✔
4301
    {
4302
        // The window we will actually request from the source raster band.
4303
        double dfReqXOff = 0.0;
55✔
4304
        double dfReqYOff = 0.0;
55✔
4305
        double dfReqXSize = 0.0;
55✔
4306
        double dfReqYSize = 0.0;
55✔
4307
        int nReqXOff = 0;
55✔
4308
        int nReqYOff = 0;
55✔
4309
        int nReqXSize = 0;
55✔
4310
        int nReqYSize = 0;
55✔
4311

4312
        // The window we will actual set _within_ the pData buffer.
4313
        int nOutXOff = 0;
55✔
4314
        int nOutYOff = 0;
55✔
4315
        int nOutXSize = 0;
55✔
4316
        int nOutYSize = 0;
55✔
4317

4318
        bool bError = false;
55✔
4319

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

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

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

4390
                if (oSourceDesc.poMaskBand->RasterIO(
21✔
4391
                        GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
4392
                        oSourceDesc.abyMask.data(), nOutXSize, nOutYSize,
21✔
4393
                        GDT_Byte, 0, 0, &sExtraArg) != CE_None)
21✔
4394
                {
4395
                    oSourceDesc.abyMask.clear();
×
4396
                    return CE_Failure;
×
4397
                }
4398
            }
4399

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

4416
            const GByte *const pabyMask =
4417
                iMaskBandIdx >= 0
4418
                    ? abyWorkBuffer.data() + iMaskBandIdx * nWorkBufferBandSize
24✔
4419
                    : oSourceDesc.abyMask.data();
79✔
4420

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

4441
            // Compose the temporary contiguous buffer into the target
4442
            // buffer, taking into account the mask
4443
            GByte *pabyOut = static_cast<GByte *>(pData) +
55✔
4444
                             static_cast<GPtrDiff_t>(nOutXOff * nPixelSpace +
55✔
4445
                                                     nOutYOff * nLineSpace);
55✔
4446

4447
            for (int iBand = 0; iBand < nBandCount && eErr == CE_None; ++iBand)
121✔
4448
            {
4449
                GByte *pabyDestBand =
66✔
4450
                    pabyOut + static_cast<GPtrDiff_t>(iBand * nBandSpace);
66✔
4451
                const GByte *pabySrc =
4452
                    abyWorkBuffer.data() + iBand * nWorkBufferBandSize;
66✔
4453

4454
                CompositeSrcWithMaskIntoDest(
66✔
4455
                    nOutXSize, nOutYSize, eBufType, nBufTypeSize, nPixelSpace,
4456
                    nLineSpace, pabySrc, pabyMask, pabyDestBand);
4457
            }
4458
        }
55✔
4459
    }
4460
    else if (m_bSameDataType && !bNeedInitBuffer && oSourceDesc.bHasNoData)
396✔
4461
    {
4462
        // We create a non-VRTComplexSource SimpleSource copy of poSource
4463
        // to be able to call DatasetRasterIO()
4464
        VRTSimpleSource oSimpleSource(poSource.get(), 1.0, 1.0);
1✔
4465

4466
        GDALRasterIOExtraArg sExtraArg;
4467
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1✔
4468
        if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
1✔
4469
        {
4470
            // cppcheck-suppress redundantAssignment
4471
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4472
        }
4473
        else
4474
        {
4475
            // cppcheck-suppress redundantAssignment
4476
            sExtraArg.eResampleAlg = m_eResampling;
1✔
4477
        }
4478

4479
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
1✔
4480
        oSimpleSource.SetRasterBand(poTileBand, false);
1✔
4481
        eErr = oSimpleSource.DatasetRasterIO(
1✔
4482
            papoBands[0]->GetRasterDataType(), nXOff, nYOff, nXSize, nYSize,
1✔
4483
            pData, nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
4484
            nPixelSpace, nLineSpace, nBandSpace, &sExtraArg);
1✔
4485
    }
4486
    else if (m_bSameDataType && !poComplexSource)
395✔
4487
    {
4488
        auto poTileBand = poTileDS->GetRasterBand(panBandMap[0]);
393✔
4489
        poSource->SetRasterBand(poTileBand, false);
393✔
4490

4491
        GDALRasterIOExtraArg sExtraArg;
4492
        INIT_RASTERIO_EXTRA_ARG(sExtraArg);
393✔
4493
        if (poTileBand->GetColorTable())
393✔
4494
        {
4495
            // cppcheck-suppress redundantAssignment
4496
            sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
×
4497
        }
4498
        else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
393✔
4499
        {
4500
            // cppcheck-suppress redundantAssignment
4501
            sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4502
        }
4503
        else
4504
        {
4505
            // cppcheck-suppress redundantAssignment
4506
            sExtraArg.eResampleAlg = m_eResampling;
393✔
4507
        }
4508

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

4531
            GDALRasterIOExtraArg sExtraArg;
4532
            INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2✔
4533
            if (poTileBand->GetColorTable())
2✔
4534
            {
4535
                // cppcheck-suppress redundantAssignment
4536
                sExtraArg.eResampleAlg = GRIORA_NearestNeighbour;
×
4537
            }
4538
            else if (psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2✔
4539
            {
4540
                // cppcheck-suppress redundantAssignment
4541
                sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
×
4542
            }
4543
            else
4544
            {
4545
                // cppcheck-suppress redundantAssignment
4546
                sExtraArg.eResampleAlg = m_eResampling;
2✔
4547
            }
4548

4549
            eErr = poSource->RasterIO(
4✔
4550
                papoBands[nBandNr - 1]->GetRasterDataType(), nXOff, nYOff,
2✔
4551
                nXSize, nYSize, pabyBandData, nBufXSize, nBufYSize, eBufType,
4552
                nPixelSpace, nLineSpace, &sExtraArg, oWorkingState);
2✔
4553
        }
4554
    }
4555
    return eErr;
451✔
4556
}
4557

4558
/************************************************************************/
4559
/*                             IRasterIO()                              */
4560
/************************************************************************/
4561

4562
CPLErr GDALTileIndexDataset::IRasterIO(
163✔
4563
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
4564
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
4565
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
4566
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
4567
{
4568
    if (eRWFlag != GF_Read)
163✔
4569
        return CE_Failure;
×
4570

4571
    if (nBufXSize < nXSize && nBufYSize < nYSize && AreOverviewsEnabled())
163✔
4572
    {
4573
        int bTried = FALSE;
2✔
4574
        const CPLErr eErr = TryOverviewRasterIO(
2✔
4575
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
4576
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
4577
            nBandSpace, psExtraArg, &bTried);
4578
        if (bTried)
2✔
4579
            return eErr;
2✔
4580
    }
4581

4582
    double dfXOff = nXOff;
161✔
4583
    double dfYOff = nYOff;
161✔
4584
    double dfXSize = nXSize;
161✔
4585
    double dfYSize = nYSize;
161✔
4586
    if (psExtraArg->bFloatingPointWindowValidity)
161✔
4587
    {
4588
        dfXOff = psExtraArg->dfXOff;
×
4589
        dfYOff = psExtraArg->dfYOff;
×
4590
        dfXSize = psExtraArg->dfXSize;
×
4591
        dfYSize = psExtraArg->dfYSize;
×
4592
    }
4593

4594
    if (!CollectSources(dfXOff, dfYOff, dfXSize, dfYSize,
161✔
4595
                        /* bMultiThreadAllowed = */ true))
4596
    {
4597
        return CE_Failure;
×
4598
    }
4599

4600
    // We might be called with nBandCount == 1 && panBandMap[0] == 0
4601
    // to mean m_poMaskBand
4602
    int nBandNrMax = 0;
161✔
4603
    for (int i = 0; i < nBandCount; ++i)
371✔
4604
    {
4605
        const int nBandNr = panBandMap[i];
210✔
4606
        nBandNrMax = std::max(nBandNrMax, nBandNr);
210✔
4607
    }
4608

4609
    const bool bNeedInitBuffer =
4610
        m_bLastMustUseMultiThreading || NeedInitBuffer(nBandCount, panBandMap);
161✔
4611

4612
    if (!bNeedInitBuffer)
161✔
4613
    {
4614
        return RenderSource(
107✔
4615
            m_aoSourceDesc.back(), bNeedInitBuffer, nBandNrMax, nXOff, nYOff,
107✔
4616
            nXSize, nYSize, dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize,
4617
            nBufYSize, pData, eBufType, nBandCount, panBandMap, nPixelSpace,
4618
            nLineSpace, nBandSpace, psExtraArg, m_oWorkingState);
214✔
4619
    }
4620
    else
4621
    {
4622
        InitBuffer(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
54✔
4623
                   panBandMap, nPixelSpace, nLineSpace, nBandSpace);
4624

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

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

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

4682
                psJob->osTileName = oSourceDesc.poFeature->GetFieldAsString(
4683
                    m_nLocationFieldIndex);
138✔
4684

4685
                if (!oQueue->SubmitJob(RasterIOJob::Func, psJob))
138✔
4686
                {
4687
                    delete psJob;
×
4688
                    bSuccess = false;
×
4689
                    break;
×
4690
                }
4691
            }
4692

4693
            while (oQueue->WaitEvent())
88✔
4694
            {
4695
                // Quite rough progress callback. We could do better by counting
4696
                // the number of contributing pixels.
4697
                if (psExtraArg->pfnProgress)
82✔
4698
                {
4699
                    psExtraArg->pfnProgress(double(nCompletedJobs.load()) /
160✔
4700
                                                nContributingSources,
4701
                                            "", psExtraArg->pProgressData);
4702
                }
4703
            }
4704

4705
            oErrorAccumulator.ReplayErrors();
6✔
4706

4707
            if (bSuccess && psExtraArg->pfnProgress)
6✔
4708
            {
4709
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4✔
4710
            }
4711

4712
            return bSuccess ? CE_None : CE_Failure;
6✔
4713
        }
4714
        else
4715
        {
4716
            // Now render from bottom of the stack to top.
4717
            for (auto &oSourceDesc : m_aoSourceDesc)
261✔
4718
            {
4719
                if (oSourceDesc.poDS &&
426✔
4720
                    RenderSource(oSourceDesc, bNeedInitBuffer, nBandNrMax,
213✔
4721
                                 nXOff, nYOff, nXSize, nYSize, dfXOff, dfYOff,
4722
                                 dfXSize, dfYSize, nBufXSize, nBufYSize, pData,
4723
                                 eBufType, nBandCount, panBandMap, nPixelSpace,
4724
                                 nLineSpace, nBandSpace, psExtraArg,
4725
                                 m_oWorkingState) != CE_None)
426✔
4726
                    return CE_Failure;
×
4727
            }
4728

4729
            if (psExtraArg->pfnProgress)
48✔
4730
            {
4731
                psExtraArg->pfnProgress(1.0, "", psExtraArg->pProgressData);
4✔
4732
            }
4733

4734
            return CE_None;
48✔
4735
        }
4736
    }
4737
}
4738

4739
/************************************************************************/
4740
/*                 GDALTileIndexDataset::RasterIOJob::Func()            */
4741
/************************************************************************/
4742

4743
void GDALTileIndexDataset::RasterIOJob::Func(void *pData)
138✔
4744
{
4745
    auto psJob =
4746
        std::unique_ptr<RasterIOJob>(static_cast<RasterIOJob *>(pData));
276✔
4747
    if (*psJob->pbSuccess)
138✔
4748
    {
4749
        const std::string osTileName(GetAbsoluteFileName(
4750
            psJob->osTileName.c_str(), psJob->poDS->GetDescription()));
276✔
4751

4752
        SourceDesc oSourceDesc;
276✔
4753

4754
        auto oAccumulator = psJob->poErrorAccumulator->InstallForCurrentScope();
275✔
4755
        CPL_IGNORE_RET_VAL(oAccumulator);
138✔
4756

4757
        const bool bCanOpenSource =
4758
            psJob->poDS->GetSourceDesc(osTileName, oSourceDesc,
137✔
4759
                                       &psJob->poQueueWorkingStates->oMutex) &&
274✔
4760
            oSourceDesc.poDS;
137✔
4761

4762
        if (!bCanOpenSource)
138✔
4763
        {
4764
            *psJob->pbSuccess = false;
1✔
4765
        }
4766
        else
4767
        {
4768
            GDALRasterIOExtraArg sArg = *(psJob->psExtraArg);
137✔
4769
            sArg.pfnProgress = nullptr;
137✔
4770
            sArg.pProgressData = nullptr;
137✔
4771

4772
            std::unique_ptr<VRTSource::WorkingState> poWorkingState;
137✔
4773
            {
4774
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
274✔
4775
                poWorkingState =
4776
                    std::move(psJob->poQueueWorkingStates->oStates.back());
137✔
4777
                psJob->poQueueWorkingStates->oStates.pop_back();
137✔
4778
                CPLAssert(poWorkingState.get());
137✔
4779
            }
4780

4781
            double dfXOff = psJob->nXOff;
137✔
4782
            double dfYOff = psJob->nYOff;
137✔
4783
            double dfXSize = psJob->nXSize;
137✔
4784
            double dfYSize = psJob->nYSize;
137✔
4785
            if (psJob->psExtraArg->bFloatingPointWindowValidity)
137✔
4786
            {
4787
                dfXOff = psJob->psExtraArg->dfXOff;
×
4788
                dfYOff = psJob->psExtraArg->dfYOff;
×
4789
                dfXSize = psJob->psExtraArg->dfXSize;
×
4790
                dfYSize = psJob->psExtraArg->dfYSize;
×
4791
            }
4792

4793
            const bool bRenderOK =
4794
                psJob->poDS->RenderSource(
274✔
4795
                    oSourceDesc, /*bNeedInitBuffer = */ true, psJob->nBandNrMax,
137✔
4796
                    psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
137✔
4797
                    dfXOff, dfYOff, dfXSize, dfYSize, psJob->nBufXSize,
137✔
4798
                    psJob->nBufYSize, psJob->pData, psJob->eBufType,
137✔
4799
                    psJob->nBandCount, psJob->panBandMap, psJob->nPixelSpace,
137✔
4800
                    psJob->nLineSpace, psJob->nBandSpace, &sArg,
137✔
4801
                    *(poWorkingState.get())) == CE_None;
137✔
4802

4803
            if (!bRenderOK)
137✔
4804
            {
4805
                *psJob->pbSuccess = false;
1✔
4806
            }
4807

4808
            {
4809
                std::lock_guard oLock(psJob->poQueueWorkingStates->oMutex);
274✔
4810
                psJob->poQueueWorkingStates->oStates.push_back(
274✔
4811
                    std::move(poWorkingState));
137✔
4812
            }
4813
        }
4814
    }
4815

4816
    ++(*psJob->pnCompletedJobs);
138✔
4817
}
138✔
4818

4819
/************************************************************************/
4820
/*                         GDALRegister_GTI()                           */
4821
/************************************************************************/
4822

4823
void GDALRegister_GTI()
1,646✔
4824
{
4825
    if (GDALGetDriverByName("GTI") != nullptr)
1,646✔
4826
        return;
281✔
4827

4828
    auto poDriver = std::make_unique<GDALDriver>();
2,730✔
4829

4830
    poDriver->SetDescription("GTI");
1,365✔
4831
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,365✔
4832
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GDAL Raster Tile Index");
1,365✔
4833
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gti.gpkg gti.fgb gti");
1,365✔
4834
    poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, GTI_PREFIX);
1,365✔
4835
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gti.html");
1,365✔
4836

4837
    poDriver->pfnOpen = GDALTileIndexDatasetOpen;
1,365✔
4838
    poDriver->pfnIdentify = GDALTileIndexDatasetIdentify;
1,365✔
4839

4840
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,365✔
4841

4842
    poDriver->SetMetadataItem(
1,365✔
4843
        GDAL_DMD_OPENOPTIONLIST,
4844
        "<OpenOptionList>"
4845
        "  <Option name='LAYER' type='string'/>"
4846
        "  <Option name='LOCATION_FIELD' type='string'/>"
4847
        "  <Option name='SORT_FIELD' type='string'/>"
4848
        "  <Option name='SORT_FIELD_ASC' type='boolean'/>"
4849
        "  <Option name='FILTER' type='string'/>"
4850
        "  <Option name='SRS' type='string'/>"
4851
        "  <Option name='RESX' type='float'/>"
4852
        "  <Option name='RESY' type='float'/>"
4853
        "  <Option name='MINX' type='float'/>"
4854
        "  <Option name='MINY' type='float'/>"
4855
        "  <Option name='MAXX' type='float'/>"
4856
        "  <Option name='MAXY' type='float'/>"
4857
        "<Option name='NUM_THREADS' type='string' description="
4858
        "'Number of worker threads for reading. Can be set to ALL_CPUS' "
4859
        "default='ALL_CPUS'/>"
4860
        "</OpenOptionList>");
1,365✔
4861

4862
#ifdef BUILT_AS_PLUGIN
4863
    // Used by gdaladdo and test_gdaladdo.py
4864
    poDriver->SetMetadataItem("IS_PLUGIN", "YES");
4865
#endif
4866

4867
    GetGDALDriverManager()->RegisterDriver(poDriver.release());
1,365✔
4868
}
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