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