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