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