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