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