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