LCOV - code coverage report
Current view: top level - frmts/gti - gdaltileindexdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1937 2095 92.5 %
Date: 2024-11-21 22:18:42 Functions: 58 58 100.0 %

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

Generated by: LCOV version 1.14