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