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