/src/gdal/frmts/rcm/rcmdataset.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: DRDC Ottawa GEOINT |
4 | | * Purpose: Radarsat Constellation Mission - XML Products (product.xml) driver |
5 | | * Author: Roberto Caron, MDA |
6 | | * on behalf of DRDC Ottawa |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2020, DRDC Ottawa |
10 | | * |
11 | | * Based on the RS2 Dataset Class |
12 | | * |
13 | | * SPDX-License-Identifier: MIT |
14 | | ****************************************************************************/ |
15 | | |
16 | | #include <time.h> |
17 | | #include <stdio.h> |
18 | | #include <sstream> |
19 | | |
20 | | #include "cpl_minixml.h" |
21 | | #include "gdal_frmts.h" |
22 | | #include "gdal_pam.h" |
23 | | #include "ogr_spatialref.h" |
24 | | #include "rcmdataset.h" |
25 | | #include "rcmdrivercore.h" |
26 | | |
27 | | #include <limits> |
28 | | |
29 | | constexpr int max_space_for_string = 32; |
30 | | |
31 | | /* RCM has a special folder that contains all LUT, Incidence Angle and Noise |
32 | | * Level files */ |
33 | | constexpr const char *CALIBRATION_FOLDER = "calibration"; |
34 | | |
35 | | /*** Function to format calibration for unique identification for Layer Name |
36 | | * ***/ |
37 | | /* |
38 | | * RCM_CALIB : { SIGMA0 | GAMMA0 | BETA0 | UNCALIB } : product.xml full path |
39 | | */ |
40 | | inline CPLString FormatCalibration(const char *pszCalibName, |
41 | | const char *pszFilename) |
42 | 0 | { |
43 | 0 | CPLString ptr; |
44 | | |
45 | | // Always begin by the layer calibration name |
46 | 0 | ptr.append(szLayerCalibration); |
47 | | |
48 | | // A separator is needed before concat calibration name |
49 | 0 | ptr += chLayerSeparator; |
50 | | // Add calibration name |
51 | 0 | ptr.append(pszCalibName); |
52 | | |
53 | | // A separator is needed before concat full filename name |
54 | 0 | ptr += chLayerSeparator; |
55 | | // Add full filename name |
56 | 0 | ptr.append(pszFilename); |
57 | | |
58 | | /* return calibration format */ |
59 | 0 | return ptr; |
60 | 0 | } |
61 | | |
62 | | /*** Function to test for valid LUT files ***/ |
63 | | static bool IsValidXMLFile(const char *pszPath) |
64 | 0 | { |
65 | | /* Return true for valid xml file, false otherwise */ |
66 | 0 | CPLXMLTreeCloser psLut(CPLParseXMLFile(pszPath)); |
67 | |
|
68 | 0 | if (psLut.get() == nullptr) |
69 | 0 | { |
70 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
71 | 0 | "ERROR: Failed to open the LUT file %s", pszPath); |
72 | 0 | } |
73 | |
|
74 | 0 | return psLut.get() != nullptr; |
75 | 0 | } |
76 | | |
77 | | static double *InterpolateValues(CSLConstList papszList, int tableSize, |
78 | | int stepSize, int numberOfValues, |
79 | | int pixelFirstLutValue) |
80 | 0 | { |
81 | | /* Allocate the right LUT size according to the product range pixel */ |
82 | 0 | double *table = |
83 | 0 | static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), tableSize)); |
84 | 0 | if (!table) |
85 | 0 | return nullptr; |
86 | | |
87 | 0 | if (stepSize <= 0) |
88 | 0 | { |
89 | | /* When negative, the range of pixel is calculated from the opposite |
90 | | * starting from the end of gains array */ |
91 | | /* Just step the range with positive value */ |
92 | 0 | const int positiveStepSize = abs(stepSize); |
93 | |
|
94 | 0 | int k = 0; |
95 | |
|
96 | 0 | if (positiveStepSize == 1) |
97 | 0 | { |
98 | | // Be fast and just copy the values because all gain values |
99 | | // represent all image wide pixel |
100 | | /* Start at the end position and store in the opposite */ |
101 | 0 | for (int i = pixelFirstLutValue; i >= 0; i--) |
102 | 0 | { |
103 | 0 | const double value = CPLAtof(papszList[i]); |
104 | 0 | table[k++] = value; |
105 | 0 | } |
106 | 0 | } |
107 | 0 | else |
108 | 0 | { |
109 | | |
110 | | /* Interpolation between 2 numbers */ |
111 | 0 | for (int i = numberOfValues - 1; i >= 0; i--) |
112 | 0 | { |
113 | | // We will consider the same value to cover the case that we |
114 | | // will hit the last pixel |
115 | 0 | double valueFrom = CPLAtof(papszList[i]); |
116 | 0 | double valueTo = valueFrom; |
117 | |
|
118 | 0 | if (i > 0) |
119 | 0 | { |
120 | | // We have room to pick the previous number to interpolate |
121 | | // with |
122 | 0 | valueTo = CPLAtof(papszList[i - 1]); |
123 | 0 | } |
124 | | |
125 | | // If the valueFrom minus ValueTo equal 0, it means to finish |
126 | | // off with the same number until the end of the table size |
127 | 0 | double interp = (valueTo - valueFrom) / positiveStepSize; |
128 | | |
129 | | // Always begin with the value FROM found |
130 | 0 | table[k++] = valueFrom; |
131 | | |
132 | | // Then add interpolation, don't forget. The stepSize is |
133 | | // actually counting our valueFrom number thus we add |
134 | | // interpolation until the last step - 1 |
135 | 0 | for (int j = 0; j < positiveStepSize - 1; j++) |
136 | 0 | { |
137 | 0 | valueFrom += interp; |
138 | 0 | table[k++] = valueFrom; |
139 | 0 | } |
140 | 0 | } |
141 | 0 | } |
142 | 0 | } |
143 | 0 | else |
144 | 0 | { |
145 | | /* When positive, the range of pixel is calculated from the beginning of |
146 | | * gains array */ |
147 | 0 | if (stepSize == 1) |
148 | 0 | { |
149 | | // Be fast and just copy the values because all gain values |
150 | | // represent all image wide pixel |
151 | 0 | for (int i = 0; i < numberOfValues; i++) |
152 | 0 | { |
153 | 0 | const double value = CPLAtof(papszList[i]); |
154 | 0 | table[i] = value; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | else |
158 | 0 | { |
159 | | /* Interpolation between 2 numbers */ |
160 | 0 | int k = 0; |
161 | 0 | for (int i = 0; i < numberOfValues; i++) |
162 | 0 | { |
163 | | // We will consider the same value to cover the case that we |
164 | | // will hit the last pixel |
165 | 0 | double valueFrom = CPLAtof(papszList[i]); |
166 | 0 | double valueTo = valueFrom; |
167 | |
|
168 | 0 | if (i < (numberOfValues)-1) |
169 | 0 | { |
170 | | // We have room to pick the next number to interpolate with |
171 | 0 | valueTo = CPLAtof(papszList[i + 1]); |
172 | 0 | } |
173 | | |
174 | | // If the valueFrom minus ValueTo equal 0, it means to finish |
175 | | // off with the same number until the end of the table size |
176 | 0 | double interp = (valueTo - valueFrom) / stepSize; |
177 | | |
178 | | // Always begin with the value FROM found |
179 | 0 | table[k++] = valueFrom; |
180 | | |
181 | | // Then add interpolation, don't forget. The stepSize is |
182 | | // actually counting our valueFrom number thus we add |
183 | | // interpolation until the last step - 1 |
184 | 0 | for (int j = 0; j < stepSize - 1; j++) |
185 | 0 | { |
186 | 0 | valueFrom += interp; |
187 | 0 | table[k++] = valueFrom; |
188 | 0 | } |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | |
|
193 | 0 | return table; |
194 | 0 | } |
195 | | |
196 | | /*** check that the referenced dataset for each band has the |
197 | | correct data type and returns whether a 2 band I+Q dataset should |
198 | | be mapped onto a single complex band. |
199 | | Returns BANDERROR for error, STRAIGHT for 1:1 mapping, TWOBANDCOMPLEX for 2 |
200 | | bands -> 1 complex band |
201 | | */ |
202 | | typedef enum |
203 | | { |
204 | | BANDERROR, |
205 | | STRAIGHT, |
206 | | TWOBANDCOMPLEX |
207 | | } BandMappingRCM; |
208 | | |
209 | | static BandMappingRCM checkBandFileMappingRCM(GDALDataType dataType, |
210 | | GDALDataset *poBandFile, |
211 | | bool isNITF) |
212 | 0 | { |
213 | |
|
214 | 0 | GDALRasterBand *band1 = poBandFile->GetRasterBand(1); |
215 | 0 | GDALDataType bandfileType = band1->GetRasterDataType(); |
216 | | // if there is one band and it has the same datatype, the band file gets |
217 | | // passed straight through |
218 | 0 | if ((poBandFile->GetRasterCount() == 1 || |
219 | 0 | poBandFile->GetRasterCount() == 4) && |
220 | 0 | dataType == bandfileType) |
221 | 0 | return STRAIGHT; |
222 | | |
223 | | // if the band file has 2 bands, they should represent I+Q |
224 | | // and be a compatible data type |
225 | 0 | if (poBandFile->GetRasterCount() == 2 && GDALDataTypeIsComplex(dataType)) |
226 | 0 | { |
227 | 0 | GDALRasterBand *band2 = poBandFile->GetRasterBand(2); |
228 | |
|
229 | 0 | if (bandfileType != band2->GetRasterDataType()) |
230 | 0 | return BANDERROR; // both bands must be same datatype |
231 | | |
232 | | // check compatible types - there are 4 complex types in GDAL |
233 | 0 | if ((dataType == GDT_CInt16 && bandfileType == GDT_Int16) || |
234 | 0 | (dataType == GDT_CInt32 && bandfileType == GDT_Int32) || |
235 | 0 | (dataType == GDT_CFloat32 && bandfileType == GDT_Float32) || |
236 | 0 | (dataType == GDT_CFloat64 && bandfileType == GDT_Float64)) |
237 | 0 | return TWOBANDCOMPLEX; |
238 | | |
239 | 0 | if ((dataType == GDT_CInt16 && bandfileType == GDT_CInt16) || |
240 | 0 | (dataType == GDT_CInt32 && bandfileType == GDT_CInt32) || |
241 | 0 | (dataType == GDT_CFloat32 && bandfileType == GDT_CFloat32) || |
242 | 0 | (dataType == GDT_CFloat64 && bandfileType == GDT_CFloat64)) |
243 | 0 | return TWOBANDCOMPLEX; |
244 | 0 | } |
245 | | |
246 | 0 | if (isNITF) |
247 | 0 | { |
248 | 0 | return STRAIGHT; |
249 | 0 | } |
250 | | |
251 | 0 | return BANDERROR; // don't accept any other combinations |
252 | 0 | } |
253 | | |
254 | | /************************************************************************/ |
255 | | /* RCMRasterBand */ |
256 | | /************************************************************************/ |
257 | | |
258 | | RCMRasterBand::RCMRasterBand(RCMDataset *poDSIn, int nBandIn, |
259 | | GDALDataType eDataTypeIn, const char *pszPole, |
260 | | GDALDataset *poBandFileIn, bool bTwoBandComplex, |
261 | | bool isOneFilePerPolIn, bool isNITFIn) |
262 | 0 | : poBandFile(poBandFileIn), poRCMDataset(poDSIn), |
263 | 0 | twoBandComplex(bTwoBandComplex), isOneFilePerPol(isOneFilePerPolIn), |
264 | 0 | isNITF(isNITFIn) |
265 | 0 | { |
266 | 0 | poDS = poDSIn; |
267 | 0 | this->nBand = nBandIn; |
268 | 0 | eDataType = eDataTypeIn; |
269 | | |
270 | | /*Check image type, whether there is one file per polarization or |
271 | | *one file containing all polarizations*/ |
272 | 0 | if (this->isOneFilePerPol) |
273 | 0 | { |
274 | 0 | poBand = poBandFile->GetRasterBand(1); |
275 | 0 | } |
276 | 0 | else |
277 | 0 | { |
278 | 0 | poBand = poBandFile->GetRasterBand(this->nBand); |
279 | 0 | } |
280 | |
|
281 | 0 | poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); |
282 | |
|
283 | 0 | if (pszPole != nullptr && strlen(pszPole) != 0) |
284 | 0 | { |
285 | 0 | SetMetadataItem("POLARIMETRIC_INTERP", pszPole); |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | | /************************************************************************/ |
290 | | /* RCMRasterBand() */ |
291 | | /************************************************************************/ |
292 | | |
293 | | RCMRasterBand::~RCMRasterBand() |
294 | | |
295 | 0 | { |
296 | 0 | if (poBandFile != nullptr) |
297 | 0 | GDALClose(reinterpret_cast<GDALRasterBandH>(poBandFile)); |
298 | 0 | } |
299 | | |
300 | | /************************************************************************/ |
301 | | /* IReadBlock() */ |
302 | | /************************************************************************/ |
303 | | |
304 | | CPLErr RCMRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) |
305 | | |
306 | 0 | { |
307 | 0 | int nRequestXSize = 0; |
308 | 0 | int nRequestYSize = 0; |
309 | 0 | GetActualBlockSize(nBlockXOff, nBlockYOff, &nRequestXSize, &nRequestYSize); |
310 | | |
311 | | // Zero initial partial right-most and bottom-most blocks |
312 | 0 | if (nRequestXSize < nBlockXSize || nRequestYSize < nBlockYSize) |
313 | 0 | { |
314 | 0 | memset(pImage, 0, |
315 | 0 | static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) * |
316 | 0 | nBlockXSize * nBlockYSize); |
317 | 0 | } |
318 | |
|
319 | 0 | int dataTypeSize = GDALGetDataTypeSizeBytes(eDataType); |
320 | 0 | GDALDataType bandFileType = |
321 | 0 | poBandFile->GetRasterBand(1)->GetRasterDataType(); |
322 | 0 | int bandFileSize = GDALGetDataTypeSizeBytes(bandFileType); |
323 | | |
324 | | // case: 2 bands representing I+Q -> one complex band |
325 | 0 | if (twoBandComplex && !this->isNITF) |
326 | 0 | { |
327 | | // int bandFileSize = GDALGetDataTypeSizeBytes(bandFileType); |
328 | | // this data type is the complex version of the band file |
329 | | // Roberto: don't check that for the moment: CPLAssert(dataTypeSize == |
330 | | // bandFileSize * 2); |
331 | |
|
332 | 0 | return |
333 | | // I and Q from each band are pixel-interleaved into this complex |
334 | | // band |
335 | 0 | poBandFile->RasterIO( |
336 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
337 | 0 | nRequestXSize, nRequestYSize, pImage, nRequestXSize, |
338 | 0 | nRequestYSize, bandFileType, 2, nullptr, dataTypeSize, |
339 | 0 | static_cast<GSpacing>(dataTypeSize) * nBlockXSize, bandFileSize, |
340 | 0 | nullptr); |
341 | 0 | } |
342 | 0 | else if (twoBandComplex && this->isNITF) |
343 | 0 | { |
344 | 0 | return poBand->RasterIO( |
345 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
346 | 0 | nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize, |
347 | 0 | eDataType, 0, static_cast<GSpacing>(dataTypeSize) * nBlockXSize, |
348 | 0 | nullptr); |
349 | 0 | } |
350 | | |
351 | 0 | if (poRCMDataset->IsComplexData()) |
352 | 0 | { |
353 | | // this data type is the complex version of the band file |
354 | | // Roberto: don't check that for the moment: CPLAssert(dataTypeSize == |
355 | | // bandFileSize * 2); |
356 | 0 | return |
357 | | // I and Q from each band are pixel-interleaved into this complex |
358 | | // band |
359 | 0 | poBandFile->RasterIO( |
360 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
361 | 0 | nRequestXSize, nRequestYSize, pImage, nRequestXSize, |
362 | 0 | nRequestYSize, bandFileType, 2, nullptr, dataTypeSize, |
363 | 0 | static_cast<GSpacing>(dataTypeSize) * nBlockXSize, bandFileSize, |
364 | 0 | nullptr); |
365 | 0 | } |
366 | | |
367 | | // case: band file == this band |
368 | | // NOTE: if the underlying band is opened with the NITF driver, it may |
369 | | // combine 2 band I+Q -> complex band |
370 | 0 | else if (poBandFile->GetRasterBand(1)->GetRasterDataType() == eDataType) |
371 | 0 | { |
372 | 0 | return poBand->RasterIO( |
373 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
374 | 0 | nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize, |
375 | 0 | eDataType, 0, static_cast<GSpacing>(dataTypeSize) * nBlockXSize, |
376 | 0 | nullptr); |
377 | 0 | } |
378 | 0 | else |
379 | 0 | { |
380 | 0 | CPLAssert(FALSE); |
381 | 0 | return CE_Failure; |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | | /************************************************************************/ |
386 | | /* ReadLUT() */ |
387 | | /************************************************************************/ |
388 | | /* Read the provided LUT in to m_ndTable */ |
389 | | /* 1. The gains list spans the range extent covered by all */ |
390 | | /* beams(if applicable). */ |
391 | | /* 2. The mapping between the entry of gains */ |
392 | | /* list and the range sample index is : the range sample */ |
393 | | /* index = gains entry index * stepSize + pixelFirstLutValue, */ |
394 | | /* where the gains entry index starts with ‘0’.For ScanSAR SLC, */ |
395 | | /* the range sample index refers to the index on the COPG */ |
396 | | /************************************************************************/ |
397 | | void RCMCalibRasterBand::ReadLUT() |
398 | 0 | { |
399 | |
|
400 | 0 | char bandNumber[12]; |
401 | 0 | snprintf(bandNumber, sizeof(bandNumber), "%d", poDS->GetRasterCount() + 1); |
402 | |
|
403 | 0 | CPLXMLTreeCloser psLUT(CPLParseXMLFile(m_pszLUTFile)); |
404 | 0 | if (!psLUT) |
405 | 0 | return; |
406 | | |
407 | 0 | this->m_nfOffset = |
408 | 0 | CPLAtof(CPLGetXMLValue(psLUT.get(), "=lut.offset", "0.0")); |
409 | |
|
410 | 0 | this->pixelFirstLutValue = |
411 | 0 | atoi(CPLGetXMLValue(psLUT.get(), "=lut.pixelFirstLutValue", "0")); |
412 | |
|
413 | 0 | this->stepSize = atoi(CPLGetXMLValue(psLUT.get(), "=lut.stepSize", "0")); |
414 | |
|
415 | 0 | this->numberOfValues = |
416 | 0 | atoi(CPLGetXMLValue(psLUT.get(), "=lut.numberOfValues", "0")); |
417 | |
|
418 | 0 | if (this->numberOfValues <= 0) |
419 | 0 | { |
420 | 0 | CPLError( |
421 | 0 | CE_Failure, CPLE_NotSupported, |
422 | 0 | "ERROR: The RCM driver does not support the LUT Number Of Values " |
423 | 0 | "equal or lower than zero."); |
424 | 0 | return; |
425 | 0 | } |
426 | | |
427 | 0 | const CPLStringList aosLUTList( |
428 | 0 | CSLTokenizeString2(CPLGetXMLValue(psLUT.get(), "=lut.gains", ""), " ", |
429 | 0 | CSLT_HONOURSTRINGS)); |
430 | |
|
431 | 0 | if (this->stepSize <= 0) |
432 | 0 | { |
433 | 0 | if (this->pixelFirstLutValue <= 0) |
434 | 0 | { |
435 | 0 | CPLError( |
436 | 0 | CE_Failure, CPLE_NotSupported, |
437 | 0 | "ERROR: The RCM driver does not support LUT Pixel First Lut " |
438 | 0 | "Value equal or lower than zero when the product is " |
439 | 0 | "descending."); |
440 | 0 | return; |
441 | 0 | } |
442 | 0 | } |
443 | | |
444 | | /* Get the Pixel Per range */ |
445 | 0 | if (this->stepSize == 0 || this->stepSize == INT_MIN || |
446 | 0 | this->numberOfValues == INT_MIN || |
447 | 0 | abs(this->stepSize) > INT_MAX / abs(this->numberOfValues)) |
448 | 0 | { |
449 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
450 | 0 | "Bad values of stepSize / numberOfValues"); |
451 | 0 | return; |
452 | 0 | } |
453 | | |
454 | 0 | this->m_nTableSize = abs(this->stepSize) * abs(this->numberOfValues); |
455 | |
|
456 | 0 | if (this->m_nTableSize < this->m_poBandDataset->GetRasterXSize()) |
457 | 0 | { |
458 | 0 | CPLError( |
459 | 0 | CE_Failure, CPLE_NotSupported, |
460 | 0 | "ERROR: The RCM driver does not support range of LUT gain values " |
461 | 0 | "lower than the full image pixel range."); |
462 | 0 | return; |
463 | 0 | } |
464 | | |
465 | | // Avoid excessive memory allocation |
466 | 0 | if (this->m_nTableSize > 1000 * 1000) |
467 | 0 | { |
468 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Too many elements in LUT: %d", |
469 | 0 | this->m_nTableSize); |
470 | 0 | return; |
471 | 0 | } |
472 | | |
473 | | /* Allocate the right LUT size according to the product range pixel */ |
474 | 0 | this->m_nfTable = |
475 | 0 | InterpolateValues(aosLUTList.List(), this->m_nTableSize, this->stepSize, |
476 | 0 | this->numberOfValues, this->pixelFirstLutValue); |
477 | 0 | if (!this->m_nfTable) |
478 | 0 | return; |
479 | | |
480 | | // 32 max + space |
481 | 0 | char *lut_gains = static_cast<char *>( |
482 | 0 | VSI_CALLOC_VERBOSE(this->m_nTableSize, max_space_for_string)); |
483 | 0 | if (!lut_gains) |
484 | 0 | return; |
485 | | |
486 | 0 | for (int i = 0; i < this->m_nTableSize; i++) |
487 | 0 | { |
488 | 0 | char lut[max_space_for_string]; |
489 | | // 6.123004711900930e+04 %e Scientific annotation |
490 | 0 | snprintf(lut, sizeof(lut), "%e ", this->m_nfTable[i]); |
491 | 0 | strcat(lut_gains, lut); |
492 | 0 | } |
493 | |
|
494 | 0 | poDS->SetMetadataItem(CPLString("LUT_GAINS_").append(bandNumber).c_str(), |
495 | 0 | lut_gains); |
496 | | // Can free this because the function SetMetadataItem takes a copy |
497 | 0 | CPLFree(lut_gains); |
498 | |
|
499 | 0 | char snum[256]; |
500 | 0 | if (this->m_eCalib == eCalibration::Sigma0) |
501 | 0 | { |
502 | 0 | poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(), |
503 | 0 | "SIGMA0"); |
504 | 0 | } |
505 | 0 | else if (this->m_eCalib == eCalibration::Beta0) |
506 | 0 | { |
507 | 0 | poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(), |
508 | 0 | "BETA0"); |
509 | 0 | } |
510 | 0 | else if (this->m_eCalib == eCalibration::Gamma) |
511 | 0 | { |
512 | 0 | poDS->SetMetadataItem(CPLString("LUT_TYPE_").append(bandNumber).c_str(), |
513 | 0 | "GAMMA"); |
514 | 0 | } |
515 | 0 | snprintf(snum, sizeof(snum), "%d", this->m_nTableSize); |
516 | 0 | poDS->SetMetadataItem(CPLString("LUT_SIZE_").append(bandNumber).c_str(), |
517 | 0 | snum); |
518 | 0 | snprintf(snum, sizeof(snum), "%f", this->m_nfOffset); |
519 | 0 | poDS->SetMetadataItem(CPLString("LUT_OFFSET_").append(bandNumber).c_str(), |
520 | 0 | snum); |
521 | 0 | } |
522 | | |
523 | | /************************************************************************/ |
524 | | /* ReadNoiseLevels() */ |
525 | | /************************************************************************/ |
526 | | /* Read the provided LUT in to m_nfTableNoiseLevels */ |
527 | | /* 1. The gains list spans the range extent covered by all */ |
528 | | /* beams(if applicable). */ |
529 | | /* 2. The mapping between the entry of gains */ |
530 | | /* list and the range sample index is : the range sample */ |
531 | | /* index = gains entry index * stepSize + pixelFirstLutValue, */ |
532 | | /* where the gains entry index starts with ‘0’.For ScanSAR SLC, */ |
533 | | /* the range sample index refers to the index on the COPG */ |
534 | | /************************************************************************/ |
535 | | void RCMCalibRasterBand::ReadNoiseLevels() |
536 | 0 | { |
537 | |
|
538 | 0 | this->m_nfTableNoiseLevels = nullptr; |
539 | |
|
540 | 0 | if (this->m_pszNoiseLevelsFile == nullptr) |
541 | 0 | { |
542 | 0 | return; |
543 | 0 | } |
544 | | |
545 | 0 | char bandNumber[12]; |
546 | 0 | snprintf(bandNumber, sizeof(bandNumber), "%d", poDS->GetRasterCount() + 1); |
547 | |
|
548 | 0 | CPLXMLTreeCloser psNoiseLevels(CPLParseXMLFile(this->m_pszNoiseLevelsFile)); |
549 | 0 | if (!psNoiseLevels) |
550 | 0 | return; |
551 | | |
552 | | // Load Beta Nought, Sigma Nought, Gamma noise levels |
553 | | // Loop through all nodes with spaces |
554 | 0 | const CPLXMLNode *psReferenceNoiseLevelNode = |
555 | 0 | CPLGetXMLNode(psNoiseLevels.get(), "=noiseLevels"); |
556 | 0 | if (!psReferenceNoiseLevelNode) |
557 | 0 | return; |
558 | | |
559 | 0 | for (const CPLXMLNode *psNodeInc = psReferenceNoiseLevelNode->psChild; |
560 | 0 | psNodeInc != nullptr; psNodeInc = psNodeInc->psNext) |
561 | 0 | { |
562 | 0 | if (EQUAL(psNodeInc->pszValue, "referenceNoiseLevel")) |
563 | 0 | { |
564 | 0 | const CPLXMLNode *psCalibType = |
565 | 0 | CPLGetXMLNode(psNodeInc, "sarCalibrationType"); |
566 | 0 | const CPLXMLNode *psPixelFirstNoiseValue = |
567 | 0 | CPLGetXMLNode(psNodeInc, "pixelFirstNoiseValue"); |
568 | 0 | const CPLXMLNode *psStepSize = CPLGetXMLNode(psNodeInc, "stepSize"); |
569 | 0 | const CPLXMLNode *psNumberOfValues = |
570 | 0 | CPLGetXMLNode(psNodeInc, "numberOfValues"); |
571 | 0 | const CPLXMLNode *psNoiseLevelValues = |
572 | 0 | CPLGetXMLNode(psNodeInc, "noiseLevelValues"); |
573 | |
|
574 | 0 | if (psCalibType != nullptr && psPixelFirstNoiseValue != nullptr && |
575 | 0 | psStepSize != nullptr && psNumberOfValues != nullptr && |
576 | 0 | psNoiseLevelValues != nullptr) |
577 | 0 | { |
578 | 0 | const char *calibType = CPLGetXMLValue(psCalibType, "", ""); |
579 | 0 | this->pixelFirstLutValueNoiseLevels = |
580 | 0 | atoi(CPLGetXMLValue(psPixelFirstNoiseValue, "", "0")); |
581 | 0 | this->stepSizeNoiseLevels = |
582 | 0 | atoi(CPLGetXMLValue(psStepSize, "", "0")); |
583 | 0 | this->numberOfValuesNoiseLevels = |
584 | 0 | atoi(CPLGetXMLValue(psNumberOfValues, "", "0")); |
585 | 0 | const char *noiseLevelValues = |
586 | 0 | CPLGetXMLValue(psNoiseLevelValues, "", ""); |
587 | 0 | if (this->stepSizeNoiseLevels > 0 && |
588 | 0 | this->numberOfValuesNoiseLevels != INT_MIN && |
589 | 0 | abs(this->numberOfValuesNoiseLevels) < |
590 | 0 | INT_MAX / this->stepSizeNoiseLevels) |
591 | 0 | { |
592 | 0 | char **papszNoiseLevelList = CSLTokenizeString2( |
593 | 0 | noiseLevelValues, " ", CSLT_HONOURSTRINGS); |
594 | | /* Get the Pixel Per range */ |
595 | 0 | this->m_nTableNoiseLevelsSize = |
596 | 0 | abs(this->stepSizeNoiseLevels) * |
597 | 0 | abs(this->numberOfValuesNoiseLevels); |
598 | |
|
599 | 0 | if ((EQUAL(calibType, "Beta Nought") && |
600 | 0 | this->m_eCalib == Beta0) || |
601 | 0 | (EQUAL(calibType, "Sigma Nought") && |
602 | 0 | this->m_eCalib == Sigma0) || |
603 | 0 | (EQUAL(calibType, "Gamma") && this->m_eCalib == Gamma)) |
604 | 0 | { |
605 | | /* Allocate the right Noise Levels size according to the |
606 | | * product range pixel */ |
607 | 0 | this->m_nfTableNoiseLevels = InterpolateValues( |
608 | 0 | papszNoiseLevelList, this->m_nTableNoiseLevelsSize, |
609 | 0 | this->stepSizeNoiseLevels, |
610 | 0 | this->numberOfValuesNoiseLevels, |
611 | 0 | this->pixelFirstLutValueNoiseLevels); |
612 | 0 | } |
613 | |
|
614 | 0 | CSLDestroy(papszNoiseLevelList); |
615 | 0 | } |
616 | |
|
617 | 0 | if (this->m_nfTableNoiseLevels != nullptr) |
618 | 0 | { |
619 | 0 | break; // We are done |
620 | 0 | } |
621 | 0 | } |
622 | 0 | } |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | | /************************************************************************/ |
627 | | /* RCMCalibRasterBand() */ |
628 | | /************************************************************************/ |
629 | | |
630 | | RCMCalibRasterBand::RCMCalibRasterBand( |
631 | | RCMDataset *poDataset, const char *pszPolarization, GDALDataType eType, |
632 | | GDALDataset *poBandDataset, eCalibration eCalib, const char *pszLUT, |
633 | | const char *pszNoiseLevels, GDALDataType eOriginalType) |
634 | 0 | : m_eCalib(eCalib), m_poBandDataset(poBandDataset), |
635 | 0 | m_eOriginalType(eOriginalType), m_pszLUTFile(VSIStrdup(pszLUT)), |
636 | 0 | m_pszNoiseLevelsFile(VSIStrdup(pszNoiseLevels)) |
637 | 0 | { |
638 | 0 | this->poDS = poDataset; |
639 | |
|
640 | 0 | if (pszPolarization != nullptr && strlen(pszPolarization) != 0) |
641 | 0 | { |
642 | 0 | SetMetadataItem("POLARIMETRIC_INTERP", pszPolarization); |
643 | 0 | } |
644 | |
|
645 | 0 | if ((eType == GDT_CInt16) || (eType == GDT_CFloat32)) |
646 | 0 | this->eDataType = GDT_CFloat32; |
647 | 0 | else |
648 | 0 | this->eDataType = GDT_Float32; |
649 | |
|
650 | 0 | GDALRasterBand *poRasterBand = poBandDataset->GetRasterBand(1); |
651 | 0 | poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize); |
652 | |
|
653 | 0 | ReadLUT(); |
654 | 0 | ReadNoiseLevels(); |
655 | 0 | } |
656 | | |
657 | | /************************************************************************/ |
658 | | /* ~RCMCalibRasterBand() */ |
659 | | /************************************************************************/ |
660 | | |
661 | | RCMCalibRasterBand::~RCMCalibRasterBand() |
662 | 0 | { |
663 | 0 | CPLFree(m_nfTable); |
664 | 0 | CPLFree(m_nfTableNoiseLevels); |
665 | 0 | CPLFree(m_pszLUTFile); |
666 | 0 | CPLFree(m_pszNoiseLevelsFile); |
667 | 0 | GDALClose(m_poBandDataset); |
668 | 0 | } |
669 | | |
670 | | /************************************************************************/ |
671 | | /* IReadBlock() */ |
672 | | /************************************************************************/ |
673 | | |
674 | | CPLErr RCMCalibRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, |
675 | | void *pImage) |
676 | 0 | { |
677 | 0 | CPLErr eErr; |
678 | 0 | int nRequestXSize = 0; |
679 | 0 | int nRequestYSize = 0; |
680 | 0 | GetActualBlockSize(nBlockXOff, nBlockYOff, &nRequestXSize, &nRequestYSize); |
681 | | |
682 | | // Zero initial partial right-most and bottom-most blocks |
683 | 0 | if (nRequestXSize < nBlockXSize || nRequestYSize < nBlockYSize) |
684 | 0 | { |
685 | 0 | memset(pImage, 0, |
686 | 0 | static_cast<size_t>(GDALGetDataTypeSizeBytes(eDataType)) * |
687 | 0 | nBlockXSize * nBlockYSize); |
688 | 0 | } |
689 | |
|
690 | 0 | if (this->m_eOriginalType == GDT_CInt16) |
691 | 0 | { |
692 | | /* read in complex values */ |
693 | 0 | GInt16 *panImageTmp = static_cast<GInt16 *>( |
694 | 0 | VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize, |
695 | 0 | GDALGetDataTypeSizeBytes(m_eOriginalType))); |
696 | 0 | if (!panImageTmp) |
697 | 0 | return CE_Failure; |
698 | | |
699 | 0 | if (m_poBandDataset->GetRasterCount() == 2) |
700 | 0 | { |
701 | 0 | eErr = m_poBandDataset->RasterIO( |
702 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
703 | 0 | nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize, |
704 | 0 | nRequestYSize, this->m_eOriginalType, 2, nullptr, 4, |
705 | 0 | nBlockXSize * 4, 4, nullptr); |
706 | | |
707 | | /* |
708 | | eErr = m_poBandDataset->RasterIO(GF_Read, |
709 | | nBlockXOff * nBlockXSize, |
710 | | nBlockYOff * nBlockYSize, |
711 | | nRequestXSize, nRequestYSize, |
712 | | panImageTmp, nRequestXSize, nRequestYSize, |
713 | | GDT_Int32, |
714 | | 2, nullptr, 4, nBlockXSize * 4, 2, nullptr); |
715 | | */ |
716 | 0 | } |
717 | 0 | else |
718 | 0 | { |
719 | 0 | eErr = m_poBandDataset->RasterIO( |
720 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
721 | 0 | nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize, |
722 | 0 | nRequestYSize, this->m_eOriginalType, 1, nullptr, 4, |
723 | 0 | nBlockXSize * 4, 0, nullptr); |
724 | | |
725 | | /* |
726 | | eErr = |
727 | | m_poBandDataset->RasterIO(GF_Read, |
728 | | nBlockXOff * nBlockXSize, |
729 | | nBlockYOff * nBlockYSize, |
730 | | nRequestXSize, nRequestYSize, |
731 | | panImageTmp, nRequestXSize, nRequestYSize, |
732 | | GDT_UInt32, |
733 | | 1, nullptr, 4, nBlockXSize * 4, 0, nullptr); |
734 | | */ |
735 | |
|
736 | 0 | #ifdef CPL_LSB |
737 | | /* First, undo the 32bit swap. */ |
738 | 0 | GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4); |
739 | | |
740 | | /* Then apply 16 bit swap. */ |
741 | 0 | GDALSwapWords(pImage, 2, nBlockXSize * nBlockYSize * 2, 2); |
742 | 0 | #endif |
743 | 0 | } |
744 | | |
745 | | /* calibrate the complex values */ |
746 | 0 | for (int i = 0; i < nRequestYSize; i++) |
747 | 0 | { |
748 | 0 | for (int j = 0; j < nRequestXSize; j++) |
749 | 0 | { |
750 | | /* calculate pixel offset in memory*/ |
751 | 0 | const int nPixOff = 2 * (i * nBlockXSize + j); |
752 | 0 | const int nTruePixOff = (i * nBlockXSize) + j; |
753 | | |
754 | | // Formula for Complex Q+J |
755 | 0 | const float real = static_cast<float>(panImageTmp[nPixOff]); |
756 | 0 | const float img = static_cast<float>(panImageTmp[nPixOff + 1]); |
757 | 0 | const float digitalValue = (real * real) + (img * img); |
758 | 0 | const float lutValue = |
759 | 0 | static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]); |
760 | 0 | const float calibValue = digitalValue / (lutValue * lutValue); |
761 | |
|
762 | 0 | reinterpret_cast<float *>(pImage)[nTruePixOff] = calibValue; |
763 | 0 | } |
764 | 0 | } |
765 | |
|
766 | 0 | CPLFree(panImageTmp); |
767 | 0 | } |
768 | | |
769 | | // If the underlying file is NITF CFloat32 |
770 | 0 | else if (this->m_eOriginalType == GDT_CFloat32 || |
771 | 0 | this->m_eOriginalType == GDT_CFloat64) |
772 | 0 | { |
773 | | /* read in complex values */ |
774 | 0 | const int dataTypeSize = |
775 | 0 | GDALGetDataTypeSizeBytes(this->m_eOriginalType); |
776 | 0 | const GDALDataType bandFileType = this->m_eOriginalType; |
777 | 0 | const int bandFileDataTypeSize = GDALGetDataTypeSizeBytes(bandFileType); |
778 | | |
779 | | /* read the original image complex values in a temporary image space */ |
780 | 0 | float *pafImageTmp = static_cast<float *>(VSI_MALLOC3_VERBOSE( |
781 | 0 | nBlockXSize, nBlockYSize, 2 * bandFileDataTypeSize)); |
782 | 0 | if (!pafImageTmp) |
783 | 0 | return CE_Failure; |
784 | | |
785 | 0 | eErr = |
786 | | // I and Q from each band are pixel-interleaved into this complex |
787 | | // band |
788 | 0 | m_poBandDataset->RasterIO( |
789 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
790 | 0 | nRequestXSize, nRequestYSize, pafImageTmp, nRequestXSize, |
791 | 0 | nRequestYSize, bandFileType, 2, nullptr, dataTypeSize, |
792 | 0 | static_cast<GSpacing>(dataTypeSize) * nBlockXSize, |
793 | 0 | bandFileDataTypeSize, nullptr); |
794 | | |
795 | | /* calibrate the complex values */ |
796 | 0 | for (int i = 0; i < nRequestYSize; i++) |
797 | 0 | { |
798 | 0 | for (int j = 0; j < nRequestXSize; j++) |
799 | 0 | { |
800 | | /* calculate pixel offset in memory*/ |
801 | 0 | const int nPixOff = 2 * (i * nBlockXSize + j); |
802 | 0 | const int nTruePixOff = (i * nBlockXSize) + j; |
803 | | |
804 | | // Formula for Complex Q+J |
805 | 0 | const float real = pafImageTmp[nPixOff]; |
806 | 0 | const float img = pafImageTmp[nPixOff + 1]; |
807 | 0 | const float digitalValue = (real * real) + (img * img); |
808 | 0 | const float lutValue = |
809 | 0 | static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]); |
810 | 0 | const float calibValue = digitalValue / (lutValue * lutValue); |
811 | |
|
812 | 0 | reinterpret_cast<float *>(pImage)[nTruePixOff] = calibValue; |
813 | 0 | } |
814 | 0 | } |
815 | |
|
816 | 0 | CPLFree(pafImageTmp); |
817 | 0 | } |
818 | | |
819 | 0 | else if (this->m_eOriginalType == GDT_Float32) |
820 | 0 | { |
821 | | /* A Float32 is actual 4 bytes */ |
822 | 0 | eErr = m_poBandDataset->RasterIO( |
823 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
824 | 0 | nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize, |
825 | 0 | GDT_Float32, 1, nullptr, 4, nBlockXSize * 4, 0, nullptr); |
826 | | |
827 | | /* iterate over detected values */ |
828 | 0 | for (int i = 0; i < nRequestYSize; i++) |
829 | 0 | { |
830 | 0 | for (int j = 0; j < nRequestXSize; j++) |
831 | 0 | { |
832 | 0 | int nPixOff = (i * nBlockXSize) + j; |
833 | | |
834 | | /* For detected products, in order to convert the digital number |
835 | | of a given range sample to a calibrated value, the digital value |
836 | | is first squared, then the offset(B) is added and the result is |
837 | | divided by the gains value(A) corresponding to the range sample. |
838 | | RCM-SP-53-0419 Issue 2/5: January 2, 2018 Page 7-56 */ |
839 | 0 | const float digitalValue = |
840 | 0 | reinterpret_cast<float *>(pImage)[nPixOff]; |
841 | 0 | const float A = |
842 | 0 | static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]); |
843 | 0 | reinterpret_cast<float *>(pImage)[nPixOff] = |
844 | 0 | ((digitalValue * digitalValue) + |
845 | 0 | static_cast<float>(this->m_nfOffset)) / |
846 | 0 | A; |
847 | 0 | } |
848 | 0 | } |
849 | 0 | } |
850 | | |
851 | 0 | else if (this->m_eOriginalType == GDT_Float64) |
852 | 0 | { |
853 | | /* A Float64 is actual 8 bytes */ |
854 | 0 | eErr = m_poBandDataset->RasterIO( |
855 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
856 | 0 | nRequestXSize, nRequestYSize, pImage, nRequestXSize, nRequestYSize, |
857 | 0 | GDT_Float64, 1, nullptr, 8, nBlockXSize * 8, 0, nullptr); |
858 | | |
859 | | /* iterate over detected values */ |
860 | 0 | for (int i = 0; i < nRequestYSize; i++) |
861 | 0 | { |
862 | 0 | for (int j = 0; j < nRequestXSize; j++) |
863 | 0 | { |
864 | 0 | int nPixOff = (i * nBlockXSize) + j; |
865 | | |
866 | | /* For detected products, in order to convert the digital number |
867 | | of a given range sample to a calibrated value, the digital value |
868 | | is first squared, then the offset(B) is added and the result is |
869 | | divided by the gains value(A) corresponding to the range sample. |
870 | | RCM-SP-53-0419 Issue 2/5: January 2, 2018 Page 7-56 */ |
871 | 0 | const float digitalValue = |
872 | 0 | reinterpret_cast<float *>(pImage)[nPixOff]; |
873 | 0 | const float A = |
874 | 0 | static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]); |
875 | 0 | reinterpret_cast<float *>(pImage)[nPixOff] = |
876 | 0 | ((digitalValue * digitalValue) + |
877 | 0 | static_cast<float>(this->m_nfOffset)) / |
878 | 0 | A; |
879 | 0 | } |
880 | 0 | } |
881 | 0 | } |
882 | | |
883 | 0 | else if (this->m_eOriginalType == GDT_UInt16) |
884 | 0 | { |
885 | | /* read in detected values */ |
886 | 0 | GUInt16 *panImageTmp = static_cast<GUInt16 *>(VSI_MALLOC3_VERBOSE( |
887 | 0 | nBlockXSize, nBlockYSize, GDALGetDataTypeSizeBytes(GDT_UInt16))); |
888 | 0 | if (!panImageTmp) |
889 | 0 | return CE_Failure; |
890 | 0 | eErr = m_poBandDataset->RasterIO( |
891 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
892 | 0 | nRequestXSize, nRequestYSize, panImageTmp, nRequestXSize, |
893 | 0 | nRequestYSize, GDT_UInt16, 1, nullptr, 2, nBlockXSize * 2, 0, |
894 | 0 | nullptr); |
895 | | |
896 | | /* iterate over detected values */ |
897 | 0 | for (int i = 0; i < nRequestYSize; i++) |
898 | 0 | { |
899 | 0 | for (int j = 0; j < nRequestXSize; j++) |
900 | 0 | { |
901 | 0 | const int nPixOff = (i * nBlockXSize) + j; |
902 | |
|
903 | 0 | const float digitalValue = |
904 | 0 | static_cast<float>(panImageTmp[nPixOff]); |
905 | 0 | const float A = |
906 | 0 | static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]); |
907 | 0 | reinterpret_cast<float *>(pImage)[nPixOff] = |
908 | 0 | ((digitalValue * digitalValue) + |
909 | 0 | static_cast<float>(this->m_nfOffset)) / |
910 | 0 | A; |
911 | 0 | } |
912 | 0 | } |
913 | 0 | CPLFree(panImageTmp); |
914 | 0 | } /* Ticket #2104: Support for ScanSAR products */ |
915 | | |
916 | 0 | else if (this->m_eOriginalType == GDT_UInt8) |
917 | 0 | { |
918 | 0 | GByte *pabyImageTmp = |
919 | 0 | static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nBlockXSize, nBlockYSize)); |
920 | 0 | if (!pabyImageTmp) |
921 | 0 | return CE_Failure; |
922 | 0 | eErr = m_poBandDataset->RasterIO( |
923 | 0 | GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, |
924 | 0 | nRequestXSize, nRequestYSize, pabyImageTmp, nRequestXSize, |
925 | 0 | nRequestYSize, GDT_UInt8, 1, nullptr, 1, nBlockXSize, 0, nullptr); |
926 | | |
927 | | /* iterate over detected values */ |
928 | 0 | for (int i = 0; i < nRequestYSize; i++) |
929 | 0 | { |
930 | 0 | for (int j = 0; j < nRequestXSize; j++) |
931 | 0 | { |
932 | 0 | const int nPixOff = (i * nBlockXSize) + j; |
933 | |
|
934 | 0 | const float digitalValue = |
935 | 0 | static_cast<float>(pabyImageTmp[nPixOff]); |
936 | 0 | const float A = |
937 | 0 | static_cast<float>(m_nfTable[nBlockXOff * nBlockXSize + j]); |
938 | 0 | reinterpret_cast<float *>(pImage)[nPixOff] = |
939 | 0 | ((digitalValue * digitalValue) + |
940 | 0 | static_cast<float>(this->m_nfOffset)) / |
941 | 0 | A; |
942 | 0 | } |
943 | 0 | } |
944 | 0 | CPLFree(pabyImageTmp); |
945 | 0 | } |
946 | 0 | else |
947 | 0 | { |
948 | 0 | CPLAssert(FALSE); |
949 | 0 | return CE_Failure; |
950 | 0 | } |
951 | 0 | return eErr; |
952 | 0 | } |
953 | | |
954 | | /************************************************************************/ |
955 | | /* ==================================================================== */ |
956 | | /* RCMDataset */ |
957 | | /* ==================================================================== */ |
958 | | /************************************************************************/ |
959 | | |
960 | | /************************************************************************/ |
961 | | /* ~RCMDataset() */ |
962 | | /************************************************************************/ |
963 | | |
964 | | RCMDataset::~RCMDataset() |
965 | | |
966 | 0 | { |
967 | 0 | RCMDataset::FlushCache(true); |
968 | |
|
969 | 0 | if (nGCPCount > 0) |
970 | 0 | { |
971 | 0 | GDALDeinitGCPs(nGCPCount, pasGCPList); |
972 | 0 | CPLFree(pasGCPList); |
973 | 0 | } |
974 | |
|
975 | 0 | RCMDataset::CloseDependentDatasets(); |
976 | |
|
977 | 0 | if (papszSubDatasets != nullptr) |
978 | 0 | CSLDestroy(papszSubDatasets); |
979 | |
|
980 | 0 | if (papszExtraFiles != nullptr) |
981 | 0 | CSLDestroy(papszExtraFiles); |
982 | |
|
983 | 0 | if (m_nfIncidenceAngleTable != nullptr) |
984 | 0 | CPLFree(m_nfIncidenceAngleTable); |
985 | 0 | } |
986 | | |
987 | | /************************************************************************/ |
988 | | /* CloseDependentDatasets() */ |
989 | | /************************************************************************/ |
990 | | |
991 | | int RCMDataset::CloseDependentDatasets() |
992 | 0 | { |
993 | 0 | int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); |
994 | |
|
995 | 0 | if (nBands != 0) |
996 | 0 | bHasDroppedRef = TRUE; |
997 | |
|
998 | 0 | for (int iBand = 0; iBand < nBands; iBand++) |
999 | 0 | { |
1000 | 0 | delete papoBands[iBand]; |
1001 | 0 | } |
1002 | 0 | nBands = 0; |
1003 | |
|
1004 | 0 | return bHasDroppedRef; |
1005 | 0 | } |
1006 | | |
1007 | | /************************************************************************/ |
1008 | | /* GetFileList() */ |
1009 | | /************************************************************************/ |
1010 | | |
1011 | | char **RCMDataset::GetFileList() |
1012 | | |
1013 | 0 | { |
1014 | 0 | char **papszFileList = GDALPamDataset::GetFileList(); |
1015 | |
|
1016 | 0 | papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles); |
1017 | |
|
1018 | 0 | return papszFileList; |
1019 | 0 | } |
1020 | | |
1021 | | /************************************************************************/ |
1022 | | /* Open() */ |
1023 | | /************************************************************************/ |
1024 | | |
1025 | | GDALDataset *RCMDataset::Open(GDALOpenInfo *poOpenInfo) |
1026 | | |
1027 | 0 | { |
1028 | | /* -------------------------------------------------------------------- */ |
1029 | | /* Is this a RCM Product.xml definition? */ |
1030 | | /* -------------------------------------------------------------------- */ |
1031 | 0 | if (!RCMDatasetIdentify(poOpenInfo)) |
1032 | 0 | { |
1033 | 0 | return nullptr; |
1034 | 0 | } |
1035 | | |
1036 | | /* -------------------------------------------------------------------- */ |
1037 | | /* Get subdataset information, if relevant */ |
1038 | | /* -------------------------------------------------------------------- */ |
1039 | 0 | const char *pszFilename = poOpenInfo->pszFilename; |
1040 | 0 | eCalibration eCalib = None; |
1041 | |
|
1042 | 0 | if (STARTS_WITH_CI(pszFilename, szLayerCalibration) && |
1043 | 0 | pszFilename[strlen(szLayerCalibration)] == chLayerSeparator) |
1044 | 0 | { |
1045 | | // The calibration name and filename begins after the hard coded layer |
1046 | | // name |
1047 | 0 | pszFilename += strlen(szLayerCalibration) + 1; |
1048 | |
|
1049 | 0 | if (STARTS_WITH_CI(pszFilename, szBETA0)) |
1050 | 0 | { |
1051 | 0 | eCalib = Beta0; |
1052 | 0 | } |
1053 | 0 | else if (STARTS_WITH_CI(pszFilename, szSIGMA0)) |
1054 | 0 | { |
1055 | 0 | eCalib = Sigma0; |
1056 | 0 | } |
1057 | 0 | else if (STARTS_WITH_CI(pszFilename, szGAMMA) || |
1058 | 0 | STARTS_WITH_CI(pszFilename, "GAMMA0")) // Cover both situation |
1059 | 0 | { |
1060 | 0 | eCalib = Gamma; |
1061 | 0 | } |
1062 | 0 | else if (STARTS_WITH_CI(pszFilename, szUNCALIB)) |
1063 | 0 | { |
1064 | 0 | eCalib = Uncalib; |
1065 | 0 | } |
1066 | 0 | else |
1067 | 0 | { |
1068 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1069 | 0 | "Unsupported calibration type"); |
1070 | 0 | return nullptr; |
1071 | 0 | } |
1072 | | |
1073 | | /* advance the pointer to the actual filename */ |
1074 | 0 | while (*pszFilename != '\0' && *pszFilename != ':') |
1075 | 0 | pszFilename++; |
1076 | |
|
1077 | 0 | if (*pszFilename == ':') |
1078 | 0 | pszFilename++; |
1079 | | |
1080 | | // need to redo the directory check: |
1081 | | // the GDALOpenInfo check would have failed because of the calibration |
1082 | | // string on the filename |
1083 | 0 | VSIStatBufL sStat; |
1084 | 0 | if (VSIStatL(pszFilename, &sStat) == 0) |
1085 | 0 | poOpenInfo->bIsDirectory = VSI_ISDIR(sStat.st_mode); |
1086 | 0 | } |
1087 | | |
1088 | 0 | CPLString osMDFilename; |
1089 | 0 | if (poOpenInfo->bIsDirectory) |
1090 | 0 | { |
1091 | | /* Check for directory access when there is a product.xml file in the |
1092 | | directory. */ |
1093 | 0 | osMDFilename = |
1094 | 0 | CPLFormCIFilenameSafe(pszFilename, "product.xml", nullptr); |
1095 | |
|
1096 | 0 | VSIStatBufL sStat; |
1097 | 0 | if (VSIStatL(osMDFilename, &sStat) != 0) |
1098 | 0 | { |
1099 | | /* If not, check for directory extra 'metadata' access when there is |
1100 | | a product.xml file in the directory. */ |
1101 | 0 | osMDFilename = CPLFormCIFilenameSafe(pszFilename, |
1102 | 0 | GetMetadataProduct(), nullptr); |
1103 | 0 | } |
1104 | 0 | } |
1105 | 0 | else |
1106 | 0 | osMDFilename = pszFilename; |
1107 | | |
1108 | | /* -------------------------------------------------------------------- */ |
1109 | | /* Ingest the Product.xml file. */ |
1110 | | /* -------------------------------------------------------------------- */ |
1111 | 0 | CPLXMLTreeCloser psProduct(CPLParseXMLFile(osMDFilename)); |
1112 | 0 | if (!psProduct) |
1113 | 0 | return nullptr; |
1114 | | |
1115 | | /* -------------------------------------------------------------------- */ |
1116 | | /* Confirm the requested access is supported. */ |
1117 | | /* -------------------------------------------------------------------- */ |
1118 | 0 | if (poOpenInfo->eAccess == GA_Update) |
1119 | 0 | { |
1120 | 0 | ReportUpdateNotSupportedByDriver("RCM"); |
1121 | 0 | return nullptr; |
1122 | 0 | } |
1123 | | |
1124 | 0 | const CPLXMLNode *psSceneAttributes = |
1125 | 0 | CPLGetXMLNode(psProduct.get(), "=product.sceneAttributes"); |
1126 | 0 | if (psSceneAttributes == nullptr) |
1127 | 0 | { |
1128 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
1129 | 0 | "ERROR: Failed to find <sceneAttributes> in document."); |
1130 | 0 | return nullptr; |
1131 | 0 | } |
1132 | | |
1133 | 0 | const CPLXMLNode *psImageAttributes = CPLGetXMLNode( |
1134 | 0 | psProduct.get(), "=product.sceneAttributes.imageAttributes"); |
1135 | 0 | if (psImageAttributes == nullptr) |
1136 | 0 | { |
1137 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
1138 | 0 | "ERROR: Failed to find <sceneAttributes.imageAttributes> in " |
1139 | 0 | "document."); |
1140 | 0 | return nullptr; |
1141 | 0 | } |
1142 | | |
1143 | 0 | int numberOfEntries = |
1144 | 0 | atoi(CPLGetXMLValue(psSceneAttributes, "numberOfEntries", "0")); |
1145 | 0 | if (numberOfEntries != 1) |
1146 | 0 | { |
1147 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
1148 | 0 | "ERROR: Only RCM with Complex Single-beam is supported."); |
1149 | 0 | return nullptr; |
1150 | 0 | } |
1151 | | |
1152 | 0 | const CPLXMLNode *psImageReferenceAttributes = |
1153 | 0 | CPLGetXMLNode(psProduct.get(), "=product.imageReferenceAttributes"); |
1154 | 0 | if (psImageReferenceAttributes == nullptr) |
1155 | 0 | { |
1156 | 0 | CPLError( |
1157 | 0 | CE_Failure, CPLE_OpenFailed, |
1158 | 0 | "ERROR: Failed to find <imageReferenceAttributes> in document."); |
1159 | 0 | return nullptr; |
1160 | 0 | } |
1161 | | |
1162 | 0 | const CPLXMLNode *psImageGenerationParameters = |
1163 | 0 | CPLGetXMLNode(psProduct.get(), "=product.imageGenerationParameters"); |
1164 | 0 | if (psImageGenerationParameters == nullptr) |
1165 | 0 | { |
1166 | 0 | CPLError( |
1167 | 0 | CE_Failure, CPLE_OpenFailed, |
1168 | 0 | "ERROR: Failed to find <imageGenerationParameters> in document."); |
1169 | 0 | return nullptr; |
1170 | 0 | } |
1171 | | |
1172 | | /* -------------------------------------------------------------------- */ |
1173 | | /* Create the dataset. */ |
1174 | | /* -------------------------------------------------------------------- */ |
1175 | 0 | auto poDS = std::make_unique<RCMDataset>(); |
1176 | |
|
1177 | 0 | poDS->psProduct = std::move(psProduct); |
1178 | | |
1179 | | /* -------------------------------------------------------------------- */ |
1180 | | /* Get overall image information. */ |
1181 | | /* -------------------------------------------------------------------- */ |
1182 | 0 | poDS->nRasterXSize = atoi(CPLGetXMLValue( |
1183 | 0 | psSceneAttributes, "imageAttributes.samplesPerLine", "-1")); |
1184 | 0 | poDS->nRasterYSize = atoi( |
1185 | 0 | CPLGetXMLValue(psSceneAttributes, "imageAttributes.numLines", "-1")); |
1186 | 0 | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) |
1187 | 0 | { |
1188 | 0 | return nullptr; |
1189 | 0 | } |
1190 | | |
1191 | | /* -------------------------------------------------------------------- */ |
1192 | | /* Check product type, as to determine if there are LUTs for */ |
1193 | | /* calibration purposes. */ |
1194 | | /* -------------------------------------------------------------------- */ |
1195 | | |
1196 | 0 | const char *pszItem = |
1197 | 0 | CPLGetXMLValue(psImageGenerationParameters, |
1198 | 0 | "generalProcessingInformation.productType", "UNK"); |
1199 | 0 | poDS->SetMetadataItem("PRODUCT_TYPE", pszItem); |
1200 | 0 | const char *pszProductType = pszItem; |
1201 | |
|
1202 | 0 | pszItem = |
1203 | 0 | CPLGetXMLValue(poDS->psProduct.get(), "=product.productId", "UNK"); |
1204 | 0 | poDS->SetMetadataItem("PRODUCT_ID", pszItem); |
1205 | |
|
1206 | 0 | pszItem = CPLGetXMLValue( |
1207 | 0 | poDS->psProduct.get(), |
1208 | 0 | "=product.securityAttributes.securityClassification", "UNK"); |
1209 | 0 | poDS->SetMetadataItem("SECURITY_CLASSIFICATION", pszItem); |
1210 | |
|
1211 | 0 | pszItem = |
1212 | 0 | CPLGetXMLValue(poDS->psProduct.get(), |
1213 | 0 | "=product.sourceAttributes.polarizationDataMode", "UNK"); |
1214 | 0 | poDS->SetMetadataItem("POLARIZATION_DATA_MODE", pszItem); |
1215 | |
|
1216 | 0 | pszItem = CPLGetXMLValue(psImageGenerationParameters, |
1217 | 0 | "generalProcessingInformation.processingFacility", |
1218 | 0 | "UNK"); |
1219 | 0 | poDS->SetMetadataItem("PROCESSING_FACILITY", pszItem); |
1220 | |
|
1221 | 0 | pszItem = |
1222 | 0 | CPLGetXMLValue(psImageGenerationParameters, |
1223 | 0 | "generalProcessingInformation.processingTime", "UNK"); |
1224 | 0 | poDS->SetMetadataItem("PROCESSING_TIME", pszItem); |
1225 | |
|
1226 | 0 | pszItem = CPLGetXMLValue(psImageGenerationParameters, |
1227 | 0 | "sarProcessingInformation.satelliteHeight", "UNK"); |
1228 | 0 | poDS->SetMetadataItem("SATELLITE_HEIGHT", pszItem); |
1229 | |
|
1230 | 0 | pszItem = CPLGetXMLValue( |
1231 | 0 | psImageGenerationParameters, |
1232 | 0 | "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK"); |
1233 | 0 | poDS->SetMetadataItem("FIRST_LINE_TIME", pszItem); |
1234 | |
|
1235 | 0 | pszItem = CPLGetXMLValue(psImageGenerationParameters, |
1236 | 0 | "sarProcessingInformation.zeroDopplerTimeLastLine", |
1237 | 0 | "UNK"); |
1238 | 0 | poDS->SetMetadataItem("LAST_LINE_TIME", pszItem); |
1239 | |
|
1240 | 0 | pszItem = CPLGetXMLValue(psImageGenerationParameters, |
1241 | 0 | "sarProcessingInformation.lutApplied", ""); |
1242 | 0 | poDS->SetMetadataItem("LUT_APPLIED", pszItem); |
1243 | | |
1244 | | /*--------------------------------------------------------------------- |
1245 | | If true, a polarization dependent application LUT has been applied |
1246 | | for each polarization channel. Otherwise the same application LUT |
1247 | | has been applied for all polarization channels. Not applicable to |
1248 | | lookupTable = "Unity*" or if dataType = "Floating-Point". |
1249 | | --------------------------------------------------------------------- */ |
1250 | 0 | pszItem = CPLGetXMLValue(psImageGenerationParameters, |
1251 | 0 | "sarProcessingInformation.perPolarizationScaling", |
1252 | 0 | "false"); |
1253 | 0 | poDS->SetMetadataItem("PER_POLARIZATION_SCALING", pszItem); |
1254 | 0 | if (EQUAL(pszItem, "true") || EQUAL(pszItem, "TRUE")) |
1255 | 0 | { |
1256 | 0 | poDS->bPerPolarizationScaling = TRUE; |
1257 | 0 | } |
1258 | | |
1259 | | /* the following cases can be assumed to have no LUTs, as per |
1260 | | * RN-RP-51-2713, but also common sense |
1261 | | * SLC represents a SLant range georeferenced Complex product |
1262 | | * (i.e., equivalent to a Single-Look Complex product for RADARSAT-1 or |
1263 | | * RADARSAT-2). • GRD or GRC represent GRound range georeferenced Detected |
1264 | | * or Complex products (GRD is equivalent to an SGX, SCN or SCW product for |
1265 | | * RADARSAT1 or RADARSAT-2). • GCD or GCC represent GeoCoded Detected or |
1266 | | * Complex products (GCD is equivalent to an SSG or SPG product for |
1267 | | * RADARSAT-1 or RADARSAT-2). |
1268 | | */ |
1269 | 0 | bool bCanCalib = false; |
1270 | 0 | if (!(STARTS_WITH_CI(pszProductType, "UNK") || |
1271 | 0 | STARTS_WITH_CI(pszProductType, "GCD") || |
1272 | 0 | STARTS_WITH_CI(pszProductType, "GCC"))) |
1273 | 0 | { |
1274 | 0 | bCanCalib = true; |
1275 | 0 | } |
1276 | | |
1277 | | /* -------------------------------------------------------------------- */ |
1278 | | /* Get dataType (so we can recognise complex data), and the */ |
1279 | | /* bitsPerSample. */ |
1280 | | /* -------------------------------------------------------------------- */ |
1281 | 0 | const char *pszSampleDataType = CPLGetXMLValue( |
1282 | 0 | psImageReferenceAttributes, "rasterAttributes.sampleType", ""); |
1283 | 0 | poDS->SetMetadataItem("SAMPLE_TYPE", pszSampleDataType); |
1284 | | |
1285 | | /* Either Integer (16 bits) or Floating-Point (32 bits) */ |
1286 | 0 | const char *pszDataType = CPLGetXMLValue(psImageReferenceAttributes, |
1287 | 0 | "rasterAttributes.dataType", ""); |
1288 | 0 | poDS->SetMetadataItem("DATA_TYPE", pszDataType); |
1289 | | |
1290 | | /* 2 entries for complex data 1 entry for magnitude detected data */ |
1291 | 0 | const char *pszBitsPerSample = CPLGetXMLValue( |
1292 | 0 | psImageReferenceAttributes, "rasterAttributes.bitsPerSample", ""); |
1293 | 0 | const int nBitsPerSample = atoi(pszBitsPerSample); |
1294 | 0 | poDS->SetMetadataItem("BITS_PER_SAMPLE", pszBitsPerSample); |
1295 | |
|
1296 | 0 | const char *pszSampledPixelSpacingTime = |
1297 | 0 | CPLGetXMLValue(psImageReferenceAttributes, |
1298 | 0 | "rasterAttributes.sampledPixelSpacingTime", "UNK"); |
1299 | 0 | poDS->SetMetadataItem("SAMPLED_PIXEL_SPACING_TIME", |
1300 | 0 | pszSampledPixelSpacingTime); |
1301 | |
|
1302 | 0 | const char *pszSampledLineSpacingTime = |
1303 | 0 | CPLGetXMLValue(psImageReferenceAttributes, |
1304 | 0 | "rasterAttributes.sampledLineSpacingTime", "UNK"); |
1305 | 0 | poDS->SetMetadataItem("SAMPLED_LINE_SPACING_TIME", |
1306 | 0 | pszSampledLineSpacingTime); |
1307 | |
|
1308 | 0 | GDALDataType eDataType; |
1309 | |
|
1310 | 0 | if (EQUAL(pszSampleDataType, "Mixed")) /* RCM MLC has Mixed sampleType */ |
1311 | 0 | { |
1312 | 0 | poDS->isComplexData = false; /* RCM MLC is detected, non-complex */ |
1313 | 0 | if (nBitsPerSample == 32) |
1314 | 0 | { |
1315 | 0 | eDataType = GDT_Float32; /* 32 bits, check read block */ |
1316 | 0 | poDS->magnitudeBits = 32; |
1317 | 0 | } |
1318 | 0 | else |
1319 | 0 | { |
1320 | 0 | eDataType = GDT_UInt16; /* 16 bits, check read block */ |
1321 | 0 | poDS->magnitudeBits = 16; |
1322 | 0 | } |
1323 | 0 | } |
1324 | 0 | else if (EQUAL(pszSampleDataType, "Complex")) |
1325 | 0 | { |
1326 | 0 | poDS->isComplexData = true; |
1327 | | /* Usually this is the same bits for both */ |
1328 | 0 | poDS->realBitsComplexData = nBitsPerSample; |
1329 | 0 | poDS->imaginaryBitsComplexData = nBitsPerSample; |
1330 | |
|
1331 | 0 | if (nBitsPerSample == 32) |
1332 | 0 | { |
1333 | 0 | eDataType = GDT_CFloat32; /* 32 bits, check read block */ |
1334 | 0 | } |
1335 | 0 | else |
1336 | 0 | { |
1337 | 0 | eDataType = GDT_CInt16; /* 16 bits, check read block */ |
1338 | 0 | } |
1339 | 0 | } |
1340 | 0 | else if (nBitsPerSample == 32 && |
1341 | 0 | EQUAL(pszSampleDataType, "Magnitude Detected")) |
1342 | 0 | { |
1343 | | /* Actually, I don't need to test the 'dataType'=' Floating-Point', we |
1344 | | * know that a 32 bits per sample */ |
1345 | 0 | eDataType = GDT_Float32; |
1346 | 0 | poDS->isComplexData = false; |
1347 | 0 | poDS->magnitudeBits = 32; |
1348 | 0 | } |
1349 | 0 | else if (nBitsPerSample == 16 && |
1350 | 0 | EQUAL(pszSampleDataType, "Magnitude Detected")) |
1351 | 0 | { |
1352 | | /* Actually, I don't need to test the 'dataType'=' Integer', we know |
1353 | | * that a 16 bits per sample */ |
1354 | 0 | eDataType = GDT_UInt16; |
1355 | 0 | poDS->isComplexData = false; |
1356 | 0 | poDS->magnitudeBits = 16; |
1357 | 0 | } |
1358 | 0 | else |
1359 | 0 | { |
1360 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1361 | 0 | "ERROR: dataType=%s and bitsPerSample=%d are not a supported " |
1362 | 0 | "configuration.", |
1363 | 0 | pszDataType, nBitsPerSample); |
1364 | 0 | return nullptr; |
1365 | 0 | } |
1366 | | |
1367 | | /* Indicates whether pixel number (i.e., range) increases or decreases with |
1368 | | range time. For GCD and GCC products, this applies to intermediate ground |
1369 | | range image data prior to geocoding. */ |
1370 | 0 | const char *pszPixelTimeOrdering = |
1371 | 0 | CPLGetXMLValue(psImageReferenceAttributes, |
1372 | 0 | "rasterAttributes.pixelTimeOrdering", "UNK"); |
1373 | 0 | poDS->SetMetadataItem("PIXEL_TIME_ORDERING", pszPixelTimeOrdering); |
1374 | | |
1375 | | /* Indicates whether line numbers (i.e., azimuth) increase or decrease with |
1376 | | azimuth time. For GCD and GCC products, this applies to intermediate ground |
1377 | | range image data prior to geocoding. */ |
1378 | 0 | const char *pszLineTimeOrdering = CPLGetXMLValue( |
1379 | 0 | psImageReferenceAttributes, "rasterAttributes.lineTimeOrdering", "UNK"); |
1380 | 0 | poDS->SetMetadataItem("LINE_TIME_ORDERING", pszLineTimeOrdering); |
1381 | | |
1382 | | /* while we're at it, extract the pixel spacing information */ |
1383 | 0 | const char *pszPixelSpacing = |
1384 | 0 | CPLGetXMLValue(psImageReferenceAttributes, |
1385 | 0 | "rasterAttributes.sampledPixelSpacing", "UNK"); |
1386 | 0 | poDS->SetMetadataItem("PIXEL_SPACING", pszPixelSpacing); |
1387 | |
|
1388 | 0 | const char *pszLineSpacing = |
1389 | 0 | CPLGetXMLValue(psImageReferenceAttributes, |
1390 | 0 | "rasterAttributes.sampledLineSpacing", "UNK"); |
1391 | 0 | poDS->SetMetadataItem("LINE_SPACING", pszLineSpacing); |
1392 | | |
1393 | | /* -------------------------------------------------------------------- */ |
1394 | | /* Open each of the data files as a complex band. */ |
1395 | | /* -------------------------------------------------------------------- */ |
1396 | 0 | std::string osBeta0LUT; |
1397 | 0 | std::string osGammaLUT; |
1398 | 0 | std::string osSigma0LUT; |
1399 | | // Same file for all calibrations except the calibration is plit inside the |
1400 | | // XML |
1401 | 0 | std::string osNoiseLevelsValues; |
1402 | |
|
1403 | 0 | const CPLString osPath = CPLGetPathSafe(osMDFilename); |
1404 | | |
1405 | | /* Get a list of all polarizations */ |
1406 | 0 | const CPLXMLNode *psSourceAttrs = |
1407 | 0 | CPLGetXMLNode(poDS->psProduct.get(), "=product.sourceAttributes"); |
1408 | 0 | if (psSourceAttrs == nullptr) |
1409 | 0 | { |
1410 | 0 | CPLError( |
1411 | 0 | CE_Failure, CPLE_OpenFailed, |
1412 | 0 | "ERROR: RCM source attributes is missing. Please contact your data " |
1413 | 0 | "provider for a corrected dataset."); |
1414 | 0 | return nullptr; |
1415 | 0 | } |
1416 | | |
1417 | 0 | const CPLXMLNode *psRadarParameters = CPLGetXMLNode( |
1418 | 0 | poDS->psProduct.get(), "=product.sourceAttributes.radarParameters"); |
1419 | 0 | if (psRadarParameters == nullptr) |
1420 | 0 | { |
1421 | 0 | CPLError( |
1422 | 0 | CE_Failure, CPLE_OpenFailed, |
1423 | 0 | "ERROR: RCM radar parameters is missing. Please contact your data " |
1424 | 0 | "provider for a corrected dataset."); |
1425 | 0 | return nullptr; |
1426 | 0 | } |
1427 | | |
1428 | 0 | const char *pszPolarizations = |
1429 | 0 | CPLGetXMLValue(psRadarParameters, "polarizations", ""); |
1430 | 0 | if (pszPolarizations == nullptr || strlen(pszPolarizations) == 0) |
1431 | 0 | { |
1432 | 0 | CPLError( |
1433 | 0 | CE_Failure, CPLE_OpenFailed, |
1434 | 0 | "ERROR: RCM polarizations list is missing. Please contact your " |
1435 | 0 | "data provider for a corrected dataset."); |
1436 | 0 | return nullptr; |
1437 | 0 | } |
1438 | 0 | poDS->SetMetadataItem("POLARIZATIONS", pszPolarizations); |
1439 | |
|
1440 | 0 | const char *psAcquisitionType = |
1441 | 0 | CPLGetXMLValue(psRadarParameters, "acquisitionType", "UNK"); |
1442 | 0 | poDS->SetMetadataItem("ACQUISITION_TYPE", psAcquisitionType); |
1443 | |
|
1444 | 0 | const char *psBeams = CPLGetXMLValue(psRadarParameters, "beams", "UNK"); |
1445 | 0 | poDS->SetMetadataItem("BEAMS", psBeams); |
1446 | |
|
1447 | 0 | const CPLStringList aosPolarizationsGrids( |
1448 | 0 | CSLTokenizeString2(pszPolarizations, " ", 0)); |
1449 | 0 | CPLStringList imageBandList; |
1450 | 0 | CPLStringList imageBandFileList; |
1451 | 0 | const int nPolarizationsGridCount = aosPolarizationsGrids.size(); |
1452 | | |
1453 | | /* File names for full resolution IPDFs. For GeoTIFF format, one entry per |
1454 | | pole; For NITF 2.1 format, only one entry. */ |
1455 | 0 | bool bIsNITF = false; |
1456 | 0 | const char *pszNITF_Filename = nullptr; |
1457 | 0 | int imageBandFileCount = 0; |
1458 | 0 | int imageBandCount = 0; |
1459 | | |
1460 | | /* Split the polarization string*/ |
1461 | 0 | auto iss = std::istringstream((CPLString(pszPolarizations)).c_str()); |
1462 | 0 | auto pol = std::string{}; |
1463 | | /* Count number of polarizations*/ |
1464 | 0 | while (iss >> pol) |
1465 | 0 | imageBandCount++; |
1466 | |
|
1467 | 0 | for (const CPLXMLNode *psNode = psImageAttributes->psChild; |
1468 | 0 | psNode != nullptr; psNode = psNode->psNext) |
1469 | 0 | { |
1470 | | /* Find the tif or ntf filename */ |
1471 | 0 | if (psNode->eType != CXT_Element || !(EQUAL(psNode->pszValue, "ipdf"))) |
1472 | 0 | continue; |
1473 | | |
1474 | | /* -------------------------------------------------------------------- |
1475 | | */ |
1476 | | /* Fetch ipdf image. Could be either tif or ntf. */ |
1477 | | /* Replace / by \\ */ |
1478 | | /* -------------------------------------------------------------------- |
1479 | | */ |
1480 | 0 | const char *pszBasedFilename = CPLGetXMLValue(psNode, "", ""); |
1481 | 0 | if (*pszBasedFilename == '\0') |
1482 | 0 | continue; |
1483 | | |
1484 | | /* Count number of image names within ipdf tag*/ |
1485 | 0 | imageBandFileCount++; |
1486 | |
|
1487 | 0 | CPLString pszUpperBasedFilename(CPLString(pszBasedFilename).toupper()); |
1488 | |
|
1489 | 0 | const bool bEndsWithNTF = |
1490 | 0 | strlen(pszUpperBasedFilename.c_str()) > 4 && |
1491 | 0 | EQUAL(pszUpperBasedFilename.c_str() + |
1492 | 0 | strlen(pszUpperBasedFilename.c_str()) - 4, |
1493 | 0 | ".NTF"); |
1494 | |
|
1495 | 0 | if (bEndsWithNTF) |
1496 | 0 | { |
1497 | | /* Found it! It would not exist one more */ |
1498 | 0 | bIsNITF = true; |
1499 | 0 | pszNITF_Filename = pszBasedFilename; |
1500 | 0 | break; |
1501 | 0 | } |
1502 | 0 | else |
1503 | 0 | { |
1504 | | /* Keep adding polarizations filename according to the pole */ |
1505 | 0 | const char *pszPole = CPLGetXMLValue(psNode, "pole", ""); |
1506 | 0 | if (*pszPole == '\0') |
1507 | 0 | { |
1508 | | /* Guard against case when pole is a null string, skip it */ |
1509 | 0 | imageBandFileCount--; |
1510 | 0 | continue; |
1511 | 0 | } |
1512 | | |
1513 | 0 | if (EQUAL(pszPole, "XC")) |
1514 | 0 | { |
1515 | | /* Skip RCM MLC's 3rd band file ##XC.tif */ |
1516 | 0 | imageBandFileCount--; |
1517 | 0 | continue; |
1518 | 0 | } |
1519 | | |
1520 | 0 | imageBandList.AddString(CPLString(pszPole).toupper()); |
1521 | 0 | imageBandFileList.AddString(pszBasedFilename); |
1522 | 0 | } |
1523 | 0 | } |
1524 | | |
1525 | | /* -------------------------------------------------------------------- */ |
1526 | | /* Incidence Angle in a sub-folder */ |
1527 | | /* called 'calibration' from the 'metadata' folder */ |
1528 | | /* -------------------------------------------------------------------- */ |
1529 | |
|
1530 | 0 | const char *pszIncidenceAngleFileName = CPLGetXMLValue( |
1531 | 0 | psImageReferenceAttributes, "incidenceAngleFileName", ""); |
1532 | |
|
1533 | 0 | if (pszIncidenceAngleFileName != nullptr) |
1534 | 0 | { |
1535 | 0 | if (CPLHasPathTraversal(pszIncidenceAngleFileName)) |
1536 | 0 | { |
1537 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1538 | 0 | "Path traversal detected in %s", |
1539 | 0 | pszIncidenceAngleFileName); |
1540 | 0 | return nullptr; |
1541 | 0 | } |
1542 | 0 | const CPLString osIncidenceAngleFilePath = CPLFormFilenameSafe( |
1543 | 0 | CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr).c_str(), |
1544 | 0 | pszIncidenceAngleFileName, nullptr); |
1545 | | /* Check if the file exist */ |
1546 | 0 | if (IsValidXMLFile(osIncidenceAngleFilePath)) |
1547 | 0 | { |
1548 | 0 | CPLXMLTreeCloser psIncidenceAngle( |
1549 | 0 | CPLParseXMLFile(osIncidenceAngleFilePath)); |
1550 | |
|
1551 | 0 | int pixelFirstLutValue = atoi( |
1552 | 0 | CPLGetXMLValue(psIncidenceAngle.get(), |
1553 | 0 | "=incidenceAngles.pixelFirstAnglesValue", "0")); |
1554 | |
|
1555 | 0 | const int stepSize = atoi(CPLGetXMLValue( |
1556 | 0 | psIncidenceAngle.get(), "=incidenceAngles.stepSize", "0")); |
1557 | 0 | const int numberOfValues = |
1558 | 0 | atoi(CPLGetXMLValue(psIncidenceAngle.get(), |
1559 | 0 | "=incidenceAngles.numberOfValues", "0")); |
1560 | |
|
1561 | 0 | if (!(stepSize == 0 || stepSize == INT_MIN || |
1562 | 0 | numberOfValues == INT_MIN || |
1563 | 0 | abs(numberOfValues) > INT_MAX / abs(stepSize))) |
1564 | 0 | { |
1565 | | /* Get the Pixel Per range */ |
1566 | 0 | const int tableSize = abs(stepSize) * abs(numberOfValues); |
1567 | |
|
1568 | 0 | CPLString angles; |
1569 | | // Loop through all nodes with spaces |
1570 | 0 | CPLXMLNode *psNextNode = |
1571 | 0 | CPLGetXMLNode(psIncidenceAngle.get(), "=incidenceAngles"); |
1572 | |
|
1573 | 0 | CPLXMLNode *psNodeInc; |
1574 | 0 | for (psNodeInc = psNextNode->psChild; psNodeInc != nullptr; |
1575 | 0 | psNodeInc = psNodeInc->psNext) |
1576 | 0 | { |
1577 | 0 | if (EQUAL(psNodeInc->pszValue, "angles")) |
1578 | 0 | { |
1579 | 0 | if (angles.length() > 0) |
1580 | 0 | { |
1581 | 0 | angles.append(" "); /* separator */ |
1582 | 0 | } |
1583 | 0 | const char *valAngle = |
1584 | 0 | CPLGetXMLValue(psNodeInc, "", ""); |
1585 | 0 | angles.append(valAngle); |
1586 | 0 | } |
1587 | 0 | } |
1588 | |
|
1589 | 0 | char **papszAngleList = |
1590 | 0 | CSLTokenizeString2(angles, " ", CSLT_HONOURSTRINGS); |
1591 | | |
1592 | | /* Allocate the right LUT size according to the product range pixel |
1593 | | */ |
1594 | 0 | poDS->m_IncidenceAngleTableSize = tableSize; |
1595 | 0 | poDS->m_nfIncidenceAngleTable = |
1596 | 0 | InterpolateValues(papszAngleList, tableSize, stepSize, |
1597 | 0 | numberOfValues, pixelFirstLutValue); |
1598 | |
|
1599 | 0 | CSLDestroy(papszAngleList); |
1600 | 0 | } |
1601 | 0 | } |
1602 | 0 | } |
1603 | | |
1604 | 0 | for (int iPoleInx = 0; iPoleInx < nPolarizationsGridCount; iPoleInx++) |
1605 | 0 | { |
1606 | | // Search for a specific band name |
1607 | 0 | const CPLString pszPole = |
1608 | 0 | CPLString(aosPolarizationsGrids[iPoleInx]).toupper(); |
1609 | | |
1610 | | // Look if the NoiseLevel file xml exist for the |
1611 | 0 | const CPLXMLNode *psRefNode = psImageReferenceAttributes->psChild; |
1612 | 0 | for (; psRefNode != nullptr; psRefNode = psRefNode->psNext) |
1613 | 0 | { |
1614 | 0 | if (EQUAL(psRefNode->pszValue, "noiseLevelFileName") && bCanCalib) |
1615 | 0 | { |
1616 | | /* Determine which incidence angle correction this is */ |
1617 | 0 | const char *pszPoleToMatch = |
1618 | 0 | CPLGetXMLValue(psRefNode, "pole", ""); |
1619 | 0 | const char *pszNoiseLevelFile = |
1620 | 0 | CPLGetXMLValue(psRefNode, "", ""); |
1621 | |
|
1622 | 0 | if (*pszPoleToMatch == '\0') |
1623 | 0 | continue; |
1624 | | |
1625 | 0 | if (EQUAL(pszPoleToMatch, "XC")) |
1626 | | /* Skip noise for RCM MLC's 3rd band XC */ |
1627 | 0 | continue; |
1628 | | |
1629 | 0 | if (EQUAL(pszNoiseLevelFile, "")) |
1630 | 0 | continue; |
1631 | | |
1632 | | /* -------------------------------------------------------------------- |
1633 | | */ |
1634 | | /* With RCM, LUT file is different per polarizarion (pole) |
1635 | | */ |
1636 | | /* The following code make sure to loop through all |
1637 | | * possible */ |
1638 | | /* 'noiseLevelFileName' and match the <ipdf 'pole' name */ |
1639 | | /* -------------------------------------------------------------------- |
1640 | | */ |
1641 | 0 | if (pszPole.compare(pszPoleToMatch) != 0) |
1642 | 0 | { |
1643 | 0 | continue; |
1644 | 0 | } |
1645 | | |
1646 | | /* -------------------------------------------------------------------- |
1647 | | */ |
1648 | | /* LUT calibration is unique per pole in a sub-folder */ |
1649 | | /* called 'calibration' from the 'metadata' folder */ |
1650 | | /* -------------------------------------------------------------------- |
1651 | | */ |
1652 | | |
1653 | 0 | if (CPLHasPathTraversal(pszNoiseLevelFile)) |
1654 | 0 | { |
1655 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1656 | 0 | "Path traversal detected in %s", |
1657 | 0 | pszNoiseLevelFile); |
1658 | 0 | return nullptr; |
1659 | 0 | } |
1660 | 0 | CPLString oNoiseLevelPath = CPLFormFilenameSafe( |
1661 | 0 | CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr) |
1662 | 0 | .c_str(), |
1663 | 0 | pszNoiseLevelFile, nullptr); |
1664 | 0 | if (IsValidXMLFile(oNoiseLevelPath)) |
1665 | 0 | { |
1666 | 0 | osNoiseLevelsValues = std::move(oNoiseLevelPath); |
1667 | 0 | } |
1668 | 0 | } |
1669 | 0 | } |
1670 | | |
1671 | | // Search again with different file |
1672 | 0 | psRefNode = psImageReferenceAttributes->psChild; |
1673 | 0 | for (; psRefNode != nullptr; psRefNode = psRefNode->psNext) |
1674 | 0 | { |
1675 | 0 | if (EQUAL(psRefNode->pszValue, "lookupTableFileName") && bCanCalib) |
1676 | 0 | { |
1677 | | /* Determine which incidence angle correction this is */ |
1678 | 0 | const char *pszLUTType = |
1679 | 0 | CPLGetXMLValue(psRefNode, "sarCalibrationType", ""); |
1680 | 0 | const char *pszPoleToMatch = |
1681 | 0 | CPLGetXMLValue(psRefNode, "pole", ""); |
1682 | 0 | const char *pszLUTFile = CPLGetXMLValue(psRefNode, "", ""); |
1683 | |
|
1684 | 0 | if (*pszPoleToMatch == '\0') |
1685 | 0 | continue; |
1686 | | |
1687 | 0 | if (EQUAL(pszPoleToMatch, "XC")) |
1688 | | /* Skip Calib for RCM MLC's 3rd band XC */ |
1689 | 0 | continue; |
1690 | | |
1691 | 0 | if (*pszLUTType == '\0') |
1692 | 0 | continue; |
1693 | | |
1694 | 0 | if (EQUAL(pszLUTType, "")) |
1695 | 0 | continue; |
1696 | | |
1697 | | /* -------------------------------------------------------------------- |
1698 | | */ |
1699 | | /* With RCM, LUT file is different per polarizarion (pole) |
1700 | | */ |
1701 | | /* The following code make sure to loop through all |
1702 | | * possible */ |
1703 | | /* 'lookupTableFileName' and match the <ipdf 'pole' name */ |
1704 | | /* -------------------------------------------------------------------- |
1705 | | */ |
1706 | 0 | if (pszPole.compare(pszPoleToMatch) != 0) |
1707 | 0 | { |
1708 | 0 | continue; |
1709 | 0 | } |
1710 | | |
1711 | | /* -------------------------------------------------------------------- |
1712 | | */ |
1713 | | /* LUT calibration is unique per pole in a sub-folder */ |
1714 | | /* called 'calibration' from the 'metadata' folder */ |
1715 | | /* -------------------------------------------------------------------- |
1716 | | */ |
1717 | 0 | if (CPLHasPathTraversal(pszLUTFile)) |
1718 | 0 | { |
1719 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1720 | 0 | "Path traversal detected in %s", pszLUTFile); |
1721 | 0 | return nullptr; |
1722 | 0 | } |
1723 | 0 | const CPLString osLUTFilePath = CPLFormFilenameSafe( |
1724 | 0 | CPLFormFilenameSafe(osPath, CALIBRATION_FOLDER, nullptr) |
1725 | 0 | .c_str(), |
1726 | 0 | pszLUTFile, nullptr); |
1727 | |
|
1728 | 0 | if (EQUAL(pszLUTType, "Beta Nought") && |
1729 | 0 | IsValidXMLFile(osLUTFilePath)) |
1730 | 0 | { |
1731 | 0 | poDS->papszExtraFiles = |
1732 | 0 | CSLAddString(poDS->papszExtraFiles, osLUTFilePath); |
1733 | |
|
1734 | 0 | CPLString pszBuf( |
1735 | 0 | FormatCalibration(szBETA0, osMDFilename.c_str())); |
1736 | 0 | osBeta0LUT = osLUTFilePath; |
1737 | |
|
1738 | 0 | const char *oldLut = |
1739 | 0 | poDS->GetMetadataItem("BETA_NOUGHT_LUT"); |
1740 | 0 | if (oldLut == nullptr) |
1741 | 0 | { |
1742 | 0 | poDS->SetMetadataItem("BETA_NOUGHT_LUT", osLUTFilePath); |
1743 | 0 | } |
1744 | 0 | else |
1745 | 0 | { |
1746 | | /* Keep adding LUT file for all bands, should be planty |
1747 | | * of space */ |
1748 | 0 | char *ptrConcatLut = |
1749 | 0 | static_cast<char *>(CPLMalloc(2048)); |
1750 | 0 | ptrConcatLut[0] = |
1751 | 0 | '\0'; /* Just initialize the first byte */ |
1752 | 0 | strcat(ptrConcatLut, oldLut); |
1753 | 0 | strcat(ptrConcatLut, ","); |
1754 | 0 | strcat(ptrConcatLut, osLUTFilePath); |
1755 | 0 | poDS->SetMetadataItem("BETA_NOUGHT_LUT", ptrConcatLut); |
1756 | 0 | CPLFree(ptrConcatLut); |
1757 | 0 | } |
1758 | |
|
1759 | 0 | poDS->papszSubDatasets = CSLSetNameValue( |
1760 | 0 | poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf); |
1761 | 0 | poDS->papszSubDatasets = CSLSetNameValue( |
1762 | 0 | poDS->papszSubDatasets, "SUBDATASET_3_DESC", |
1763 | 0 | "Beta Nought calibrated"); |
1764 | 0 | } |
1765 | 0 | else if (EQUAL(pszLUTType, "Sigma Nought") && |
1766 | 0 | IsValidXMLFile(osLUTFilePath)) |
1767 | 0 | { |
1768 | 0 | poDS->papszExtraFiles = |
1769 | 0 | CSLAddString(poDS->papszExtraFiles, osLUTFilePath); |
1770 | |
|
1771 | 0 | CPLString pszBuf( |
1772 | 0 | FormatCalibration(szSIGMA0, osMDFilename.c_str())); |
1773 | 0 | osSigma0LUT = osLUTFilePath; |
1774 | |
|
1775 | 0 | const char *oldLut = |
1776 | 0 | poDS->GetMetadataItem("SIGMA_NOUGHT_LUT"); |
1777 | 0 | if (oldLut == nullptr) |
1778 | 0 | { |
1779 | 0 | poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", |
1780 | 0 | osLUTFilePath); |
1781 | 0 | } |
1782 | 0 | else |
1783 | 0 | { |
1784 | | /* Keep adding LUT file for all bands, should be planty |
1785 | | * of space */ |
1786 | 0 | char *ptrConcatLut = |
1787 | 0 | static_cast<char *>(CPLMalloc(2048)); |
1788 | 0 | ptrConcatLut[0] = |
1789 | 0 | '\0'; /* Just initialize the first byte */ |
1790 | 0 | strcat(ptrConcatLut, oldLut); |
1791 | 0 | strcat(ptrConcatLut, ","); |
1792 | 0 | strcat(ptrConcatLut, osLUTFilePath); |
1793 | 0 | poDS->SetMetadataItem("SIGMA_NOUGHT_LUT", ptrConcatLut); |
1794 | 0 | CPLFree(ptrConcatLut); |
1795 | 0 | } |
1796 | |
|
1797 | 0 | poDS->papszSubDatasets = CSLSetNameValue( |
1798 | 0 | poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf); |
1799 | 0 | poDS->papszSubDatasets = CSLSetNameValue( |
1800 | 0 | poDS->papszSubDatasets, "SUBDATASET_2_DESC", |
1801 | 0 | "Sigma Nought calibrated"); |
1802 | 0 | } |
1803 | 0 | else if (EQUAL(pszLUTType, "Gamma") && |
1804 | 0 | IsValidXMLFile(osLUTFilePath)) |
1805 | 0 | { |
1806 | 0 | poDS->papszExtraFiles = |
1807 | 0 | CSLAddString(poDS->papszExtraFiles, osLUTFilePath); |
1808 | |
|
1809 | 0 | CPLString pszBuf( |
1810 | 0 | FormatCalibration(szGAMMA, osMDFilename.c_str())); |
1811 | 0 | osGammaLUT = osLUTFilePath; |
1812 | |
|
1813 | 0 | const char *oldLut = poDS->GetMetadataItem("GAMMA_LUT"); |
1814 | 0 | if (oldLut == nullptr) |
1815 | 0 | { |
1816 | 0 | poDS->SetMetadataItem("GAMMA_LUT", osLUTFilePath); |
1817 | 0 | } |
1818 | 0 | else |
1819 | 0 | { |
1820 | | /* Keep adding LUT file for all bands, should be planty |
1821 | | * of space */ |
1822 | 0 | char *ptrConcatLut = |
1823 | 0 | static_cast<char *>(CPLMalloc(2048)); |
1824 | 0 | ptrConcatLut[0] = |
1825 | 0 | '\0'; /* Just initialize the first byte */ |
1826 | 0 | strcat(ptrConcatLut, oldLut); |
1827 | 0 | strcat(ptrConcatLut, ","); |
1828 | 0 | strcat(ptrConcatLut, osLUTFilePath); |
1829 | 0 | poDS->SetMetadataItem("GAMMA_LUT", ptrConcatLut); |
1830 | 0 | CPLFree(ptrConcatLut); |
1831 | 0 | } |
1832 | |
|
1833 | 0 | poDS->papszSubDatasets = CSLSetNameValue( |
1834 | 0 | poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf); |
1835 | 0 | poDS->papszSubDatasets = CSLSetNameValue( |
1836 | 0 | poDS->papszSubDatasets, "SUBDATASET_4_DESC", |
1837 | 0 | "Gamma calibrated"); |
1838 | 0 | } |
1839 | 0 | } |
1840 | 0 | } |
1841 | | |
1842 | | /* -------------------------------------------------------------------- |
1843 | | */ |
1844 | | /* Fetch ipdf image. Could be either tif or ntf. */ |
1845 | | /* Replace / by \\ */ |
1846 | | /* -------------------------------------------------------------------- |
1847 | | */ |
1848 | 0 | const char *pszBasedFilename; |
1849 | 0 | if (bIsNITF) |
1850 | 0 | { |
1851 | 0 | pszBasedFilename = pszNITF_Filename; |
1852 | 0 | } |
1853 | 0 | else |
1854 | 0 | { |
1855 | 0 | const int bandPositionIndex = imageBandList.FindString(pszPole); |
1856 | 0 | if (bandPositionIndex < 0) |
1857 | 0 | { |
1858 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
1859 | 0 | "ERROR: RCM cannot find the polarization %s. Please " |
1860 | 0 | "contact your data provider for a corrected dataset", |
1861 | 0 | pszPole.c_str()); |
1862 | 0 | return nullptr; |
1863 | 0 | } |
1864 | | |
1865 | 0 | pszBasedFilename = imageBandFileList[bandPositionIndex]; |
1866 | 0 | } |
1867 | | |
1868 | | /* -------------------------------------------------------------------- |
1869 | | */ |
1870 | | /* Form full filename (path of product.xml + basename). */ |
1871 | | /* -------------------------------------------------------------------- |
1872 | | */ |
1873 | 0 | std::string osPathImage = osPath; |
1874 | 0 | std::string osBasedImage = pszBasedFilename; |
1875 | 0 | if (STARTS_WITH(osBasedImage.c_str(), "../") || |
1876 | 0 | STARTS_WITH(osBasedImage.c_str(), "..\\")) |
1877 | 0 | { |
1878 | 0 | osPathImage = CPLGetPathSafe(osPath); |
1879 | 0 | osBasedImage = osBasedImage.substr(strlen("../")); |
1880 | 0 | } |
1881 | 0 | if (CPLHasPathTraversal(osBasedImage.c_str())) |
1882 | 0 | { |
1883 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1884 | 0 | "Path traversal detected in %s", osBasedImage.c_str()); |
1885 | 0 | return nullptr; |
1886 | 0 | } |
1887 | 0 | const std::string osFullname = CPLFormFilenameSafe( |
1888 | 0 | osPathImage.c_str(), osBasedImage.c_str(), nullptr); |
1889 | | |
1890 | | /* -------------------------------------------------------------------- |
1891 | | */ |
1892 | | /* Try and open the file. */ |
1893 | | /* -------------------------------------------------------------------- |
1894 | | */ |
1895 | 0 | auto poBandFile = std::unique_ptr<GDALDataset>(GDALDataset::Open( |
1896 | 0 | osFullname.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR)); |
1897 | 0 | if (poBandFile == nullptr || poBandFile->GetRasterCount() == 0) |
1898 | 0 | { |
1899 | 0 | continue; |
1900 | 0 | } |
1901 | | |
1902 | 0 | poDS->papszExtraFiles = |
1903 | 0 | CSLAddString(poDS->papszExtraFiles, osFullname.c_str()); |
1904 | | |
1905 | | /* Some CFloat32 NITF files have nBitsPerSample incorrectly reported */ |
1906 | | /* as 16, and get misinterpreted as CInt16. Check the underlying NITF |
1907 | | */ |
1908 | | /* and override if this is the case. */ |
1909 | 0 | if (poBandFile->GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32) |
1910 | 0 | eDataType = GDT_CFloat32; |
1911 | |
|
1912 | 0 | BandMappingRCM b = |
1913 | 0 | checkBandFileMappingRCM(eDataType, poBandFile.get(), bIsNITF); |
1914 | 0 | if (b == BANDERROR) |
1915 | 0 | { |
1916 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1917 | 0 | "The underlying band files do not have an appropriate " |
1918 | 0 | "data type."); |
1919 | 0 | return nullptr; |
1920 | 0 | } |
1921 | 0 | bool twoBandComplex = b == TWOBANDCOMPLEX; |
1922 | 0 | bool isOneFilePerPol = (imageBandCount == imageBandFileCount); |
1923 | | |
1924 | | /* -------------------------------------------------------------------- |
1925 | | */ |
1926 | | /* Create the band. */ |
1927 | | /* -------------------------------------------------------------------- |
1928 | | */ |
1929 | 0 | int bandNum = poDS->GetRasterCount() + 1; |
1930 | 0 | if (eCalib == None || eCalib == Uncalib) |
1931 | 0 | { |
1932 | 0 | RCMRasterBand *poBand = new RCMRasterBand( |
1933 | 0 | poDS.get(), bandNum, eDataType, pszPole, poBandFile.release(), |
1934 | 0 | twoBandComplex, isOneFilePerPol, bIsNITF); |
1935 | |
|
1936 | 0 | poDS->SetBand(poDS->GetRasterCount() + 1, poBand); |
1937 | 0 | } |
1938 | 0 | else |
1939 | 0 | { |
1940 | 0 | const char *pszLUT = osSigma0LUT.c_str(); |
1941 | 0 | switch (eCalib) |
1942 | 0 | { |
1943 | 0 | case Sigma0: |
1944 | 0 | pszLUT = osSigma0LUT.c_str(); |
1945 | 0 | break; |
1946 | 0 | case Beta0: |
1947 | 0 | pszLUT = osBeta0LUT.c_str(); |
1948 | 0 | break; |
1949 | 0 | case Gamma: |
1950 | 0 | pszLUT = osGammaLUT.c_str(); |
1951 | 0 | break; |
1952 | 0 | default: |
1953 | | /* we should bomb gracefully... */ |
1954 | 0 | break; |
1955 | 0 | } |
1956 | 0 | if (pszLUT[0] == '\0') |
1957 | 0 | { |
1958 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "LUT missing."); |
1959 | 0 | return nullptr; |
1960 | 0 | } |
1961 | | |
1962 | | // The variable 'osNoiseLevelsValues' is always the same for a ban |
1963 | | // name except the XML contains different calibration name |
1964 | 0 | if (poDS->isComplexData) |
1965 | 0 | { |
1966 | | // If Complex, always 32 bits |
1967 | 0 | RCMCalibRasterBand *poBand = new RCMCalibRasterBand( |
1968 | 0 | poDS.get(), pszPole, GDT_Float32, poBandFile.release(), |
1969 | 0 | eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType); |
1970 | 0 | poDS->SetBand(poDS->GetRasterCount() + 1, poBand); |
1971 | 0 | } |
1972 | 0 | else |
1973 | 0 | { |
1974 | | // Whatever the datatype was previously set |
1975 | 0 | RCMCalibRasterBand *poBand = new RCMCalibRasterBand( |
1976 | 0 | poDS.get(), pszPole, eDataType, poBandFile.release(), |
1977 | 0 | eCalib, pszLUT, osNoiseLevelsValues.c_str(), eDataType); |
1978 | 0 | poDS->SetBand(poDS->GetRasterCount() + 1, poBand); |
1979 | 0 | } |
1980 | 0 | } |
1981 | 0 | } |
1982 | | |
1983 | 0 | if (poDS->papszSubDatasets != nullptr && eCalib == None) |
1984 | 0 | { |
1985 | 0 | CPLString pszBuf = FormatCalibration(szUNCALIB, osMDFilename.c_str()); |
1986 | 0 | poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, |
1987 | 0 | "SUBDATASET_1_NAME", pszBuf); |
1988 | 0 | poDS->papszSubDatasets = |
1989 | 0 | CSLSetNameValue(poDS->papszSubDatasets, "SUBDATASET_1_DESC", |
1990 | 0 | "Uncalibrated digital numbers"); |
1991 | 0 | } |
1992 | 0 | else if (poDS->papszSubDatasets != nullptr) |
1993 | 0 | { |
1994 | 0 | CSLDestroy(poDS->papszSubDatasets); |
1995 | 0 | poDS->papszSubDatasets = nullptr; |
1996 | 0 | } |
1997 | | |
1998 | | /* -------------------------------------------------------------------- */ |
1999 | | /* Set the appropriate MATRIX_REPRESENTATION. */ |
2000 | | /* -------------------------------------------------------------------- */ |
2001 | |
|
2002 | 0 | if (poDS->GetRasterCount() == 4 && |
2003 | 0 | (eDataType == GDT_CInt16 || eDataType == GDT_CFloat32)) |
2004 | 0 | { |
2005 | 0 | poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING"); |
2006 | 0 | } |
2007 | | |
2008 | | /* -------------------------------------------------------------------- */ |
2009 | | /* Collect a few useful metadata items. */ |
2010 | | /* -------------------------------------------------------------------- */ |
2011 | |
|
2012 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, "satellite", ""); |
2013 | 0 | poDS->SetMetadataItem("SATELLITE_IDENTIFIER", pszItem); |
2014 | |
|
2015 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, "sensor", ""); |
2016 | 0 | poDS->SetMetadataItem("SENSOR_IDENTIFIER", pszItem); |
2017 | | |
2018 | | /* Get beam mode mnemonic */ |
2019 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, "beamMode", "UNK"); |
2020 | 0 | poDS->SetMetadataItem("BEAM_MODE", pszItem); |
2021 | |
|
2022 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeMnemonic", "UNK"); |
2023 | 0 | poDS->SetMetadataItem("BEAM_MODE_MNEMONIC", pszItem); |
2024 | |
|
2025 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, "beamModeDefinitionId", "UNK"); |
2026 | 0 | poDS->SetMetadataItem("BEAM_MODE_DEFINITION_ID", pszItem); |
2027 | |
|
2028 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, "rawDataStartTime", "UNK"); |
2029 | 0 | poDS->SetMetadataItem("ACQUISITION_START_TIME", pszItem); |
2030 | |
|
2031 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, "inputDatasetFacilityId", "UNK"); |
2032 | 0 | poDS->SetMetadataItem("FACILITY_IDENTIFIER", pszItem); |
2033 | |
|
2034 | 0 | pszItem = CPLGetXMLValue(psSourceAttrs, |
2035 | 0 | "orbitAndAttitude.orbitInformation.passDirection", |
2036 | 0 | "UNK"); |
2037 | 0 | poDS->SetMetadataItem("ORBIT_DIRECTION", pszItem); |
2038 | 0 | pszItem = CPLGetXMLValue( |
2039 | 0 | psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataSource", |
2040 | 0 | "UNK"); |
2041 | 0 | poDS->SetMetadataItem("ORBIT_DATA_SOURCE", pszItem); |
2042 | 0 | pszItem = CPLGetXMLValue( |
2043 | 0 | psSourceAttrs, "orbitAndAttitude.orbitInformation.orbitDataFileName", |
2044 | 0 | "UNK"); |
2045 | 0 | poDS->SetMetadataItem("ORBIT_DATA_FILE", pszItem); |
2046 | | |
2047 | | /* Get incidence angle information. DONE */ |
2048 | 0 | pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngNearRng", |
2049 | 0 | "UNK"); |
2050 | 0 | poDS->SetMetadataItem("NEAR_RANGE_INCIDENCE_ANGLE", pszItem); |
2051 | |
|
2052 | 0 | pszItem = CPLGetXMLValue(psSceneAttributes, "imageAttributes.incAngFarRng", |
2053 | 0 | "UNK"); |
2054 | 0 | poDS->SetMetadataItem("FAR_RANGE_INCIDENCE_ANGLE", pszItem); |
2055 | |
|
2056 | 0 | pszItem = CPLGetXMLValue(psSceneAttributes, |
2057 | 0 | "imageAttributes.slantRangeNearEdge", "UNK"); |
2058 | 0 | poDS->SetMetadataItem("SLANT_RANGE_NEAR_EDGE", pszItem); |
2059 | |
|
2060 | 0 | pszItem = CPLGetXMLValue(psSceneAttributes, |
2061 | 0 | "imageAttributes.slantRangeFarEdge", "UNK"); |
2062 | 0 | poDS->SetMetadataItem("SLANT_RANGE_FAR_EDGE", pszItem); |
2063 | | |
2064 | | /*--------------------------------------------------------------------- */ |
2065 | | /* Collect Map projection/Geotransform information, if present.DONE */ |
2066 | | /* In RCM, there is no file that indicates */ |
2067 | | /* -------------------------------------------------------------------- */ |
2068 | 0 | const CPLXMLNode *psMapProjection = CPLGetXMLNode( |
2069 | 0 | psImageReferenceAttributes, "geographicInformation.mapProjection"); |
2070 | |
|
2071 | 0 | if (psMapProjection != nullptr) |
2072 | 0 | { |
2073 | 0 | const CPLXMLNode *psPos = |
2074 | 0 | CPLGetXMLNode(psMapProjection, "positioningInformation"); |
2075 | |
|
2076 | 0 | pszItem = |
2077 | 0 | CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", "UNK"); |
2078 | 0 | poDS->SetMetadataItem("MAP_PROJECTION_DESCRIPTOR", pszItem); |
2079 | |
|
2080 | 0 | pszItem = |
2081 | 0 | CPLGetXMLValue(psMapProjection, "mapProjectionOrientation", "UNK"); |
2082 | 0 | poDS->SetMetadataItem("MAP_PROJECTION_ORIENTATION", pszItem); |
2083 | |
|
2084 | 0 | pszItem = CPLGetXMLValue(psMapProjection, "resamplingKernel", "UNK"); |
2085 | 0 | poDS->SetMetadataItem("RESAMPLING_KERNEL", pszItem); |
2086 | |
|
2087 | 0 | pszItem = CPLGetXMLValue(psMapProjection, "satelliteHeading", "UNK"); |
2088 | 0 | poDS->SetMetadataItem("SATELLITE_HEADING", pszItem); |
2089 | |
|
2090 | 0 | if (psPos != nullptr) |
2091 | 0 | { |
2092 | 0 | const double tl_x = CPLStrtod( |
2093 | 0 | CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.easting", |
2094 | 0 | "0.0"), |
2095 | 0 | nullptr); |
2096 | 0 | const double tl_y = CPLStrtod( |
2097 | 0 | CPLGetXMLValue(psPos, "upperLeftCorner.mapCoordinate.northing", |
2098 | 0 | "0.0"), |
2099 | 0 | nullptr); |
2100 | 0 | const double bl_x = CPLStrtod( |
2101 | 0 | CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.easting", |
2102 | 0 | "0.0"), |
2103 | 0 | nullptr); |
2104 | 0 | const double bl_y = CPLStrtod( |
2105 | 0 | CPLGetXMLValue(psPos, "lowerLeftCorner.mapCoordinate.northing", |
2106 | 0 | "0.0"), |
2107 | 0 | nullptr); |
2108 | 0 | const double tr_x = CPLStrtod( |
2109 | 0 | CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.easting", |
2110 | 0 | "0.0"), |
2111 | 0 | nullptr); |
2112 | 0 | const double tr_y = CPLStrtod( |
2113 | 0 | CPLGetXMLValue(psPos, "upperRightCorner.mapCoordinate.northing", |
2114 | 0 | "0.0"), |
2115 | 0 | nullptr); |
2116 | 0 | poDS->m_gt.xscale = (tr_x - tl_x) / (poDS->nRasterXSize - 1); |
2117 | 0 | poDS->m_gt.yrot = (tr_y - tl_y) / (poDS->nRasterXSize - 1); |
2118 | 0 | poDS->m_gt.xrot = (bl_x - tl_x) / (poDS->nRasterYSize - 1); |
2119 | 0 | poDS->m_gt.yscale = (bl_y - tl_y) / (poDS->nRasterYSize - 1); |
2120 | 0 | poDS->m_gt.xorig = |
2121 | 0 | (tl_x - 0.5 * poDS->m_gt.xscale - 0.5 * poDS->m_gt.xrot); |
2122 | 0 | poDS->m_gt.yorig = |
2123 | 0 | (tl_y - 0.5 * poDS->m_gt.yrot - 0.5 * poDS->m_gt.yscale); |
2124 | | |
2125 | | /* Use bottom right pixel to test geotransform */ |
2126 | 0 | const double br_x = CPLStrtod( |
2127 | 0 | CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.easting", |
2128 | 0 | "0.0"), |
2129 | 0 | nullptr); |
2130 | 0 | const double br_y = CPLStrtod( |
2131 | 0 | CPLGetXMLValue(psPos, "lowerRightCorner.mapCoordinate.northing", |
2132 | 0 | "0.0"), |
2133 | 0 | nullptr); |
2134 | 0 | const double testx = |
2135 | 0 | poDS->m_gt.xorig + |
2136 | 0 | poDS->m_gt.xscale * (poDS->nRasterXSize - 0.5) + |
2137 | 0 | poDS->m_gt.xrot * (poDS->nRasterYSize - 0.5); |
2138 | 0 | const double testy = poDS->m_gt.yorig + |
2139 | 0 | poDS->m_gt.yrot * (poDS->nRasterXSize - 0.5) + |
2140 | 0 | poDS->m_gt.yscale * (poDS->nRasterYSize - 0.5); |
2141 | | |
2142 | | /* Give 1/4 pixel numerical error leeway in calculating location |
2143 | | based on affine transform */ |
2144 | 0 | if ((fabs(testx - br_x) > |
2145 | 0 | fabs(0.25 * (poDS->m_gt.xscale + poDS->m_gt.xrot))) || |
2146 | 0 | (fabs(testy - br_y) > |
2147 | 0 | fabs(0.25 * (poDS->m_gt.yrot + poDS->m_gt.yscale)))) |
2148 | 0 | { |
2149 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2150 | 0 | "WARNING: Unexpected error in calculating affine " |
2151 | 0 | "transform: corner coordinates inconsistent."); |
2152 | 0 | } |
2153 | 0 | else |
2154 | 0 | { |
2155 | 0 | poDS->bHaveGeoTransform = TRUE; |
2156 | 0 | } |
2157 | 0 | } |
2158 | 0 | } |
2159 | | |
2160 | | /* -------------------------------------------------------------------- */ |
2161 | | /* Collect Projection String Information.DONE */ |
2162 | | /* -------------------------------------------------------------------- */ |
2163 | 0 | const CPLXMLNode *psEllipsoid = |
2164 | 0 | CPLGetXMLNode(psImageReferenceAttributes, |
2165 | 0 | "geographicInformation.ellipsoidParameters"); |
2166 | |
|
2167 | 0 | if (psEllipsoid != nullptr) |
2168 | 0 | { |
2169 | 0 | OGRSpatialReference oLL, oPrj; |
2170 | |
|
2171 | 0 | const char *pszGeodeticTerrainHeight = |
2172 | 0 | CPLGetXMLValue(psEllipsoid, "geodeticTerrainHeight", "UNK"); |
2173 | 0 | poDS->SetMetadataItem("GEODETIC_TERRAIN_HEIGHT", |
2174 | 0 | pszGeodeticTerrainHeight); |
2175 | |
|
2176 | 0 | const char *pszEllipsoidName = |
2177 | 0 | CPLGetXMLValue(psEllipsoid, "ellipsoidName", ""); |
2178 | 0 | double minor_axis = |
2179 | 0 | CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMinorAxis", "0.0")); |
2180 | 0 | double major_axis = |
2181 | 0 | CPLAtof(CPLGetXMLValue(psEllipsoid, "semiMajorAxis", "0.0")); |
2182 | |
|
2183 | 0 | if (EQUAL(pszEllipsoidName, "") || (minor_axis == 0.0) || |
2184 | 0 | (major_axis == 0.0)) |
2185 | 0 | { |
2186 | 0 | oLL.SetWellKnownGeogCS("WGS84"); |
2187 | 0 | oPrj.SetWellKnownGeogCS("WGS84"); |
2188 | |
|
2189 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2190 | 0 | "WARNING: Incomplete ellipsoid " |
2191 | 0 | "information. Using wgs-84 parameters."); |
2192 | 0 | } |
2193 | 0 | else if (EQUAL(pszEllipsoidName, "WGS84") || |
2194 | 0 | EQUAL(pszEllipsoidName, "WGS 1984")) |
2195 | 0 | { |
2196 | 0 | oLL.SetWellKnownGeogCS("WGS84"); |
2197 | 0 | oPrj.SetWellKnownGeogCS("WGS84"); |
2198 | 0 | } |
2199 | 0 | else |
2200 | 0 | { |
2201 | 0 | const double inv_flattening = |
2202 | 0 | major_axis / (major_axis - minor_axis); |
2203 | 0 | oLL.SetGeogCS("", "", pszEllipsoidName, major_axis, inv_flattening); |
2204 | 0 | oPrj.SetGeogCS("", "", pszEllipsoidName, major_axis, |
2205 | 0 | inv_flattening); |
2206 | 0 | } |
2207 | |
|
2208 | 0 | if (psMapProjection != nullptr) |
2209 | 0 | { |
2210 | 0 | const char *pszProj = |
2211 | 0 | CPLGetXMLValue(psMapProjection, "mapProjectionDescriptor", ""); |
2212 | 0 | bool bUseProjInfo = false; |
2213 | |
|
2214 | 0 | const CPLXMLNode *psUtmParams = |
2215 | 0 | CPLGetXMLNode(psMapProjection, "utmProjectionParameters"); |
2216 | |
|
2217 | 0 | const CPLXMLNode *psNspParams = |
2218 | 0 | CPLGetXMLNode(psMapProjection, "nspProjectionParameters"); |
2219 | |
|
2220 | 0 | if ((psUtmParams != nullptr) && poDS->bHaveGeoTransform) |
2221 | 0 | { |
2222 | | /* double origEasting, origNorthing; */ |
2223 | 0 | bool bNorth = true; |
2224 | |
|
2225 | 0 | const int utmZone = |
2226 | 0 | atoi(CPLGetXMLValue(psUtmParams, "utmZone", "")); |
2227 | 0 | const char *pszHemisphere = |
2228 | 0 | CPLGetXMLValue(psUtmParams, "hemisphere", ""); |
2229 | | #if 0 |
2230 | | origEasting = CPLStrtod(CPLGetXMLValue( |
2231 | | psUtmParams, "mapOriginFalseEasting", "0.0"), nullptr); |
2232 | | origNorthing = CPLStrtod(CPLGetXMLValue( |
2233 | | psUtmParams, "mapOriginFalseNorthing", "0.0"), nullptr); |
2234 | | #endif |
2235 | 0 | if (STARTS_WITH_CI(pszHemisphere, "southern")) |
2236 | 0 | bNorth = FALSE; |
2237 | |
|
2238 | 0 | if (STARTS_WITH_CI(pszProj, "UTM")) |
2239 | 0 | { |
2240 | 0 | oPrj.SetUTM(utmZone, bNorth); |
2241 | 0 | bUseProjInfo = true; |
2242 | 0 | } |
2243 | 0 | } |
2244 | 0 | else if ((psNspParams != nullptr) && poDS->bHaveGeoTransform) |
2245 | 0 | { |
2246 | 0 | const double origEasting = CPLStrtod( |
2247 | 0 | CPLGetXMLValue(psNspParams, "mapOriginFalseEasting", "0.0"), |
2248 | 0 | nullptr); |
2249 | 0 | const double origNorthing = |
2250 | 0 | CPLStrtod(CPLGetXMLValue(psNspParams, |
2251 | 0 | "mapOriginFalseNorthing", "0.0"), |
2252 | 0 | nullptr); |
2253 | 0 | const double copLong = CPLStrtod( |
2254 | 0 | CPLGetXMLValue(psNspParams, "centerOfProjectionLongitude", |
2255 | 0 | "0.0"), |
2256 | 0 | nullptr); |
2257 | 0 | const double copLat = CPLStrtod( |
2258 | 0 | CPLGetXMLValue(psNspParams, "centerOfProjectionLatitude", |
2259 | 0 | "0.0"), |
2260 | 0 | nullptr); |
2261 | 0 | const double sP1 = CPLStrtod( |
2262 | 0 | CPLGetXMLValue(psNspParams, "standardParallels1", "0.0"), |
2263 | 0 | nullptr); |
2264 | 0 | const double sP2 = CPLStrtod( |
2265 | 0 | CPLGetXMLValue(psNspParams, "standardParallels2", "0.0"), |
2266 | 0 | nullptr); |
2267 | |
|
2268 | 0 | if (STARTS_WITH_CI(pszProj, "ARC")) |
2269 | 0 | { |
2270 | | /* Albers Conical Equal Area */ |
2271 | 0 | oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting, |
2272 | 0 | origNorthing); |
2273 | 0 | bUseProjInfo = true; |
2274 | 0 | } |
2275 | 0 | else if (STARTS_WITH_CI(pszProj, "LCC")) |
2276 | 0 | { |
2277 | | /* Lambert Conformal Conic */ |
2278 | 0 | oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting, |
2279 | 0 | origNorthing); |
2280 | 0 | bUseProjInfo = true; |
2281 | 0 | } |
2282 | 0 | else if (STARTS_WITH_CI(pszProj, "STPL")) |
2283 | 0 | { |
2284 | | /* State Plate |
2285 | | ASSUMPTIONS: "zone" in product.xml matches USGS |
2286 | | definition as expected by ogr spatial reference; NAD83 |
2287 | | zones (versus NAD27) are assumed. */ |
2288 | |
|
2289 | 0 | const int nSPZone = |
2290 | 0 | atoi(CPLGetXMLValue(psNspParams, "zone", "1")); |
2291 | |
|
2292 | 0 | oPrj.SetStatePlane(nSPZone, TRUE, nullptr, 0.0); |
2293 | 0 | bUseProjInfo = true; |
2294 | 0 | } |
2295 | 0 | } |
2296 | |
|
2297 | 0 | if (bUseProjInfo) |
2298 | 0 | { |
2299 | 0 | poDS->m_oSRS = std::move(oPrj); |
2300 | 0 | } |
2301 | 0 | else |
2302 | 0 | { |
2303 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2304 | 0 | "WARNING: Unable to interpret projection information; " |
2305 | 0 | "check mapProjection info in product.xml!"); |
2306 | 0 | } |
2307 | 0 | } |
2308 | |
|
2309 | 0 | poDS->m_oGCPSRS = std::move(oLL); |
2310 | 0 | } |
2311 | | |
2312 | | /* -------------------------------------------------------------------- */ |
2313 | | /* Collect GCPs.DONE */ |
2314 | | /* -------------------------------------------------------------------- */ |
2315 | 0 | const CPLXMLNode *psGeoGrid = CPLGetXMLNode( |
2316 | 0 | psImageReferenceAttributes, "geographicInformation.geolocationGrid"); |
2317 | |
|
2318 | 0 | if (psGeoGrid != nullptr) |
2319 | 0 | { |
2320 | | /* count GCPs */ |
2321 | 0 | poDS->nGCPCount = 0; |
2322 | |
|
2323 | 0 | for (const CPLXMLNode *psNode = psGeoGrid->psChild; psNode != nullptr; |
2324 | 0 | psNode = psNode->psNext) |
2325 | 0 | { |
2326 | 0 | if (EQUAL(psNode->pszValue, "imageTiePoint")) |
2327 | 0 | poDS->nGCPCount++; |
2328 | 0 | } |
2329 | |
|
2330 | 0 | poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>( |
2331 | 0 | CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount)); |
2332 | |
|
2333 | 0 | poDS->nGCPCount = 0; |
2334 | |
|
2335 | 0 | for (const CPLXMLNode *psNode = psGeoGrid->psChild; psNode != nullptr; |
2336 | 0 | psNode = psNode->psNext) |
2337 | 0 | { |
2338 | 0 | GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount; |
2339 | |
|
2340 | 0 | if (!EQUAL(psNode->pszValue, "imageTiePoint")) |
2341 | 0 | continue; |
2342 | | |
2343 | 0 | poDS->nGCPCount++; |
2344 | |
|
2345 | 0 | char szID[32]; |
2346 | 0 | snprintf(szID, sizeof(szID), "%d", poDS->nGCPCount); |
2347 | 0 | psGCP->pszId = CPLStrdup(szID); |
2348 | 0 | psGCP->pszInfo = CPLStrdup(""); |
2349 | 0 | psGCP->dfGCPPixel = |
2350 | 0 | CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.pixel", "0")); |
2351 | 0 | psGCP->dfGCPLine = |
2352 | 0 | CPLAtof(CPLGetXMLValue(psNode, "imageCoordinate.line", "0")); |
2353 | 0 | psGCP->dfGCPX = CPLAtof( |
2354 | 0 | CPLGetXMLValue(psNode, "geodeticCoordinate.longitude", "")); |
2355 | 0 | psGCP->dfGCPY = CPLAtof( |
2356 | 0 | CPLGetXMLValue(psNode, "geodeticCoordinate.latitude", "")); |
2357 | 0 | psGCP->dfGCPZ = CPLAtof( |
2358 | 0 | CPLGetXMLValue(psNode, "geodeticCoordinate.height", "")); |
2359 | 0 | } |
2360 | 0 | } |
2361 | | |
2362 | | /* -------------------------------------------------------------------- */ |
2363 | | /* Collect RPC.DONE */ |
2364 | | /* -------------------------------------------------------------------- */ |
2365 | 0 | const CPLXMLNode *psRationalFunctions = CPLGetXMLNode( |
2366 | 0 | psImageReferenceAttributes, "geographicInformation.rationalFunctions"); |
2367 | 0 | if (psRationalFunctions != nullptr) |
2368 | 0 | { |
2369 | 0 | char **papszRPC = nullptr; |
2370 | 0 | static const char *const apszXMLToGDALMapping[] = { |
2371 | 0 | "biasError", |
2372 | 0 | "ERR_BIAS", |
2373 | 0 | "randomError", |
2374 | 0 | "ERR_RAND", |
2375 | | //"lineFitQuality", "????", |
2376 | | //"pixelFitQuality", "????", |
2377 | 0 | "lineOffset", |
2378 | 0 | "LINE_OFF", |
2379 | 0 | "pixelOffset", |
2380 | 0 | "SAMP_OFF", |
2381 | 0 | "latitudeOffset", |
2382 | 0 | "LAT_OFF", |
2383 | 0 | "longitudeOffset", |
2384 | 0 | "LONG_OFF", |
2385 | 0 | "heightOffset", |
2386 | 0 | "HEIGHT_OFF", |
2387 | 0 | "lineScale", |
2388 | 0 | "LINE_SCALE", |
2389 | 0 | "pixelScale", |
2390 | 0 | "SAMP_SCALE", |
2391 | 0 | "latitudeScale", |
2392 | 0 | "LAT_SCALE", |
2393 | 0 | "longitudeScale", |
2394 | 0 | "LONG_SCALE", |
2395 | 0 | "heightScale", |
2396 | 0 | "HEIGHT_SCALE", |
2397 | 0 | "lineNumeratorCoefficients", |
2398 | 0 | "LINE_NUM_COEFF", |
2399 | 0 | "lineDenominatorCoefficients", |
2400 | 0 | "LINE_DEN_COEFF", |
2401 | 0 | "pixelNumeratorCoefficients", |
2402 | 0 | "SAMP_NUM_COEFF", |
2403 | 0 | "pixelDenominatorCoefficients", |
2404 | 0 | "SAMP_DEN_COEFF", |
2405 | 0 | }; |
2406 | 0 | for (size_t i = 0; i < CPL_ARRAYSIZE(apszXMLToGDALMapping); i += 2) |
2407 | 0 | { |
2408 | 0 | const char *pszValue = CPLGetXMLValue( |
2409 | 0 | psRationalFunctions, apszXMLToGDALMapping[i], nullptr); |
2410 | 0 | if (pszValue) |
2411 | 0 | papszRPC = CSLSetNameValue( |
2412 | 0 | papszRPC, apszXMLToGDALMapping[i + 1], pszValue); |
2413 | 0 | } |
2414 | 0 | poDS->GDALDataset::SetMetadata(papszRPC, GDAL_MDD_RPC); |
2415 | 0 | CSLDestroy(papszRPC); |
2416 | 0 | } |
2417 | | |
2418 | | /* -------------------------------------------------------------------- */ |
2419 | | /* Initialize any PAM information. */ |
2420 | | /* -------------------------------------------------------------------- */ |
2421 | 0 | CPLString osDescription; |
2422 | 0 | CPLString osSubdatasetName; |
2423 | 0 | bool useSubdatasets = true; |
2424 | |
|
2425 | 0 | switch (eCalib) |
2426 | 0 | { |
2427 | 0 | case Sigma0: |
2428 | 0 | { |
2429 | 0 | osSubdatasetName = szSIGMA0; |
2430 | 0 | osDescription = FormatCalibration(szSIGMA0, osMDFilename.c_str()); |
2431 | 0 | } |
2432 | 0 | break; |
2433 | 0 | case Beta0: |
2434 | 0 | { |
2435 | 0 | osSubdatasetName = szBETA0; |
2436 | 0 | osDescription = FormatCalibration(szBETA0, osMDFilename.c_str()); |
2437 | 0 | } |
2438 | 0 | break; |
2439 | 0 | case Gamma: |
2440 | 0 | { |
2441 | 0 | osSubdatasetName = szGAMMA; |
2442 | 0 | osDescription = FormatCalibration(szGAMMA, osMDFilename.c_str()); |
2443 | 0 | } |
2444 | 0 | break; |
2445 | 0 | case Uncalib: |
2446 | 0 | { |
2447 | 0 | osSubdatasetName = szUNCALIB; |
2448 | 0 | osDescription = FormatCalibration(szUNCALIB, osMDFilename.c_str()); |
2449 | 0 | } |
2450 | 0 | break; |
2451 | 0 | default: |
2452 | 0 | osSubdatasetName = szUNCALIB; |
2453 | 0 | osDescription = osMDFilename; |
2454 | 0 | useSubdatasets = false; |
2455 | 0 | } |
2456 | | |
2457 | 0 | CPL_IGNORE_RET_VAL(osSubdatasetName); |
2458 | |
|
2459 | 0 | if (eCalib != None) |
2460 | 0 | poDS->papszExtraFiles = |
2461 | 0 | CSLAddString(poDS->papszExtraFiles, osMDFilename); |
2462 | | |
2463 | | /* -------------------------------------------------------------------- */ |
2464 | | /* Initialize any PAM information. */ |
2465 | | /* -------------------------------------------------------------------- */ |
2466 | 0 | poDS->SetDescription(osDescription); |
2467 | |
|
2468 | 0 | poDS->SetPhysicalFilename(osMDFilename); |
2469 | 0 | poDS->SetSubdatasetName(osDescription); |
2470 | |
|
2471 | 0 | poDS->TryLoadXML(); |
2472 | | |
2473 | | /* -------------------------------------------------------------------- */ |
2474 | | /* Check for overviews. */ |
2475 | | /* -------------------------------------------------------------------- */ |
2476 | 0 | if (useSubdatasets) |
2477 | 0 | poDS->oOvManager.Initialize(poDS.get(), ":::VIRTUAL:::"); |
2478 | 0 | else |
2479 | 0 | poDS->oOvManager.Initialize(poDS.get(), osMDFilename); |
2480 | |
|
2481 | 0 | return poDS.release(); |
2482 | 0 | } |
2483 | | |
2484 | | /************************************************************************/ |
2485 | | /* GetGCPCount() */ |
2486 | | /************************************************************************/ |
2487 | | |
2488 | | int RCMDataset::GetGCPCount() |
2489 | | |
2490 | 0 | { |
2491 | 0 | return nGCPCount; |
2492 | 0 | } |
2493 | | |
2494 | | /************************************************************************/ |
2495 | | /* GetGCPSpatialRef() */ |
2496 | | /************************************************************************/ |
2497 | | |
2498 | | const OGRSpatialReference *RCMDataset::GetGCPSpatialRef() const |
2499 | | |
2500 | 0 | { |
2501 | 0 | return m_oGCPSRS.IsEmpty() || nGCPCount == 0 ? nullptr : &m_oGCPSRS; |
2502 | 0 | } |
2503 | | |
2504 | | /************************************************************************/ |
2505 | | /* GetGCPs() */ |
2506 | | /************************************************************************/ |
2507 | | |
2508 | | const GDAL_GCP *RCMDataset::GetGCPs() |
2509 | | |
2510 | 0 | { |
2511 | 0 | return pasGCPList; |
2512 | 0 | } |
2513 | | |
2514 | | /************************************************************************/ |
2515 | | /* GetSpatialRef() */ |
2516 | | /************************************************************************/ |
2517 | | |
2518 | | const OGRSpatialReference *RCMDataset::GetSpatialRef() const |
2519 | | |
2520 | 0 | { |
2521 | 0 | return m_oSRS.IsEmpty() ? nullptr : &m_oSRS; |
2522 | 0 | } |
2523 | | |
2524 | | /************************************************************************/ |
2525 | | /* GetGeoTransform() */ |
2526 | | /************************************************************************/ |
2527 | | |
2528 | | CPLErr RCMDataset::GetGeoTransform(GDALGeoTransform >) const |
2529 | | |
2530 | 0 | { |
2531 | 0 | gt = m_gt; |
2532 | |
|
2533 | 0 | if (bHaveGeoTransform) |
2534 | 0 | { |
2535 | 0 | return CE_None; |
2536 | 0 | } |
2537 | | |
2538 | 0 | return CE_Failure; |
2539 | 0 | } |
2540 | | |
2541 | | /************************************************************************/ |
2542 | | /* GetMetadataDomainList() */ |
2543 | | /************************************************************************/ |
2544 | | |
2545 | | char **RCMDataset::GetMetadataDomainList() |
2546 | 0 | { |
2547 | 0 | return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE, |
2548 | 0 | GDAL_MDD_SUBDATASETS, nullptr); |
2549 | 0 | } |
2550 | | |
2551 | | /************************************************************************/ |
2552 | | /* GetMetadata() */ |
2553 | | /************************************************************************/ |
2554 | | |
2555 | | CSLConstList RCMDataset::GetMetadata(const char *pszDomain) |
2556 | | |
2557 | 0 | { |
2558 | 0 | if (pszDomain != nullptr && |
2559 | 0 | STARTS_WITH_CI(pszDomain, GDAL_MDD_SUBDATASETS) && |
2560 | 0 | papszSubDatasets != nullptr) |
2561 | 0 | return papszSubDatasets; |
2562 | | |
2563 | 0 | return GDALDataset::GetMetadata(pszDomain); |
2564 | 0 | } |
2565 | | |
2566 | | /************************************************************************/ |
2567 | | /* GDALRegister_RCM() */ |
2568 | | /************************************************************************/ |
2569 | | |
2570 | | void GDALRegister_RCM() |
2571 | | |
2572 | 22 | { |
2573 | 22 | if (GDALGetDriverByName("RCM") != nullptr) |
2574 | 0 | { |
2575 | 0 | return; |
2576 | 0 | } |
2577 | | |
2578 | 22 | GDALDriver *poDriver = new GDALDriver(); |
2579 | 22 | RCMDriverSetCommonMetadata(poDriver); |
2580 | | |
2581 | 22 | poDriver->pfnOpen = RCMDataset::Open; |
2582 | | |
2583 | 22 | GetGDALDriverManager()->RegisterDriver(poDriver); |
2584 | 22 | } |