Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Polarimetric Workstation
4 : * Purpose: Radarsat 2 - XML Products (product.xml) driver
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10 : * Copyright (c) 2020, Defence Research and Development Canada (DRDC) Ottawa Research Centre
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_minixml.h"
16 : #include "gdal_frmts.h"
17 : #include "gdal_pam.h"
18 : #include "gdal_driver.h"
19 : #include "gdal_drivermanager.h"
20 : #include "gdal_openinfo.h"
21 : #include "gdal_cpp_functions.h"
22 : #include "ogr_spatialref.h"
23 :
24 : typedef enum eCalibration_t
25 : {
26 : Sigma0 = 0,
27 : Gamma,
28 : Beta0,
29 : Uncalib,
30 : None
31 : } eCalibration;
32 :
33 : /*** Function to test for valid LUT files ***/
34 15 : static bool IsValidXMLFile(const char *pszPath, const char *pszLut)
35 : {
36 15 : if (CPLHasPathTraversal(pszLut))
37 : {
38 0 : CPLError(CE_Failure, CPLE_NotSupported, "Path traversal detected in %s",
39 : pszLut);
40 0 : return false;
41 : }
42 : /* Return true for valid xml file, false otherwise */
43 : char *pszLutFile =
44 15 : VSIStrdup(CPLFormFilenameSafe(pszPath, pszLut, nullptr).c_str());
45 :
46 15 : CPLXMLTreeCloser psLut(CPLParseXMLFile(pszLutFile));
47 :
48 15 : CPLFree(pszLutFile);
49 :
50 15 : return psLut.get() != nullptr;
51 : }
52 :
53 : // Check that the referenced dataset for each band has the
54 : // correct data type and returns whether a 2 band I+Q dataset should
55 : // be mapped onto a single complex band.
56 : // Returns BANDERROR for error, STRAIGHT for 1:1 mapping, TWOBANDCOMPLEX for 2
57 : // bands -> 1 complex band
58 : typedef enum
59 : {
60 : BANDERROR,
61 : STRAIGHT,
62 : TWOBANDCOMPLEX
63 : } BandMapping;
64 :
65 10 : static BandMapping GetBandFileMapping(GDALDataType eDataType,
66 : GDALDataset *poBandFile)
67 : {
68 :
69 10 : GDALRasterBand *poBand1 = poBandFile->GetRasterBand(1);
70 10 : GDALDataType eBandDataType1 = poBand1->GetRasterDataType();
71 :
72 : // if there is one band and it has the same datatype, the band file gets
73 : // passed straight through
74 10 : if (poBandFile->GetRasterCount() == 1 && eDataType == eBandDataType1)
75 10 : return STRAIGHT;
76 :
77 : // if the band file has 2 bands, they should represent I+Q
78 : // and be a compatible data type
79 0 : if (poBandFile->GetRasterCount() == 2 && GDALDataTypeIsComplex(eDataType))
80 : {
81 0 : GDALRasterBand *band2 = poBandFile->GetRasterBand(2);
82 :
83 0 : if (eBandDataType1 != band2->GetRasterDataType())
84 0 : return BANDERROR; // both bands must be same datatype
85 :
86 : // check compatible types - there are 4 complex types in GDAL
87 0 : if ((eDataType == GDT_CInt16 && eBandDataType1 == GDT_Int16) ||
88 0 : (eDataType == GDT_CInt32 && eBandDataType1 == GDT_Int32) ||
89 0 : (eDataType == GDT_CFloat32 && eBandDataType1 == GDT_Float32) ||
90 0 : (eDataType == GDT_CFloat64 && eBandDataType1 == GDT_Float64))
91 0 : return TWOBANDCOMPLEX;
92 : }
93 0 : return BANDERROR; // don't accept any other combinations
94 : }
95 :
96 : /************************************************************************/
97 : /* ==================================================================== */
98 : /* RS2Dataset */
99 : /* ==================================================================== */
100 : /************************************************************************/
101 :
102 : class RS2Dataset final : public GDALPamDataset
103 : {
104 : CPLXMLNode *psProduct;
105 :
106 : int nGCPCount;
107 : GDAL_GCP *pasGCPList;
108 : OGRSpatialReference m_oSRS{};
109 : OGRSpatialReference m_oGCPSRS{};
110 : char **papszSubDatasets;
111 : GDALGeoTransform m_gt{};
112 : bool bHaveGeoTransform;
113 :
114 : char **papszExtraFiles;
115 :
116 : CPL_DISALLOW_COPY_ASSIGN(RS2Dataset)
117 :
118 : protected:
119 : int CloseDependentDatasets() override;
120 :
121 : public:
122 : RS2Dataset();
123 : ~RS2Dataset() override;
124 :
125 : int GetGCPCount() override;
126 : const OGRSpatialReference *GetGCPSpatialRef() const override;
127 : const GDAL_GCP *GetGCPs() override;
128 :
129 : const OGRSpatialReference *GetSpatialRef() const override;
130 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
131 :
132 : char **GetMetadataDomainList() override;
133 : char **GetMetadata(const char *pszDomain = "") override;
134 : char **GetFileList(void) override;
135 :
136 : static GDALDataset *Open(GDALOpenInfo *);
137 : static int Identify(GDALOpenInfo *);
138 :
139 : CPLXMLNode *GetProduct()
140 : {
141 : return psProduct;
142 : }
143 : };
144 :
145 : /************************************************************************/
146 : /* ==================================================================== */
147 : /* RS2RasterBand */
148 : /* ==================================================================== */
149 : /************************************************************************/
150 :
151 : class RS2RasterBand final : public GDALPamRasterBand
152 : {
153 : GDALDataset *poBandFile = nullptr;
154 :
155 : // 2 bands representing I+Q -> one complex band
156 : // otherwise poBandFile is passed straight through
157 : bool bIsTwoBandComplex = false;
158 :
159 : CPL_DISALLOW_COPY_ASSIGN(RS2RasterBand)
160 :
161 : public:
162 : RS2RasterBand(RS2Dataset *poDSIn, GDALDataType eDataTypeIn,
163 : const char *pszPole, GDALDataset *poBandFile,
164 : bool bTwoBandComplex = false);
165 : ~RS2RasterBand() override;
166 :
167 : CPLErr IReadBlock(int, int, void *) override;
168 :
169 : static GDALDataset *Open(GDALOpenInfo *);
170 : };
171 :
172 : /************************************************************************/
173 : /* RS2RasterBand */
174 : /************************************************************************/
175 :
176 8 : RS2RasterBand::RS2RasterBand(RS2Dataset *poDSIn, GDALDataType eDataTypeIn,
177 : const char *pszPole, GDALDataset *poBandFileIn,
178 8 : bool bTwoBandComplex)
179 8 : : poBandFile(poBandFileIn)
180 : {
181 8 : poDS = poDSIn;
182 :
183 8 : GDALRasterBand *poSrcBand = poBandFile->GetRasterBand(1);
184 :
185 8 : poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
186 :
187 8 : eDataType = eDataTypeIn;
188 :
189 8 : bIsTwoBandComplex = bTwoBandComplex;
190 :
191 8 : if (*pszPole != '\0')
192 8 : SetMetadataItem("POLARIMETRIC_INTERP", pszPole);
193 8 : }
194 :
195 : /************************************************************************/
196 : /* RSRasterBand() */
197 : /************************************************************************/
198 :
199 16 : RS2RasterBand::~RS2RasterBand()
200 :
201 : {
202 8 : if (poBandFile != nullptr)
203 8 : GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile));
204 16 : }
205 :
206 : /************************************************************************/
207 : /* IReadBlock() */
208 : /************************************************************************/
209 :
210 40 : CPLErr RS2RasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
211 :
212 : {
213 : /* -------------------------------------------------------------------- */
214 : /* If the last strip is partial, we need to avoid */
215 : /* over-requesting. We also need to initialize the extra part */
216 : /* of the block to zero. */
217 : /* -------------------------------------------------------------------- */
218 : int nRequestYSize;
219 40 : if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
220 : {
221 0 : nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
222 0 : memset(pImage, 0,
223 0 : static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
224 0 : nBlockXSize * nBlockYSize);
225 : }
226 : else
227 : {
228 40 : nRequestYSize = nBlockYSize;
229 : }
230 :
231 : /*-------------------------------------------------------------------- */
232 : /* If the input imagery is tiled, also need to avoid over- */
233 : /* requesting in the X-direction. */
234 : /* ------------------------------------------------------------------- */
235 : int nRequestXSize;
236 40 : if ((nBlockXOff + 1) * nBlockXSize > nRasterXSize)
237 : {
238 0 : nRequestXSize = nRasterXSize - nBlockXOff * nBlockXSize;
239 0 : memset(pImage, 0,
240 0 : static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
241 0 : nBlockXSize * nBlockYSize);
242 : }
243 : else
244 : {
245 40 : nRequestXSize = nBlockXSize;
246 : }
247 40 : if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 2)
248 0 : return poBandFile->RasterIO(
249 0 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
250 : nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
251 0 : GDT_Int16, 2, nullptr, 4, nBlockXSize * 4, 2, nullptr);
252 :
253 : /* -------------------------------------------------------------------- */
254 : /* File has one sample marked as sample format void, a 32bits. */
255 : /* -------------------------------------------------------------------- */
256 40 : else if (eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 1)
257 : {
258 0 : CPLErr eErr = poBandFile->RasterIO(
259 0 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
260 : nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
261 0 : GDT_UInt32, 1, nullptr, 4, nBlockXSize * 4, 0, nullptr);
262 :
263 : #ifdef CPL_LSB
264 : /* First, undo the 32bit swap. */
265 0 : GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
266 :
267 : /* Then apply 16 bit swap. */
268 0 : GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
269 : #endif
270 :
271 0 : return eErr;
272 : }
273 :
274 : /* -------------------------------------------------------------------- */
275 : /* The 16bit case is straight forward. The underlying file */
276 : /* looks like a 16bit unsigned data too. */
277 : /* -------------------------------------------------------------------- */
278 40 : else if (eDataType == GDT_UInt16)
279 0 : return poBandFile->RasterIO(
280 0 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
281 : nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
282 0 : GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, nullptr);
283 40 : else if (eDataType == GDT_Byte)
284 : /* Ticket #2104: Support for ScanSAR products */
285 80 : return poBandFile->RasterIO(
286 40 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
287 : nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize,
288 40 : GDT_Byte, 1, nullptr, 1, nBlockXSize, 0, nullptr);
289 :
290 0 : CPLAssert(false);
291 : return CE_Failure;
292 : }
293 :
294 : /************************************************************************/
295 : /* ==================================================================== */
296 : /* RS2CalibRasterBand */
297 : /* ==================================================================== */
298 : /************************************************************************/
299 : /* Returns data that has been calibrated to sigma nought, gamma */
300 : /* or beta nought. */
301 : /************************************************************************/
302 :
303 : class RS2CalibRasterBand final : public GDALPamRasterBand
304 : {
305 : private:
306 : // eCalibration m_eCalib;
307 : GDALDataset *m_poBandDataset;
308 : GDALDataType m_eType; /* data type of data being ingested */
309 : float *m_nfTable;
310 : int m_nTableSize;
311 : float m_nfOffset;
312 : char *m_pszLUTFile;
313 :
314 : CPL_DISALLOW_COPY_ASSIGN(RS2CalibRasterBand)
315 :
316 : void ReadLUT();
317 :
318 : public:
319 : RS2CalibRasterBand(RS2Dataset *poDataset, const char *pszPolarization,
320 : GDALDataType eType, GDALDataset *poBandDataset,
321 : eCalibration eCalib, const char *pszLUT);
322 : ~RS2CalibRasterBand() override;
323 :
324 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
325 : };
326 :
327 : /************************************************************************/
328 : /* ReadLUT() */
329 : /************************************************************************/
330 : /* Read the provided LUT in to m_ndTable */
331 : /************************************************************************/
332 2 : void RS2CalibRasterBand::ReadLUT()
333 : {
334 2 : CPLXMLNode *psLUT = CPLParseXMLFile(m_pszLUTFile);
335 :
336 2 : this->m_nfOffset = static_cast<float>(
337 2 : CPLAtof(CPLGetXMLValue(psLUT, "=lut.offset", "0.0")));
338 :
339 2 : char **papszLUTList = CSLTokenizeString2(
340 : CPLGetXMLValue(psLUT, "=lut.gains", ""), " ", CSLT_HONOURSTRINGS);
341 :
342 2 : m_nTableSize = CSLCount(papszLUTList);
343 :
344 2 : m_nfTable = static_cast<float *>(CPLMalloc(sizeof(float) * m_nTableSize));
345 :
346 514 : for (int i = 0; i < m_nTableSize; i++)
347 : {
348 512 : m_nfTable[i] = static_cast<float>(CPLAtof(papszLUTList[i]));
349 : }
350 :
351 2 : CPLDestroyXMLNode(psLUT);
352 :
353 2 : CSLDestroy(papszLUTList);
354 2 : }
355 :
356 : /************************************************************************/
357 : /* RS2CalibRasterBand() */
358 : /************************************************************************/
359 :
360 2 : RS2CalibRasterBand::RS2CalibRasterBand(
361 : RS2Dataset *poDataset, const char *pszPolarization, GDALDataType eType,
362 2 : GDALDataset *poBandDataset, eCalibration /* eCalib */, const char *pszLUT)
363 : : // m_eCalib(eCalib),
364 : m_poBandDataset(poBandDataset), m_eType(eType), m_nfTable(nullptr),
365 2 : m_nTableSize(0), m_nfOffset(0), m_pszLUTFile(VSIStrdup(pszLUT))
366 : {
367 2 : poDS = poDataset;
368 :
369 2 : if (*pszPolarization != '\0')
370 : {
371 2 : SetMetadataItem("POLARIMETRIC_INTERP", pszPolarization);
372 : }
373 :
374 2 : if (eType == GDT_CInt16)
375 0 : eDataType = GDT_CFloat32;
376 : else
377 2 : eDataType = GDT_Float32;
378 :
379 2 : GDALRasterBand *poRasterBand = poBandDataset->GetRasterBand(1);
380 2 : poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
381 :
382 2 : ReadLUT();
383 2 : }
384 :
385 : /************************************************************************/
386 : /* ~RS2CalibRasterBand() */
387 : /************************************************************************/
388 :
389 4 : RS2CalibRasterBand::~RS2CalibRasterBand()
390 : {
391 2 : CPLFree(m_nfTable);
392 2 : CPLFree(m_pszLUTFile);
393 2 : GDALClose(m_poBandDataset);
394 4 : }
395 :
396 : /************************************************************************/
397 : /* IReadBlock() */
398 : /************************************************************************/
399 :
400 20 : CPLErr RS2CalibRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
401 : void *pImage)
402 : {
403 : /* -------------------------------------------------------------------- */
404 : /* If the last strip is partial, we need to avoid */
405 : /* over-requesting. We also need to initialize the extra part */
406 : /* of the block to zero. */
407 : /* -------------------------------------------------------------------- */
408 : int nRequestYSize;
409 20 : if ((nBlockYOff + 1) * nBlockYSize > nRasterYSize)
410 : {
411 0 : nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
412 0 : memset(pImage, 0,
413 0 : static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) *
414 0 : nBlockXSize * nBlockYSize);
415 : }
416 : else
417 : {
418 20 : nRequestYSize = nBlockYSize;
419 : }
420 :
421 : CPLErr eErr;
422 20 : if (m_eType == GDT_CInt16)
423 : {
424 : /* read in complex values */
425 : GInt16 *pnImageTmp = reinterpret_cast<GInt16 *>(
426 0 : CPLMalloc(2 * sizeof(int16_t) * nBlockXSize * nBlockYSize));
427 0 : if (m_poBandDataset->GetRasterCount() == 2)
428 : {
429 0 : eErr = m_poBandDataset->RasterIO(
430 0 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
431 : nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize,
432 0 : nRequestYSize, GDT_Int16, 2, nullptr, 4, nBlockXSize * 4, 2,
433 : nullptr);
434 : }
435 : else
436 : {
437 0 : eErr = m_poBandDataset->RasterIO(
438 0 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
439 : nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize,
440 0 : nRequestYSize, GDT_UInt32, 1, nullptr, 4, nBlockXSize * 4, 0,
441 : nullptr);
442 :
443 : #ifdef CPL_LSB
444 : /* First, undo the 32bit swap. */
445 0 : GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
446 :
447 : /* Then apply 16 bit swap. */
448 0 : GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2);
449 : #endif
450 : }
451 :
452 : /* calibrate the complex values */
453 0 : for (int i = 0; i < nBlockYSize; i++)
454 : {
455 0 : for (int j = 0; j < nBlockXSize; j++)
456 : {
457 : /* calculate pixel offset in memory*/
458 0 : int nPixOff = (2 * (i * nBlockXSize)) + (j * 2);
459 :
460 0 : reinterpret_cast<float *>(pImage)[nPixOff] =
461 0 : static_cast<float>(pnImageTmp[nPixOff]) /
462 0 : (m_nfTable[nBlockXOff + j]);
463 0 : reinterpret_cast<float *>(pImage)[nPixOff + 1] =
464 0 : static_cast<float>(pnImageTmp[nPixOff + 1]) /
465 0 : (m_nfTable[nBlockXOff + j]);
466 : }
467 : }
468 0 : CPLFree(pnImageTmp);
469 : }
470 : // If the underlying file is NITF CFloat32
471 20 : else if (eDataType == GDT_CFloat32 &&
472 0 : m_poBandDataset->GetRasterCount() == 1)
473 : {
474 0 : eErr = m_poBandDataset->RasterIO(
475 0 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
476 : nBlockXSize, nRequestYSize, pImage, nBlockXSize, nRequestYSize,
477 : GDT_CFloat32, 1, nullptr, 2 * static_cast<int>(sizeof(float)),
478 0 : nBlockXSize * 2 * static_cast<GSpacing>(sizeof(float)), 0, nullptr);
479 :
480 : /* calibrate the complex values */
481 0 : for (int i = 0; i < nBlockYSize; i++)
482 : {
483 0 : for (int j = 0; j < nBlockXSize; j++)
484 : {
485 : /* calculate pixel offset in memory*/
486 0 : const int nPixOff = 2 * (i * nBlockXSize + j);
487 :
488 0 : reinterpret_cast<float *>(pImage)[nPixOff] /=
489 0 : m_nfTable[nBlockXOff * nBlockXSize + j];
490 0 : reinterpret_cast<float *>(pImage)[nPixOff + 1] /=
491 0 : m_nfTable[nBlockXOff * nBlockXSize + j];
492 : }
493 : }
494 : }
495 20 : else if (m_eType == GDT_UInt16)
496 : {
497 : /* read in detected values */
498 : GUInt16 *pnImageTmp = reinterpret_cast<GUInt16 *>(
499 0 : CPLMalloc(sizeof(uint16_t) * nBlockXSize * nBlockYSize));
500 0 : eErr = m_poBandDataset->RasterIO(
501 0 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
502 : nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize, nRequestYSize,
503 0 : GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, nullptr);
504 :
505 : /* iterate over detected values */
506 0 : for (int i = 0; i < nBlockYSize; i++)
507 : {
508 0 : for (int j = 0; j < nBlockXSize; j++)
509 : {
510 0 : int nPixOff = (i * nBlockXSize) + j;
511 :
512 0 : reinterpret_cast<float *>(pImage)[nPixOff] =
513 0 : ((static_cast<float>(pnImageTmp[nPixOff]) *
514 0 : static_cast<float>(pnImageTmp[nPixOff])) +
515 0 : m_nfOffset) /
516 0 : m_nfTable[nBlockXOff + j];
517 : }
518 : }
519 0 : CPLFree(pnImageTmp);
520 : } /* Ticket #2104: Support for ScanSAR products */
521 20 : else if (m_eType == GDT_Byte)
522 : {
523 : GByte *pnImageTmp = reinterpret_cast<GByte *>(
524 20 : CPLMalloc(static_cast<size_t>(nBlockXSize) * nBlockYSize));
525 40 : eErr = m_poBandDataset->RasterIO(
526 20 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize,
527 : nBlockXSize, nRequestYSize, pnImageTmp, nBlockXSize, nRequestYSize,
528 : GDT_Byte, 1, nullptr, 1, 1, 0, nullptr);
529 :
530 : /* iterate over detected values */
531 40 : for (int i = 0; i < nBlockYSize; i++)
532 : {
533 420 : for (int j = 0; j < nBlockXSize; j++)
534 : {
535 400 : int nPixOff = (i * nBlockXSize) + j;
536 :
537 400 : reinterpret_cast<float *>(pImage)[nPixOff] =
538 400 : ((pnImageTmp[nPixOff] * pnImageTmp[nPixOff]) + m_nfOffset) /
539 400 : m_nfTable[nBlockXOff + j];
540 : }
541 : }
542 20 : CPLFree(pnImageTmp);
543 : }
544 : else
545 : {
546 0 : CPLAssert(false);
547 : return CE_Failure;
548 : }
549 20 : return eErr;
550 : }
551 :
552 : /************************************************************************/
553 : /* ==================================================================== */
554 : /* RS2Dataset */
555 : /* ==================================================================== */
556 : /************************************************************************/
557 :
558 : /************************************************************************/
559 : /* RS2Dataset() */
560 : /************************************************************************/
561 :
562 5 : RS2Dataset::RS2Dataset()
563 : : psProduct(nullptr), nGCPCount(0), pasGCPList(nullptr),
564 : papszSubDatasets(nullptr), bHaveGeoTransform(FALSE),
565 5 : papszExtraFiles(nullptr)
566 : {
567 5 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
568 5 : m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
569 5 : }
570 :
571 : /************************************************************************/
572 : /* ~RS2Dataset() */
573 : /************************************************************************/
574 :
575 10 : RS2Dataset::~RS2Dataset()
576 :
577 : {
578 5 : RS2Dataset::FlushCache(true);
579 :
580 5 : CPLDestroyXMLNode(psProduct);
581 :
582 5 : if (nGCPCount > 0)
583 : {
584 5 : GDALDeinitGCPs(nGCPCount, pasGCPList);
585 5 : CPLFree(pasGCPList);
586 : }
587 :
588 5 : RS2Dataset::CloseDependentDatasets();
589 :
590 5 : CSLDestroy(papszSubDatasets);
591 5 : CSLDestroy(papszExtraFiles);
592 10 : }
593 :
594 : /************************************************************************/
595 : /* CloseDependentDatasets() */
596 : /************************************************************************/
597 :
598 13 : int RS2Dataset::CloseDependentDatasets()
599 : {
600 13 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
601 :
602 13 : if (nBands != 0)
603 5 : bHasDroppedRef = TRUE;
604 :
605 23 : for (int iBand = 0; iBand < nBands; iBand++)
606 : {
607 10 : delete papoBands[iBand];
608 : }
609 13 : nBands = 0;
610 :
611 13 : return bHasDroppedRef;
612 : }
613 :
614 : /************************************************************************/
615 : /* GetFileList() */
616 : /************************************************************************/
617 :
618 2 : char **RS2Dataset::GetFileList()
619 :
620 : {
621 2 : char **papszFileList = GDALPamDataset::GetFileList();
622 :
623 2 : papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
624 :
625 2 : return papszFileList;
626 : }
627 :
628 : /************************************************************************/
629 : /* Identify() */
630 : /************************************************************************/
631 :
632 63504 : int RS2Dataset::Identify(GDALOpenInfo *poOpenInfo)
633 : {
634 : /* Check for the case where we're trying to read the calibrated data: */
635 63504 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "RADARSAT_2_CALIB:"))
636 : {
637 2 : return TRUE;
638 : }
639 :
640 : /* Check for directory access when there is a product.xml file in the
641 : directory. */
642 63502 : if (poOpenInfo->bIsDirectory)
643 : {
644 0 : const CPLString osMDFilename = CPLFormCIFilenameSafe(
645 1234 : poOpenInfo->pszFilename, "product.xml", nullptr);
646 :
647 1234 : GDALOpenInfo oOpenInfo(osMDFilename.c_str(), GA_ReadOnly);
648 617 : return Identify(&oOpenInfo);
649 : }
650 :
651 : /* otherwise, do our normal stuff */
652 62885 : if (strlen(poOpenInfo->pszFilename) < 11 ||
653 60964 : !EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 11,
654 : "product.xml"))
655 62253 : return FALSE;
656 :
657 632 : if (poOpenInfo->nHeaderBytes < 100)
658 622 : return FALSE;
659 :
660 10 : if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
661 8 : "/rs2") == nullptr ||
662 8 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
663 : "<product") == nullptr)
664 2 : return FALSE;
665 :
666 8 : return TRUE;
667 : }
668 :
669 : /************************************************************************/
670 : /* Open() */
671 : /************************************************************************/
672 :
673 5 : GDALDataset *RS2Dataset::Open(GDALOpenInfo *poOpenInfo)
674 :
675 : {
676 : /* -------------------------------------------------------------------- */
677 : /* Is this a RADARSAT-2 Product.xml definition? */
678 : /* -------------------------------------------------------------------- */
679 5 : if (!RS2Dataset::Identify(poOpenInfo))
680 : {
681 0 : return nullptr;
682 : }
683 :
684 : /* -------------------------------------------------------------------- */
685 : /* Get subdataset information, if relevant */
686 : /* -------------------------------------------------------------------- */
687 5 : const char *pszFilename = poOpenInfo->pszFilename;
688 5 : eCalibration eCalib = None;
689 :
690 5 : if (STARTS_WITH_CI(pszFilename, "RADARSAT_2_CALIB:"))
691 : {
692 1 : pszFilename += 17;
693 :
694 1 : if (STARTS_WITH_CI(pszFilename, "BETA0"))
695 1 : eCalib = Beta0;
696 0 : else if (STARTS_WITH_CI(pszFilename, "SIGMA0"))
697 0 : eCalib = Sigma0;
698 0 : else if (STARTS_WITH_CI(pszFilename, "GAMMA"))
699 0 : eCalib = Gamma;
700 0 : else if (STARTS_WITH_CI(pszFilename, "UNCALIB"))
701 0 : eCalib = Uncalib;
702 : else
703 0 : eCalib = None;
704 :
705 : /* advance the pointer to the actual filename */
706 6 : while (*pszFilename != '\0' && *pszFilename != ':')
707 5 : pszFilename++;
708 :
709 1 : if (*pszFilename == ':')
710 1 : pszFilename++;
711 :
712 : // need to redo the directory check:
713 : // the GDALOpenInfo check would have failed because of the calibration
714 : // string on the filename
715 : VSIStatBufL sStat;
716 1 : if (VSIStatL(pszFilename, &sStat) == 0)
717 1 : poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode);
718 : }
719 :
720 10 : CPLString osMDFilename;
721 5 : if (poOpenInfo->bIsDirectory)
722 : {
723 : osMDFilename =
724 0 : CPLFormCIFilenameSafe(pszFilename, "product.xml", nullptr);
725 : }
726 : else
727 5 : osMDFilename = pszFilename;
728 :
729 : /* -------------------------------------------------------------------- */
730 : /* Ingest the Product.xml file. */
731 : /* -------------------------------------------------------------------- */
732 5 : CPLXMLNode *psProduct = CPLParseXMLFile(osMDFilename);
733 5 : if (psProduct == nullptr)
734 0 : return nullptr;
735 :
736 : /* -------------------------------------------------------------------- */
737 : /* Confirm the requested access is supported. */
738 : /* -------------------------------------------------------------------- */
739 5 : if (poOpenInfo->eAccess == GA_Update)
740 : {
741 0 : CPLDestroyXMLNode(psProduct);
742 0 : ReportUpdateNotSupportedByDriver("RS2");
743 0 : return nullptr;
744 : }
745 :
746 : CPLXMLNode *psImageAttributes =
747 5 : CPLGetXMLNode(psProduct, "=product.imageAttributes");
748 5 : if (psImageAttributes == nullptr)
749 : {
750 0 : CPLDestroyXMLNode(psProduct);
751 0 : CPLError(CE_Failure, CPLE_OpenFailed,
752 : "Failed to find <imageAttributes> in document.");
753 0 : return nullptr;
754 : }
755 :
756 : CPLXMLNode *psImageGenerationParameters =
757 5 : CPLGetXMLNode(psProduct, "=product.imageGenerationParameters");
758 5 : if (psImageGenerationParameters == nullptr)
759 : {
760 0 : CPLDestroyXMLNode(psProduct);
761 0 : CPLError(CE_Failure, CPLE_OpenFailed,
762 : "Failed to find <imageGenerationParameters> in document.");
763 0 : return nullptr;
764 : }
765 :
766 : /* -------------------------------------------------------------------- */
767 : /* Create the dataset. */
768 : /* -------------------------------------------------------------------- */
769 10 : auto poDS = std::make_unique<RS2Dataset>();
770 :
771 5 : poDS->psProduct = psProduct;
772 :
773 : /* -------------------------------------------------------------------- */
774 : /* Get overall image information. */
775 : /* -------------------------------------------------------------------- */
776 5 : poDS->nRasterXSize = atoi(CPLGetXMLValue(
777 : psImageAttributes, "rasterAttributes.numberOfSamplesPerLine", "-1"));
778 5 : poDS->nRasterYSize = atoi(CPLGetXMLValue(
779 : psImageAttributes, "rasterAttributes.numberofLines", "-1"));
780 5 : if (poDS->nRasterXSize <= 1 || poDS->nRasterYSize <= 1)
781 : {
782 0 : CPLError(
783 : CE_Failure, CPLE_OpenFailed,
784 : "Non-sane raster dimensions provided in product.xml. If this is "
785 : "a valid RADARSAT-2 scene, please contact your data provider for "
786 : "a corrected dataset.");
787 0 : return nullptr;
788 : }
789 :
790 : /* -------------------------------------------------------------------- */
791 : /* Check product type, as to determine if there are LUTs for */
792 : /* calibration purposes. */
793 : /* -------------------------------------------------------------------- */
794 :
795 : const char *pszProductType =
796 5 : CPLGetXMLValue(psImageGenerationParameters,
797 : "generalProcessingInformation.productType", "UNK");
798 :
799 5 : poDS->SetMetadataItem("PRODUCT_TYPE", pszProductType);
800 :
801 : /* the following cases can be assumed to have no LUTs, as per
802 : * RN-RP-51-2713, but also common sense
803 : */
804 5 : bool bCanCalib = false;
805 5 : if (!(STARTS_WITH_CI(pszProductType, "UNK") ||
806 5 : STARTS_WITH_CI(pszProductType, "SSG") ||
807 5 : STARTS_WITH_CI(pszProductType, "SPG")))
808 : {
809 5 : bCanCalib = true;
810 : }
811 :
812 : /* -------------------------------------------------------------------- */
813 : /* Get dataType (so we can recognise complex data), and the */
814 : /* bitsPerSample. */
815 : /* -------------------------------------------------------------------- */
816 : const char *pszDataType =
817 5 : CPLGetXMLValue(psImageAttributes, "rasterAttributes.dataType", "");
818 5 : const int nBitsPerSample = atoi(CPLGetXMLValue(
819 : psImageAttributes, "rasterAttributes.bitsPerSample", ""));
820 :
821 : GDALDataType eDataType;
822 5 : if (nBitsPerSample == 16 && EQUAL(pszDataType, "Complex"))
823 0 : eDataType = GDT_CInt16;
824 5 : else if (nBitsPerSample == 32 &&
825 0 : EQUAL(pszDataType,
826 : "Complex")) // NITF datasets can come in this configuration
827 0 : eDataType = GDT_CFloat32;
828 5 : else if (nBitsPerSample == 16 && STARTS_WITH_CI(pszDataType, "Mag"))
829 0 : eDataType = GDT_UInt16;
830 5 : else if (nBitsPerSample == 8 && STARTS_WITH_CI(pszDataType, "Mag"))
831 5 : eDataType = GDT_Byte;
832 : else
833 : {
834 0 : CPLError(
835 : CE_Failure, CPLE_AppDefined,
836 : "dataType=%s, bitsPerSample=%d: not a supported configuration.",
837 : pszDataType, nBitsPerSample);
838 0 : return nullptr;
839 : }
840 :
841 : /* while we're at it, extract the pixel spacing information */
842 5 : const char *pszPixelSpacing = CPLGetXMLValue(
843 : psImageAttributes, "rasterAttributes.sampledPixelSpacing", "UNK");
844 5 : poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing);
845 :
846 5 : const char *pszLineSpacing = CPLGetXMLValue(
847 : psImageAttributes, "rasterAttributes.sampledLineSpacing", "UNK");
848 5 : poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing);
849 :
850 : /* -------------------------------------------------------------------- */
851 : /* Open each of the data files as a complex band. */
852 : /* -------------------------------------------------------------------- */
853 10 : CPLString osBeta0LUT;
854 10 : CPLString osGammaLUT;
855 10 : CPLString osSigma0LUT;
856 :
857 10 : std::string osPath = CPLGetPathSafe(osMDFilename);
858 : ;
859 5 : const int nFLen = static_cast<int>(osMDFilename.size());
860 :
861 5 : CPLXMLNode *psNode = psImageAttributes->psChild;
862 40 : for (; psNode != nullptr; psNode = psNode->psNext)
863 : {
864 35 : if (psNode->eType != CXT_Element ||
865 35 : !(EQUAL(psNode->pszValue, "fullResolutionImageData") ||
866 25 : EQUAL(psNode->pszValue, "lookupTable")))
867 25 : continue;
868 :
869 25 : if (EQUAL(psNode->pszValue, "lookupTable") && bCanCalib)
870 : {
871 : /* Determine which incidence angle correction this is */
872 : const char *pszLUTType =
873 15 : CPLGetXMLValue(psNode, "incidenceAngleCorrection", "");
874 15 : const char *pszLUTFile = CPLGetXMLValue(psNode, "", "");
875 15 : if (CPLHasPathTraversal(pszLUTFile))
876 : {
877 0 : CPLError(CE_Failure, CPLE_NotSupported,
878 : "Path traversal detected in %s", pszLUTFile);
879 0 : return nullptr;
880 : }
881 : CPLString osLUTFilePath =
882 30 : CPLFormFilenameSafe(osPath.c_str(), pszLUTFile, nullptr);
883 :
884 15 : if (EQUAL(pszLUTType, ""))
885 0 : continue;
886 20 : else if (EQUAL(pszLUTType, "Beta Nought") &&
887 5 : IsValidXMLFile(osPath.c_str(), pszLUTFile))
888 : {
889 10 : poDS->papszExtraFiles =
890 5 : CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
891 :
892 5 : const size_t nBufLen = nFLen + 27;
893 5 : char *pszBuf = static_cast<char *>(CPLMalloc(nBufLen));
894 5 : osBeta0LUT = pszLUTFile;
895 5 : poDS->SetMetadataItem("BETA_NOUGHT_LUT", pszLUTFile);
896 :
897 5 : snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:BETA0:%s",
898 : osMDFilename.c_str());
899 5 : poDS->papszSubDatasets = CSLSetNameValue(
900 5 : poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf);
901 10 : poDS->papszSubDatasets =
902 5 : CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_3_DESC",
903 : "Beta Nought calibrated");
904 5 : CPLFree(pszBuf);
905 : }
906 15 : else if (EQUAL(pszLUTType, "Sigma Nought") &&
907 5 : IsValidXMLFile(osPath.c_str(), pszLUTFile))
908 : {
909 10 : poDS->papszExtraFiles =
910 5 : CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
911 :
912 5 : const size_t nBufLen = nFLen + 27;
913 5 : char *pszBuf = static_cast<char *>(CPLMalloc(nBufLen));
914 5 : osSigma0LUT = pszLUTFile;
915 5 : poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", pszLUTFile);
916 :
917 5 : snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:SIGMA0:%s",
918 : osMDFilename.c_str());
919 5 : poDS->papszSubDatasets = CSLSetNameValue(
920 5 : poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf);
921 10 : poDS->papszSubDatasets =
922 5 : CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_2_DESC",
923 : "Sigma Nought calibrated");
924 5 : CPLFree(pszBuf);
925 : }
926 10 : else if (EQUAL(pszLUTType, "Gamma") &&
927 5 : IsValidXMLFile(osPath.c_str(), pszLUTFile))
928 : {
929 10 : poDS->papszExtraFiles =
930 5 : CSLAddString(poDS->papszExtraFiles, osLUTFilePath);
931 :
932 5 : const size_t nBufLen = nFLen + 27;
933 5 : char *pszBuf = static_cast<char *>(CPLMalloc(nBufLen));
934 5 : osGammaLUT = pszLUTFile;
935 5 : poDS->SetMetadataItem("GAMMA_LUT", pszLUTFile);
936 5 : snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:GAMMA:%s",
937 : osMDFilename.c_str());
938 5 : poDS->papszSubDatasets = CSLSetNameValue(
939 5 : poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf);
940 10 : poDS->papszSubDatasets =
941 5 : CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_4_DESC",
942 : "Gamma calibrated");
943 5 : CPLFree(pszBuf);
944 : }
945 15 : continue;
946 : }
947 :
948 : /* --------------------------------------------------------------------
949 : */
950 : /* Fetch filename. */
951 : /* --------------------------------------------------------------------
952 : */
953 10 : const char *pszBasename = CPLGetXMLValue(psNode, "", "");
954 10 : if (*pszBasename == '\0')
955 0 : continue;
956 10 : std::string osPathImage = osPath;
957 10 : std::string osBasename = pszBasename;
958 20 : if (STARTS_WITH(osBasename.c_str(), "../") ||
959 10 : STARTS_WITH(osBasename.c_str(), "..\\"))
960 : {
961 0 : osPathImage = CPLGetPathSafe(osPath.c_str());
962 0 : osBasename = osBasename.substr(strlen("../"));
963 : }
964 10 : if (CPLHasPathTraversal(osBasename.c_str()))
965 : {
966 0 : CPLError(CE_Failure, CPLE_NotSupported,
967 : "Path traversal detected in %s", osBasename.c_str());
968 0 : return nullptr;
969 : }
970 : /* --------------------------------------------------------------------
971 : */
972 : /* Form full filename (path of product.xml + basename). */
973 : /* --------------------------------------------------------------------
974 : */
975 : const std::string osFullname = CPLFormFilenameSafe(
976 10 : osPathImage.c_str(), osBasename.c_str(), nullptr);
977 :
978 : /* --------------------------------------------------------------------
979 : */
980 : /* Try and open the file. */
981 : /* --------------------------------------------------------------------
982 : */
983 10 : GDALDataset *poBandFile = GDALDataset::Open(
984 : osFullname.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR);
985 10 : if (poBandFile == nullptr)
986 : {
987 0 : continue;
988 : }
989 10 : if (poBandFile->GetRasterCount() == 0)
990 : {
991 0 : delete poBandFile;
992 0 : continue;
993 : }
994 :
995 : /* Some CFloat32 NITF files have nBitsPerSample incorrectly reported */
996 : /* as 16, and get misinterpreted as CInt16. Check the underlying NITF
997 : */
998 : /* and override if this is the case. */
999 10 : if (poBandFile->GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
1000 0 : eDataType = GDT_CFloat32;
1001 :
1002 10 : BandMapping b = GetBandFileMapping(eDataType, poBandFile);
1003 10 : const bool twoBandComplex = b == TWOBANDCOMPLEX;
1004 :
1005 20 : poDS->papszExtraFiles =
1006 10 : CSLAddString(poDS->papszExtraFiles, osFullname.c_str());
1007 :
1008 : /* --------------------------------------------------------------------
1009 : */
1010 : /* Create the band. */
1011 : /* --------------------------------------------------------------------
1012 : */
1013 10 : if (eCalib == None || eCalib == Uncalib)
1014 : {
1015 : RS2RasterBand *poBand = new RS2RasterBand(
1016 8 : poDS.get(), eDataType, CPLGetXMLValue(psNode, "pole", ""),
1017 8 : poBandFile, twoBandComplex);
1018 :
1019 8 : poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
1020 : }
1021 : else
1022 : {
1023 2 : const char *pszLUT = nullptr;
1024 2 : switch (eCalib)
1025 : {
1026 0 : case Sigma0:
1027 0 : pszLUT = osSigma0LUT;
1028 0 : break;
1029 2 : case Beta0:
1030 2 : pszLUT = osBeta0LUT;
1031 2 : break;
1032 0 : case Gamma:
1033 0 : pszLUT = osGammaLUT;
1034 0 : break;
1035 0 : default:
1036 : /* we should bomb gracefully... */
1037 0 : pszLUT = osSigma0LUT;
1038 : }
1039 : RS2CalibRasterBand *poBand = new RS2CalibRasterBand(
1040 2 : poDS.get(), CPLGetXMLValue(psNode, "pole", ""), eDataType,
1041 : poBandFile, eCalib,
1042 2 : CPLFormFilenameSafe(osPath.c_str(), pszLUT, nullptr).c_str());
1043 2 : poDS->SetBand(poDS->GetRasterCount() + 1, poBand);
1044 : }
1045 : }
1046 :
1047 5 : if (poDS->papszSubDatasets != nullptr && eCalib == None)
1048 : {
1049 4 : const size_t nBufLen = nFLen + 28;
1050 4 : char *pszBuf = static_cast<char *>(CPLMalloc(nBufLen));
1051 4 : snprintf(pszBuf, nBufLen, "RADARSAT_2_CALIB:UNCALIB:%s",
1052 : osMDFilename.c_str());
1053 4 : poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
1054 : "SUBDATASET_1_NAME", pszBuf);
1055 8 : poDS->papszSubDatasets =
1056 4 : CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_1_DESC",
1057 : "Uncalibrated digital numbers");
1058 4 : CPLFree(pszBuf);
1059 : }
1060 1 : else if (poDS->papszSubDatasets != nullptr)
1061 : {
1062 1 : CSLDestroy(poDS->papszSubDatasets);
1063 1 : poDS->papszSubDatasets = nullptr;
1064 : }
1065 :
1066 : /* -------------------------------------------------------------------- */
1067 : /* Set the appropriate MATRIX_REPRESENTATION. */
1068 : /* -------------------------------------------------------------------- */
1069 :
1070 5 : if (poDS->GetRasterCount() == 4 &&
1071 0 : (eDataType == GDT_CInt16 || eDataType == GDT_CFloat32))
1072 : {
1073 0 : poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
1074 : }
1075 :
1076 : /* -------------------------------------------------------------------- */
1077 : /* Collect a few useful metadata items */
1078 : /* -------------------------------------------------------------------- */
1079 :
1080 : CPLXMLNode *psSourceAttrs =
1081 5 : CPLGetXMLNode(psProduct, "=product.sourceAttributes");
1082 5 : const char *pszItem = CPLGetXMLValue(psSourceAttrs, "satellite", "");
1083 5 : poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem);
1084 :
1085 5 : pszItem = CPLGetXMLValue(psSourceAttrs, "sensor", "");
1086 5 : poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem);
1087 :
1088 5 : if (psSourceAttrs != nullptr)
1089 : {
1090 : /* Get beam mode mnemonic */
1091 5 : pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeMnemonic", "UNK");
1092 5 : poDS->SetMetadataItem("BEAM_MODE", pszItem);
1093 5 : pszItem = CPLGetXMLValue(psSourceAttrs, "rawDataStartTime", "UNK");
1094 5 : poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem);
1095 :
1096 : pszItem =
1097 5 : CPLGetXMLValue(psSourceAttrs, "inputDatasetFacilityId", "UNK");
1098 5 : poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem);
1099 :
1100 5 : pszItem = CPLGetXMLValue(
1101 : psSourceAttrs, "orbitAndAttitude.orbitInformation.passDirection",
1102 : "UNK");
1103 5 : poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem);
1104 5 : pszItem = CPLGetXMLValue(
1105 : psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataSource",
1106 : "UNK");
1107 5 : poDS->SetMetadataItem("ORBIT_DATA_SOURCE", pszItem);
1108 5 : pszItem = CPLGetXMLValue(
1109 : psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataFile",
1110 : "UNK");
1111 5 : poDS->SetMetadataItem("ORBIT_DATA_FILE", pszItem);
1112 : }
1113 :
1114 : CPLXMLNode *psSarProcessingInformation =
1115 5 : CPLGetXMLNode(psProduct, "=product.imageGenerationParameters");
1116 :
1117 5 : if (psSarProcessingInformation != nullptr)
1118 : {
1119 : /* Get incidence angle information */
1120 5 : pszItem = CPLGetXMLValue(
1121 : psSarProcessingInformation,
1122 : "sarProcessingInformation.incidenceAngleNearRange", "UNK");
1123 5 : poDS->SetMetadataItem("NEAR_RANGE_INCIDENCE_ANGLE", pszItem);
1124 :
1125 5 : pszItem = CPLGetXMLValue(
1126 : psSarProcessingInformation,
1127 : "sarProcessingInformation.incidenceAngleFarRange", "UNK");
1128 5 : poDS->SetMetadataItem("FAR_RANGE_INCIDENCE_ANGLE", pszItem);
1129 :
1130 5 : pszItem = CPLGetXMLValue(psSarProcessingInformation,
1131 : "sarProcessingInformation.slantRangeNearEdge",
1132 : "UNK");
1133 5 : poDS->SetMetadataItem("SLANT_RANGE_NEAR_EDGE", pszItem);
1134 :
1135 5 : pszItem = CPLGetXMLValue(
1136 : psSarProcessingInformation,
1137 : "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK");
1138 5 : poDS->SetMetadataItem("FIRST_LINE_TIME", pszItem);
1139 :
1140 5 : pszItem = CPLGetXMLValue(
1141 : psSarProcessingInformation,
1142 : "sarProcessingInformation.zeroDopplerTimeLastLine", "UNK");
1143 5 : poDS->SetMetadataItem("LAST_LINE_TIME", pszItem);
1144 :
1145 : pszItem =
1146 5 : CPLGetXMLValue(psSarProcessingInformation,
1147 : "generalProcessingInformation.productType", "UNK");
1148 5 : poDS->SetMetadataItem("PRODUCT_TYPE", pszItem);
1149 :
1150 5 : pszItem = CPLGetXMLValue(
1151 : psSarProcessingInformation,
1152 : "generalProcessingInformation.processingFacility", "UNK");
1153 5 : poDS->SetMetadataItem("PROCESSING_FACILITY", pszItem);
1154 :
1155 5 : pszItem = CPLGetXMLValue(psSarProcessingInformation,
1156 : "generalProcessingInformation.processingTime",
1157 : "UNK");
1158 5 : poDS->SetMetadataItem("PROCESSING_TIME", pszItem);
1159 : }
1160 :
1161 : /*--------------------------------------------------------------------- */
1162 : /* Collect Map projection/Geotransform information, if present */
1163 : /* -------------------------------------------------------------------- */
1164 : CPLXMLNode *psMapProjection =
1165 5 : CPLGetXMLNode(psImageAttributes, "geographicInformation.mapProjection");
1166 :
1167 5 : if (psMapProjection != nullptr)
1168 : {
1169 : CPLXMLNode *psPos =
1170 0 : CPLGetXMLNode(psMapProjection, "positioningInformation");
1171 :
1172 : pszItem =
1173 0 : CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "UNK");
1174 0 : poDS->SetMetadataItem("MAP_PROJECTION_DESCRIPTOR", pszItem);
1175 :
1176 : pszItem =
1177 0 : CPLGetXMLValue(psMapProjection, "mapProjectionOrientation", "UNK");
1178 0 : poDS->SetMetadataItem("MAP_PROJECTION_ORIENTATION", pszItem);
1179 :
1180 0 : pszItem = CPLGetXMLValue(psMapProjection, "resamplingKernel", "UNK");
1181 0 : poDS->SetMetadataItem("RESAMPLING_KERNEL", pszItem);
1182 :
1183 0 : pszItem = CPLGetXMLValue(psMapProjection, "satelliteHeading", "UNK");
1184 0 : poDS->SetMetadataItem("SATELLITE_HEADING", pszItem);
1185 :
1186 0 : if (psPos != nullptr)
1187 : {
1188 0 : const double tl_x = CPLStrtod(
1189 : CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.easting",
1190 : "0.0"),
1191 : nullptr);
1192 0 : const double tl_y = CPLStrtod(
1193 : CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.northing",
1194 : "0.0"),
1195 : nullptr);
1196 0 : const double bl_x = CPLStrtod(
1197 : CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.easting",
1198 : "0.0"),
1199 : nullptr);
1200 0 : const double bl_y = CPLStrtod(
1201 : CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.northing",
1202 : "0.0"),
1203 : nullptr);
1204 0 : const double tr_x = CPLStrtod(
1205 : CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.easting",
1206 : "0.0"),
1207 : nullptr);
1208 0 : const double tr_y = CPLStrtod(
1209 : CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.northing",
1210 : "0.0"),
1211 : nullptr);
1212 0 : poDS->m_gt[1] = (tr_x - tl_x) / (poDS->nRasterXSize - 1);
1213 0 : poDS->m_gt[4] = (tr_y - tl_y) / (poDS->nRasterXSize - 1);
1214 0 : poDS->m_gt[2] = (bl_x - tl_x) / (poDS->nRasterYSize - 1);
1215 0 : poDS->m_gt[5] = (bl_y - tl_y) / (poDS->nRasterYSize - 1);
1216 0 : poDS->m_gt[0] = (tl_x - 0.5 * poDS->m_gt[1] - 0.5 * poDS->m_gt[2]);
1217 0 : poDS->m_gt[3] = (tl_y - 0.5 * poDS->m_gt[4] - 0.5 * poDS->m_gt[5]);
1218 :
1219 : /* Use bottom right pixel to test geotransform */
1220 0 : const double br_x = CPLStrtod(
1221 : CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.easting",
1222 : "0.0"),
1223 : nullptr);
1224 0 : const double br_y = CPLStrtod(
1225 : CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.northing",
1226 : "0.0"),
1227 : nullptr);
1228 0 : const double testx = poDS->m_gt[0] +
1229 0 : poDS->m_gt[1] * (poDS->nRasterXSize - 0.5) +
1230 0 : poDS->m_gt[2] * (poDS->nRasterYSize - 0.5);
1231 0 : const double testy = poDS->m_gt[3] +
1232 0 : poDS->m_gt[4] * (poDS->nRasterXSize - 0.5) +
1233 0 : poDS->m_gt[5] * (poDS->nRasterYSize - 0.5);
1234 :
1235 : /* Give 1/4 pixel numerical error leeway in calculating location
1236 : based on affine transform */
1237 0 : if ((fabs(testx - br_x) >
1238 0 : fabs(0.25 * (poDS->m_gt[1] + poDS->m_gt[2]))) ||
1239 0 : (fabs(testy - br_y) >
1240 0 : fabs(0.25 * (poDS->m_gt[4] + poDS->m_gt[5]))))
1241 : {
1242 0 : CPLError(CE_Warning, CPLE_AppDefined,
1243 : "Unexpected error in calculating affine transform: "
1244 : "corner coordinates inconsistent.");
1245 : }
1246 : else
1247 : {
1248 0 : poDS->bHaveGeoTransform = TRUE;
1249 : }
1250 : }
1251 : }
1252 :
1253 : /* -------------------------------------------------------------------- */
1254 : /* Collect Projection String Information */
1255 : /* -------------------------------------------------------------------- */
1256 : CPLXMLNode *psEllipsoid =
1257 5 : CPLGetXMLNode(psImageAttributes,
1258 : "geographicInformation.referenceEllipsoidParameters");
1259 :
1260 5 : if (psEllipsoid != nullptr)
1261 : {
1262 10 : OGRSpatialReference oLL, oPrj;
1263 5 : oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1264 5 : oPrj.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1265 :
1266 : const char *pszGeodeticTerrainHeight =
1267 5 : CPLGetXMLValue(psEllipsoid, "geodeticTerrainHeight", "UNK");
1268 5 : poDS->SetMetadataItem("GEODETIC_TERRAIN_HEIGHT",
1269 5 : pszGeodeticTerrainHeight);
1270 :
1271 : const char *pszEllipsoidName =
1272 5 : CPLGetXMLValue(psEllipsoid, "ellipsoidName", "");
1273 : double minor_axis =
1274 5 : CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMinorAxis", "0.0"));
1275 : double major_axis =
1276 5 : CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMajorAxis", "0.0"));
1277 :
1278 5 : if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) ||
1279 : (major_axis == 0.0))
1280 : {
1281 0 : CPLError(CE_Warning, CPLE_AppDefined,
1282 : "Warning- incomplete"
1283 : " ellipsoid information. Using wgs-84 parameters.\n");
1284 0 : oLL.SetWellKnownGeogCS("WGS84");
1285 0 : oPrj.SetWellKnownGeogCS("WGS84");
1286 : }
1287 5 : else if (EQUAL(pszEllipsoidName, "WGS84") ||
1288 0 : EQUAL(pszEllipsoidName, "WGS 1984"))
1289 : {
1290 5 : oLL.SetWellKnownGeogCS("WGS84");
1291 5 : oPrj.SetWellKnownGeogCS("WGS84");
1292 : }
1293 : else
1294 : {
1295 0 : const double inv_flattening =
1296 0 : major_axis / (major_axis - minor_axis);
1297 0 : oLL.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening);
1298 0 : oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis,
1299 : inv_flattening);
1300 : }
1301 :
1302 5 : if (psMapProjection != nullptr)
1303 : {
1304 : const char *pszProj =
1305 0 : CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "");
1306 0 : bool bUseProjInfo = false;
1307 :
1308 : CPLXMLNode *psUtmParams =
1309 0 : CPLGetXMLNode(psMapProjection, "utmProjectionParameters");
1310 :
1311 : CPLXMLNode *psNspParams =
1312 0 : CPLGetXMLNode(psMapProjection, "nspProjectionParameters");
1313 :
1314 0 : if ((psUtmParams != nullptr) && poDS->bHaveGeoTransform)
1315 : {
1316 : /* double origEasting, origNorthing; */
1317 0 : bool bNorth = true;
1318 :
1319 : const int utmZone =
1320 0 : atoi(CPLGetXMLValue(psUtmParams, "utmZone", ""));
1321 : const char *pszHemisphere =
1322 0 : CPLGetXMLValue(psUtmParams, "hemisphere", "");
1323 : #if 0
1324 : origEasting = CPLStrtod(CPLGetXMLValue(
1325 : psUtmParams, "mapOriginFalseEasting", "0.0" ), nullptr);
1326 : origNorthing = CPLStrtod(CPLGetXMLValue(
1327 : psUtmParams, "mapOriginFalseNorthing", "0.0" ), nullptr);
1328 : #endif
1329 0 : if (STARTS_WITH_CI(pszHemisphere, "southern"))
1330 0 : bNorth = FALSE;
1331 :
1332 0 : if (STARTS_WITH_CI(pszProj, "UTM"))
1333 : {
1334 0 : oPrj.SetUTM(utmZone, bNorth);
1335 0 : bUseProjInfo = true;
1336 : }
1337 : }
1338 0 : else if ((psNspParams != nullptr) && poDS->bHaveGeoTransform)
1339 : {
1340 0 : const double origEasting = CPLStrtod(
1341 : CPLGetXMLValue(psNspParams, "mapOriginFalseEasting", "0.0"),
1342 : nullptr);
1343 : const double origNorthing =
1344 0 : CPLStrtod(CPLGetXMLValue(psNspParams,
1345 : "mapOriginFalseNorthing", "0.0"),
1346 : nullptr);
1347 0 : const double copLong = CPLStrtod(
1348 : CPLGetXMLValue(psNspParams, "centerOfProjectionLongitude",
1349 : "0.0"),
1350 : nullptr);
1351 0 : const double copLat = CPLStrtod(
1352 : CPLGetXMLValue(psNspParams, "centerOfProjectionLatitude",
1353 : "0.0"),
1354 : nullptr);
1355 0 : const double sP1 = CPLStrtod(
1356 : CPLGetXMLValue(psNspParams, "standardParallels1", "0.0"),
1357 : nullptr);
1358 0 : const double sP2 = CPLStrtod(
1359 : CPLGetXMLValue(psNspParams, "standardParallels2", "0.0"),
1360 : nullptr);
1361 :
1362 0 : if (STARTS_WITH_CI(pszProj, "ARC"))
1363 : {
1364 : /* Albers Conical Equal Area */
1365 0 : oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting,
1366 : origNorthing);
1367 0 : bUseProjInfo = true;
1368 : }
1369 0 : else if (STARTS_WITH_CI(pszProj, "LCC"))
1370 : {
1371 : /* Lambert Conformal Conic */
1372 0 : oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting,
1373 : origNorthing);
1374 0 : bUseProjInfo = true;
1375 : }
1376 0 : else if (STARTS_WITH_CI(pszProj, "STPL"))
1377 : {
1378 : /* State Plate
1379 : ASSUMPTIONS: "zone" in product.xml matches USGS
1380 : definition as expected by ogr spatial reference; NAD83
1381 : zones (versus NAD27) are assumed. */
1382 :
1383 : const int nSPZone =
1384 0 : atoi(CPLGetXMLValue(psNspParams, "zone", "1"));
1385 :
1386 0 : oPrj.SetStatePlane(nSPZone, TRUE, nullptr, 0.0);
1387 0 : bUseProjInfo = true;
1388 : }
1389 : }
1390 :
1391 0 : if (bUseProjInfo)
1392 : {
1393 0 : poDS->m_oSRS = std::move(oPrj);
1394 : }
1395 : else
1396 : {
1397 0 : CPLError(CE_Warning, CPLE_AppDefined,
1398 : "Unable to interpret "
1399 : "projection information; check mapProjection info in "
1400 : "product.xml!");
1401 : }
1402 : }
1403 :
1404 5 : poDS->m_oGCPSRS = std::move(oLL);
1405 : }
1406 :
1407 : /* -------------------------------------------------------------------- */
1408 : /* Collect GCPs. */
1409 : /* -------------------------------------------------------------------- */
1410 5 : CPLXMLNode *psGeoGrid = CPLGetXMLNode(
1411 : psImageAttributes, "geographicInformation.geolocationGrid");
1412 :
1413 5 : if (psGeoGrid != nullptr)
1414 : {
1415 : /* count GCPs */
1416 5 : poDS->nGCPCount = 0;
1417 :
1418 25 : for (psNode = psGeoGrid->psChild; psNode != nullptr;
1419 20 : psNode = psNode->psNext)
1420 : {
1421 20 : if (EQUAL(psNode->pszValue, "imageTiePoint"))
1422 20 : poDS->nGCPCount++;
1423 : }
1424 :
1425 10 : poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>(
1426 5 : CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount));
1427 :
1428 5 : poDS->nGCPCount = 0;
1429 :
1430 25 : for (psNode = psGeoGrid->psChild; psNode != nullptr;
1431 20 : psNode = psNode->psNext)
1432 : {
1433 20 : GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount;
1434 :
1435 20 : if (!EQUAL(psNode->pszValue, "imageTiePoint"))
1436 0 : continue;
1437 :
1438 20 : poDS->nGCPCount++;
1439 :
1440 : char szID[32];
1441 20 : snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount);
1442 20 : psGCP->pszId = CPLStrdup(szID);
1443 20 : psGCP->pszInfo = CPLStrdup("");
1444 20 : psGCP->dfGCPPixel =
1445 20 : CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.pixel", "0")) +
1446 : 0.5;
1447 20 : psGCP->dfGCPLine =
1448 20 : CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.line", "0")) +
1449 : 0.5;
1450 20 : psGCP->dfGCPX = CPLAtof(
1451 : CPLGetXMLValue(psNode, "geodeticCoordinate.longitude", ""));
1452 20 : psGCP->dfGCPY = CPLAtof(
1453 : CPLGetXMLValue(psNode, "geodeticCoordinate.latitude", ""));
1454 20 : psGCP->dfGCPZ = CPLAtof(
1455 : CPLGetXMLValue(psNode, "geodeticCoordinate.height", ""));
1456 : }
1457 : }
1458 :
1459 : /* -------------------------------------------------------------------- */
1460 : /* Collect RPC. */
1461 : /* -------------------------------------------------------------------- */
1462 5 : CPLXMLNode *psRationalFunctions = CPLGetXMLNode(
1463 : psImageAttributes, "geographicInformation.rationalFunctions");
1464 5 : if (psRationalFunctions != nullptr)
1465 : {
1466 5 : char **papszRPC = nullptr;
1467 : static const char *const apszXMLToGDALMapping[] = {
1468 : "biasError",
1469 : "ERR_BIAS",
1470 : "randomError",
1471 : "ERR_RAND",
1472 : //"lineFitQuality", "????",
1473 : //"pixelFitQuality", "????",
1474 : "lineOffset",
1475 : "LINE_OFF",
1476 : "pixelOffset",
1477 : "SAMP_OFF",
1478 : "latitudeOffset",
1479 : "LAT_OFF",
1480 : "longitudeOffset",
1481 : "LONG_OFF",
1482 : "heightOffset",
1483 : "HEIGHT_OFF",
1484 : "lineScale",
1485 : "LINE_SCALE",
1486 : "pixelScale",
1487 : "SAMP_SCALE",
1488 : "latitudeScale",
1489 : "LAT_SCALE",
1490 : "longitudeScale",
1491 : "LONG_SCALE",
1492 : "heightScale",
1493 : "HEIGHT_SCALE",
1494 : "lineNumeratorCoefficients",
1495 : "LINE_NUM_COEFF",
1496 : "lineDenominatorCoefficients",
1497 : "LINE_DEN_COEFF",
1498 : "pixelNumeratorCoefficients",
1499 : "SAMP_NUM_COEFF",
1500 : "pixelDenominatorCoefficients",
1501 : "SAMP_DEN_COEFF",
1502 : };
1503 85 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszXMLToGDALMapping); i += 2)
1504 : {
1505 160 : const char *pszValue = CPLGetXMLValue(
1506 80 : psRationalFunctions, apszXMLToGDALMapping[i], nullptr);
1507 80 : if (pszValue)
1508 80 : papszRPC = CSLSetNameValue(
1509 80 : papszRPC, apszXMLToGDALMapping[i + 1], pszValue);
1510 : }
1511 5 : poDS->GDALDataset::SetMetadata(papszRPC, "RPC");
1512 5 : CSLDestroy(papszRPC);
1513 : }
1514 :
1515 : /* -------------------------------------------------------------------- */
1516 : /* Initialize any PAM information. */
1517 : /* -------------------------------------------------------------------- */
1518 10 : CPLString osDescription;
1519 :
1520 5 : switch (eCalib)
1521 : {
1522 0 : case Sigma0:
1523 : osDescription.Printf("RADARSAT_2_CALIB:SIGMA0:%s",
1524 0 : osMDFilename.c_str());
1525 0 : break;
1526 1 : case Beta0:
1527 : osDescription.Printf("RADARSAT_2_CALIB:BETA0:%s",
1528 1 : osMDFilename.c_str());
1529 1 : break;
1530 0 : case Gamma:
1531 : osDescription.Printf("RADARSAT_2_CALIB:GAMMA0:%s",
1532 0 : osMDFilename.c_str());
1533 0 : break;
1534 0 : case Uncalib:
1535 : osDescription.Printf("RADARSAT_2_CALIB:UNCALIB:%s",
1536 0 : osMDFilename.c_str());
1537 0 : break;
1538 4 : default:
1539 4 : osDescription = osMDFilename;
1540 : }
1541 :
1542 5 : if (eCalib != None)
1543 1 : poDS->papszExtraFiles =
1544 1 : CSLAddString(poDS->papszExtraFiles, osMDFilename);
1545 :
1546 : /* -------------------------------------------------------------------- */
1547 : /* Initialize any PAM information. */
1548 : /* -------------------------------------------------------------------- */
1549 5 : poDS->SetDescription(osDescription);
1550 :
1551 5 : poDS->SetPhysicalFilename(osMDFilename);
1552 5 : poDS->SetSubdatasetName(osDescription);
1553 :
1554 5 : poDS->TryLoadXML();
1555 :
1556 : /* -------------------------------------------------------------------- */
1557 : /* Check for overviews. */
1558 : /* -------------------------------------------------------------------- */
1559 5 : poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::");
1560 :
1561 5 : return poDS.release();
1562 : }
1563 :
1564 : /************************************************************************/
1565 : /* GetGCPCount() */
1566 : /************************************************************************/
1567 :
1568 0 : int RS2Dataset::GetGCPCount()
1569 :
1570 : {
1571 0 : return nGCPCount;
1572 : }
1573 :
1574 : /************************************************************************/
1575 : /* GetGCPSpatialRef() */
1576 : /************************************************************************/
1577 :
1578 0 : const OGRSpatialReference *RS2Dataset::GetGCPSpatialRef() const
1579 :
1580 : {
1581 0 : return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
1582 : }
1583 :
1584 : /************************************************************************/
1585 : /* GetGCPs() */
1586 : /************************************************************************/
1587 :
1588 0 : const GDAL_GCP *RS2Dataset::GetGCPs()
1589 :
1590 : {
1591 0 : return pasGCPList;
1592 : }
1593 :
1594 : /************************************************************************/
1595 : /* GetSpatialRef() */
1596 : /************************************************************************/
1597 :
1598 0 : const OGRSpatialReference *RS2Dataset::GetSpatialRef() const
1599 :
1600 : {
1601 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
1602 : }
1603 :
1604 : /************************************************************************/
1605 : /* GetGeoTransform() */
1606 : /************************************************************************/
1607 :
1608 0 : CPLErr RS2Dataset::GetGeoTransform(GDALGeoTransform >) const
1609 :
1610 : {
1611 0 : gt = m_gt;
1612 :
1613 0 : if (bHaveGeoTransform)
1614 0 : return CE_None;
1615 :
1616 0 : return CE_Failure;
1617 : }
1618 :
1619 : /************************************************************************/
1620 : /* GetMetadataDomainList() */
1621 : /************************************************************************/
1622 :
1623 0 : char **RS2Dataset::GetMetadataDomainList()
1624 : {
1625 0 : return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
1626 0 : "SUBDATASETS", nullptr);
1627 : }
1628 :
1629 : /************************************************************************/
1630 : /* GetMetadata() */
1631 : /************************************************************************/
1632 :
1633 1 : char **RS2Dataset::GetMetadata(const char *pszDomain)
1634 :
1635 : {
1636 1 : if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS") &&
1637 0 : papszSubDatasets != nullptr)
1638 0 : return papszSubDatasets;
1639 :
1640 1 : return GDALDataset::GetMetadata(pszDomain);
1641 : }
1642 :
1643 : /************************************************************************/
1644 : /* GDALRegister_RS2() */
1645 : /************************************************************************/
1646 :
1647 2038 : void GDALRegister_RS2()
1648 :
1649 : {
1650 2038 : if (GDALGetDriverByName("RS2") != nullptr)
1651 283 : return;
1652 :
1653 1755 : GDALDriver *poDriver = new GDALDriver();
1654 :
1655 1755 : poDriver->SetDescription("RS2");
1656 1755 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1657 1755 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "RadarSat 2 XML Product");
1658 1755 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rs2.html");
1659 1755 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1660 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1661 :
1662 1755 : poDriver->pfnOpen = RS2Dataset::Open;
1663 1755 : poDriver->pfnIdentify = RS2Dataset::Identify;
1664 :
1665 1755 : GetGDALDriverManager()->RegisterDriver(poDriver);
1666 : }
|