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