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