Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: SPOT Dimap Driver
4 : * Purpose: Implementation of SPOT Dimap driver.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : * Docs: http://www.spotimage.fr/dimap/spec/documentation/refdoc.htm
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
11 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include "cpl_minixml.h"
17 : #include "gdal_frmts.h"
18 : #include "gdal_pam.h"
19 : #include "ogr_spatialref.h"
20 : #include "mdreader/reader_pleiades.h"
21 : #include "vrtdataset.h"
22 : #include <map>
23 : #include <algorithm>
24 :
25 : /************************************************************************/
26 : /* ==================================================================== */
27 : /* DIMAPDataset */
28 : /* ==================================================================== */
29 : /************************************************************************/
30 :
31 : class DIMAPDataset final : public GDALPamDataset
32 : {
33 : CPLXMLNode *psProduct{};
34 :
35 : CPLXMLNode *psProductDim{}; // DIMAP2, DIM_<product_id>.XML
36 : CPLXMLNode *psProductStrip{}; // DIMAP2, STRIP_<product_id>.XML
37 : CPLString osRPCFilename{}; // DIMAP2, RPC_<product_id>.XML
38 :
39 : VRTDataset *poVRTDS{};
40 :
41 : int nGCPCount{};
42 : GDAL_GCP *pasGCPList{};
43 :
44 : OGRSpatialReference m_oSRS{};
45 : OGRSpatialReference m_oGCPSRS{};
46 :
47 : int bHaveGeoTransform{};
48 : GDALGeoTransform m_gt{};
49 :
50 : CPLString osMDFilename{};
51 : CPLString osImageDSFilename{};
52 : CPLString osDIMAPFilename{};
53 : int nProductVersion = 1;
54 :
55 : char **papszXMLDimapMetadata{};
56 :
57 : CPL_DISALLOW_COPY_ASSIGN(DIMAPDataset)
58 :
59 : protected:
60 : int CloseDependentDatasets() override;
61 :
62 : int ReadImageInformation();
63 : int ReadImageInformation2(); // DIMAP 2.
64 :
65 : void SetMetadataFromXML(CPLXMLNode *psProduct,
66 : const char *const apszMetadataTranslation[],
67 : bool bKeysFromRoot = true);
68 :
69 : public:
70 : DIMAPDataset();
71 : ~DIMAPDataset() override;
72 :
73 : const OGRSpatialReference *GetSpatialRef() const override;
74 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
75 : int GetGCPCount() override;
76 : const OGRSpatialReference *GetGCPSpatialRef() const override;
77 : const GDAL_GCP *GetGCPs() override;
78 : char **GetMetadataDomainList() override;
79 : char **GetMetadata(const char *pszDomain) override;
80 : char **GetFileList() override;
81 :
82 : CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
83 : GDALDataType, int, BANDMAP_TYPE, GSpacing, GSpacing,
84 : GSpacing, GDALRasterIOExtraArg *psExtraArg) override;
85 :
86 : static int Identify(GDALOpenInfo *);
87 : static GDALDataset *Open(GDALOpenInfo *);
88 :
89 : CPLXMLNode *GetProduct()
90 : {
91 : return psProduct;
92 : }
93 : };
94 :
95 : /************************************************************************/
96 : /* ==================================================================== */
97 : /* DIMAPDataset */
98 : /* ==================================================================== */
99 : /************************************************************************/
100 :
101 : /************************************************************************/
102 : /* DIMAPDataset() */
103 : /************************************************************************/
104 :
105 10 : DIMAPDataset::DIMAPDataset()
106 : {
107 10 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
108 10 : m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
109 10 : }
110 :
111 : /************************************************************************/
112 : /* ~DIMAPDataset() */
113 : /************************************************************************/
114 :
115 20 : DIMAPDataset::~DIMAPDataset()
116 :
117 : {
118 10 : DIMAPDataset::FlushCache(true);
119 :
120 10 : CPLDestroyXMLNode(psProduct);
121 :
122 10 : if (psProductDim != nullptr && psProductDim != psProduct)
123 8 : CPLDestroyXMLNode(psProductDim);
124 10 : if (psProductStrip != nullptr)
125 6 : CPLDestroyXMLNode(psProductStrip);
126 10 : if (nGCPCount > 0)
127 : {
128 2 : GDALDeinitGCPs(nGCPCount, pasGCPList);
129 2 : CPLFree(pasGCPList);
130 : }
131 :
132 10 : CSLDestroy(papszXMLDimapMetadata);
133 :
134 10 : DIMAPDataset::CloseDependentDatasets();
135 20 : }
136 :
137 : /************************************************************************/
138 : /* CloseDependentDatasets() */
139 : /************************************************************************/
140 :
141 25 : int DIMAPDataset::CloseDependentDatasets()
142 : {
143 25 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
144 :
145 25 : if (poVRTDS != nullptr)
146 : {
147 10 : delete poVRTDS;
148 10 : poVRTDS = nullptr;
149 10 : bHasDroppedRef = TRUE;
150 : }
151 :
152 25 : return bHasDroppedRef;
153 : }
154 :
155 : /************************************************************************/
156 : /* GetMetadataDomainList() */
157 : /************************************************************************/
158 :
159 0 : char **DIMAPDataset::GetMetadataDomainList()
160 : {
161 0 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
162 0 : TRUE, "xml:dimap", nullptr);
163 : }
164 :
165 : /************************************************************************/
166 : /* GetMetadata() */
167 : /* */
168 : /* We implement special support for fetching the full product */
169 : /* metadata as xml. */
170 : /************************************************************************/
171 :
172 17 : char **DIMAPDataset::GetMetadata(const char *pszDomain)
173 :
174 : {
175 17 : if (pszDomain && EQUAL(pszDomain, "xml:dimap"))
176 : {
177 0 : if (papszXMLDimapMetadata == nullptr)
178 : {
179 0 : papszXMLDimapMetadata =
180 0 : static_cast<char **>(CPLCalloc(sizeof(char *), 2));
181 0 : papszXMLDimapMetadata[0] = CPLSerializeXMLTree(psProduct);
182 : }
183 0 : return papszXMLDimapMetadata;
184 : }
185 :
186 17 : return GDALPamDataset::GetMetadata(pszDomain);
187 : }
188 :
189 : /************************************************************************/
190 : /* GetSpatialRef() */
191 : /************************************************************************/
192 :
193 1 : const OGRSpatialReference *DIMAPDataset::GetSpatialRef() const
194 :
195 : {
196 1 : if (!m_oSRS.IsEmpty())
197 0 : return &m_oSRS;
198 :
199 1 : return GDALPamDataset::GetSpatialRef();
200 : }
201 :
202 : /************************************************************************/
203 : /* GetGeoTransform() */
204 : /************************************************************************/
205 :
206 0 : CPLErr DIMAPDataset::GetGeoTransform(GDALGeoTransform >) const
207 :
208 : {
209 0 : if (bHaveGeoTransform)
210 : {
211 0 : gt = m_gt;
212 0 : return CE_None;
213 : }
214 :
215 0 : return GDALPamDataset::GetGeoTransform(gt);
216 : }
217 :
218 : /************************************************************************/
219 : /* GetFileList() */
220 : /************************************************************************/
221 :
222 0 : char **DIMAPDataset::GetFileList()
223 :
224 : {
225 0 : char **papszFileList = GDALPamDataset::GetFileList();
226 0 : char **papszImageFiles = poVRTDS->GetFileList();
227 :
228 0 : papszFileList = CSLInsertStrings(papszFileList, -1, papszImageFiles);
229 :
230 0 : CSLDestroy(papszImageFiles);
231 :
232 0 : return papszFileList;
233 : }
234 :
235 : /************************************************************************/
236 : /* ==================================================================== */
237 : /* DIMAPRasterBand */
238 : /* ==================================================================== */
239 : /************************************************************************/
240 :
241 : class DIMAPRasterBand final : public GDALPamRasterBand
242 : {
243 : friend class DIMAPDataset;
244 :
245 : VRTSourcedRasterBand *poVRTBand;
246 :
247 : CPL_DISALLOW_COPY_ASSIGN(DIMAPRasterBand)
248 :
249 : public:
250 : DIMAPRasterBand(DIMAPDataset *, int, VRTSourcedRasterBand *);
251 :
252 60 : ~DIMAPRasterBand() override
253 30 : {
254 60 : }
255 :
256 : CPLErr IReadBlock(int, int, void *) override;
257 : CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
258 : GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
259 : GDALRasterIOExtraArg *psExtraArg) override;
260 : int GetOverviewCount() override;
261 : GDALRasterBand *GetOverview(int) override;
262 : CPLErr ComputeRasterMinMax(int bApproxOK, double adfMinMax[2]) override;
263 : CPLErr ComputeStatistics(int bApproxOK, double *pdfMin, double *pdfMax,
264 : double *pdfMean, double *pdfStdDev,
265 : GDALProgressFunc, void *pProgressData) override;
266 :
267 : CPLErr GetHistogram(double dfMin, double dfMax, int nBuckets,
268 : GUIntBig *panHistogram, int bIncludeOutOfRange,
269 : int bApproxOK, GDALProgressFunc,
270 : void *pProgressData) override;
271 : };
272 :
273 : /************************************************************************/
274 : /* DIMAPRasterBand() */
275 : /************************************************************************/
276 :
277 30 : DIMAPRasterBand::DIMAPRasterBand(DIMAPDataset *poDIMAPDS, int nBandIn,
278 30 : VRTSourcedRasterBand *poVRTBandIn)
279 30 : : poVRTBand(poVRTBandIn)
280 : {
281 30 : poDS = poDIMAPDS;
282 30 : nBand = nBandIn;
283 30 : eDataType = poVRTBandIn->GetRasterDataType();
284 :
285 30 : poVRTBandIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
286 30 : }
287 :
288 : /************************************************************************/
289 : /* IReadBlock() */
290 : /************************************************************************/
291 :
292 0 : CPLErr DIMAPRasterBand::IReadBlock(int iBlockX, int iBlockY, void *pBuffer)
293 :
294 : {
295 0 : return poVRTBand->ReadBlock(iBlockX, iBlockY, pBuffer);
296 : }
297 :
298 : /************************************************************************/
299 : /* IRasterIO() */
300 : /************************************************************************/
301 :
302 12 : CPLErr DIMAPRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
303 : int nXSize, int nYSize, void *pData,
304 : int nBufXSize, int nBufYSize,
305 : GDALDataType eBufType, GSpacing nPixelSpace,
306 : GSpacing nLineSpace,
307 : GDALRasterIOExtraArg *psExtraArg)
308 :
309 : {
310 12 : if (GDALPamRasterBand::GetOverviewCount() > 0)
311 : {
312 0 : return GDALPamRasterBand::IRasterIO(
313 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
314 0 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
315 : }
316 :
317 : // If not exist DIMAP overviews, try to use band source overviews.
318 12 : return poVRTBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
319 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
320 12 : nLineSpace, psExtraArg);
321 : }
322 :
323 : /************************************************************************/
324 : /* IRasterIO() */
325 : /************************************************************************/
326 :
327 2 : CPLErr DIMAPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
328 : int nXSize, int nYSize, void *pData,
329 : int nBufXSize, int nBufYSize,
330 : GDALDataType eBufType, int nBandCount,
331 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
332 : GSpacing nLineSpace, GSpacing nBandSpace,
333 : GDALRasterIOExtraArg *psExtraArg)
334 :
335 : {
336 2 : if (cpl::down_cast<DIMAPRasterBand *>(papoBands[0])
337 2 : ->GDALPamRasterBand::GetOverviewCount() > 0)
338 : {
339 0 : return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
340 : pData, nBufXSize, nBufYSize, eBufType,
341 : nBandCount, panBandMap, nPixelSpace,
342 0 : nLineSpace, nBandSpace, psExtraArg);
343 : }
344 :
345 2 : return poVRTDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
346 : nBufXSize, nBufYSize, eBufType, nBandCount,
347 : panBandMap, nPixelSpace, nLineSpace, nBandSpace,
348 2 : psExtraArg);
349 : }
350 :
351 : /************************************************************************/
352 : /* GetOverviewCount() */
353 : /************************************************************************/
354 :
355 3 : int DIMAPRasterBand::GetOverviewCount()
356 : {
357 3 : if (GDALPamRasterBand::GetOverviewCount() > 0)
358 : {
359 0 : return GDALPamRasterBand::GetOverviewCount();
360 : }
361 3 : return poVRTBand->GetOverviewCount();
362 : }
363 :
364 : /************************************************************************/
365 : /* GetOverview() */
366 : /************************************************************************/
367 :
368 0 : GDALRasterBand *DIMAPRasterBand::GetOverview(int iOvr)
369 : {
370 0 : if (GDALPamRasterBand::GetOverviewCount() > 0)
371 : {
372 0 : return GDALPamRasterBand::GetOverview(iOvr);
373 : }
374 0 : return poVRTBand->GetOverview(iOvr);
375 : }
376 :
377 : /************************************************************************/
378 : /* ComputeRasterMinMax() */
379 : /************************************************************************/
380 :
381 6 : CPLErr DIMAPRasterBand::ComputeRasterMinMax(int bApproxOK, double adfMinMax[2])
382 : {
383 6 : if (GDALPamRasterBand::GetOverviewCount() > 0)
384 : {
385 0 : return GDALPamRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
386 : }
387 6 : return poVRTBand->ComputeRasterMinMax(bApproxOK, adfMinMax);
388 : }
389 :
390 : /************************************************************************/
391 : /* ComputeStatistics() */
392 : /************************************************************************/
393 :
394 0 : CPLErr DIMAPRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
395 : double *pdfMax, double *pdfMean,
396 : double *pdfStdDev,
397 : GDALProgressFunc pfnProgress,
398 : void *pProgressData)
399 : {
400 0 : if (GDALPamRasterBand::GetOverviewCount() > 0)
401 : {
402 0 : return GDALPamRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
403 : pdfMean, pdfStdDev,
404 0 : pfnProgress, pProgressData);
405 : }
406 0 : return poVRTBand->ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean,
407 0 : pdfStdDev, pfnProgress, pProgressData);
408 : }
409 :
410 : /************************************************************************/
411 : /* GetHistogram() */
412 : /************************************************************************/
413 :
414 0 : CPLErr DIMAPRasterBand::GetHistogram(double dfMin, double dfMax, int nBuckets,
415 : GUIntBig *panHistogram,
416 : int bIncludeOutOfRange, int bApproxOK,
417 : GDALProgressFunc pfnProgress,
418 : void *pProgressData)
419 : {
420 0 : if (GDALPamRasterBand::GetOverviewCount() > 0)
421 : {
422 0 : return GDALPamRasterBand::GetHistogram(
423 : dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
424 0 : pfnProgress, pProgressData);
425 : }
426 0 : return poVRTBand->GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
427 : bIncludeOutOfRange, bApproxOK, pfnProgress,
428 0 : pProgressData);
429 : }
430 :
431 : /************************************************************************/
432 : /* Identify() */
433 : /************************************************************************/
434 :
435 61931 : int DIMAPDataset::Identify(GDALOpenInfo *poOpenInfo)
436 :
437 : {
438 61931 : if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
439 4 : return true;
440 :
441 61927 : if (poOpenInfo->nHeaderBytes >= 100)
442 : {
443 6096 : if ((strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
444 6086 : "<Dimap_Document") == nullptr) &&
445 6086 : (strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
446 : "<PHR_DIMAP_Document") == nullptr))
447 6086 : return FALSE;
448 :
449 10 : return TRUE;
450 : }
451 55831 : else if (poOpenInfo->bIsDirectory)
452 : {
453 : // DIMAP file.
454 621 : CPLString osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
455 621 : "METADATA.DIM", nullptr);
456 :
457 : VSIStatBufL sStat;
458 621 : if (VSIStatL(osMDFilename, &sStat) == 0)
459 : {
460 : // Make sure this is really a Dimap format.
461 0 : GDALOpenInfo oOpenInfo(osMDFilename, GA_ReadOnly, nullptr);
462 0 : if (oOpenInfo.nHeaderBytes >= 100)
463 : {
464 0 : if (strstr(reinterpret_cast<char *>(oOpenInfo.pabyHeader),
465 : "<Dimap_Document") == nullptr)
466 0 : return FALSE;
467 :
468 0 : return TRUE;
469 : }
470 : }
471 : else
472 : {
473 : // DIMAP 2 file.
474 621 : osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
475 621 : "VOL_PHR.XML", nullptr);
476 :
477 621 : if (VSIStatL(osMDFilename, &sStat) == 0)
478 4 : return TRUE;
479 :
480 : // DIMAP VHR2020 file.
481 617 : osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
482 617 : "VOL_PNEO.XML", nullptr);
483 :
484 617 : if (VSIStatL(osMDFilename, &sStat) == 0)
485 2 : return TRUE;
486 :
487 615 : return FALSE;
488 : }
489 : }
490 :
491 55210 : return FALSE;
492 : }
493 :
494 : /************************************************************************/
495 : /* Open() */
496 : /************************************************************************/
497 :
498 10 : GDALDataset *DIMAPDataset::Open(GDALOpenInfo *poOpenInfo)
499 :
500 : {
501 10 : if (!Identify(poOpenInfo))
502 0 : return nullptr;
503 :
504 : /* -------------------------------------------------------------------- */
505 : /* Confirm the requested access is supported. */
506 : /* -------------------------------------------------------------------- */
507 10 : if (poOpenInfo->eAccess == GA_Update)
508 : {
509 0 : ReportUpdateNotSupportedByDriver("DIMAP");
510 0 : return nullptr;
511 : }
512 : /* -------------------------------------------------------------------- */
513 : /* Get the metadata filename. */
514 : /* -------------------------------------------------------------------- */
515 20 : CPLString osFilename;
516 20 : CPLString osSelectedSubdataset;
517 :
518 10 : if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
519 : {
520 2 : CPLStringList aosTokens(CSLTokenizeString2(poOpenInfo->pszFilename, ":",
521 2 : CSLT_HONOURSTRINGS));
522 2 : if (aosTokens.size() != 3)
523 0 : return nullptr;
524 :
525 2 : osFilename = aosTokens[1];
526 2 : osSelectedSubdataset = aosTokens[2];
527 : }
528 : else
529 : {
530 8 : osFilename = poOpenInfo->pszFilename;
531 : }
532 :
533 : VSIStatBufL sStat;
534 20 : std::string osMDFilename(osFilename);
535 10 : if (VSIStatL(osFilename.c_str(), &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
536 : {
537 : osMDFilename =
538 5 : CPLFormCIFilenameSafe(osFilename, "METADATA.DIM", nullptr);
539 :
540 : /* DIMAP2 */
541 5 : if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
542 : {
543 : osMDFilename =
544 5 : CPLFormCIFilenameSafe(osFilename, "VOL_PHR.XML", nullptr);
545 5 : if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
546 : {
547 : // DIMAP VHR2020 file.
548 : osMDFilename =
549 1 : CPLFormCIFilenameSafe(osFilename, "VOL_PNEO.XML", nullptr);
550 : }
551 : }
552 : }
553 :
554 : /* -------------------------------------------------------------------- */
555 : /* Ingest the xml file. */
556 : /* -------------------------------------------------------------------- */
557 20 : CPLXMLTreeCloser psProduct(CPLParseXMLFile(osMDFilename.c_str()));
558 10 : if (psProduct == nullptr)
559 0 : return nullptr;
560 :
561 10 : const CPLXMLNode *psDoc = CPLGetXMLNode(psProduct.get(), "=Dimap_Document");
562 10 : if (!psDoc)
563 0 : psDoc = CPLGetXMLNode(psProduct.get(), "=PHR_DIMAP_Document");
564 :
565 : // We check the for the tag Metadata_Identification.METADATA_FORMAT.
566 : // The metadata will be set to 2.0 for DIMAP2.
567 10 : double dfMetadataFormatVersion = CPLAtof(CPLGetXMLValue(
568 : CPLGetXMLNode(psDoc, "Metadata_Identification.METADATA_FORMAT"),
569 : "version", "1"));
570 :
571 10 : const int nProductVersion = dfMetadataFormatVersion >= 2.0 ? 2 : 1;
572 :
573 20 : std::string osImageDSFilename;
574 20 : std::string osDIMAPFilename;
575 20 : std::string osRPCFilename;
576 20 : CPLXMLTreeCloser psProductDim(nullptr);
577 20 : CPLXMLTreeCloser psProductStrip(nullptr);
578 :
579 20 : CPLStringList aosSubdatasets;
580 :
581 : // Check needed information for the DIMAP format.
582 10 : if (nProductVersion == 1)
583 : {
584 : const CPLXMLNode *psImageAttributes =
585 2 : CPLGetXMLNode(psDoc, "Raster_Dimensions");
586 2 : if (psImageAttributes == nullptr)
587 : {
588 0 : CPLError(CE_Failure, CPLE_OpenFailed,
589 : "Failed to find <Raster_Dimensions> in document.");
590 0 : return nullptr;
591 : }
592 : }
593 : else // DIMAP2.
594 : {
595 : // Verify if the opened file is not already a product dimap
596 8 : if (CPLGetXMLNode(psDoc, "Raster_Data"))
597 : {
598 2 : std::swap(psProductDim, psProduct);
599 2 : osDIMAPFilename = osMDFilename;
600 : }
601 : else
602 : {
603 : // Verify the presence of the DIMAP product file.
604 : const CPLXMLNode *psDatasetComponents =
605 6 : CPLGetXMLNode(psDoc, "Dataset_Content.Dataset_Components");
606 :
607 6 : if (psDatasetComponents == nullptr)
608 : {
609 0 : CPLError(CE_Failure, CPLE_OpenFailed,
610 : "Failed to find <Dataset_Components> in document.");
611 0 : return nullptr;
612 : }
613 :
614 6 : for (CPLXMLNode *psDatasetComponent = psDatasetComponents->psChild;
615 17 : psDatasetComponent != nullptr;
616 11 : psDatasetComponent = psDatasetComponent->psNext)
617 : {
618 : const char *pszComponentType =
619 11 : CPLGetXMLValue(psDatasetComponent, "COMPONENT_TYPE", "");
620 11 : if (strcmp(pszComponentType, "DIMAP") == 0)
621 : {
622 : // DIMAP product found.
623 10 : const char *pszHref = CPLGetXMLValue(
624 : psDatasetComponent, "COMPONENT_PATH.href", "");
625 : const CPLString osComponentTitle(CPLGetXMLValue(
626 10 : psDatasetComponent, "COMPONENT_TITLE", ""));
627 : const CPLString osComponentTitleLaundered(
628 10 : CPLString(osComponentTitle).replaceAll(' ', '_'));
629 :
630 20 : if (strlen(pszHref) > 0 && osDIMAPFilename.empty() &&
631 10 : (osSelectedSubdataset.empty() ||
632 3 : osSelectedSubdataset == osComponentTitleLaundered))
633 : {
634 6 : if (CPLHasPathTraversal(pszHref))
635 : {
636 0 : CPLError(CE_Failure, CPLE_NotSupported,
637 : "Path traversal detected in %s", pszHref);
638 0 : return nullptr;
639 : }
640 :
641 6 : if (poOpenInfo->bIsDirectory)
642 : {
643 3 : osDIMAPFilename = CPLFormCIFilenameSafe(
644 3 : poOpenInfo->pszFilename, pszHref, nullptr);
645 : }
646 : else
647 : {
648 : CPLString osPath =
649 3 : CPLGetPathSafe(osMDFilename.c_str());
650 : osDIMAPFilename =
651 3 : CPLFormFilenameSafe(osPath, pszHref, nullptr);
652 : }
653 :
654 : // Data file might be specified there.
655 6 : const char *pszDataFileHref = CPLGetXMLValue(
656 : psDatasetComponent,
657 : "Data_Files.Data_File.DATA_FILE_PATH.href", "");
658 :
659 6 : if (strlen(pszDataFileHref) > 0)
660 : {
661 : CPLString osPath =
662 0 : CPLGetPathSafe(osMDFilename.c_str());
663 0 : osImageDSFilename = CPLFormFilenameSafe(
664 0 : osPath, pszDataFileHref, nullptr);
665 0 : if (CPLHasPathTraversal(osImageDSFilename.c_str()))
666 : {
667 0 : CPLError(CE_Failure, CPLE_NotSupported,
668 : "Path traversal detected in %s",
669 : osImageDSFilename.c_str());
670 0 : return nullptr;
671 : }
672 : }
673 : }
674 :
675 : const int iIdx =
676 10 : static_cast<int>(aosSubdatasets.size() / 2 + 1);
677 : aosSubdatasets.SetNameValue(
678 : CPLSPrintf("SUBDATASET_%d_NAME", iIdx),
679 : CPLSPrintf("DIMAP:\"%s\":%s", poOpenInfo->pszFilename,
680 10 : osComponentTitleLaundered.c_str()));
681 : aosSubdatasets.SetNameValue(
682 : CPLSPrintf("SUBDATASET_%d_DESC", iIdx),
683 10 : CPLSPrintf("Component %s", osComponentTitle.c_str()));
684 : }
685 : }
686 :
687 6 : psProductDim.reset(CPLParseXMLFile(osDIMAPFilename.c_str()));
688 6 : if (psProductDim == nullptr)
689 : {
690 0 : return nullptr;
691 : }
692 : }
693 :
694 : // We need the {STRIP|RPC}_<product_id>.XML file for a few metadata.
695 : const CPLXMLNode *psDocDim =
696 8 : CPLGetXMLNode(psProductDim.get(), "=Dimap_Document");
697 8 : if (!psDocDim)
698 0 : psDocDim = CPLGetXMLNode(psProductDim.get(), "=PHR_DIMAP_Document");
699 :
700 : const CPLXMLNode *psDatasetSources =
701 8 : CPLGetXMLNode(psDocDim, "Dataset_Sources");
702 8 : if (psDatasetSources != nullptr)
703 : {
704 7 : CPLString osSTRIPFilename;
705 :
706 7 : for (CPLXMLNode *psDatasetSource = psDatasetSources->psChild;
707 8 : psDatasetSource != nullptr;
708 1 : psDatasetSource = psDatasetSource->psNext)
709 : {
710 : const char *pszSourceType =
711 7 : CPLGetXMLValue(psDatasetSource, "SOURCE_TYPE", "");
712 7 : if (strcmp(pszSourceType, "Strip_Source") == 0)
713 : {
714 7 : const char *pszHref = CPLGetXMLValue(
715 : psDatasetSource, "Component.COMPONENT_PATH.href", "");
716 :
717 7 : if (strlen(pszHref) > 0) // STRIP product found.
718 : {
719 6 : if (CPLHasPathTraversal(pszHref))
720 : {
721 0 : CPLError(CE_Failure, CPLE_NotSupported,
722 : "Path traversal detected in %s", pszHref);
723 0 : return nullptr;
724 : }
725 : CPLString osPath =
726 6 : CPLGetPathSafe(osDIMAPFilename.c_str());
727 : osSTRIPFilename =
728 6 : CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
729 6 : if (VSIStatL(osSTRIPFilename, &sStat) == 0)
730 : {
731 6 : psProductStrip.reset(
732 : CPLParseXMLFile(osSTRIPFilename));
733 6 : break;
734 : }
735 : }
736 : }
737 : }
738 : }
739 :
740 8 : const CPLXMLNode *psDatasetRFMComponents = CPLGetXMLNode(
741 : psDocDim, "Geoposition.Geoposition_Models.Rational_Function_Model");
742 8 : if (psDatasetRFMComponents != nullptr)
743 : {
744 7 : for (const CPLXMLNode *psDatasetRFMComponent =
745 : psDatasetRFMComponents->psChild;
746 7 : psDatasetRFMComponent != nullptr;
747 0 : psDatasetRFMComponent = psDatasetRFMComponent->psNext)
748 : {
749 7 : const char *pszComponentTitle = CPLGetXMLValue(
750 : psDatasetRFMComponent, "COMPONENT_TITLE", "");
751 7 : if (strcmp(pszComponentTitle, "RPC Model") == 0)
752 : {
753 7 : const char *pszHref = CPLGetXMLValue(
754 : psDatasetRFMComponent, "COMPONENT_PATH.href", "");
755 :
756 7 : if (strlen(pszHref) > 0) // RPC product found.
757 : {
758 7 : if (CPLHasPathTraversal(pszHref))
759 : {
760 0 : CPLError(CE_Failure, CPLE_NotSupported,
761 : "Path traversal detected in %s", pszHref);
762 0 : return nullptr;
763 : }
764 : CPLString osPath =
765 14 : CPLGetPathSafe(osDIMAPFilename.c_str());
766 : osRPCFilename =
767 7 : CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
768 7 : break;
769 : }
770 : }
771 : }
772 : }
773 : }
774 :
775 : /* -------------------------------------------------------------------- */
776 : /* Create the dataset. */
777 : /* -------------------------------------------------------------------- */
778 20 : auto poDS = std::make_unique<DIMAPDataset>();
779 :
780 10 : if (osSelectedSubdataset.empty() && aosSubdatasets.size() > 2)
781 : {
782 2 : poDS->GDALDataset::SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
783 : }
784 10 : poDS->psProduct = psProduct.release();
785 10 : poDS->psProductDim = psProductDim.release();
786 10 : poDS->psProductStrip = psProductStrip.release();
787 10 : poDS->osRPCFilename = std::move(osRPCFilename);
788 10 : poDS->nProductVersion = nProductVersion;
789 10 : poDS->osMDFilename = std::move(osMDFilename);
790 10 : poDS->osImageDSFilename = std::move(osImageDSFilename);
791 10 : poDS->osDIMAPFilename = std::move(osDIMAPFilename);
792 :
793 10 : const int res = (nProductVersion == 2) ? poDS->ReadImageInformation2()
794 2 : : poDS->ReadImageInformation();
795 :
796 10 : if (res == FALSE)
797 : {
798 0 : return nullptr;
799 : }
800 :
801 10 : return poDS.release();
802 : }
803 :
804 : /************************************************************************/
805 : /* ReadImageInformation() DIMAP Version 1 */
806 : /************************************************************************/
807 :
808 2 : int DIMAPDataset::ReadImageInformation()
809 : {
810 2 : CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
811 2 : if (!psDoc)
812 0 : psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
813 :
814 : /* -------------------------------------------------------------------- */
815 : /* Get overall image information. */
816 : /* -------------------------------------------------------------------- */
817 :
818 : // TODO: DIMAP 1 probably handle mosaics? Like DIMAP 2?
819 :
820 : /* -------------------------------------------------------------------- */
821 : /* Get the name of the underlying file. */
822 : /* -------------------------------------------------------------------- */
823 :
824 : const char *pszHref =
825 2 : CPLGetXMLValue(psDoc, "Data_Access.Data_File.DATA_FILE_PATH.href", "");
826 4 : CPLString osPath = CPLGetPathSafe(osMDFilename);
827 4 : CPLString osImageFilename = CPLFormFilenameSafe(osPath, pszHref, nullptr);
828 2 : if (CPLHasPathTraversal(pszHref))
829 : {
830 0 : CPLError(CE_Failure, CPLE_NotSupported, "Path traversal detected in %s",
831 : pszHref);
832 0 : return false;
833 : }
834 : /* -------------------------------------------------------------------- */
835 : /* Try and open the file. */
836 : /* -------------------------------------------------------------------- */
837 :
838 : auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
839 4 : osImageFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
840 2 : if (poImageDS == nullptr)
841 : {
842 0 : return FALSE;
843 : }
844 2 : nRasterXSize = poImageDS->GetRasterXSize();
845 2 : nRasterYSize = poImageDS->GetRasterYSize();
846 :
847 : /* -------------------------------------------------------------------- */
848 : /* Create and initialize the corresponding VRT dataset used to */
849 : /* manage the tiled data access. */
850 : /* -------------------------------------------------------------------- */
851 2 : poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
852 :
853 : // Don't try to write a VRT file.
854 2 : poVRTDS->SetWritable(FALSE);
855 :
856 4 : for (int iBand = 0; iBand < poImageDS->GetRasterCount(); iBand++)
857 : {
858 2 : poVRTDS->AddBand(
859 2 : poImageDS->GetRasterBand(iBand + 1)->GetRasterDataType(), nullptr);
860 :
861 : VRTSourcedRasterBand *poVRTBand =
862 : reinterpret_cast<VRTSourcedRasterBand *>(
863 2 : poVRTDS->GetRasterBand(iBand + 1));
864 :
865 2 : poVRTBand->AddSimpleSource(osImageFilename, iBand + 1, 0, 0,
866 2 : nRasterXSize, nRasterYSize, 0, 0,
867 2 : nRasterXSize, nRasterYSize);
868 : }
869 :
870 : /* -------------------------------------------------------------------- */
871 : /* Create band information objects. */
872 : /* -------------------------------------------------------------------- */
873 4 : for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
874 : {
875 2 : SetBand(iBand, new DIMAPRasterBand(this, iBand,
876 : static_cast<VRTSourcedRasterBand *>(
877 2 : poVRTDS->GetRasterBand(iBand))));
878 : }
879 :
880 : /* -------------------------------------------------------------------- */
881 : /* Try to collect simple insertion point. */
882 : /* -------------------------------------------------------------------- */
883 : CPLXMLNode *psGeoLoc =
884 2 : CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
885 :
886 2 : if (psGeoLoc != nullptr)
887 : {
888 0 : bHaveGeoTransform = TRUE;
889 0 : m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
890 0 : m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
891 0 : m_gt[2] = 0.0;
892 0 : m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
893 0 : m_gt[4] = 0.0;
894 0 : m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
895 : }
896 : else
897 : {
898 : // Try to get geotransform from underlying raster.
899 2 : if (poImageDS->GetGeoTransform(m_gt) == CE_None)
900 0 : bHaveGeoTransform = TRUE;
901 : }
902 :
903 : /* -------------------------------------------------------------------- */
904 : /* Collect GCPs. */
905 : /* -------------------------------------------------------------------- */
906 2 : psGeoLoc = CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Points");
907 :
908 2 : if (psGeoLoc != nullptr)
909 : {
910 : // Count gcps.
911 2 : nGCPCount = 0;
912 10 : for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
913 8 : psNode = psNode->psNext)
914 : {
915 8 : if (EQUAL(psNode->pszValue, "Tie_Point"))
916 8 : nGCPCount++;
917 : }
918 :
919 2 : pasGCPList =
920 2 : static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nGCPCount));
921 :
922 2 : nGCPCount = 0;
923 :
924 10 : for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
925 8 : psNode = psNode->psNext)
926 : {
927 8 : GDAL_GCP *psGCP = pasGCPList + nGCPCount;
928 :
929 8 : if (!EQUAL(psNode->pszValue, "Tie_Point"))
930 0 : continue;
931 :
932 8 : nGCPCount++;
933 :
934 8 : char szID[32] = {};
935 8 : snprintf(szID, sizeof(szID), "%d", nGCPCount);
936 8 : psGCP->pszId = CPLStrdup(szID);
937 8 : psGCP->pszInfo = CPLStrdup("");
938 8 : psGCP->dfGCPPixel =
939 8 : CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_X", "0")) - 0.5;
940 8 : psGCP->dfGCPLine =
941 8 : CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_Y", "0")) - 0.5;
942 8 : psGCP->dfGCPX =
943 8 : CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_X", ""));
944 8 : psGCP->dfGCPY =
945 8 : CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Y", ""));
946 8 : psGCP->dfGCPZ =
947 8 : CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Z", ""));
948 : }
949 : }
950 :
951 : /* -------------------------------------------------------------------- */
952 : /* Collect the CRS. For now we look only for EPSG codes. */
953 : /* -------------------------------------------------------------------- */
954 2 : const char *pszSRS = CPLGetXMLValue(
955 : psDoc, "Coordinate_Reference_System.Horizontal_CS.HORIZONTAL_CS_CODE",
956 : nullptr);
957 :
958 2 : if (pszSRS != nullptr)
959 : {
960 2 : OGRSpatialReference &oSRS = nGCPCount > 0 ? m_oGCPSRS : m_oSRS;
961 2 : oSRS.SetFromUserInput(
962 : pszSRS, OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
963 : }
964 : else
965 : {
966 : // Check underlying raster for SRS. We have cases where
967 : // HORIZONTAL_CS_CODE is empty and the underlying raster
968 : // is georeferenced (rprinceley).
969 0 : const auto poSRS = poImageDS->GetSpatialRef();
970 0 : if (poSRS)
971 : {
972 0 : m_oSRS = *poSRS;
973 : }
974 : }
975 :
976 : /* -------------------------------------------------------------------- */
977 : /* Translate other metadata of interest. */
978 : /* -------------------------------------------------------------------- */
979 : static const char *const apszMetadataTranslation[] = {
980 : "Production",
981 : "",
982 : "Production.Facility",
983 : "FACILITY_",
984 : "Dataset_Sources.Source_Information.Scene_Source",
985 : "",
986 : "Data_Processing",
987 : "",
988 : "Image_Interpretation.Spectral_Band_Info",
989 : "SPECTRAL_",
990 : nullptr,
991 : nullptr};
992 :
993 2 : SetMetadataFromXML(psProduct, apszMetadataTranslation);
994 :
995 : /* -------------------------------------------------------------------- */
996 : /* Set Band metadata from the <Spectral_Band_Info> content */
997 : /* -------------------------------------------------------------------- */
998 :
999 : CPLXMLNode *psImageInterpretationNode =
1000 2 : CPLGetXMLNode(psDoc, "Image_Interpretation");
1001 2 : if (psImageInterpretationNode != nullptr)
1002 : {
1003 2 : CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
1004 4 : while (psSpectralBandInfoNode != nullptr)
1005 : {
1006 2 : if (psSpectralBandInfoNode->eType == CXT_Element &&
1007 2 : EQUAL(psSpectralBandInfoNode->pszValue, "Spectral_Band_Info"))
1008 : {
1009 2 : CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
1010 2 : int nBandIndex = 0;
1011 14 : while (psTag != nullptr)
1012 : {
1013 12 : if (psTag->eType == CXT_Element &&
1014 12 : psTag->psChild != nullptr &&
1015 12 : psTag->psChild->eType == CXT_Text &&
1016 12 : psTag->pszValue != nullptr)
1017 : {
1018 12 : if (EQUAL(psTag->pszValue, "BAND_INDEX"))
1019 : {
1020 2 : nBandIndex = atoi(psTag->psChild->pszValue);
1021 4 : if (nBandIndex <= 0 ||
1022 2 : nBandIndex > poImageDS->GetRasterCount())
1023 : {
1024 0 : CPLError(CE_Warning, CPLE_AppDefined,
1025 : "Bad BAND_INDEX value : %s",
1026 0 : psTag->psChild->pszValue);
1027 0 : nBandIndex = 0;
1028 : }
1029 : }
1030 10 : else if (nBandIndex >= 1)
1031 : {
1032 10 : GetRasterBand(nBandIndex)
1033 10 : ->SetMetadataItem(psTag->pszValue,
1034 10 : psTag->psChild->pszValue);
1035 : }
1036 : }
1037 12 : psTag = psTag->psNext;
1038 : }
1039 : }
1040 2 : psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
1041 : }
1042 : }
1043 :
1044 : /* -------------------------------------------------------------------- */
1045 : /* Initialize any PAM information. */
1046 : /* -------------------------------------------------------------------- */
1047 2 : SetDescription(osMDFilename);
1048 2 : TryLoadXML();
1049 :
1050 : /* -------------------------------------------------------------------- */
1051 : /* Check for overviews. */
1052 : /* -------------------------------------------------------------------- */
1053 2 : oOvManager.Initialize(this, osMDFilename);
1054 :
1055 : // CID 163546 - poTileDS dereferenced above.
1056 : // coverity[leaked_storage]
1057 2 : return TRUE;
1058 : }
1059 :
1060 : /************************************************************************/
1061 : /* ReadImageInformation() DIMAP Version 2 */
1062 : /************************************************************************/
1063 :
1064 8 : int DIMAPDataset::ReadImageInformation2()
1065 : {
1066 8 : CPLXMLNode *psDoc = CPLGetXMLNode(psProductDim, "=Dimap_Document");
1067 8 : if (!psDoc)
1068 0 : psDoc = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
1069 :
1070 : CPLXMLNode *psImageAttributes =
1071 8 : CPLGetXMLNode(psDoc, "Raster_Data.Raster_Dimensions");
1072 8 : if (psImageAttributes == nullptr)
1073 : {
1074 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1075 : "Failed to find <Raster_Dimensions> in document.");
1076 0 : return FALSE;
1077 : }
1078 :
1079 : /* -------------------------------------------------------------------- */
1080 : /* Get overall image information. */
1081 : /* -------------------------------------------------------------------- */
1082 :
1083 : /*
1084 : <Raster_Dimensions>
1085 : <NROWS>30</NROWS>
1086 : <NCOLS>20</NCOLS>
1087 : <NBANDS>4</NBANDS>
1088 : <Tile_Set>
1089 : <NTILES>2</NTILES>
1090 : <Regular_Tiling>
1091 : <NTILES_SIZE nrows="20" ncols="20"/>
1092 : <NTILES_COUNT ntiles_R="2" ntiles_C="1"/>
1093 : <OVERLAP_ROW>0</OVERLAP_ROW>
1094 : <OVERLAP_COL>0</OVERLAP_COL>
1095 : </Regular_Tiling>
1096 : </Tile_Set>
1097 : </Raster_Dimensions>
1098 : */
1099 :
1100 : const int l_nBands =
1101 8 : atoi(CPLGetXMLValue(psImageAttributes, "NBANDS", "-1"));
1102 8 : nRasterXSize = atoi(CPLGetXMLValue(psImageAttributes, "NCOLS", "-1"));
1103 8 : nRasterYSize = atoi(CPLGetXMLValue(psImageAttributes, "NROWS", "-1"));
1104 8 : if (nRasterXSize <= 0 || nRasterYSize <= 0)
1105 : {
1106 0 : CPLError(CE_Failure, CPLE_AppDefined,
1107 : "Invalid NCOLS(=%d)/NROWS(=%d) value", nRasterXSize,
1108 : nRasterYSize);
1109 0 : return FALSE;
1110 : }
1111 8 : int nTileWidth = atoi(CPLGetXMLValue(
1112 : psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.ncols", "-1"));
1113 8 : int nTileHeight = atoi(CPLGetXMLValue(
1114 : psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.nrows", "-1"));
1115 8 : int nOverlapRow = atoi(CPLGetXMLValue(
1116 : psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_ROW", "-1"));
1117 8 : int nOverlapCol = atoi(CPLGetXMLValue(
1118 : psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_COL", "-1"));
1119 : const int nBits =
1120 8 : atoi(CPLGetXMLValue(psDoc, "Raster_Data.Raster_Encoding.NBITS", "-1"));
1121 : CPLString osDataFormat =
1122 16 : CPLGetXMLValue(psDoc, "Raster_Data.Data_Access.DATA_FILE_FORMAT", "");
1123 8 : if (osDataFormat == "image/jp2")
1124 : {
1125 1 : SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
1126 : }
1127 :
1128 : // For VHR2020: SPECTRAL_PROCESSING = PAN, MS, MS-FS, PMS, PMS-N, PMS-X,
1129 : // PMS-FS
1130 : const CPLString osSpectralProcessing = CPLGetXMLValue(
1131 : psDoc, "Processing_Information.Product_Settings.SPECTRAL_PROCESSING",
1132 16 : "");
1133 : const bool bTwoDataFilesPerTile =
1134 8 : osSpectralProcessing == "MS-FS" || osSpectralProcessing == "PMS-FS";
1135 :
1136 : /* -------------------------------------------------------------------- */
1137 : /* Get the name of the underlying file. */
1138 : /* -------------------------------------------------------------------- */
1139 :
1140 : CPLXMLNode *psDataFiles =
1141 8 : CPLGetXMLNode(psDoc, "Raster_Data.Data_Access.Data_Files");
1142 :
1143 : /* <Data_Files>
1144 : <Data_File tile_R="1" tile_C="1">
1145 : <DATA_FILE_PATH href="IMG_foo_R1C1.TIF"/>
1146 : </Data_File>
1147 : <Data_File tile_R="2" tile_C="1">
1148 : <DATA_FILE_PATH href="IMG_foo_R2C1.TIF"/>
1149 : </Data_File>
1150 : </Data_Files>
1151 : */
1152 :
1153 : struct TileIdx
1154 : {
1155 : int nRow;
1156 : int nCol;
1157 : int nPart; // typically 0. But for VHR2020 0=RGB, 1=NED
1158 :
1159 15 : TileIdx(int nRowIn, int nColIn, int nPartIn = 0)
1160 15 : : nRow(nRowIn), nCol(nColIn), nPart(nPartIn)
1161 : {
1162 15 : }
1163 :
1164 21 : bool operator<(const TileIdx &other) const
1165 : {
1166 21 : if (nRow < other.nRow)
1167 12 : return true;
1168 9 : if (nRow > other.nRow)
1169 6 : return false;
1170 3 : if (nCol < other.nCol)
1171 0 : return true;
1172 3 : if (nCol > other.nCol)
1173 0 : return false;
1174 3 : return nPart < other.nPart;
1175 : }
1176 : };
1177 :
1178 16 : std::map<TileIdx, CPLString> oMapTileIdxToName;
1179 8 : int nImageDSRow = 1, nImageDSCol = 1;
1180 8 : if (psDataFiles)
1181 : {
1182 8 : const CPLString osPath = CPLGetPathSafe(osDIMAPFilename);
1183 17 : for (int nPart = 0; psDataFiles != nullptr;
1184 9 : psDataFiles = psDataFiles->psNext, nPart++)
1185 : {
1186 27 : for (CPLXMLNode *psDataFile = psDataFiles->psChild; psDataFile;
1187 18 : psDataFile = psDataFile->psNext)
1188 : {
1189 18 : if (psDataFile->eType == CXT_Element &&
1190 18 : strcmp(psDataFile->pszValue, "Data_File") == 0)
1191 : {
1192 : const char *pszR =
1193 15 : CPLGetXMLValue(psDataFile, "tile_R", nullptr);
1194 : const char *pszC =
1195 15 : CPLGetXMLValue(psDataFile, "tile_C", nullptr);
1196 15 : const char *pszHref = CPLGetXMLValue(
1197 : psDataFile, "DATA_FILE_PATH.href", nullptr);
1198 15 : if (pszR && pszC && pszHref)
1199 : {
1200 15 : int nRow = atoi(pszR);
1201 15 : int nCol = atoi(pszC);
1202 15 : if (nRow < 0 || nCol < 0)
1203 : {
1204 0 : return false;
1205 : }
1206 : std::string osTileFilename(
1207 15 : CPLFormCIFilenameSafe(osPath, pszHref, nullptr));
1208 15 : if (CPLHasPathTraversal(pszHref))
1209 : {
1210 0 : CPLError(CE_Failure, CPLE_NotSupported,
1211 : "Path traversal detected in %s", pszHref);
1212 0 : return false;
1213 : }
1214 22 : if ((nRow == 1 && nCol == 1 && nPart == 0) ||
1215 7 : osImageDSFilename.empty())
1216 : {
1217 8 : osImageDSFilename = osTileFilename;
1218 8 : nImageDSRow = nRow;
1219 8 : nImageDSCol = nCol;
1220 : }
1221 15 : oMapTileIdxToName[TileIdx(nRow, nCol, nPart)] =
1222 30 : std::move(osTileFilename);
1223 : }
1224 : }
1225 : }
1226 : }
1227 8 : if (nOverlapRow > 0 || nOverlapCol > 0)
1228 : {
1229 0 : CPLError(CE_Warning, CPLE_AppDefined,
1230 : "Overlap between tiles is not handled currently. "
1231 : "Only taking into account top left tile");
1232 0 : oMapTileIdxToName.clear();
1233 0 : oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
1234 : }
1235 : }
1236 : else
1237 : {
1238 0 : oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
1239 : }
1240 :
1241 8 : if (osImageDSFilename.empty())
1242 : {
1243 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1244 : "Failed to find <DATA_FILE_PATH> in document.");
1245 0 : return FALSE;
1246 : }
1247 :
1248 : /* -------------------------------------------------------------------- */
1249 : /* Try and open the file. */
1250 : /* -------------------------------------------------------------------- */
1251 : auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1252 16 : osImageDSFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
1253 8 : if (poImageDS == nullptr)
1254 : {
1255 0 : return FALSE;
1256 : }
1257 8 : if (bTwoDataFilesPerTile)
1258 : {
1259 1 : if (l_nBands != 6 || poImageDS->GetRasterCount() != 3)
1260 : {
1261 0 : CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
1262 0 : return FALSE;
1263 : }
1264 : }
1265 7 : else if (poImageDS->GetRasterCount() != l_nBands)
1266 : {
1267 0 : CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
1268 0 : return FALSE;
1269 : }
1270 :
1271 8 : if (nTileWidth > 0 && nTileHeight > 0)
1272 : {
1273 : // ok
1274 : }
1275 3 : else if (oMapTileIdxToName.size() == 1 ||
1276 1 : (bTwoDataFilesPerTile && oMapTileIdxToName.size() == 2))
1277 : {
1278 2 : nTileWidth = poImageDS->GetRasterXSize();
1279 2 : nTileHeight = poImageDS->GetRasterYSize();
1280 : }
1281 :
1282 8 : if (!(nTileWidth > 0 && nTileHeight > 0))
1283 : {
1284 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot get tile dimension");
1285 0 : return FALSE;
1286 : }
1287 :
1288 : /* -------------------------------------------------------------------- */
1289 : /* Create and initialize the corresponding VRT dataset used to */
1290 : /* manage the tiled data access. */
1291 : /* -------------------------------------------------------------------- */
1292 8 : poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
1293 :
1294 : // Don't try to write a VRT file.
1295 8 : poVRTDS->SetWritable(FALSE);
1296 :
1297 36 : for (int iBand = 0; iBand < l_nBands; iBand++)
1298 : {
1299 56 : auto poSrcBandFirstImage = poImageDS->GetRasterBand(
1300 28 : iBand < poImageDS->GetRasterCount() ? iBand + 1 : 1);
1301 56 : CPLStringList aosAddBandOptions;
1302 : int nSrcBlockXSize, nSrcBlockYSize;
1303 28 : poSrcBandFirstImage->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
1304 55 : if (oMapTileIdxToName.size() == 1 ||
1305 27 : ((nTileWidth % nSrcBlockXSize) == 0 &&
1306 15 : (nTileHeight % nSrcBlockYSize) == 0))
1307 : {
1308 : aosAddBandOptions.SetNameValue("BLOCKXSIZE",
1309 16 : CPLSPrintf("%d", nSrcBlockXSize));
1310 : aosAddBandOptions.SetNameValue("BLOCKYSIZE",
1311 16 : CPLSPrintf("%d", nSrcBlockYSize));
1312 : }
1313 28 : poVRTDS->AddBand(poSrcBandFirstImage->GetRasterDataType(),
1314 28 : aosAddBandOptions.List());
1315 :
1316 : VRTSourcedRasterBand *poVRTBand =
1317 : reinterpret_cast<VRTSourcedRasterBand *>(
1318 28 : poVRTDS->GetRasterBand(iBand + 1));
1319 28 : if (nBits > 0 && nBits != 8 && nBits != 16)
1320 : {
1321 21 : poVRTBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
1322 21 : "IMAGE_STRUCTURE");
1323 : }
1324 :
1325 83 : for (const auto &oTileIdxNameTuple : oMapTileIdxToName)
1326 : {
1327 55 : const int nRow = oTileIdxNameTuple.first.nRow;
1328 55 : const int nCol = oTileIdxNameTuple.first.nCol;
1329 55 : if (static_cast<int64_t>(nRow - 1) * nTileHeight < nRasterYSize &&
1330 55 : static_cast<int64_t>(nCol - 1) * nTileWidth < nRasterXSize)
1331 : {
1332 : int nSrcBand;
1333 55 : if (bTwoDataFilesPerTile)
1334 : {
1335 12 : const int nPart = oTileIdxNameTuple.first.nPart;
1336 12 : if (nPart == 0 && iBand < 3)
1337 : {
1338 3 : nSrcBand = iBand + 1;
1339 : }
1340 9 : else if (nPart == 1 && iBand >= 3)
1341 : {
1342 3 : nSrcBand = iBand + 1 - 3;
1343 : }
1344 : else
1345 : {
1346 6 : continue;
1347 : }
1348 : }
1349 : else
1350 : {
1351 43 : nSrcBand = iBand + 1;
1352 : }
1353 :
1354 49 : int nHeight = nTileHeight;
1355 49 : if (static_cast<int64_t>(nRow) * nTileHeight > nRasterYSize)
1356 : {
1357 21 : nHeight = nRasterYSize - (nRow - 1) * nTileHeight;
1358 : }
1359 49 : int nWidth = nTileWidth;
1360 49 : if (static_cast<int64_t>(nCol) * nTileWidth > nRasterXSize)
1361 : {
1362 0 : nWidth = nRasterXSize - (nCol - 1) * nTileWidth;
1363 : }
1364 :
1365 49 : poVRTBand->AddSimpleSource(
1366 : oTileIdxNameTuple.second, nSrcBand, 0, 0, nWidth, nHeight,
1367 49 : (nCol - 1) * nTileWidth, (nRow - 1) * nTileHeight, nWidth,
1368 : nHeight);
1369 : }
1370 : }
1371 : }
1372 :
1373 : /* -------------------------------------------------------------------- */
1374 : /* Expose Overviews if available */
1375 : /* -------------------------------------------------------------------- */
1376 8 : auto poSrcBandFirstImage = poImageDS->GetRasterBand(1);
1377 : const int nSrcOverviews =
1378 8 : std::min(30, poSrcBandFirstImage->GetOverviewCount());
1379 8 : if (nSrcOverviews > 0)
1380 : {
1381 6 : CPLConfigOptionSetter oSetter("VRT_VIRTUAL_OVERVIEWS", "YES", false);
1382 6 : std::unique_ptr<int[]> ovrLevels(new int[nSrcOverviews]);
1383 3 : int iLvl = 1;
1384 6 : for (int i = 0; i < nSrcOverviews; i++)
1385 : {
1386 3 : iLvl *= 2;
1387 3 : ovrLevels[i] = iLvl;
1388 : }
1389 3 : poVRTDS->IBuildOverviews("average", nSrcOverviews, ovrLevels.get(), 0,
1390 3 : nullptr, nullptr, nullptr, nullptr);
1391 : }
1392 :
1393 : #ifdef DEBUG_VERBOSE
1394 : CPLDebug("DIMAP", "VRT XML: %s", poVRTDS->GetMetadata("xml:VRT")[0]);
1395 : #endif
1396 :
1397 : /* -------------------------------------------------------------------- */
1398 : /* Create band information objects. */
1399 : /* -------------------------------------------------------------------- */
1400 36 : for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
1401 : {
1402 : GDALRasterBand *poBand = new DIMAPRasterBand(
1403 : this, iBand,
1404 28 : static_cast<VRTSourcedRasterBand *>(poVRTDS->GetRasterBand(iBand)));
1405 28 : if (nBits > 0 && nBits != 8 && nBits != 16)
1406 : {
1407 21 : poBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
1408 21 : "IMAGE_STRUCTURE");
1409 : }
1410 28 : if (bTwoDataFilesPerTile)
1411 : {
1412 6 : switch (iBand)
1413 : {
1414 1 : case 1:
1415 : {
1416 1 : poBand->SetColorInterpretation(GCI_RedBand);
1417 1 : poBand->SetDescription("Red");
1418 1 : break;
1419 : }
1420 1 : case 2:
1421 : {
1422 1 : poBand->SetColorInterpretation(GCI_GreenBand);
1423 1 : poBand->SetDescription("Green");
1424 1 : break;
1425 : }
1426 1 : case 3:
1427 : {
1428 1 : poBand->SetColorInterpretation(GCI_BlueBand);
1429 1 : poBand->SetDescription("Blue");
1430 1 : break;
1431 : }
1432 1 : case 4:
1433 : {
1434 1 : poBand->SetColorInterpretation(GCI_NIRBand);
1435 1 : poBand->SetDescription("NIR");
1436 1 : break;
1437 : }
1438 1 : case 5:
1439 : {
1440 1 : poBand->SetColorInterpretation(GCI_RedEdgeBand);
1441 1 : poBand->SetDescription("Red Edge");
1442 1 : break;
1443 : }
1444 1 : case 6:
1445 : {
1446 1 : poBand->SetColorInterpretation(GCI_CoastalBand);
1447 1 : poBand->SetDescription("Deep Blue");
1448 1 : break;
1449 : }
1450 0 : default:
1451 0 : break;
1452 : }
1453 : }
1454 22 : else if (l_nBands == 1 && osSpectralProcessing == "PAN")
1455 : {
1456 0 : poBand->SetColorInterpretation(GCI_PanBand);
1457 0 : poBand->SetDescription("Panchromatic");
1458 : }
1459 28 : SetBand(iBand, poBand);
1460 : }
1461 :
1462 : /* -------------------------------------------------------------------- */
1463 : /* Try to collect simple insertion point. */
1464 : /* -------------------------------------------------------------------- */
1465 : CPLXMLNode *psGeoLoc =
1466 8 : CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
1467 :
1468 8 : if (psGeoLoc != nullptr)
1469 : {
1470 1 : bHaveGeoTransform = TRUE;
1471 1 : m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
1472 1 : m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
1473 1 : m_gt[2] = 0.0;
1474 1 : m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
1475 1 : m_gt[4] = 0.0;
1476 1 : m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
1477 : }
1478 : else
1479 : {
1480 : // Try to get geotransform from underlying raster,
1481 : // but make sure it is a real geotransform.
1482 13 : if (poImageDS->GetGeoTransform(m_gt) == CE_None &&
1483 6 : !(m_gt[0] <= 1.5 && fabs(m_gt[3]) <= 1.5))
1484 : {
1485 6 : bHaveGeoTransform = TRUE;
1486 : // fix up the origin if we did not get the geotransform from the
1487 : // top-left tile
1488 6 : m_gt[0] -= (nImageDSCol - 1) * m_gt[1] * nTileWidth +
1489 6 : (nImageDSRow - 1) * m_gt[2] * nTileHeight;
1490 12 : m_gt[3] -= (nImageDSCol - 1) * m_gt[4] * nTileWidth +
1491 6 : (nImageDSRow - 1) * m_gt[5] * nTileHeight;
1492 : }
1493 : }
1494 :
1495 : /* -------------------------------------------------------------------- */
1496 : /* Collect the CRS. For now we look only for EPSG codes. */
1497 : /* -------------------------------------------------------------------- */
1498 8 : const char *pszSRS = CPLGetXMLValue(
1499 : psDoc, "Coordinate_Reference_System.Projected_CRS.PROJECTED_CRS_CODE",
1500 : nullptr);
1501 8 : if (pszSRS == nullptr)
1502 8 : pszSRS = CPLGetXMLValue(
1503 : psDoc, "Coordinate_Reference_System.Geodetic_CRS.GEODETIC_CRS_CODE",
1504 : nullptr);
1505 :
1506 8 : if (pszSRS != nullptr)
1507 : {
1508 8 : if (bHaveGeoTransform)
1509 : {
1510 7 : OGRSpatialReference &oSRS = m_oSRS;
1511 7 : oSRS.SetFromUserInput(
1512 : pszSRS,
1513 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1514 : }
1515 : }
1516 : else
1517 : {
1518 : // Check underlying raster for SRS. We have cases where
1519 : // HORIZONTAL_CS_CODE is empty and the underlying raster
1520 : // is georeferenced (rprinceley).
1521 0 : const auto poSRS = poImageDS->GetSpatialRef();
1522 0 : GDALGeoTransform tmpGT;
1523 0 : if (poSRS && poImageDS->GetGeoTransform(tmpGT) == CE_None)
1524 : {
1525 0 : m_oSRS = *poSRS;
1526 : }
1527 : }
1528 :
1529 : /* -------------------------------------------------------------------- */
1530 : /* Translate other metadata of interest: DIM_<product_name>.XML */
1531 : /* -------------------------------------------------------------------- */
1532 :
1533 : static const char *const apszMetadataTranslationDim[] = {
1534 : "Product_Information.Delivery_Identification",
1535 : "DATASET_",
1536 : "Product_Information.Producer_Information",
1537 : "DATASET_",
1538 : "Dataset_Sources.Source_Identification.Strip_Source",
1539 : "",
1540 : "Processing_Information.Production_Facility",
1541 : "FACILITY_",
1542 : "Processing_Information.Product_Settings",
1543 : "",
1544 : "Processing_Information.Product_Settings.Geometric_Settings",
1545 : "GEOMETRIC_",
1546 : "Processing_Information.Product_Settings.Radiometric_Settings",
1547 : "RADIOMETRIC_",
1548 : "Quality_Assessment.Imaging_Quality_Measurement",
1549 : "CLOUDCOVER_",
1550 : nullptr,
1551 : nullptr};
1552 :
1553 8 : SetMetadataFromXML(psProductDim, apszMetadataTranslationDim);
1554 :
1555 : /* -------------------------------------------------------------------- */
1556 : /* Translate other metadata of interest: STRIP_<product_name>.XML */
1557 : /* -------------------------------------------------------------------- */
1558 :
1559 : static const char *const apszMetadataTranslationStrip[] = {
1560 : "Catalog.Full_Strip.Notations.Cloud_And_Quality_Notation."
1561 : "Data_Strip_Notation",
1562 : "CLOUDCOVER_",
1563 : "Acquisition_Configuration.Platform_Configuration."
1564 : "Ephemeris_Configuration",
1565 : "EPHEMERIS_",
1566 : nullptr,
1567 : nullptr};
1568 :
1569 8 : if (psProductStrip != nullptr)
1570 6 : SetMetadataFromXML(psProductStrip, apszMetadataTranslationStrip);
1571 :
1572 8 : if (!osRPCFilename.empty())
1573 : {
1574 : GDALMDReaderPleiades *poReader =
1575 7 : GDALMDReaderPleiades::CreateReaderForRPC(osRPCFilename);
1576 7 : char **papszRPC = poReader->LoadRPCXmlFile(psDoc);
1577 7 : delete poReader;
1578 7 : if (papszRPC)
1579 7 : SetMetadata(papszRPC, "RPC");
1580 7 : CSLDestroy(papszRPC);
1581 : }
1582 :
1583 : CPLXMLNode *psLocatedUseAreaNode =
1584 8 : CPLGetXMLNode(psDoc, "Geometric_Data.Use_Area");
1585 8 : if (psLocatedUseAreaNode != nullptr)
1586 : {
1587 4 : CPLXMLNode *psLocatedGeometricValuesNode =
1588 : psLocatedUseAreaNode->psChild;
1589 11 : while (psLocatedGeometricValuesNode != nullptr)
1590 : {
1591 : CPLXMLNode *psLocationType =
1592 11 : CPLGetXMLNode(psLocatedGeometricValuesNode, "LOCATION_TYPE");
1593 11 : if (psLocationType == nullptr ||
1594 11 : psLocationType->psChild == nullptr ||
1595 11 : !EQUAL(psLocationType->psChild->pszValue, "center"))
1596 : {
1597 7 : psLocatedGeometricValuesNode =
1598 : psLocatedGeometricValuesNode->psNext;
1599 7 : continue;
1600 : }
1601 : static const char *const apszLGVTranslationDim[] = {
1602 : "SATELLITE_ALTITUDE",
1603 : "",
1604 : "Acquisition_Angles",
1605 : "",
1606 : "Solar_Incidences",
1607 : "",
1608 : "Ground_Sample_Distance",
1609 : "",
1610 : nullptr,
1611 : nullptr};
1612 :
1613 4 : SetMetadataFromXML(psLocatedGeometricValuesNode,
1614 : apszLGVTranslationDim, false);
1615 4 : break;
1616 : }
1617 : }
1618 :
1619 : /* -------------------------------------------------------------------- */
1620 : /* Set Band metadata from the <Band_Radiance> and */
1621 : /* <Band_Spectral_Range> content */
1622 : /* -------------------------------------------------------------------- */
1623 :
1624 8 : CPLXMLNode *psImageInterpretationNode = CPLGetXMLNode(
1625 : psDoc,
1626 : "Radiometric_Data.Radiometric_Calibration.Instrument_Calibration."
1627 : "Band_Measurement_List");
1628 8 : if (psImageInterpretationNode != nullptr)
1629 : {
1630 7 : CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
1631 100 : while (psSpectralBandInfoNode != nullptr)
1632 : {
1633 93 : if (psSpectralBandInfoNode->eType == CXT_Element &&
1634 93 : (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance") ||
1635 66 : EQUAL(psSpectralBandInfoNode->pszValue,
1636 39 : "Band_Spectral_Range") ||
1637 39 : EQUAL(psSpectralBandInfoNode->pszValue,
1638 : "Band_Solar_Irradiance")))
1639 : {
1640 162 : CPLString osName;
1641 :
1642 81 : if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance"))
1643 27 : osName = "RADIANCE_";
1644 54 : else if (EQUAL(psSpectralBandInfoNode->pszValue,
1645 : "Band_Spectral_Range"))
1646 27 : osName = "SPECTRAL_RANGE_";
1647 27 : else if (EQUAL(psSpectralBandInfoNode->pszValue,
1648 : "Band_Solar_Irradiance"))
1649 27 : osName = "SOLAR_IRRADIANCE_";
1650 :
1651 81 : CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
1652 81 : int nBandIndex = 0;
1653 651 : while (psTag != nullptr)
1654 : {
1655 570 : if (psTag->eType == CXT_Element &&
1656 570 : psTag->psChild != nullptr &&
1657 558 : psTag->pszValue != nullptr &&
1658 558 : (psTag->psChild->eType == CXT_Text ||
1659 6 : EQUAL(psTag->pszValue, "FWHM")))
1660 : {
1661 558 : if (EQUAL(psTag->pszValue, "BAND_ID"))
1662 : {
1663 81 : nBandIndex = 0;
1664 81 : if (EQUAL(psTag->psChild->pszValue, "P") ||
1665 81 : EQUAL(psTag->psChild->pszValue, "PAN") ||
1666 81 : EQUAL(psTag->psChild->pszValue, "B0") ||
1667 63 : EQUAL(psTag->psChild->pszValue, "R"))
1668 21 : nBandIndex = 1;
1669 60 : else if (EQUAL(psTag->psChild->pszValue, "B1") ||
1670 45 : EQUAL(psTag->psChild->pszValue, "G"))
1671 18 : nBandIndex = 2;
1672 42 : else if (EQUAL(psTag->psChild->pszValue, "B2") ||
1673 27 : EQUAL(psTag->psChild->pszValue, "B"))
1674 18 : nBandIndex = 3;
1675 24 : else if (EQUAL(psTag->psChild->pszValue, "B3") ||
1676 9 : EQUAL(psTag->psChild->pszValue, "NIR"))
1677 18 : nBandIndex = 4;
1678 6 : else if (EQUAL(psTag->psChild->pszValue, "RE"))
1679 3 : nBandIndex = 5;
1680 3 : else if (EQUAL(psTag->psChild->pszValue, "DB"))
1681 3 : nBandIndex = 6;
1682 :
1683 162 : if (nBandIndex <= 0 ||
1684 81 : nBandIndex > GetRasterCount())
1685 : {
1686 0 : CPLError(CE_Warning, CPLE_AppDefined,
1687 : "Bad BAND_ID value : %s",
1688 0 : psTag->psChild->pszValue);
1689 0 : nBandIndex = 0;
1690 : }
1691 : }
1692 477 : else if (nBandIndex >= 1)
1693 : {
1694 954 : CPLString osMDName = osName;
1695 477 : osMDName += psTag->pszValue;
1696 :
1697 477 : auto poBand = GetRasterBand(nBandIndex);
1698 477 : if (EQUAL(psTag->pszValue, "FWHM"))
1699 : {
1700 6 : if (const char *pszMIN =
1701 6 : CPLGetXMLValue(psTag, "MIN", nullptr))
1702 6 : poBand->SetMetadataItem(
1703 12 : (osMDName + "_MIN").c_str(), pszMIN);
1704 6 : if (const char *pszMAX =
1705 6 : CPLGetXMLValue(psTag, "MAX", nullptr))
1706 6 : poBand->SetMetadataItem(
1707 12 : (osMDName + "_MAX").c_str(), pszMAX);
1708 : }
1709 : else
1710 : {
1711 471 : poBand->SetMetadataItem(
1712 471 : osMDName, psTag->psChild->pszValue);
1713 : }
1714 : }
1715 : }
1716 570 : psTag = psTag->psNext;
1717 : }
1718 : }
1719 93 : psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
1720 : }
1721 : }
1722 :
1723 : // Fill raster band IMAGERY metadata domain from FWHM metadata.
1724 36 : for (int i = 1; i <= nBands; ++i)
1725 : {
1726 28 : auto poBand = GetRasterBand(i);
1727 : const char *SPECTRAL_RANGE_MEASURE_UNIT =
1728 28 : poBand->GetMetadataItem("SPECTRAL_RANGE_MEASURE_UNIT");
1729 : const char *SPECTRAL_RANGE_FWHM_MIN =
1730 28 : poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MIN");
1731 : const char *SPECTRAL_RANGE_FWHM_MAX =
1732 28 : poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MAX");
1733 28 : if (SPECTRAL_RANGE_MEASURE_UNIT && SPECTRAL_RANGE_FWHM_MIN &&
1734 6 : SPECTRAL_RANGE_FWHM_MAX &&
1735 6 : (EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ||
1736 5 : EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "micrometer")))
1737 : {
1738 6 : const double dfFactorToMicrometer =
1739 6 : EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ? 1e-3 : 1.0;
1740 : const double dfMin =
1741 6 : CPLAtof(SPECTRAL_RANGE_FWHM_MIN) * dfFactorToMicrometer;
1742 : const double dfMax =
1743 6 : CPLAtof(SPECTRAL_RANGE_FWHM_MAX) * dfFactorToMicrometer;
1744 6 : poBand->SetMetadataItem("CENTRAL_WAVELENGTH_UM",
1745 6 : CPLSPrintf("%.3f", (dfMin + dfMax) / 2),
1746 6 : "IMAGERY");
1747 6 : poBand->SetMetadataItem(
1748 6 : "FWHM_UM", CPLSPrintf("%.3f", dfMax - dfMin), "IMAGERY");
1749 : }
1750 : }
1751 :
1752 : /* -------------------------------------------------------------------- */
1753 : /* Initialize any PAM information. */
1754 : /* -------------------------------------------------------------------- */
1755 8 : SetDescription(osMDFilename);
1756 8 : TryLoadXML();
1757 :
1758 : /* -------------------------------------------------------------------- */
1759 : /* Check for overviews. */
1760 : /* -------------------------------------------------------------------- */
1761 8 : oOvManager.Initialize(this, osMDFilename);
1762 :
1763 8 : return TRUE;
1764 : }
1765 :
1766 : /************************************************************************/
1767 : /* SetMetadataFromXML() */
1768 : /************************************************************************/
1769 :
1770 20 : void DIMAPDataset::SetMetadataFromXML(
1771 : CPLXMLNode *psProductIn, const char *const apszMetadataTranslation[],
1772 : bool bKeysFromRoot)
1773 : {
1774 20 : CPLXMLNode *psDoc = psProductIn;
1775 20 : if (bKeysFromRoot)
1776 : {
1777 16 : psDoc = CPLGetXMLNode(psProductIn, "=Dimap_Document");
1778 16 : if (psDoc == nullptr)
1779 : {
1780 0 : psDoc = CPLGetXMLNode(psProductIn, "=PHR_DIMAP_Document");
1781 : }
1782 : }
1783 :
1784 20 : bool bWarnedDiscarding = false;
1785 :
1786 122 : for (int iTrItem = 0; apszMetadataTranslation[iTrItem] != nullptr;
1787 102 : iTrItem += 2)
1788 : {
1789 : CPLXMLNode *psParent =
1790 102 : CPLGetXMLNode(psDoc, apszMetadataTranslation[iTrItem]);
1791 :
1792 102 : if (psParent == nullptr)
1793 16 : continue;
1794 :
1795 : // Logic to support directly access a name/value entry
1796 86 : if (psParent->psChild != nullptr &&
1797 86 : psParent->psChild->eType == CXT_Text)
1798 : {
1799 8 : CPLString osName = apszMetadataTranslation[iTrItem + 1];
1800 4 : osName += apszMetadataTranslation[iTrItem];
1801 : // Limit size to avoid perf issues when inserting
1802 : // in metadata list
1803 4 : if (osName.size() < 128)
1804 4 : SetMetadataItem(osName, psParent->psChild->pszValue);
1805 0 : else if (!bWarnedDiscarding)
1806 : {
1807 0 : bWarnedDiscarding = true;
1808 0 : CPLDebug("DIMAP", "Discarding too long metadata item");
1809 : }
1810 4 : continue;
1811 : }
1812 :
1813 : // Logic to support a parent element with many name/values.
1814 82 : CPLXMLNode *psTarget = psParent->psChild;
1815 525 : for (; psTarget != nullptr && psTarget != psParent;
1816 443 : psTarget = psTarget->psNext)
1817 : {
1818 443 : if (psTarget->eType == CXT_Element && psTarget->psChild != nullptr)
1819 : {
1820 874 : CPLString osName = apszMetadataTranslation[iTrItem + 1];
1821 :
1822 437 : if (psTarget->psChild->eType == CXT_Text)
1823 : {
1824 307 : osName += psTarget->pszValue;
1825 : // Limit size to avoid perf issues when inserting
1826 : // in metadata list
1827 307 : if (osName.size() < 128)
1828 307 : SetMetadataItem(osName, psTarget->psChild->pszValue);
1829 0 : else if (!bWarnedDiscarding)
1830 : {
1831 0 : bWarnedDiscarding = true;
1832 0 : CPLDebug("DIMAP", "Discarding too long metadata item");
1833 : }
1834 : }
1835 130 : else if (psTarget->psChild->eType == CXT_Attribute)
1836 : {
1837 : // find the tag value, at the end of the attributes.
1838 76 : for (CPLXMLNode *psNode = psTarget->psChild;
1839 219 : psNode != nullptr; psNode = psNode->psNext)
1840 : {
1841 143 : if (psNode->eType == CXT_Attribute)
1842 76 : continue;
1843 67 : else if (psNode->eType == CXT_Text)
1844 : {
1845 67 : osName += psTarget->pszValue;
1846 : // Limit size to avoid perf issues when inserting
1847 : // in metadata list
1848 67 : if (osName.size() < 128)
1849 67 : SetMetadataItem(osName, psNode->pszValue);
1850 0 : else if (!bWarnedDiscarding)
1851 : {
1852 0 : bWarnedDiscarding = true;
1853 0 : CPLDebug("DIMAP",
1854 : "Discarding too long metadata item");
1855 : }
1856 : }
1857 : }
1858 : }
1859 : }
1860 : }
1861 : }
1862 20 : }
1863 :
1864 : /************************************************************************/
1865 : /* GetGCPCount() */
1866 : /************************************************************************/
1867 :
1868 1 : int DIMAPDataset::GetGCPCount()
1869 :
1870 : {
1871 1 : return nGCPCount;
1872 : }
1873 :
1874 : /************************************************************************/
1875 : /* GetGCPSpatialRef() */
1876 : /************************************************************************/
1877 :
1878 1 : const OGRSpatialReference *DIMAPDataset::GetGCPSpatialRef() const
1879 :
1880 : {
1881 1 : return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
1882 : }
1883 :
1884 : /************************************************************************/
1885 : /* GetGCPs() */
1886 : /************************************************************************/
1887 :
1888 1 : const GDAL_GCP *DIMAPDataset::GetGCPs()
1889 :
1890 : {
1891 1 : return pasGCPList;
1892 : }
1893 :
1894 : /************************************************************************/
1895 : /* GDALRegister_DIMAP() */
1896 : /************************************************************************/
1897 :
1898 2024 : void GDALRegister_DIMAP()
1899 :
1900 : {
1901 2024 : if (GDALGetDriverByName("DIMAP") != nullptr)
1902 283 : return;
1903 :
1904 1741 : GDALDriver *poDriver = new GDALDriver();
1905 :
1906 1741 : poDriver->SetDescription("DIMAP");
1907 1741 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1908 1741 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SPOT DIMAP");
1909 1741 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/dimap.html");
1910 1741 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1911 1741 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1912 :
1913 1741 : poDriver->pfnOpen = DIMAPDataset::Open;
1914 1741 : poDriver->pfnIdentify = DIMAPDataset::Identify;
1915 :
1916 1741 : GetGDALDriverManager()->RegisterDriver(poDriver);
1917 : }
|