Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: ASI CEOS Translator
4 : * Purpose: GDALDataset driver for CEOS translator.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Atlantis Scientific Inc.
9 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ceos.h"
15 : #include "cpl_string.h"
16 : #include "gdal_frmts.h"
17 : #include "gdal_priv.h"
18 : #include "rawdataset.h"
19 : #include "ogr_srs_api.h"
20 : #include "cpl_vsi_virtual.h"
21 :
22 : #include <algorithm>
23 : #include <cinttypes>
24 :
25 0 : static GInt16 CastToGInt16(float val)
26 : {
27 0 : if (val < -32768.0)
28 0 : val = -32768.0;
29 :
30 0 : if (val > 32767)
31 0 : val = 32767.0;
32 :
33 0 : return static_cast<GInt16>(val);
34 : }
35 :
36 : // CeosExtension[][i] ordered by i:
37 : // CEOS_VOLUME_DIR_FILE = 0
38 : // CEOS_LEADER_FILE = 1
39 : // CEOS_IMAGRY_OPT_FILE = 2
40 : // CEOS_TRAILER_FILE = 3
41 : // CEOS_NULL_VOL_FILE = 4
42 : // and last item (idx 5) is the method to derive auxiliary filenames
43 : static const char *const CeosExtension[][CEOS_FILE_COUNT + 1] = {
44 : {"vol", "led", "img", "trl", "nul", "ext"},
45 : {"vol", "lea", "img", "trl", "nul", "ext"},
46 : {"vol", "led", "img", "tra", "nul", "ext"},
47 : {"vol", "lea", "img", "tra", "nul", "ext"},
48 : {"vdf", "slf", "sdf", "stf", "nvd", "ext"},
49 :
50 : {"vdf", "ldr", "img", "tra", "nul", "ext2"},
51 :
52 : /* Jers from Japan- not sure if this is generalized as much as it could be
53 : */
54 : {"VOLD", "Sarl_01", "Imop_%02d", "Sart_01", "NULL", "base"},
55 :
56 : /* Radarsat: basename, not extension */
57 : {"vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_vdf", "base"},
58 :
59 : /* Ers-1: basename, not extension */
60 : {"vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_dat", "base"},
61 :
62 : /* Ers-2 from Telaviv */
63 : {"volume", "leader", "image", "trailer", "nul_dat", "whole"},
64 :
65 : /* Ers-1 from D-PAF */
66 : {"VDF", "LF", "SLC", "", "", "ext"},
67 :
68 : /* Radarsat-1 per #2051 */
69 : {"vol", "sarl", "sard", "sart", "nvol", "ext"},
70 :
71 : /* Radarsat-1 ASF */
72 : {"", "L", "D", "", "", "ext"},
73 :
74 : /* PALSAR-2 ALOS2 / PALSAR-3 ALOS4 */
75 : {"VOL", "LED", "", "TRL", "", "ALOS2-ALOS4"},
76 :
77 : /* end marker */
78 : {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}};
79 :
80 : static int ProcessData(VSILFILE *fp, int fileid, CeosSARVolume_t *sar,
81 : int max_records, vsi_l_offset max_bytes,
82 : bool bSilentWrongRecordNumber);
83 :
84 124 : static CeosTypeCode_t QuadToTC(int a, int b, int c, int d)
85 : {
86 : CeosTypeCode_t abcd;
87 :
88 124 : abcd.UCharCode.Subtype1 = (unsigned char)a;
89 124 : abcd.UCharCode.Type = (unsigned char)b;
90 124 : abcd.UCharCode.Subtype2 = (unsigned char)c;
91 124 : abcd.UCharCode.Subtype3 = (unsigned char)d;
92 :
93 124 : return abcd;
94 : }
95 :
96 : #define LEADER_DATASET_SUMMARY_TC QuadToTC(18, 10, 18, 20)
97 : #define LEADER_DATASET_SUMMARY_ERS2_TC QuadToTC(10, 10, 31, 20)
98 : #define LEADER_RADIOMETRIC_COMPENSATION_TC QuadToTC(18, 51, 18, 20)
99 : #define VOLUME_DESCRIPTOR_RECORD_TC QuadToTC(192, 192, 18, 18)
100 : #define IMAGE_HEADER_RECORD_TC QuadToTC(63, 192, 18, 18)
101 : #define LEADER_RADIOMETRIC_DATA_RECORD_TC QuadToTC(18, 50, 18, 20)
102 : #define LEADER_MAP_PROJ_RECORD_TC QuadToTC(10, 20, 31, 20)
103 :
104 : /* JERS from Japan has MAP_PROJ record with different identifiers */
105 : /* see CEOS-SAR-CCT Iss/Rev: 2/0 February 10, 1989 */
106 : #define LEADER_MAP_PROJ_RECORD_JERS_TC QuadToTC(18, 20, 18, 20)
107 :
108 : /* Leader from ASF has different identifiers */
109 : #define LEADER_MAP_PROJ_RECORD_ASF_TC QuadToTC(10, 20, 18, 20)
110 : #define LEADER_DATASET_SUMMARY_ASF_TC QuadToTC(10, 10, 18, 20)
111 : #define LEADER_FACILITY_ASF_TC QuadToTC(90, 210, 18, 61)
112 :
113 : /* For ERS calibration and incidence angle information */
114 : #define ERS_GENERAL_FACILITY_DATA_TC QuadToTC(10, 200, 31, 50)
115 : #define ERS_GENERAL_FACILITY_DATA_ALT_TC QuadToTC(10, 216, 31, 50)
116 :
117 : #define RSAT_PROC_PARAM_TC QuadToTC(18, 120, 18, 20)
118 :
119 : /* PALSAR-2 ALOS2 */
120 : // https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
121 : // Table 3.2-3 Record Type of Each Record
122 : #define LEADER_PLATFORM_POSITION_PALSAR_TC QuadToTC(18, 30, 18, 20)
123 : #define LEADER_ATTITUDE_PALSAR_TC QuadToTC(18, 40, 18, 20)
124 : #define LEADER_RADIOMETRIC_PALSAR_TC QuadToTC(18, 50, 18, 20)
125 : #define LEADER_FACILITY_RELATED_PALSAR_TC QuadToTC(18, 200, 18, 70)
126 : #define IMAGE_PROCESSED_DATA_RECORD_PALSAR_TC QuadToTC(50, 11, 18, 20)
127 :
128 : /************************************************************************/
129 : /* ==================================================================== */
130 : /* SAR_CEOSDataset */
131 : /* ==================================================================== */
132 : /************************************************************************/
133 :
134 : class SAR_CEOSRasterBand;
135 : class CCPRasterBand;
136 : class PALSARRasterBand;
137 :
138 : class SAR_CEOSDataset final : public GDALPamDataset
139 : {
140 : friend class SAR_CEOSRasterBand;
141 : friend class CCPRasterBand;
142 : friend class PALSARRasterBand;
143 :
144 : CeosSARVolume_t sVolume;
145 :
146 : VSILFILE *fpImage;
147 :
148 : char **papszTempMD;
149 :
150 : bool m_bHasScannedForGCP = false;
151 : OGRSpatialReference m_oSRS{};
152 : int nGCPCount;
153 : GDAL_GCP *pasGCPList;
154 :
155 : void ScanForGCPs();
156 : void ScanForMetadata();
157 : int ScanForMapProjection();
158 : char **papszExtraFiles;
159 :
160 : public:
161 : SAR_CEOSDataset();
162 : ~SAR_CEOSDataset() override;
163 :
164 : int GetGCPCount() override;
165 : const OGRSpatialReference *GetGCPSpatialRef() const override;
166 : const GDAL_GCP *GetGCPs() override;
167 :
168 : char **GetMetadataDomainList() override;
169 : CSLConstList GetMetadata(const char *pszDomain) override;
170 :
171 : static GDALDataset *Open(GDALOpenInfo *);
172 : char **GetFileList(void) override;
173 : };
174 :
175 : /************************************************************************/
176 : /* ==================================================================== */
177 : /* CCPRasterBand */
178 : /* ==================================================================== */
179 : /************************************************************************/
180 :
181 : class CCPRasterBand final : public GDALPamRasterBand
182 : {
183 : friend class SAR_CEOSDataset;
184 :
185 : public:
186 : CCPRasterBand(SAR_CEOSDataset *, int, GDALDataType);
187 :
188 : CPLErr IReadBlock(int, int, void *) override;
189 : };
190 :
191 : /************************************************************************/
192 : /* ==================================================================== */
193 : /* PALSARRasterBand */
194 : /* ==================================================================== */
195 : /************************************************************************/
196 :
197 : class PALSARRasterBand final : public GDALPamRasterBand
198 : {
199 : friend class SAR_CEOSDataset;
200 :
201 : public:
202 : PALSARRasterBand(SAR_CEOSDataset *, int);
203 :
204 : CPLErr IReadBlock(int, int, void *) override;
205 : };
206 :
207 : /************************************************************************/
208 : /* ==================================================================== */
209 : /* SAR_CEOSRasterBand */
210 : /* ==================================================================== */
211 : /************************************************************************/
212 :
213 : class SAR_CEOSRasterBand final : public GDALPamRasterBand
214 : {
215 : friend class SAR_CEOSDataset;
216 :
217 : public:
218 : SAR_CEOSRasterBand(SAR_CEOSDataset *, int, GDALDataType);
219 :
220 : CPLErr IReadBlock(int, int, void *) override;
221 : };
222 :
223 : /************************************************************************/
224 : /* SAR_CEOSRasterBand() */
225 : /************************************************************************/
226 :
227 0 : SAR_CEOSRasterBand::SAR_CEOSRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn,
228 0 : GDALDataType eType)
229 :
230 : {
231 0 : poDS = poGDSIn;
232 0 : nBand = nBandIn;
233 :
234 0 : eDataType = eType;
235 :
236 0 : nBlockXSize = poGDSIn->nRasterXSize;
237 0 : nBlockYSize = 1;
238 0 : }
239 :
240 : /************************************************************************/
241 : /* IReadBlock() */
242 : /************************************************************************/
243 :
244 0 : CPLErr SAR_CEOSRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
245 : void *pImage)
246 : {
247 0 : SAR_CEOSDataset *poGDS = cpl::down_cast<SAR_CEOSDataset *>(poDS);
248 :
249 0 : struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
250 :
251 0 : uint64_t offset = 0;
252 0 : CalcCeosSARImageFilePosition(&(poGDS->sVolume), nBand, nBlockYOff + 1,
253 : nullptr, &offset);
254 :
255 0 : offset += ImageDesc->ImageDataStart;
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Load all the pixel data associated with this scanline. */
259 : /* Ensure we handle multiple record scanlines properly. */
260 : /* -------------------------------------------------------------------- */
261 0 : int nPixelsRead = 0;
262 :
263 : GByte *pabyRecord =
264 0 : (GByte *)VSI_MALLOC2_VERBOSE(ImageDesc->BytesPerPixel, nBlockXSize);
265 0 : if (!pabyRecord)
266 0 : return CE_Failure;
267 :
268 0 : for (int iRecord = 0; iRecord < ImageDesc->RecordsPerLine; iRecord++)
269 : {
270 : int nPixelsToRead;
271 :
272 0 : if (nPixelsRead + ImageDesc->PixelsPerRecord > nBlockXSize)
273 0 : nPixelsToRead = nBlockXSize - nPixelsRead;
274 : else
275 0 : nPixelsToRead = ImageDesc->PixelsPerRecord;
276 :
277 0 : CPL_IGNORE_RET_VAL(VSIFSeekL(poGDS->fpImage, offset, SEEK_SET));
278 0 : CPL_IGNORE_RET_VAL(VSIFReadL(
279 0 : pabyRecord +
280 0 : static_cast<size_t>(nPixelsRead) * ImageDesc->BytesPerPixel,
281 0 : 1, static_cast<size_t>(nPixelsToRead) * ImageDesc->BytesPerPixel,
282 : poGDS->fpImage));
283 :
284 0 : nPixelsRead += nPixelsToRead;
285 0 : offset += ImageDesc->BytesPerRecord;
286 : }
287 :
288 : /* -------------------------------------------------------------------- */
289 : /* Copy the desired band out based on the size of the type, and */
290 : /* the interleaving mode. */
291 : /* -------------------------------------------------------------------- */
292 0 : const int nBytesPerSample = GDALGetDataTypeSizeBytes(eDataType);
293 :
294 0 : if (ImageDesc->ChannelInterleaving == CEOS_IL_PIXEL)
295 : {
296 0 : GDALCopyWords(pabyRecord + (nBand - 1) * nBytesPerSample, eDataType,
297 : ImageDesc->BytesPerPixel, pImage, eDataType,
298 : nBytesPerSample, nBlockXSize);
299 : }
300 0 : else if (ImageDesc->ChannelInterleaving == CEOS_IL_LINE)
301 : {
302 0 : GDALCopyWords(pabyRecord + (nBand - 1) * nBytesPerSample * nBlockXSize,
303 : eDataType, nBytesPerSample, pImage, eDataType,
304 : nBytesPerSample, nBlockXSize);
305 : }
306 0 : else if (ImageDesc->ChannelInterleaving == CEOS_IL_BAND)
307 : {
308 0 : memcpy(pImage, pabyRecord,
309 0 : static_cast<size_t>(nBytesPerSample) * nBlockXSize);
310 : }
311 :
312 : #ifdef CPL_LSB
313 0 : GDALSwapWords(pImage, nBytesPerSample, nBlockXSize, nBytesPerSample);
314 : #endif
315 :
316 0 : CPLFree(pabyRecord);
317 :
318 0 : return CE_None;
319 : }
320 :
321 : /************************************************************************/
322 : /* ==================================================================== */
323 : /* CCPRasterBand */
324 : /* ==================================================================== */
325 : /************************************************************************/
326 :
327 : /************************************************************************/
328 : /* CCPRasterBand() */
329 : /************************************************************************/
330 :
331 0 : CCPRasterBand::CCPRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn,
332 0 : GDALDataType eType)
333 :
334 : {
335 0 : poDS = poGDSIn;
336 0 : nBand = nBandIn;
337 :
338 0 : eDataType = eType;
339 :
340 0 : nBlockXSize = poGDSIn->nRasterXSize;
341 0 : nBlockYSize = 1;
342 :
343 0 : if (nBand == 1)
344 0 : SetMetadataItem("POLARIMETRIC_INTERP", "HH");
345 0 : else if (nBand == 2)
346 0 : SetMetadataItem("POLARIMETRIC_INTERP", "HV");
347 0 : else if (nBand == 3)
348 0 : SetMetadataItem("POLARIMETRIC_INTERP", "VH");
349 0 : else if (nBand == 4)
350 0 : SetMetadataItem("POLARIMETRIC_INTERP", "VV");
351 0 : }
352 :
353 : /************************************************************************/
354 : /* IReadBlock() */
355 : /************************************************************************/
356 :
357 : /* From: http://southport.jpl.nasa.gov/software/dcomp/dcomp.html
358 :
359 : ysca = sqrt{ [ (Byte(2) / 254 ) + 1.5] 2Byte(1) }
360 :
361 : Re(SHH) = byte(3) ysca/127
362 :
363 : Im(SHH) = byte(4) ysca/127
364 :
365 : Re(SHV) = byte(5) ysca/127
366 :
367 : Im(SHV) = byte(6) ysca/127
368 :
369 : Re(SVH) = byte(7) ysca/127
370 :
371 : Im(SVH) = byte(8) ysca/127
372 :
373 : Re(SVV) = byte(9) ysca/127
374 :
375 : Im(SVV) = byte(10) ysca/127
376 :
377 : */
378 :
379 0 : CPLErr CCPRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
380 : void *pImage)
381 : {
382 0 : SAR_CEOSDataset *poGDS = cpl::down_cast<SAR_CEOSDataset *>(poDS);
383 :
384 0 : struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
385 :
386 0 : const vsi_l_offset offset =
387 0 : ImageDesc->FileDescriptorLength +
388 0 : static_cast<vsi_l_offset>(ImageDesc->BytesPerRecord) * nBlockYOff +
389 0 : ImageDesc->ImageDataStart;
390 :
391 : /* -------------------------------------------------------------------- */
392 : /* Load all the pixel data associated with this scanline. */
393 : /* -------------------------------------------------------------------- */
394 0 : const int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
395 :
396 0 : GByte *pabyRecord = (GByte *)CPLMalloc(nBytesToRead);
397 :
398 0 : if (VSIFSeekL(poGDS->fpImage, offset, SEEK_SET) != 0 ||
399 0 : (int)VSIFReadL(pabyRecord, 1, nBytesToRead, poGDS->fpImage) !=
400 : nBytesToRead)
401 : {
402 0 : CPLError(CE_Failure, CPLE_FileIO,
403 : "Error reading %d bytes of CEOS record data at offset %" PRIu64
404 : ".\n"
405 : "Reading file %s failed.",
406 : nBytesToRead, static_cast<uint64_t>(offset),
407 0 : poGDS->GetDescription());
408 0 : CPLFree(pabyRecord);
409 0 : return CE_Failure;
410 : }
411 :
412 : /* -------------------------------------------------------------------- */
413 : /* Initialize our power table if this is our first time through. */
414 : /* -------------------------------------------------------------------- */
415 : static float afPowTable[256];
416 : static bool bPowTableInitialized = false;
417 :
418 0 : if (!bPowTableInitialized)
419 : {
420 0 : bPowTableInitialized = true;
421 :
422 0 : for (int i = 0; i < 256; i++)
423 : {
424 0 : afPowTable[i] = (float)pow(2.0, i - 128);
425 : }
426 : }
427 :
428 : /* -------------------------------------------------------------------- */
429 : /* Copy the desired band out based on the size of the type, and */
430 : /* the interleaving mode. */
431 : /* -------------------------------------------------------------------- */
432 0 : for (int iX = 0; iX < nBlockXSize; iX++)
433 : {
434 0 : unsigned char *pabyGroup = pabyRecord + iX * ImageDesc->BytesPerPixel;
435 0 : signed char *Byte =
436 : (signed char *)pabyGroup - 1; /* A ones based alias */
437 : double dfReSHH, dfImSHH, dfReSHV, dfImSHV, dfReSVH, dfImSVH, dfReSVV,
438 : dfImSVV;
439 :
440 : const double dfScale =
441 0 : sqrt((Byte[2] / 254.0 + 1.5) * afPowTable[Byte[1] + 128]);
442 :
443 0 : if (nBand == 1)
444 : {
445 0 : dfReSHH = Byte[3] * dfScale / 127.0;
446 0 : dfImSHH = Byte[4] * dfScale / 127.0;
447 :
448 0 : ((float *)pImage)[iX * 2] = (float)dfReSHH;
449 0 : ((float *)pImage)[iX * 2 + 1] = (float)dfImSHH;
450 : }
451 0 : else if (nBand == 2)
452 : {
453 0 : dfReSHV = Byte[5] * dfScale / 127.0;
454 0 : dfImSHV = Byte[6] * dfScale / 127.0;
455 :
456 0 : ((float *)pImage)[iX * 2] = (float)dfReSHV;
457 0 : ((float *)pImage)[iX * 2 + 1] = (float)dfImSHV;
458 : }
459 0 : else if (nBand == 3)
460 : {
461 0 : dfReSVH = Byte[7] * dfScale / 127.0;
462 0 : dfImSVH = Byte[8] * dfScale / 127.0;
463 :
464 0 : ((float *)pImage)[iX * 2] = (float)dfReSVH;
465 0 : ((float *)pImage)[iX * 2 + 1] = (float)dfImSVH;
466 : }
467 0 : else if (nBand == 4)
468 : {
469 0 : dfReSVV = Byte[9] * dfScale / 127.0;
470 0 : dfImSVV = Byte[10] * dfScale / 127.0;
471 :
472 0 : ((float *)pImage)[iX * 2] = (float)dfReSVV;
473 0 : ((float *)pImage)[iX * 2 + 1] = (float)dfImSVV;
474 : }
475 : }
476 :
477 0 : CPLFree(pabyRecord);
478 :
479 0 : return CE_None;
480 : }
481 :
482 : /************************************************************************/
483 : /* ==================================================================== */
484 : /* PALSARRasterBand */
485 : /* ==================================================================== */
486 : /************************************************************************/
487 :
488 : /************************************************************************/
489 : /* PALSARRasterBand() */
490 : /************************************************************************/
491 :
492 0 : PALSARRasterBand::PALSARRasterBand(SAR_CEOSDataset *poGDSIn, int nBandIn)
493 :
494 : {
495 0 : poDS = poGDSIn;
496 0 : nBand = nBandIn;
497 :
498 0 : eDataType = GDT_CInt16;
499 :
500 0 : nBlockXSize = poGDSIn->nRasterXSize;
501 0 : nBlockYSize = 1;
502 :
503 0 : if (nBand == 1)
504 0 : SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_11");
505 0 : else if (nBand == 2)
506 0 : SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_22");
507 0 : else if (nBand == 3)
508 0 : SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_33");
509 0 : else if (nBand == 4)
510 0 : SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_12");
511 0 : else if (nBand == 5)
512 0 : SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_13");
513 0 : else if (nBand == 6)
514 0 : SetMetadataItem("POLARIMETRIC_INTERP", "Covariance_23");
515 0 : }
516 :
517 : /************************************************************************/
518 : /* IReadBlock() */
519 : /* */
520 : /* Based on ERSDAC-VX-CEOS-004 */
521 : /************************************************************************/
522 :
523 0 : CPLErr PALSARRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
524 : void *pImage)
525 : {
526 0 : SAR_CEOSDataset *poGDS = cpl::down_cast<SAR_CEOSDataset *>(poDS);
527 :
528 0 : struct CeosSARImageDesc *ImageDesc = &(poGDS->sVolume.ImageDesc);
529 :
530 0 : const vsi_l_offset offset =
531 0 : ImageDesc->FileDescriptorLength +
532 0 : static_cast<vsi_l_offset>(ImageDesc->BytesPerRecord) * nBlockYOff +
533 0 : ImageDesc->ImageDataStart;
534 :
535 : /* -------------------------------------------------------------------- */
536 : /* Load all the pixel data associated with this scanline. */
537 : /* -------------------------------------------------------------------- */
538 0 : const int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize;
539 :
540 0 : GByte *pabyRecord = (GByte *)CPLMalloc(nBytesToRead);
541 :
542 0 : if (VSIFSeekL(poGDS->fpImage, offset, SEEK_SET) != 0 ||
543 0 : (int)VSIFReadL(pabyRecord, 1, nBytesToRead, poGDS->fpImage) !=
544 : nBytesToRead)
545 : {
546 0 : CPLError(CE_Failure, CPLE_FileIO,
547 : "Error reading %d bytes of CEOS record data at offset %" PRIu64
548 : ".\n"
549 : "Reading file %s failed.",
550 : nBytesToRead, static_cast<uint64_t>(offset),
551 0 : poGDS->GetDescription());
552 0 : CPLFree(pabyRecord);
553 0 : return CE_Failure;
554 : }
555 :
556 : /* -------------------------------------------------------------------- */
557 : /* Copy the desired band out based on the size of the type, and */
558 : /* the interleaving mode. */
559 : /* -------------------------------------------------------------------- */
560 0 : if (nBand == 1 || nBand == 2 || nBand == 3)
561 : {
562 : // we need to pre-initialize things to set the imaginary component to 0
563 0 : memset(pImage, 0, nBlockXSize * 4);
564 :
565 0 : GDALCopyWords(pabyRecord + 4 * (nBand - 1), GDT_Int16, 18, pImage,
566 : GDT_Int16, 4, nBlockXSize);
567 : #ifdef CPL_LSB
568 0 : GDALSwapWords(pImage, 2, nBlockXSize, 4);
569 : #endif
570 : }
571 : else
572 : {
573 0 : GDALCopyWords(pabyRecord + 6 + 4 * (nBand - 4), GDT_CInt16, 18, pImage,
574 : GDT_CInt16, 4, nBlockXSize);
575 : #ifdef CPL_LSB
576 0 : GDALSwapWords(pImage, 2, nBlockXSize * 2, 2);
577 : #endif
578 : }
579 0 : CPLFree(pabyRecord);
580 :
581 : /* -------------------------------------------------------------------- */
582 : /* Convert the values into covariance form as per: */
583 : /* -------------------------------------------------------------------- */
584 : /*
585 : ** 1) PALSAR- adjust so that it reads bands as a covariance matrix, and
586 : ** set polarimetric interpretation accordingly:
587 : **
588 : ** Covariance_11=HH*conj(HH): already there
589 : ** Covariance_22=2*HV*conj(HV): need a factor of 2
590 : ** Covariance_33=VV*conj(VV): already there
591 : ** Covariance_12=sqrt(2)*HH*conj(HV): need the sqrt(2) factor
592 : ** Covariance_13=HH*conj(VV): already there
593 : ** Covariance_23=sqrt(2)*HV*conj(VV): need to take the conjugate, then
594 : ** multiply by sqrt(2)
595 : **
596 : */
597 :
598 0 : if (nBand == 2)
599 : {
600 0 : GInt16 *panLine = (GInt16 *)pImage;
601 :
602 0 : for (int i = 0; i < nBlockXSize * 2; i++)
603 : {
604 0 : panLine[i] = (GInt16)CastToGInt16((float)2.0 * panLine[i]);
605 : }
606 : }
607 0 : else if (nBand == 4)
608 : {
609 0 : const double sqrt_2 = pow(2.0, 0.5);
610 0 : GInt16 *panLine = (GInt16 *)pImage;
611 :
612 0 : for (int i = 0; i < nBlockXSize * 2; i++)
613 : {
614 0 : panLine[i] =
615 0 : (GInt16)CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5));
616 : }
617 : }
618 0 : else if (nBand == 6)
619 : {
620 0 : GInt16 *panLine = (GInt16 *)pImage;
621 0 : const double sqrt_2 = pow(2.0, 0.5);
622 :
623 : // real portion - just multiple by sqrt(2)
624 0 : for (int i = 0; i < nBlockXSize * 2; i += 2)
625 : {
626 0 : panLine[i] =
627 0 : (GInt16)CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5));
628 : }
629 :
630 : // imaginary portion - conjugate and multiply
631 0 : for (int i = 1; i < nBlockXSize * 2; i += 2)
632 : {
633 0 : panLine[i] =
634 0 : (GInt16)CastToGInt16((float)floor(-panLine[i] * sqrt_2 + 0.5));
635 : }
636 : }
637 :
638 0 : return CE_None;
639 : }
640 :
641 : /************************************************************************/
642 : /* ==================================================================== */
643 : /* SAR_CEOSDataset */
644 : /* ==================================================================== */
645 : /************************************************************************/
646 :
647 : /************************************************************************/
648 : /* SAR_CEOSDataset() */
649 : /************************************************************************/
650 :
651 7 : SAR_CEOSDataset::SAR_CEOSDataset()
652 : : fpImage(nullptr), papszTempMD(nullptr), nGCPCount(0), pasGCPList(nullptr),
653 7 : papszExtraFiles(nullptr)
654 : {
655 7 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
656 7 : m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
657 :
658 7 : sVolume.Flavor = 0;
659 7 : sVolume.Sensor = 0;
660 7 : sVolume.ProductType = 0;
661 7 : sVolume.FileNamingConvention = 0;
662 :
663 7 : sVolume.VolumeDirectoryFile = 0;
664 7 : sVolume.SARLeaderFile = 0;
665 7 : sVolume.ImagryOptionsFile = 0;
666 7 : sVolume.SARTrailerFile = 0;
667 7 : sVolume.NullVolumeDirectoryFile = 0;
668 :
669 7 : sVolume.ImageDesc.ImageDescValid = 0;
670 7 : sVolume.ImageDesc.NumChannels = 0;
671 7 : sVolume.ImageDesc.ChannelInterleaving = 0;
672 7 : sVolume.ImageDesc.DataType = 0;
673 7 : sVolume.ImageDesc.BytesPerRecord = 0;
674 7 : sVolume.ImageDesc.Lines = 0;
675 7 : sVolume.ImageDesc.TopBorderPixels = 0;
676 7 : sVolume.ImageDesc.BottomBorderPixels = 0;
677 7 : sVolume.ImageDesc.PixelsPerLine = 0;
678 7 : sVolume.ImageDesc.LeftBorderPixels = 0;
679 7 : sVolume.ImageDesc.RightBorderPixels = 0;
680 7 : sVolume.ImageDesc.BytesPerPixel = 0;
681 7 : sVolume.ImageDesc.RecordsPerLine = 0;
682 7 : sVolume.ImageDesc.PixelsPerRecord = 0;
683 7 : sVolume.ImageDesc.ImageDataStart = 0;
684 7 : sVolume.ImageDesc.ImageSuffixData = 0;
685 7 : sVolume.ImageDesc.FileDescriptorLength = 0;
686 7 : sVolume.ImageDesc.PixelOrder = 0;
687 7 : sVolume.ImageDesc.LineOrder = 0;
688 7 : sVolume.ImageDesc.PixelDataBytesPerRecord = 0;
689 :
690 7 : sVolume.RecordList = nullptr;
691 7 : }
692 :
693 : /************************************************************************/
694 : /* ~SAR_CEOSDataset() */
695 : /************************************************************************/
696 :
697 14 : SAR_CEOSDataset::~SAR_CEOSDataset()
698 :
699 : {
700 7 : FlushCache(true);
701 :
702 7 : CSLDestroy(papszTempMD);
703 :
704 7 : if (fpImage != nullptr)
705 7 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
706 :
707 7 : if (nGCPCount > 0)
708 : {
709 1 : GDALDeinitGCPs(nGCPCount, pasGCPList);
710 : }
711 7 : CPLFree(pasGCPList);
712 :
713 7 : if (sVolume.RecordList)
714 : {
715 109 : for (Link_t *Links = sVolume.RecordList; Links != nullptr;
716 102 : Links = Links->next)
717 : {
718 102 : if (Links->object)
719 : {
720 102 : DeleteCeosRecord((CeosRecord_t *)Links->object);
721 102 : Links->object = nullptr;
722 : }
723 : }
724 7 : DestroyList(sVolume.RecordList);
725 : }
726 7 : FreeRecipes();
727 7 : CSLDestroy(papszExtraFiles);
728 14 : }
729 :
730 : /************************************************************************/
731 : /* GetGCPCount() */
732 : /************************************************************************/
733 :
734 3 : int SAR_CEOSDataset::GetGCPCount()
735 :
736 : {
737 3 : if (!m_bHasScannedForGCP)
738 3 : ScanForGCPs();
739 3 : return nGCPCount;
740 : }
741 :
742 : /************************************************************************/
743 : /* GetGCPSpatialRef() */
744 : /************************************************************************/
745 :
746 0 : const OGRSpatialReference *SAR_CEOSDataset::GetGCPSpatialRef() const
747 :
748 : {
749 0 : if (!m_bHasScannedForGCP)
750 0 : const_cast<SAR_CEOSDataset *>(this)->ScanForGCPs();
751 0 : if (nGCPCount > 0)
752 0 : return &m_oSRS;
753 :
754 0 : return nullptr;
755 : }
756 :
757 : /************************************************************************/
758 : /* GetGCP() */
759 : /************************************************************************/
760 :
761 0 : const GDAL_GCP *SAR_CEOSDataset::GetGCPs()
762 :
763 : {
764 0 : if (!m_bHasScannedForGCP)
765 0 : ScanForGCPs();
766 0 : return pasGCPList;
767 : }
768 :
769 : /************************************************************************/
770 : /* GetMetadataDomainList() */
771 : /************************************************************************/
772 :
773 0 : char **SAR_CEOSDataset::GetMetadataDomainList()
774 : {
775 0 : return CSLAddString(GDALDataset::GetMetadataDomainList(),
776 0 : "ceos-FFF-n-n-n-n:r");
777 : }
778 :
779 : /************************************************************************/
780 : /* GetMetadata() */
781 : /* */
782 : /* We provide our own GetMetadata() so that we can override */
783 : /* behavior for some very specialized domain names intended to */
784 : /* give us access to raw record data. */
785 : /* */
786 : /* The domain must look like: */
787 : /* ceos-FFF-n-n-n-n:r */
788 : /* */
789 : /* FFF - The file id - one of vol, lea, img, trl or nul. */
790 : /* n-n-n-n - the record type code such as 18-10-18-20 for the */
791 : /* dataset summary record in the leader file. */
792 : /* :r - The zero based record number to fetch (optional) */
793 : /* */
794 : /* Note that only records that are pre-loaded will be */
795 : /* accessible, and this normally means that most image records */
796 : /* are not available. */
797 : /************************************************************************/
798 :
799 3 : CSLConstList SAR_CEOSDataset::GetMetadata(const char *pszDomain)
800 :
801 : {
802 3 : if (pszDomain == nullptr || !STARTS_WITH_CI(pszDomain, "ceos-"))
803 3 : return GDALDataset::GetMetadata(pszDomain);
804 :
805 : /* -------------------------------------------------------------------- */
806 : /* Identify which file to fetch the file from. */
807 : /* -------------------------------------------------------------------- */
808 0 : int nFileId = -1;
809 :
810 0 : if (STARTS_WITH_CI(pszDomain, "ceos-vol"))
811 : {
812 0 : nFileId = CEOS_VOLUME_DIR_FILE;
813 : }
814 0 : else if (STARTS_WITH_CI(pszDomain, "ceos-lea"))
815 : {
816 0 : nFileId = CEOS_LEADER_FILE;
817 : }
818 0 : else if (STARTS_WITH_CI(pszDomain, "ceos-img"))
819 : {
820 0 : nFileId = CEOS_IMAGRY_OPT_FILE;
821 : }
822 0 : else if (STARTS_WITH_CI(pszDomain, "ceos-trl"))
823 : {
824 0 : nFileId = CEOS_TRAILER_FILE;
825 : }
826 0 : else if (STARTS_WITH_CI(pszDomain, "ceos-nul"))
827 : {
828 0 : nFileId = CEOS_NULL_VOL_FILE;
829 : }
830 : else
831 0 : return nullptr;
832 :
833 0 : pszDomain += 8;
834 :
835 : /* -------------------------------------------------------------------- */
836 : /* Identify the record type. */
837 : /* -------------------------------------------------------------------- */
838 0 : int a, b, c, d, nRecordIndex = -1;
839 :
840 0 : if (sscanf(pszDomain, "-%d-%d-%d-%d:%d", &a, &b, &c, &d, &nRecordIndex) !=
841 0 : 5 &&
842 0 : sscanf(pszDomain, "-%d-%d-%d-%d", &a, &b, &c, &d) != 4)
843 : {
844 0 : return nullptr;
845 : }
846 :
847 0 : CeosTypeCode_t sTypeCode = QuadToTC(a, b, c, d);
848 :
849 : /* -------------------------------------------------------------------- */
850 : /* Try to fetch the record. */
851 : /* -------------------------------------------------------------------- */
852 0 : CeosRecord_t *record = FindCeosRecord(sVolume.RecordList, sTypeCode,
853 : nFileId, -1, nRecordIndex);
854 :
855 0 : if (record == nullptr)
856 0 : return nullptr;
857 :
858 : /* -------------------------------------------------------------------- */
859 : /* Massage the data into a safe textual format. The RawRecord */
860 : /* just has zero bytes turned into spaces while the */
861 : /* EscapedRecord has regular backslash escaping applied to zero */
862 : /* chars, double quotes, and backslashes. */
863 : /* just turn zero bytes into spaces. */
864 : /* -------------------------------------------------------------------- */
865 :
866 0 : CSLDestroy(papszTempMD);
867 :
868 : // Escaped version
869 0 : char *pszSafeCopy = CPLEscapeString((char *)record->Buffer, record->Length,
870 : CPLES_BackslashQuotable);
871 0 : papszTempMD = CSLSetNameValue(nullptr, "EscapedRecord", pszSafeCopy);
872 0 : CPLFree(pszSafeCopy);
873 :
874 : // Copy with '\0' replaced by spaces.
875 :
876 0 : pszSafeCopy = (char *)CPLCalloc(1, record->Length + 1);
877 0 : memcpy(pszSafeCopy, record->Buffer, record->Length);
878 :
879 0 : for (int i = 0; i < record->Length; i++)
880 0 : if (pszSafeCopy[i] == '\0')
881 0 : pszSafeCopy[i] = ' ';
882 :
883 0 : papszTempMD = CSLSetNameValue(papszTempMD, "RawRecord", pszSafeCopy);
884 :
885 0 : CPLFree(pszSafeCopy);
886 :
887 0 : return papszTempMD;
888 : }
889 :
890 : /************************************************************************/
891 : /* ScanForMetadata() */
892 : /************************************************************************/
893 :
894 7 : void SAR_CEOSDataset::ScanForMetadata()
895 :
896 : {
897 : /* -------------------------------------------------------------------- */
898 : /* Get the volume id (with the sensor name) */
899 : /* -------------------------------------------------------------------- */
900 : const CeosRecord_t *record =
901 7 : FindCeosRecord(sVolume.RecordList, VOLUME_DESCRIPTOR_RECORD_TC,
902 : CEOS_VOLUME_DIR_FILE, -1, -1);
903 :
904 : struct FieldDef
905 : {
906 : const char *pszMetadataItemName;
907 : int nOffsetInRecord;
908 : const char *pszFormat;
909 : };
910 :
911 14 : CPLString osVolId;
912 : char szField[128];
913 7 : szField[0] = '\0';
914 7 : if (record != nullptr)
915 : {
916 3 : GetCeosField(record, 61, "A16", szField);
917 3 : osVolId = szField;
918 3 : osVolId.Trim();
919 3 : SetMetadataItem("CEOS_LOGICAL_VOLUME_ID", osVolId.c_str());
920 :
921 3 : constexpr FieldDef asFieldDefs[] = {
922 : {"CEOS_PROCESSING_FACILITY", 149, "A12"},
923 : {"CEOS_PROCESSING_AGENCY", 141, "A8"},
924 : {"CEOS_PROCESSING_COUNTRY", 129, "A12"},
925 : {"CEOS_SOFTWARE_ID", 33, "A12"},
926 : {"CEOS_PRODUCT_ID", 261, "A8"},
927 : {"CEOS_VOLSET_ID", 77, "A16"},
928 : };
929 21 : for (const auto &sDef : asFieldDefs)
930 : {
931 18 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
932 36 : CPLString osField(szField);
933 18 : osField.Trim();
934 18 : if (!osField.empty())
935 15 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
936 : }
937 : }
938 :
939 : /* ==================================================================== */
940 : /* Dataset summary record. */
941 : /* ==================================================================== */
942 7 : record = FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
943 : CEOS_LEADER_FILE, -1, -1);
944 :
945 7 : if (record == nullptr)
946 : record =
947 4 : FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_ASF_TC,
948 : CEOS_LEADER_FILE, -1, -1);
949 :
950 7 : if (record == nullptr)
951 2 : record = FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_TC,
952 : CEOS_TRAILER_FILE, -1, -1);
953 :
954 7 : if (record == nullptr)
955 : record =
956 2 : FindCeosRecord(sVolume.RecordList, LEADER_DATASET_SUMMARY_ERS2_TC,
957 : CEOS_LEADER_FILE, -1, -1);
958 :
959 7 : if (record != nullptr)
960 : {
961 5 : constexpr FieldDef asFieldDefs[] = {
962 : {"CEOS_ACQUISITION_TIME", 69, "A32"},
963 : {"CEOS_ASC_DES", 101, "A16"}, // Ascending/Descending (RSAT only)
964 : {"CEOS_TRUE_HEADING", 149, "A16"}, // at least for ERS2
965 : {"CEOS_ELLIPSOID", 165, "A16"},
966 : {"CEOS_SEMI_MAJOR", 181, "A16"},
967 : {"CEOS_SEMI_MINOR", 197, "A16"},
968 : {"CEOS_SCENE_LENGTH_KM", 341, "A16"},
969 : {"CEOS_SCENE_WIDTH_KM", 357, "A16"},
970 : {"CEOS_MISSION_ID", 397, "A16"},
971 : {"CEOS_SENSOR_ID", 413, "A32"},
972 : {"CEOS_ORBIT_NUMBER", 445, "A8"},
973 : {"CEOS_PLATFORM_LATITUDE", 453, "A8"},
974 : {"CEOS_PLATFORM_LONGITUDE", 461, "A8"},
975 : {"CEOS_PLATFORM_HEADING", 469, "A8"}, // at least for ERS2
976 : {"CEOS_SENSOR_CLOCK_ANGLE", 477, "A8"}, // Look Angle
977 : {"CEOS_INC_ANGLE", 485, "A8"}, // Incidence Angle
978 : {"CEOS_FACILITY", 1047, "A16"},
979 : {"CEOS_PIXEL_TIME_DIR", 1527,
980 : "A8"}, // Pixel time direction indicator
981 : {"CEOS_LINE_SPACING_METERS", 1687, "A16"},
982 : {"CEOS_PIXEL_SPACING_METERS", 1703, "A16"},
983 : };
984 105 : for (const auto &sDef : asFieldDefs)
985 : {
986 100 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
987 200 : CPLString osField(szField);
988 100 : osField.Trim();
989 256 : if (!osField.empty() &&
990 78 : (osVolId.find("RSAT") != std::string::npos ||
991 78 : !EQUAL(sDef.pszMetadataItemName, "CEOS_ASC_DES")))
992 : {
993 76 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
994 : }
995 : }
996 : }
997 :
998 : /* -------------------------------------------------------------------- */
999 : /* Get the beam mode, for radarsat. */
1000 : /* -------------------------------------------------------------------- */
1001 : record =
1002 7 : FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_COMPENSATION_TC,
1003 : CEOS_LEADER_FILE, -1, -1);
1004 :
1005 7 : if (osVolId.find("RSAT") != std::string::npos && record != nullptr)
1006 : {
1007 0 : GetCeosField(record, 4189, "A16", szField);
1008 0 : CPLString osField(szField);
1009 0 : osField.Trim();
1010 :
1011 0 : SetMetadataItem("CEOS_BEAM_TYPE", osField.c_str());
1012 : }
1013 :
1014 : /* ==================================================================== */
1015 : /* ERS calibration and incidence angle info */
1016 : /* ==================================================================== */
1017 7 : record = FindCeosRecord(sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_TC,
1018 : CEOS_LEADER_FILE, -1, -1);
1019 :
1020 7 : if (record == nullptr)
1021 : record =
1022 7 : FindCeosRecord(sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_ALT_TC,
1023 : CEOS_LEADER_FILE, -1, -1);
1024 :
1025 7 : if (record != nullptr)
1026 : {
1027 0 : GetCeosField(record, 13, "A64", szField);
1028 0 : CPLString osField(szField);
1029 0 : osField.Trim();
1030 :
1031 : /* Avoid PCS records, which don't contain necessary info */
1032 0 : if (osField.find("GENERAL") == std::string::npos)
1033 0 : record = nullptr;
1034 : }
1035 :
1036 7 : if (record != nullptr)
1037 : {
1038 0 : constexpr FieldDef asFieldDefs[] = {
1039 : {"CEOS_INC_ANGLE_FIRST_RANGE", 583, "A16"},
1040 : {"CEOS_INC_ANGLE_CENTRE_RANGE", 599, "A16"},
1041 : {"CEOS_INC_ANGLE_LAST_RANGE", 615, "A16"},
1042 : {"CEOS_CALIBRATION_CONSTANT_K", 663, "A16"},
1043 : {"CEOS_GROUND_TO_SLANT_C0", 1855, "A20"},
1044 : {"CEOS_GROUND_TO_SLANT_C1", 1875, "A20"},
1045 : {"CEOS_GROUND_TO_SLANT_C2", 1895, "A20"},
1046 : {"CEOS_GROUND_TO_SLANT_C3", 1915, "A20"},
1047 : };
1048 0 : for (const auto &sDef : asFieldDefs)
1049 : {
1050 0 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
1051 0 : CPLString osField(szField);
1052 0 : osField.Trim();
1053 0 : if (!osField.empty())
1054 : {
1055 0 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
1056 : }
1057 : }
1058 : }
1059 : /* -------------------------------------------------------------------- */
1060 : /* Detailed Processing Parameters (Radarsat) */
1061 : /* -------------------------------------------------------------------- */
1062 7 : record = FindCeosRecord(sVolume.RecordList, RSAT_PROC_PARAM_TC,
1063 : CEOS_LEADER_FILE, -1, -1);
1064 :
1065 7 : if (record == nullptr)
1066 7 : record = FindCeosRecord(sVolume.RecordList, RSAT_PROC_PARAM_TC,
1067 : CEOS_TRAILER_FILE, -1, -1);
1068 :
1069 7 : if (record != nullptr)
1070 : {
1071 0 : constexpr FieldDef asFieldDefs[] = {
1072 : {"CEOS_PROC_START", 192, "A21"},
1073 : {"CEOS_PROC_STOP", 213, "A21"},
1074 : {"CEOS_EPH_ORB_DATA_0", 4649, "A16"},
1075 : {"CEOS_EPH_ORB_DATA_1", 4665, "A16"},
1076 : {"CEOS_EPH_ORB_DATA_2", 4681, "A16"},
1077 : {"CEOS_EPH_ORB_DATA_3", 4697, "A16"},
1078 : {"CEOS_EPH_ORB_DATA_4", 4713, "A16"},
1079 : {"CEOS_EPH_ORB_DATA_5", 4729, "A16"},
1080 : {"CEOS_EPH_ORB_DATA_6", 4745, "A16"},
1081 : {"CEOS_GROUND_TO_SLANT_C0", 4908, "A16"},
1082 : {"CEOS_GROUND_TO_SLANT_C1", 4924, "A16"},
1083 : {"CEOS_GROUND_TO_SLANT_C2", 4940, "A16"},
1084 : {"CEOS_GROUND_TO_SLANT_C3", 4956, "A16"},
1085 : {"CEOS_GROUND_TO_SLANT_C4", 4972, "A16"},
1086 : {"CEOS_GROUND_TO_SLANT_C5", 4988, "A16"},
1087 : {"CEOS_INC_ANGLE_FIRST_RANGE", 7334, "A16"},
1088 : {"CEOS_INC_ANGLE_LAST_RANGE", 7350, "A16"},
1089 : };
1090 0 : for (const auto &sDef : asFieldDefs)
1091 : {
1092 0 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
1093 0 : CPLString osField(szField);
1094 0 : osField.Trim();
1095 0 : if (!osField.empty())
1096 : {
1097 0 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
1098 : }
1099 : }
1100 : }
1101 : /* -------------------------------------------------------------------- */
1102 : /* Get process-to-raw data coordinate translation values. These */
1103 : /* are likely specific to Atlantis APP products. */
1104 : /* -------------------------------------------------------------------- */
1105 7 : record = FindCeosRecord(sVolume.RecordList, IMAGE_HEADER_RECORD_TC,
1106 : CEOS_IMAGRY_OPT_FILE, -1, -1);
1107 :
1108 7 : if (record != nullptr)
1109 : {
1110 4 : constexpr FieldDef asFieldDefs[] = {
1111 : {"CEOS_DM_CORNER", 449, "A4"},
1112 : {"CEOS_DM_TRANSPOSE", 453, "A4"},
1113 : {"CEOS_DM_START_SAMPLE", 457, "A4"},
1114 : {"CEOS_DM_START_PULSE", 461, "A5"},
1115 : {"CEOS_DM_FAST_ALPHA", 466, "A16"},
1116 : {"CEOS_DM_FAST_BETA", 482, "A16"},
1117 : {"CEOS_DM_SLOW_ALPHA", 498, "A16"},
1118 : {"CEOS_DM_SLOW_BETA", 514, "A16"},
1119 : {"CEOS_DM_FAST_ALPHA_2", 530, "A16"},
1120 : };
1121 40 : for (const auto &sDef : asFieldDefs)
1122 : {
1123 36 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
1124 72 : CPLString osField(szField);
1125 36 : osField.Trim();
1126 36 : if (!osField.empty())
1127 : {
1128 18 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
1129 : }
1130 : }
1131 : }
1132 :
1133 : /* -------------------------------------------------------------------- */
1134 : /* Try to find calibration information from Radiometric Data */
1135 : /* Record. */
1136 : /* -------------------------------------------------------------------- */
1137 : record =
1138 7 : FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_DATA_RECORD_TC,
1139 : CEOS_LEADER_FILE, -1, -1);
1140 :
1141 7 : if (record == nullptr)
1142 4 : record = FindCeosRecord(sVolume.RecordList,
1143 : LEADER_RADIOMETRIC_DATA_RECORD_TC,
1144 : CEOS_TRAILER_FILE, -1, -1);
1145 :
1146 7 : if (record != nullptr)
1147 : {
1148 3 : GetCeosField(record, 8317, "A16", szField);
1149 6 : CPLString osField(szField);
1150 3 : osField.Trim();
1151 3 : if (!osField.empty())
1152 0 : SetMetadataItem("CEOS_CALIBRATION_OFFSET", osField.c_str());
1153 : }
1154 :
1155 : /* -------------------------------------------------------------------- */
1156 : /* For ERS Standard Format Landsat scenes we pick up the */
1157 : /* calibration offset and gain from the Radiometric Ancillary */
1158 : /* Record. */
1159 : /* -------------------------------------------------------------------- */
1160 : record =
1161 7 : FindCeosRecord(sVolume.RecordList, QuadToTC(0x3f, 0x24, 0x12, 0x09),
1162 : CEOS_LEADER_FILE, -1, -1);
1163 7 : if (record != nullptr)
1164 : {
1165 0 : constexpr FieldDef asFieldDefs[] = {
1166 : {"CEOS_OFFSET_A0", 29, "A20"},
1167 : {"CEOS_GAIN_A1", 49, "A20"},
1168 : };
1169 0 : for (const auto &sDef : asFieldDefs)
1170 : {
1171 0 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
1172 0 : CPLString osField(szField);
1173 0 : osField.Trim();
1174 0 : if (!osField.empty())
1175 : {
1176 0 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
1177 : }
1178 : }
1179 : }
1180 :
1181 : /* -------------------------------------------------------------------- */
1182 : /* For ERS Standard Format Landsat scenes we pick up the */
1183 : /* gain setting from the Scene Header Record. */
1184 : /* -------------------------------------------------------------------- */
1185 : record =
1186 7 : FindCeosRecord(sVolume.RecordList, QuadToTC(0x12, 0x12, 0x12, 0x09),
1187 : CEOS_LEADER_FILE, -1, -1);
1188 7 : if (record != nullptr)
1189 : {
1190 0 : GetCeosField(record, 1486, "A1", szField);
1191 0 : szField[1] = '\0';
1192 :
1193 0 : if (szField[0] == 'H' || szField[0] == 'V')
1194 0 : SetMetadataItem("CEOS_GAIN_SETTING", szField);
1195 : }
1196 :
1197 : /* -------------------------------------------------------------------- */
1198 : /* PALSAR-2 ALOS2 Platform position data */
1199 : /* -------------------------------------------------------------------- */
1200 : record =
1201 7 : FindCeosRecord(sVolume.RecordList, LEADER_PLATFORM_POSITION_PALSAR_TC,
1202 : CEOS_LEADER_FILE, -1, -1);
1203 7 : if (record && record->Length > 387)
1204 : {
1205 : // Table 3.3-7 Platform position data records
1206 : // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
1207 3 : constexpr FieldDef asFieldDefs[] = {
1208 : {"CEOS_PLATFORM_POS_ORBITAL_ELEMENTS_DESIGNATOR", 13, "A32"},
1209 : {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_1", 45, "A16"},
1210 : {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_2", 61, "A16"},
1211 : {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_3", 77, "A16"},
1212 : {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_4", 93, "A16"},
1213 : {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_5", 109, "A16"},
1214 : {"CEOS_PLATFORM_POS_ORBITAL_ELEMENT_6", 125, "A16"},
1215 : {"CEOS_PLATFORM_POS_NUMBER_POINTS", 141, "A4"},
1216 : {"CEOS_PLATFORM_POS_YEAR_POINT_1", 145, "A4"},
1217 : {"CEOS_PLATFORM_POS_MONTH_POINT_1", 149, "A4"},
1218 : {"CEOS_PLATFORM_POS_DAY_POINT_1", 153, "A4"},
1219 : {"CEOS_PLATFORM_POS_DAY_IN_YEAR_POINT_1", 157, "A4"},
1220 : {"CEOS_PLATFORM_POS_SECONDS_OF_DAY_POINT_1", 161, "A22"},
1221 : {"CEOS_PLATFORM_POS_TIME_INTERVAL_SECONDS", 183, "A22"},
1222 : {"CEOS_PLATFORM_POS_REF_COORD_SYS", 205, "A64"},
1223 : {"CEOS_PLATFORM_POS_GREENWICH_MEAN_HOUR_ANGLE_DEG", 269, "A22"},
1224 : {"CEOS_PLATFORM_POS_ALONG_TRACK_POS_ERROR_METERS", 291, "A16"},
1225 : {"CEOS_PLATFORM_POS_ACROSS_TRACK_POS_ERROR_METERS", 307, "A16"},
1226 : {"CEOS_PLATFORM_POS_RADIAL_POS_ERROR_METERS", 323, "A16"},
1227 : {"CEOS_PLATFORM_POS_ALONG_TRACK_VELOCITY_ERROR_METERS_PER_SEC", 339,
1228 : "A16"},
1229 : {"CEOS_PLATFORM_POS_ACROSS_TRACK_VELOCITY_ERROR_METERS_PER_SEC",
1230 : 355, "A16"},
1231 : {"CEOS_PLATFORM_POS_RADIAL_VELOCITY_ERROR_METERS_PER_SEC", 371,
1232 : "A16"},
1233 : };
1234 3 : int nPoints = 0;
1235 3 : constexpr int OFFSET_POINT_1 = 387;
1236 3 : constexpr int POINT_SIZE = 6 * 22;
1237 69 : for (const auto &sDef : asFieldDefs)
1238 : {
1239 66 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
1240 132 : CPLString osField(szField);
1241 66 : osField.Trim();
1242 66 : if (!osField.empty())
1243 : {
1244 63 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
1245 63 : if (EQUAL(sDef.pszMetadataItemName,
1246 : "CEOS_PLATFORM_POS_NUMBER_POINTS"))
1247 : {
1248 9 : nPoints = std::clamp(atoi(osField), 0,
1249 6 : (record->Length - OFFSET_POINT_1) /
1250 3 : POINT_SIZE);
1251 : }
1252 : }
1253 : }
1254 :
1255 3 : constexpr FieldDef asPointFieldDefs[] = {
1256 : {"VECTOR_X_METERS", 0, "A22"},
1257 : {"VECTOR_Y_METERS", 22, "A22"},
1258 : {"VECTOR_Z_METERS", 44, "A22"},
1259 : {"VECTOR_X_DERIV_METERS_PER_SEC", 66, "A22"},
1260 : {"VECTOR_Y_DERIV_METERS_PER_SEC", 88, "A22"},
1261 : {"VECTOR_Z_DERIV_METERS_PER_SEC", 110, "A22"},
1262 : };
1263 87 : for (int iPnt = 0; iPnt < nPoints; ++iPnt)
1264 : {
1265 588 : for (const auto &sDef : asPointFieldDefs)
1266 : {
1267 504 : GetCeosField(record,
1268 504 : sDef.nOffsetInRecord + OFFSET_POINT_1 +
1269 504 : iPnt * POINT_SIZE,
1270 504 : sDef.pszFormat, szField);
1271 1008 : CPLString osField(szField);
1272 504 : osField.Trim();
1273 504 : if (!osField.empty())
1274 : {
1275 1512 : SetMetadataItem(std::string("CEOS_PLATFORM_POS_")
1276 504 : .append(sDef.pszMetadataItemName)
1277 504 : .append("_POINT_")
1278 1008 : .append(std::to_string(iPnt + 1))
1279 : .c_str(),
1280 504 : osField.c_str());
1281 : }
1282 : }
1283 : }
1284 :
1285 : {
1286 3 : GetCeosField(record, 4101, "A1", szField);
1287 6 : CPLString osField(szField);
1288 3 : osField.Trim();
1289 3 : if (!osField.empty())
1290 : {
1291 3 : SetMetadataItem("CEOS_PLATFORM_POS_LEAP_SECOND",
1292 3 : osField.c_str());
1293 : }
1294 : }
1295 : }
1296 :
1297 : /* -------------------------------------------------------------------- */
1298 : /* PALSAR-2 ALOS2 Attitude data */
1299 : /* -------------------------------------------------------------------- */
1300 7 : record = FindCeosRecord(sVolume.RecordList, LEADER_ATTITUDE_PALSAR_TC,
1301 : CEOS_LEADER_FILE, -1, -1);
1302 7 : if (record)
1303 : {
1304 : // Table 3.3-8 Attitude data records
1305 : // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
1306 :
1307 3 : int nPoints = 0;
1308 3 : constexpr int OFFSET_POINT_1 = 17;
1309 3 : constexpr int POINT_SIZE = 120;
1310 : {
1311 3 : GetCeosField(record, 13, "A4", szField);
1312 6 : CPLString osField(szField);
1313 3 : osField.Trim();
1314 3 : if (!osField.empty())
1315 : {
1316 3 : SetMetadataItem("CEOS_PLATFORM_ATT_NUMBER_POINTS",
1317 3 : osField.c_str());
1318 3 : nPoints =
1319 9 : std::clamp(atoi(osField), 0,
1320 3 : (record->Length - OFFSET_POINT_1) / POINT_SIZE);
1321 : }
1322 : }
1323 :
1324 3 : constexpr FieldDef asFieldDefs[] = {
1325 : {"DAY_OF_YEAR", 17, "A4"},
1326 : {"MILLISECOND_OF_DAY", 21, "A8"},
1327 : {"PITCH_QUALITY_FLAG", 29, "A4"},
1328 : {"ROLL_QUALITY_FLAG", 33, "A4"},
1329 : {"YAW_QUALITY_FLAG", 37, "A4"},
1330 : {"PITCH_DEG", 41, "A14"},
1331 : {"ROLL_DEG", 55, "A14"},
1332 : {"YAW_DEG", 69, "A14"},
1333 : {"PITCH_RATE_QUALITY_FLAG", 83, "A4"},
1334 : {"ROL_RATE_QUALITY_FLAG", 87, "A4"},
1335 : {"YAW_RATE_QUALITY_FLAG", 91, "A4"},
1336 : {"PITCH_RATE", 95, "A14"},
1337 : {"ROLL_RATE", 109, "A14"},
1338 : {"YAW_RATE", 123, "A14"},
1339 : };
1340 59 : for (int iPnt = 0; iPnt < nPoints; ++iPnt)
1341 : {
1342 840 : for (const auto &sDef : asFieldDefs)
1343 : {
1344 784 : GetCeosField(record, sDef.nOffsetInRecord + iPnt * POINT_SIZE,
1345 784 : sDef.pszFormat, szField);
1346 1568 : CPLString osField(szField);
1347 784 : osField.Trim();
1348 784 : if (!osField.empty())
1349 : {
1350 2352 : SetMetadataItem(std::string("CEOS_PLATFORM_ATT_")
1351 784 : .append(sDef.pszMetadataItemName)
1352 784 : .append("_POINT_")
1353 1568 : .append(std::to_string(iPnt + 1))
1354 : .c_str(),
1355 784 : osField.c_str());
1356 : }
1357 : }
1358 : }
1359 : }
1360 :
1361 : /* -------------------------------------------------------------------- */
1362 : /* PALSAR-2 ALOS2 Radiometric data */
1363 : /* -------------------------------------------------------------------- */
1364 7 : record = FindCeosRecord(sVolume.RecordList, LEADER_RADIOMETRIC_PALSAR_TC,
1365 : CEOS_LEADER_FILE, -1, -1);
1366 7 : if (record)
1367 : {
1368 : // Table 3.3-9 Radiometric data records
1369 : // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
1370 3 : constexpr FieldDef asFieldDefs[] = {
1371 : {"CEOS_RADIOMETRIC_CALIBRATION_FACTOR", 21, "A16"},
1372 : {"CEOS_RADIOMETRIC_DT_1_1_REAL", 37, "A16"},
1373 : {"CEOS_RADIOMETRIC_DT_1_1_IMAG", 53, "A16"},
1374 : {"CEOS_RADIOMETRIC_DT_1_2_REAL", 69, "A16"},
1375 : {"CEOS_RADIOMETRIC_DT_1_2_IMAG", 85, "A16"},
1376 : {"CEOS_RADIOMETRIC_DT_2_1_REAL", 101, "A16"},
1377 : {"CEOS_RADIOMETRIC_DT_2_1_IMAG", 117, "A16"},
1378 : {"CEOS_RADIOMETRIC_DT_2_2_REAL", 133, "A16"},
1379 : {"CEOS_RADIOMETRIC_DT_2_2_IMAG", 149, "A16"},
1380 : {"CEOS_RADIOMETRIC_DR_1_1_REAL", 165, "A16"},
1381 : {"CEOS_RADIOMETRIC_DR_1_1_IMAG", 181, "A16"},
1382 : {"CEOS_RADIOMETRIC_DR_1_2_REAL", 197, "A16"},
1383 : {"CEOS_RADIOMETRIC_DR_1_2_IMAG", 213, "A16"},
1384 : {"CEOS_RADIOMETRIC_DR_2_1_REAL", 229, "A16"},
1385 : {"CEOS_RADIOMETRIC_DR_2_1_IMAG", 245, "A16"},
1386 : {"CEOS_RADIOMETRIC_DR_2_2_REAL", 261, "A16"},
1387 : {"CEOS_RADIOMETRIC_DR_2_2_IMAG", 277, "A16"},
1388 : };
1389 54 : for (const auto &sDef : asFieldDefs)
1390 : {
1391 51 : GetCeosField(record, sDef.nOffsetInRecord, sDef.pszFormat, szField);
1392 102 : CPLString osField(szField);
1393 51 : osField.Trim();
1394 51 : if (!osField.empty())
1395 : {
1396 51 : SetMetadataItem(sDef.pszMetadataItemName, osField.c_str());
1397 : }
1398 : }
1399 : }
1400 :
1401 : /* -------------------------------------------------------------------- */
1402 : /* PALSAR-2 ALOS2 Facility related data */
1403 : /* -------------------------------------------------------------------- */
1404 7 : constexpr int FACILITY_RELATED_DATA_5_LAT_LONG_CONV_FACTORS = 5;
1405 7 : record = FindCeosRecord(
1406 : sVolume.RecordList, LEADER_FACILITY_RELATED_PALSAR_TC, CEOS_LEADER_FILE,
1407 : -1, FACILITY_RELATED_DATA_5_LAT_LONG_CONV_FACTORS - 1);
1408 7 : if (record && record->Length == 5000)
1409 : {
1410 : // Table 3-17 Facility related data records 5
1411 : // of https://www.eorc.jaxa.jp/ALOS/en/alos-2/pdf/product_format_description/PALSAR-2_xx_Format_CEOS_E_g.pdf
1412 :
1413 4 : std::string coeffs;
1414 22 : for (int i = 0; i < 10; ++i)
1415 : {
1416 20 : GetCeosField(record, 17 + i * 20, "A20", szField);
1417 40 : CPLString osField(szField);
1418 20 : osField.Trim();
1419 20 : if (!coeffs.empty())
1420 9 : coeffs += ' ';
1421 20 : coeffs += osField;
1422 : }
1423 : // 10 coefficients to convert from the map projection (E, N) to pixel (P)
1424 2 : SetMetadataItem("CEOS_FACILITY_5_XY_PROJECTED_TO_PIXEL_COEFFICIENTS",
1425 2 : coeffs.c_str());
1426 :
1427 2 : coeffs.clear();
1428 22 : for (int i = 0; i < 10; ++i)
1429 : {
1430 20 : GetCeosField(record, 17 + 10 * 20 + i * 20, "A20", szField);
1431 40 : CPLString osField(szField);
1432 20 : osField.Trim();
1433 20 : if (!coeffs.empty())
1434 9 : coeffs += ' ';
1435 20 : coeffs += osField;
1436 : }
1437 : // 10 coefficients to convert from the map projection (E, N) to line (L)
1438 2 : SetMetadataItem("CEOS_FACILITY_5_XY_PROJECTED_TO_LINE_COEFFICIENTS",
1439 2 : coeffs.c_str());
1440 :
1441 2 : coeffs.clear();
1442 52 : for (int i = 0; i < 25; ++i)
1443 : {
1444 50 : GetCeosField(record, 1025 + i * 20, "A20", szField);
1445 100 : CPLString osField(szField);
1446 50 : osField.Trim();
1447 50 : if (!coeffs.empty())
1448 48 : coeffs += ' ';
1449 50 : coeffs += osField;
1450 : }
1451 : // 25 coefficients of the 8th polynomial expression to convert from pixel (P) and line (L) to latitude (φ)
1452 2 : SetMetadataItem("CEOS_FACILITY_5_PIXEL_LINE_TO_LAT_COEFFICIENTS",
1453 2 : coeffs.c_str());
1454 :
1455 2 : coeffs.clear();
1456 52 : for (int i = 0; i < 25; ++i)
1457 : {
1458 50 : GetCeosField(record, 1025 + i * 20, "A20", szField);
1459 100 : CPLString osField(szField);
1460 50 : osField.Trim();
1461 50 : if (!coeffs.empty())
1462 48 : coeffs += ' ';
1463 50 : coeffs += osField;
1464 : }
1465 : // 25 coefficients of the 8th polynomial expression to convert from pixel (P) and line (L) to longitude (λ)
1466 2 : SetMetadataItem("CEOS_FACILITY_5_PIXEL_LINE_TO_LON_COEFFICIENTS",
1467 2 : coeffs.c_str());
1468 :
1469 : {
1470 2 : GetCeosField(record, 2025, "A20", szField);
1471 4 : CPLString osField(szField);
1472 2 : osField.Trim();
1473 2 : SetMetadataItem("CEOS_FACILITY_5_ORIGIN_PIXEL", osField.c_str());
1474 : }
1475 :
1476 : {
1477 2 : GetCeosField(record, 2045, "A20", szField);
1478 4 : CPLString osField(szField);
1479 2 : osField.Trim();
1480 2 : SetMetadataItem("CEOS_FACILITY_5_ORIGIN_LINE", osField.c_str());
1481 : }
1482 :
1483 2 : coeffs.clear();
1484 52 : for (int i = 0; i < 25; ++i)
1485 : {
1486 50 : GetCeosField(record, 2065 + i * 20, "A20", szField);
1487 100 : CPLString osField(szField);
1488 50 : osField.Trim();
1489 50 : if (!coeffs.empty())
1490 48 : coeffs += ' ';
1491 50 : coeffs += osField;
1492 : }
1493 : // 25 coefficients of the 8th polynomial expression to convert from latitude (Φ) and longitude (Λ) to pixel (p)
1494 2 : SetMetadataItem("CEOS_FACILITY_5_LAT_LON_TO_PIXEL_COEFFICIENTS",
1495 2 : coeffs.c_str());
1496 :
1497 2 : coeffs.clear();
1498 52 : for (int i = 0; i < 25; ++i)
1499 : {
1500 50 : GetCeosField(record, 2065 + 25 * 20 + i * 20, "A20", szField);
1501 100 : CPLString osField(szField);
1502 50 : osField.Trim();
1503 50 : if (!coeffs.empty())
1504 48 : coeffs += ' ';
1505 50 : coeffs += osField;
1506 : }
1507 : // 25 coefficients of the 8th polynomial expression to convert from latitude (Φ) and longitude (Λ) to pixel (p)
1508 2 : SetMetadataItem("CEOS_FACILITY_5_LAT_LON_TO_LINE_COEFFICIENTS",
1509 2 : coeffs.c_str());
1510 :
1511 : {
1512 2 : GetCeosField(record, 3065, "A20", szField);
1513 4 : CPLString osField(szField);
1514 2 : osField.Trim();
1515 2 : SetMetadataItem("CEOS_FACILITY_5_ORIGIN_LAT", osField.c_str());
1516 : }
1517 :
1518 : {
1519 2 : GetCeosField(record, 3085, "A20", szField);
1520 4 : CPLString osField(szField);
1521 2 : osField.Trim();
1522 2 : SetMetadataItem("CEOS_FACILITY_5_ORIGIN_LON", osField.c_str());
1523 : }
1524 : }
1525 :
1526 : /* -------------------------------------------------------------------- */
1527 : /* PALSAR Level 1.5/Level 3.1 processed record metadata */
1528 : /* -------------------------------------------------------------------- */
1529 7 : record = FindCeosRecord(sVolume.RecordList,
1530 : IMAGE_PROCESSED_DATA_RECORD_PALSAR_TC,
1531 : CEOS_IMAGRY_OPT_FILE, -1, 2);
1532 7 : constexpr int NEEDED_SIZE_IN_PROCESSED_DATA_RECORD_PALSAR = 128;
1533 7 : if (record && record->Length >= NEEDED_SIZE_IN_PROCESSED_DATA_RECORD_PALSAR)
1534 : {
1535 : const auto ReadProcessedDataRecord =
1536 6 : [this](const CeosRecord_t *curRecord,
1537 54 : const char *pszMDNameSuffix = "")
1538 : {
1539 6 : int32_t nInt32 = 0;
1540 :
1541 6 : GetCeosField(curRecord, 65, "B4", &nInt32);
1542 6 : SetMetadataItem(CPLSPrintf("CEOS_SLANT_RANGE_FIRST_PIXEL%s_METERS",
1543 : pszMDNameSuffix),
1544 6 : CPLSPrintf("%d", nInt32));
1545 :
1546 6 : GetCeosField(curRecord, 69, "B4", &nInt32);
1547 6 : SetMetadataItem(CPLSPrintf("CEOS_SLANT_RANGE_MID_PIXEL%s_METERS",
1548 : pszMDNameSuffix),
1549 6 : CPLSPrintf("%d", nInt32));
1550 :
1551 6 : GetCeosField(curRecord, 73, "B4", &nInt32);
1552 6 : SetMetadataItem(CPLSPrintf("CEOS_SLANT_RANGE_LAST_PIXEL%s_METERS",
1553 : pszMDNameSuffix),
1554 6 : CPLSPrintf("%d", nInt32));
1555 :
1556 6 : GetCeosField(curRecord, 77, "B4", &nInt32);
1557 6 : SetMetadataItem(CPLSPrintf("CEOS_DOPPLER_CENTROID_FIRST_PIXEL%s_HZ",
1558 : pszMDNameSuffix),
1559 6 : CPLSPrintf("%.3f", nInt32 / 1000.0));
1560 :
1561 6 : GetCeosField(curRecord, 81, "B4", &nInt32);
1562 6 : SetMetadataItem(CPLSPrintf("CEOS_DOPPLER_CENTROID_MID_PIXEL%s_HZ",
1563 : pszMDNameSuffix),
1564 6 : CPLSPrintf("%.3f", nInt32 / 1000.0));
1565 :
1566 6 : GetCeosField(curRecord, 85, "B4", &nInt32);
1567 6 : SetMetadataItem(CPLSPrintf("CEOS_DOPPLER_CENTROID_LAST_PIXEL%s_HZ",
1568 : pszMDNameSuffix),
1569 6 : CPLSPrintf("%.3f", nInt32 / 1000.0));
1570 :
1571 6 : GetCeosField(curRecord, 89, "B4", &nInt32);
1572 6 : SetMetadataItem(
1573 : CPLSPrintf("CEOS_AZIMUTH_FM_RATE_FIRST_PIXEL%s_HZ_PER_MS",
1574 : pszMDNameSuffix),
1575 6 : CPLSPrintf("%d", nInt32));
1576 :
1577 6 : GetCeosField(curRecord, 93, "B4", &nInt32);
1578 6 : SetMetadataItem(
1579 : CPLSPrintf("CEOS_AZIMUTH_FM_RATE_MID_PIXEL%s_HZ_PER_MS",
1580 : pszMDNameSuffix),
1581 6 : CPLSPrintf("%d", nInt32));
1582 :
1583 6 : GetCeosField(curRecord, 97, "B4", &nInt32);
1584 6 : SetMetadataItem(
1585 : CPLSPrintf("CEOS_AZIMUTH_FM_RATE_LAST_PIXEL%s_HZ_PER_MS",
1586 : pszMDNameSuffix),
1587 6 : CPLSPrintf("%d", nInt32));
1588 6 : };
1589 :
1590 5 : ReadProcessedDataRecord(record);
1591 :
1592 : // The above values are per-record. In practice they seem to be constant
1593 : // among all records, but I could not find any statement on that, so
1594 : // also read and report them from the last record.
1595 : // We cannot use FindCeosRecord() to fetch it because ProcessData() limits
1596 : // to 4 records for the image file (for memory and performance reasons)
1597 5 : struct CeosSARImageDesc *ImageDesc = &(sVolume.ImageDesc);
1598 5 : const vsi_l_offset nOffsetToLastRecordStart =
1599 5 : ImageDesc->FileDescriptorLength +
1600 5 : static_cast<vsi_l_offset>(ImageDesc->BytesPerRecord) *
1601 5 : (nRasterYSize - 1);
1602 : CeosRecord_t lastRecord;
1603 5 : memset(&lastRecord, 0, sizeof(lastRecord));
1604 : std::vector<GByte> abyLeader(
1605 10 : NEEDED_SIZE_IN_PROCESSED_DATA_RECORD_PALSAR);
1606 10 : if (fpImage->Seek(nOffsetToLastRecordStart, SEEK_SET) == 0 &&
1607 5 : fpImage->Read(abyLeader.data(), abyLeader.size()) ==
1608 5 : abyLeader.size())
1609 : {
1610 1 : lastRecord.Buffer = abyLeader.data();
1611 1 : lastRecord.Length = static_cast<int>(abyLeader.size());
1612 1 : ReadProcessedDataRecord(&lastRecord, "_LAST_LINE");
1613 : }
1614 : }
1615 7 : }
1616 :
1617 : /************************************************************************/
1618 : /* ScanForMapProjection() */
1619 : /* */
1620 : /* Try to find a map projection record, and read corner points */
1621 : /* from it. This has only been tested with ERS products. */
1622 : /************************************************************************/
1623 :
1624 0 : int SAR_CEOSDataset::ScanForMapProjection()
1625 :
1626 : {
1627 : /* -------------------------------------------------------------------- */
1628 : /* Find record, and try to determine if it has useful GCPs. */
1629 : /* -------------------------------------------------------------------- */
1630 :
1631 : CeosRecord_t *record =
1632 0 : FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_TC,
1633 : CEOS_LEADER_FILE, -1, -1);
1634 :
1635 0 : int gcp_ordering_mode = CEOS_STD_MAPREC_GCP_ORDER;
1636 : /* JERS from Japan */
1637 0 : if (record == nullptr)
1638 : record =
1639 0 : FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_JERS_TC,
1640 : CEOS_LEADER_FILE, -1, -1);
1641 :
1642 0 : if (record == nullptr)
1643 : {
1644 : record =
1645 0 : FindCeosRecord(sVolume.RecordList, LEADER_MAP_PROJ_RECORD_ASF_TC,
1646 : CEOS_LEADER_FILE, -1, -1);
1647 0 : gcp_ordering_mode = CEOS_ASF_MAPREC_GCP_ORDER;
1648 : }
1649 0 : if (record == nullptr)
1650 : {
1651 0 : record = FindCeosRecord(sVolume.RecordList, LEADER_FACILITY_ASF_TC,
1652 : CEOS_LEADER_FILE, -1, -1);
1653 0 : gcp_ordering_mode = CEOS_ASF_FACREC_GCP_ORDER;
1654 : }
1655 :
1656 0 : if (record == nullptr)
1657 0 : return FALSE;
1658 :
1659 : char szField[100];
1660 0 : memset(szField, 0, 17);
1661 0 : GetCeosField(record, 29, "A16", szField);
1662 :
1663 0 : int GCPFieldSize = 16;
1664 0 : int GCPOffset = 1073;
1665 :
1666 0 : if (!STARTS_WITH_CI(szField, "Slant Range") &&
1667 0 : !STARTS_WITH_CI(szField, "Ground Range") &&
1668 0 : !STARTS_WITH_CI(szField, "GEOCODED"))
1669 : {
1670 : /* detect ASF map projection record */
1671 0 : GetCeosField(record, 1079, "A7", szField);
1672 0 : if (!STARTS_WITH_CI(szField, "Slant") &&
1673 0 : !STARTS_WITH_CI(szField, "Ground"))
1674 : {
1675 0 : return FALSE;
1676 : }
1677 : else
1678 : {
1679 0 : GCPFieldSize = 17;
1680 0 : GCPOffset = 157;
1681 : }
1682 : }
1683 :
1684 : char FieldSize[4];
1685 0 : snprintf(FieldSize, sizeof(FieldSize), "A%d", GCPFieldSize);
1686 :
1687 0 : GetCeosField(record, GCPOffset, FieldSize, szField);
1688 0 : if (STARTS_WITH_CI(szField, " "))
1689 0 : return FALSE;
1690 :
1691 : /* -------------------------------------------------------------------- */
1692 : /* Read corner points. */
1693 : /* -------------------------------------------------------------------- */
1694 0 : nGCPCount = 4;
1695 0 : pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), nGCPCount);
1696 :
1697 0 : GDALInitGCPs(nGCPCount, pasGCPList);
1698 :
1699 0 : for (int i = 0; i < nGCPCount; i++)
1700 : {
1701 : char szId[32];
1702 :
1703 0 : snprintf(szId, sizeof(szId), "%d", i + 1);
1704 0 : CPLFree(pasGCPList[i].pszId);
1705 0 : pasGCPList[i].pszId = CPLStrdup(szId);
1706 :
1707 0 : GetCeosField(record, GCPOffset + (GCPFieldSize * 2) * i, FieldSize,
1708 : szField);
1709 0 : pasGCPList[i].dfGCPY = CPLAtof(szField);
1710 0 : GetCeosField(record, GCPOffset + GCPFieldSize + (GCPFieldSize * 2) * i,
1711 : FieldSize, szField);
1712 0 : pasGCPList[i].dfGCPX = CPLAtof(szField);
1713 0 : pasGCPList[i].dfGCPZ = 0.0;
1714 : }
1715 :
1716 : /* Map Projection Record has the order UL UR LR LL
1717 : ASF Facility Data Record has the order UL,LL,UR,LR
1718 : ASF Map Projection Record has the order LL, LR, UR, UL */
1719 :
1720 0 : pasGCPList[0].dfGCPLine = 0.5;
1721 0 : pasGCPList[0].dfGCPPixel = 0.5;
1722 :
1723 0 : switch (gcp_ordering_mode)
1724 : {
1725 0 : case CEOS_ASF_FACREC_GCP_ORDER:
1726 0 : pasGCPList[1].dfGCPLine = nRasterYSize - 0.5;
1727 0 : pasGCPList[1].dfGCPPixel = 0.5;
1728 :
1729 0 : pasGCPList[2].dfGCPLine = 0.5;
1730 0 : pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
1731 :
1732 0 : pasGCPList[3].dfGCPLine = nRasterYSize - 0.5;
1733 0 : pasGCPList[3].dfGCPPixel = nRasterXSize - 0.5;
1734 0 : break;
1735 0 : case CEOS_STD_MAPREC_GCP_ORDER:
1736 0 : pasGCPList[1].dfGCPLine = 0.5;
1737 0 : pasGCPList[1].dfGCPPixel = nRasterXSize - 0.5;
1738 :
1739 0 : pasGCPList[2].dfGCPLine = nRasterYSize - 0.5;
1740 0 : pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
1741 :
1742 0 : pasGCPList[3].dfGCPLine = nRasterYSize - 0.5;
1743 0 : pasGCPList[3].dfGCPPixel = 0.5;
1744 0 : break;
1745 0 : case CEOS_ASF_MAPREC_GCP_ORDER:
1746 0 : pasGCPList[0].dfGCPLine = nRasterYSize - 0.5;
1747 0 : pasGCPList[0].dfGCPPixel = 0.5;
1748 :
1749 0 : pasGCPList[1].dfGCPLine = nRasterYSize - 0.5;
1750 0 : pasGCPList[1].dfGCPPixel = nRasterXSize - 0.5;
1751 :
1752 0 : pasGCPList[2].dfGCPLine = 0.5;
1753 0 : pasGCPList[2].dfGCPPixel = nRasterXSize - 0.5;
1754 :
1755 0 : pasGCPList[3].dfGCPLine = 0.5;
1756 0 : pasGCPList[3].dfGCPPixel = 0.5;
1757 0 : break;
1758 : }
1759 :
1760 0 : return TRUE;
1761 : }
1762 :
1763 : /************************************************************************/
1764 : /* ScanForGCPs() */
1765 : /************************************************************************/
1766 :
1767 3 : void SAR_CEOSDataset::ScanForGCPs()
1768 :
1769 : {
1770 3 : m_bHasScannedForGCP = true;
1771 :
1772 : /* -------------------------------------------------------------------- */
1773 : /* Do we have a standard 180 bytes of prefix data (192 bytes */
1774 : /* including the record marker information)? If not, it is */
1775 : /* unlikely that the GCPs are available. */
1776 : /* -------------------------------------------------------------------- */
1777 3 : if (sVolume.ImageDesc.ImageDataStart < 192)
1778 : {
1779 0 : ScanForMapProjection();
1780 0 : return;
1781 : }
1782 :
1783 : /* ASF L1 products do not have valid data
1784 : in the lat/long first/mid/last fields */
1785 3 : const char *pszValue = GetMetadataItem("CEOS_FACILITY");
1786 3 : if ((pszValue != nullptr) && (strncmp(pszValue, "ASF", 3) == 0))
1787 : {
1788 0 : ScanForMapProjection();
1789 0 : return;
1790 : }
1791 :
1792 3 : if (GetRasterBand(1)->GetRasterDataType() == GDT_CFloat32)
1793 : {
1794 2 : const char *pszVolSetId = GetMetadataItem("CEOS_VOLSET_ID");
1795 2 : if (pszVolSetId && (STARTS_WITH(pszVolSetId, "ALOS2 SAR") ||
1796 1 : STARTS_WITH(pszVolSetId, "ALOS4 SAR")))
1797 : {
1798 : // No GCPs in those products. Helps for performance when
1799 : // reading in zip archives (particularly on network)
1800 2 : return;
1801 : }
1802 : }
1803 :
1804 : /* -------------------------------------------------------------------- */
1805 : /* Just sample fix scanlines through the image for GCPs, to */
1806 : /* return 15 GCPs. That is an adequate coverage for most */
1807 : /* purposes. A GCP is collected from the beginning, middle and */
1808 : /* end of each scanline. */
1809 : /* -------------------------------------------------------------------- */
1810 1 : nGCPCount = 0;
1811 1 : int nGCPMax = 15;
1812 1 : pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), nGCPMax);
1813 :
1814 1 : int nStep = (GetRasterYSize() - 1) / (nGCPMax / 3 - 1);
1815 6 : for (int iScanline = 0; iScanline < GetRasterYSize(); iScanline += nStep)
1816 : {
1817 5 : if (nGCPCount > nGCPMax - 3)
1818 0 : break;
1819 :
1820 : uint64_t nFileOffset;
1821 5 : CalcCeosSARImageFilePosition(&sVolume, 1, iScanline + 1, nullptr,
1822 : &nFileOffset);
1823 :
1824 : GInt32 anRecord[192 / 4];
1825 10 : if (VSIFSeekL(fpImage, nFileOffset, SEEK_SET) != 0 ||
1826 5 : VSIFReadL(anRecord, 1, 192, fpImage) != 192)
1827 0 : break;
1828 :
1829 : /* loop over first, middle and last pixel gcps */
1830 :
1831 20 : for (int iGCP = 0; iGCP < 3; iGCP++)
1832 : {
1833 15 : const int nLat = CPL_MSBWORD32(anRecord[132 / 4 + iGCP]);
1834 15 : const int nLong = CPL_MSBWORD32(anRecord[144 / 4 + iGCP]);
1835 :
1836 15 : if (nLat != 0 || nLong != 0)
1837 : {
1838 15 : GDALInitGCPs(1, pasGCPList + nGCPCount);
1839 :
1840 15 : CPLFree(pasGCPList[nGCPCount].pszId);
1841 :
1842 : char szId[32];
1843 15 : snprintf(szId, sizeof(szId), "%d", nGCPCount + 1);
1844 15 : pasGCPList[nGCPCount].pszId = CPLStrdup(szId);
1845 :
1846 15 : pasGCPList[nGCPCount].dfGCPX = nLong / 1000000.0;
1847 15 : pasGCPList[nGCPCount].dfGCPY = nLat / 1000000.0;
1848 15 : pasGCPList[nGCPCount].dfGCPZ = 0.0;
1849 :
1850 15 : pasGCPList[nGCPCount].dfGCPLine = iScanline + 0.5;
1851 :
1852 15 : if (iGCP == 0)
1853 5 : pasGCPList[nGCPCount].dfGCPPixel = 0.5;
1854 10 : else if (iGCP == 1)
1855 5 : pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() / 2.0;
1856 : else
1857 5 : pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize() - 0.5;
1858 :
1859 15 : nGCPCount++;
1860 : }
1861 : }
1862 : }
1863 : /* If general GCP's were not found, look for Map Projection (e.g. JERS) */
1864 1 : if (nGCPCount == 0)
1865 : {
1866 0 : CPLFree(pasGCPList);
1867 0 : pasGCPList = nullptr;
1868 0 : ScanForMapProjection();
1869 0 : return;
1870 : }
1871 : }
1872 :
1873 : /************************************************************************/
1874 : /* Open() */
1875 : /************************************************************************/
1876 :
1877 41362 : GDALDataset *SAR_CEOSDataset::Open(GDALOpenInfo *poOpenInfo)
1878 :
1879 : {
1880 : /* -------------------------------------------------------------------- */
1881 : /* Does this appear to be a valid ceos leader record? */
1882 : /* -------------------------------------------------------------------- */
1883 41362 : if (poOpenInfo->nHeaderBytes < CEOS_HEADER_LENGTH ||
1884 8503 : poOpenInfo->fpL == nullptr)
1885 32879 : return nullptr;
1886 :
1887 8483 : if ((poOpenInfo->pabyHeader[4] != 0x3f &&
1888 8475 : poOpenInfo->pabyHeader[4] != 0x32) ||
1889 13 : poOpenInfo->pabyHeader[5] != 0xc0 ||
1890 10 : poOpenInfo->pabyHeader[6] != 0x12 || poOpenInfo->pabyHeader[7] != 0x12)
1891 8473 : return nullptr;
1892 :
1893 : // some products (#1862) have byte swapped record length/number
1894 : // values and will blow stuff up -- explicitly ignore if record index
1895 : // value appears to be little endian.
1896 10 : if (poOpenInfo->pabyHeader[0] != 0)
1897 3 : return nullptr;
1898 :
1899 : /* -------------------------------------------------------------------- */
1900 : /* Confirm the requested access is supported. */
1901 : /* -------------------------------------------------------------------- */
1902 7 : if (poOpenInfo->eAccess == GA_Update)
1903 : {
1904 0 : ReportUpdateNotSupportedByDriver("SAR_CEOS");
1905 0 : return nullptr;
1906 : }
1907 :
1908 : /* -------------------------------------------------------------------- */
1909 : /* Create a corresponding GDALDataset. */
1910 : /* -------------------------------------------------------------------- */
1911 :
1912 14 : auto poDS = std::make_unique<SAR_CEOSDataset>();
1913 7 : std::swap(poDS->fpImage, poOpenInfo->fpL);
1914 :
1915 7 : CeosSARVolume_t *psVolume = &(poDS->sVolume);
1916 7 : InitCeosSARVolume(psVolume, 0);
1917 :
1918 : /* -------------------------------------------------------------------- */
1919 : /* Try to read the current file as an imagery file. */
1920 : /* -------------------------------------------------------------------- */
1921 :
1922 7 : psVolume->ImagryOptionsFile = TRUE;
1923 7 : if (ProcessData(poDS->fpImage, CEOS_IMAGRY_OPT_FILE, psVolume,
1924 7 : /* max_records = */ 4, VSI_L_OFFSET_MAX, false) != CE_None)
1925 : {
1926 0 : return nullptr;
1927 : }
1928 :
1929 : /* -------------------------------------------------------------------- */
1930 : /* Try the various filenames. */
1931 : /* -------------------------------------------------------------------- */
1932 7 : char *pszPath = CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
1933 : char *pszBasename =
1934 7 : CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str());
1935 : char *pszExtension =
1936 7 : CPLStrdup(CPLGetExtensionSafe(poOpenInfo->pszFilename).c_str());
1937 :
1938 : int nBand;
1939 7 : if (strlen(pszBasename) > 4)
1940 7 : nBand = atoi(pszBasename + 4);
1941 : else
1942 0 : nBand = 0;
1943 :
1944 7 : const bool bIsALOS2Or4 =
1945 14 : strlen(pszBasename) >= strlen("IMG-HH-ALOS2") &&
1946 10 : EQUALN(pszBasename, "IMG-", strlen("IMG-")) &&
1947 3 : (EQUALN(pszBasename + strlen("IMG-HH"), "-ALOS2", strlen("-ALOS2")) ||
1948 1 : EQUALN(pszBasename + strlen("IMG-HH"), "-ALOS4", strlen("-ALOS4")));
1949 7 : if (bIsALOS2Or4 && strlen(pszExtension) >= 3 &&
1950 3 : pszExtension[strlen(pszExtension) - 3] == '-' &&
1951 0 : pszExtension[strlen(pszExtension) - 2] == 'F')
1952 : {
1953 0 : pszExtension[strlen(pszExtension) - 3] = 0;
1954 : }
1955 :
1956 42 : for (int iFile = 0; iFile < CEOS_FILE_COUNT; iFile++)
1957 : {
1958 : /* skip image file ... we already did it */
1959 35 : if (iFile == CEOS_IMAGRY_OPT_FILE)
1960 7 : continue;
1961 :
1962 409 : for (int e = 0; CeosExtension[e][iFile] != nullptr; ++e)
1963 : {
1964 390 : std::string osFilename;
1965 :
1966 390 : const char *const pszMethod = CeosExtension[e][CEOS_FILE_COUNT];
1967 390 : const char *const pszFilePart = CeosExtension[e][iFile];
1968 :
1969 : /* build filename */
1970 390 : if (EQUAL(pszMethod, "base"))
1971 : {
1972 : char szMadeBasename[32];
1973 :
1974 84 : snprintf(szMadeBasename, sizeof(szMadeBasename), pszFilePart,
1975 : nBand);
1976 : osFilename =
1977 84 : CPLFormFilenameSafe(pszPath, szMadeBasename, pszExtension);
1978 : }
1979 306 : else if (EQUAL(pszMethod, "ext"))
1980 : {
1981 : osFilename =
1982 224 : CPLFormFilenameSafe(pszPath, pszBasename, pszFilePart);
1983 : }
1984 82 : else if (EQUAL(pszMethod, "whole"))
1985 : {
1986 28 : osFilename = CPLFormFilenameSafe(pszPath, pszFilePart, "");
1987 : }
1988 :
1989 : // This is for SAR SLC as per the SAR Toolbox (from ASF).
1990 54 : else if (EQUAL(pszMethod, "ext2"))
1991 : {
1992 : char szThisExtension[32];
1993 :
1994 28 : if (strlen(pszExtension) > 3)
1995 12 : snprintf(szThisExtension, sizeof(szThisExtension), "%s%s",
1996 : pszFilePart, pszExtension + 3);
1997 : else
1998 16 : snprintf(szThisExtension, sizeof(szThisExtension), "%s",
1999 : pszFilePart);
2000 :
2001 : osFilename =
2002 28 : CPLFormFilenameSafe(pszPath, pszBasename, szThisExtension);
2003 : }
2004 :
2005 26 : else if (EQUAL(pszMethod, "ALOS2-ALOS4"))
2006 : {
2007 26 : if (bIsALOS2Or4 && CeosExtension[e][iFile][0] != 0)
2008 : {
2009 18 : osFilename = CPLFormFilenameSafe(
2010 : pszPath,
2011 18 : std::string(pszFilePart)
2012 9 : .append(pszBasename + strlen("IMG-HH"))
2013 : .c_str(),
2014 9 : pszExtension);
2015 : }
2016 : else
2017 : {
2018 17 : continue;
2019 : }
2020 : }
2021 :
2022 : else
2023 : {
2024 0 : CPLError(CE_Fatal, CPLE_AppDefined, "should not happen");
2025 : }
2026 :
2027 : /* try to open */
2028 373 : VSILFILE *process_fp = VSIFOpenL(osFilename.c_str(), "rb");
2029 :
2030 : /* try upper case */
2031 373 : if (process_fp == nullptr)
2032 : {
2033 8112 : for (int i = static_cast<int>(osFilename.size()) - 1;
2034 8112 : i >= 0 && osFilename[i] != '/' && osFilename[i] != '\\';
2035 : i--)
2036 : {
2037 7750 : if (osFilename[i] >= 'a' && osFilename[i] <= 'z')
2038 1942 : osFilename[i] = osFilename[i] - 'a' + 'A';
2039 : }
2040 :
2041 362 : process_fp = VSIFOpenL(osFilename.c_str(), "rb");
2042 : }
2043 :
2044 373 : if (process_fp != nullptr)
2045 : {
2046 11 : CPLDebug("CEOS", "Opened %s.\n", osFilename.c_str());
2047 :
2048 22 : poDS->papszExtraFiles =
2049 11 : CSLAddString(poDS->papszExtraFiles, osFilename.c_str());
2050 :
2051 11 : CPL_IGNORE_RET_VAL(VSIFSeekL(process_fp, 0, SEEK_END));
2052 11 : const bool bSilentWrongRecordNumber =
2053 11 : bIsALOS2Or4 && iFile == CEOS_TRAILER_FILE;
2054 11 : if (ProcessData(process_fp, iFile, psVolume, -1,
2055 : VSIFTellL(process_fp),
2056 11 : bSilentWrongRecordNumber) == CE_None)
2057 : {
2058 9 : switch (iFile)
2059 : {
2060 3 : case CEOS_VOLUME_DIR_FILE:
2061 3 : psVolume->VolumeDirectoryFile = TRUE;
2062 3 : break;
2063 5 : case CEOS_LEADER_FILE:
2064 5 : psVolume->SARLeaderFile = TRUE;
2065 5 : break;
2066 1 : case CEOS_TRAILER_FILE:
2067 1 : psVolume->SARTrailerFile = TRUE;
2068 1 : break;
2069 0 : case CEOS_NULL_VOL_FILE:
2070 0 : psVolume->NullVolumeDirectoryFile = TRUE;
2071 0 : break;
2072 : }
2073 :
2074 9 : CPL_IGNORE_RET_VAL(VSIFCloseL(process_fp));
2075 9 : break; /* Exit the while loop, we have this data type*/
2076 : }
2077 :
2078 2 : CPL_IGNORE_RET_VAL(VSIFCloseL(process_fp));
2079 : }
2080 : }
2081 : }
2082 :
2083 7 : CPLFree(pszPath);
2084 7 : CPLFree(pszBasename);
2085 7 : CPLFree(pszExtension);
2086 :
2087 : /* -------------------------------------------------------------------- */
2088 : /* Check that we have an image description. */
2089 : /* -------------------------------------------------------------------- */
2090 7 : GetCeosSARImageDesc(psVolume);
2091 7 : struct CeosSARImageDesc *psImageDesc = &(psVolume->ImageDesc);
2092 7 : if (!psImageDesc->ImageDescValid)
2093 : {
2094 0 : CPLDebug("CEOS",
2095 : "Unable to extract CEOS image description\n"
2096 : "from %s.",
2097 : poOpenInfo->pszFilename);
2098 :
2099 0 : return nullptr;
2100 : }
2101 :
2102 : /* -------------------------------------------------------------------- */
2103 : /* Establish image type. */
2104 : /* -------------------------------------------------------------------- */
2105 : GDALDataType eType;
2106 :
2107 7 : switch (psImageDesc->DataType)
2108 : {
2109 2 : case CEOS_TYP_CHAR:
2110 : case CEOS_TYP_UCHAR:
2111 2 : eType = GDT_UInt8;
2112 2 : break;
2113 :
2114 0 : case CEOS_TYP_SHORT:
2115 0 : eType = GDT_Int16;
2116 0 : break;
2117 :
2118 0 : case CEOS_TYP_COMPLEX_SHORT:
2119 : case CEOS_TYP_PALSAR_COMPLEX_SHORT:
2120 0 : eType = GDT_CInt16;
2121 0 : break;
2122 :
2123 3 : case CEOS_TYP_USHORT:
2124 3 : eType = GDT_UInt16;
2125 3 : break;
2126 :
2127 0 : case CEOS_TYP_LONG:
2128 0 : eType = GDT_Int32;
2129 0 : break;
2130 :
2131 0 : case CEOS_TYP_ULONG:
2132 0 : eType = GDT_UInt32;
2133 0 : break;
2134 :
2135 0 : case CEOS_TYP_FLOAT:
2136 0 : eType = GDT_Float32;
2137 0 : break;
2138 :
2139 0 : case CEOS_TYP_DOUBLE:
2140 0 : eType = GDT_Float64;
2141 0 : break;
2142 :
2143 2 : case CEOS_TYP_COMPLEX_FLOAT:
2144 : case CEOS_TYP_CCP_COMPLEX_FLOAT:
2145 2 : eType = GDT_CFloat32;
2146 2 : break;
2147 :
2148 0 : default:
2149 0 : CPLError(CE_Failure, CPLE_AppDefined,
2150 : "Unsupported CEOS image data type %d.\n",
2151 : psImageDesc->DataType);
2152 0 : return nullptr;
2153 : }
2154 :
2155 : /* -------------------------------------------------------------------- */
2156 : /* Capture some information from the file that is of interest. */
2157 : /* -------------------------------------------------------------------- */
2158 14 : poDS->nRasterXSize = psImageDesc->PixelsPerLine +
2159 7 : psImageDesc->LeftBorderPixels +
2160 7 : psImageDesc->RightBorderPixels;
2161 7 : poDS->nRasterYSize = psImageDesc->Lines;
2162 :
2163 : /* -------------------------------------------------------------------- */
2164 : /* Special case for compressed cross products. */
2165 : /* -------------------------------------------------------------------- */
2166 7 : if (psImageDesc->DataType == CEOS_TYP_CCP_COMPLEX_FLOAT)
2167 : {
2168 0 : for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
2169 : {
2170 0 : poDS->SetBand(
2171 0 : poDS->nBands + 1,
2172 0 : new CCPRasterBand(poDS.get(), poDS->nBands + 1, eType));
2173 : }
2174 :
2175 : /* mark this as a Scattering Matrix product */
2176 0 : if (poDS->GetRasterCount() == 4)
2177 : {
2178 0 : poDS->SetMetadataItem("MATRIX_REPRESENTATION", "SCATTERING");
2179 : }
2180 : }
2181 :
2182 : /* -------------------------------------------------------------------- */
2183 : /* Special case for PALSAR data. */
2184 : /* -------------------------------------------------------------------- */
2185 7 : else if (psImageDesc->DataType == CEOS_TYP_PALSAR_COMPLEX_SHORT)
2186 : {
2187 0 : for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
2188 : {
2189 0 : poDS->SetBand(poDS->nBands + 1,
2190 0 : new PALSARRasterBand(poDS.get(), poDS->nBands + 1));
2191 : }
2192 :
2193 : /* mark this as a Symmetrized Covariance product if appropriate */
2194 0 : if (poDS->GetRasterCount() == 6)
2195 : {
2196 0 : poDS->SetMetadataItem("MATRIX_REPRESENTATION",
2197 0 : "SYMMETRIZED_COVARIANCE");
2198 : }
2199 : }
2200 :
2201 : /* -------------------------------------------------------------------- */
2202 : /* Roll our own ... */
2203 : /* -------------------------------------------------------------------- */
2204 7 : else if (psImageDesc->RecordsPerLine > 1 ||
2205 7 : psImageDesc->DataType == CEOS_TYP_CHAR ||
2206 7 : psImageDesc->DataType == CEOS_TYP_LONG ||
2207 7 : psImageDesc->DataType == CEOS_TYP_ULONG ||
2208 7 : psImageDesc->DataType == CEOS_TYP_DOUBLE)
2209 : {
2210 0 : for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
2211 : {
2212 0 : poDS->SetBand(
2213 0 : poDS->nBands + 1,
2214 0 : new SAR_CEOSRasterBand(poDS.get(), poDS->nBands + 1, eType));
2215 0 : }
2216 : }
2217 :
2218 : /* -------------------------------------------------------------------- */
2219 : /* Use raw services for well behaved files. */
2220 : /* -------------------------------------------------------------------- */
2221 : else
2222 : {
2223 : uint64_t StartData;
2224 7 : CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr, &StartData);
2225 :
2226 : /*StartData += psImageDesc->ImageDataStart; */
2227 :
2228 : uint64_t nLineOff1, nLineOff2;
2229 7 : CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr, &nLineOff1);
2230 7 : CalcCeosSARImageFilePosition(psVolume, 1, 2, nullptr, &nLineOff2);
2231 :
2232 7 : const int nLineSize = static_cast<int>(nLineOff2 - nLineOff1);
2233 :
2234 14 : for (int iBand = 0; iBand < psImageDesc->NumChannels; iBand++)
2235 : {
2236 : uint64_t nStartData;
2237 : int nPixelOffset, nLineOffset;
2238 :
2239 7 : if (psImageDesc->ChannelInterleaving == CEOS_IL_PIXEL)
2240 : {
2241 0 : CalcCeosSARImageFilePosition(psVolume, 1, 1, nullptr,
2242 : &nStartData);
2243 :
2244 0 : nStartData += psImageDesc->ImageDataStart;
2245 0 : nStartData +=
2246 0 : cpl::fits_on<int>(psImageDesc->BytesPerPixel * iBand);
2247 :
2248 0 : nPixelOffset =
2249 0 : psImageDesc->BytesPerPixel * psImageDesc->NumChannels;
2250 0 : nLineOffset = nLineSize;
2251 : }
2252 7 : else if (psImageDesc->ChannelInterleaving == CEOS_IL_LINE)
2253 : {
2254 0 : CalcCeosSARImageFilePosition(psVolume, iBand + 1, 1, nullptr,
2255 : &nStartData);
2256 :
2257 0 : nStartData += psImageDesc->ImageDataStart;
2258 0 : nPixelOffset = psImageDesc->BytesPerPixel;
2259 0 : nLineOffset = nLineSize * psImageDesc->NumChannels;
2260 : }
2261 7 : else if (psImageDesc->ChannelInterleaving == CEOS_IL_BAND)
2262 : {
2263 7 : CalcCeosSARImageFilePosition(psVolume, iBand + 1, 1, nullptr,
2264 : &nStartData);
2265 :
2266 7 : nStartData += psImageDesc->ImageDataStart;
2267 7 : nPixelOffset = psImageDesc->BytesPerPixel;
2268 7 : nLineOffset = nLineSize;
2269 : }
2270 : else
2271 : {
2272 0 : CPLAssert(false);
2273 0 : return nullptr;
2274 : }
2275 :
2276 : auto poBand = RawRasterBand::Create(
2277 21 : poDS.get(), poDS->nBands + 1, poDS->fpImage, nStartData,
2278 : nPixelOffset, nLineOffset, eType,
2279 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
2280 7 : RawRasterBand::OwnFP::NO);
2281 7 : if (!poBand)
2282 0 : return nullptr;
2283 7 : poDS->SetBand(poDS->nBands + 1, std::move(poBand));
2284 : }
2285 : }
2286 :
2287 : /* -------------------------------------------------------------------- */
2288 : /* Collect metadata. */
2289 : /* -------------------------------------------------------------------- */
2290 7 : poDS->ScanForMetadata();
2291 :
2292 : /* -------------------------------------------------------------------- */
2293 : /* Initialize any PAM information. */
2294 : /* -------------------------------------------------------------------- */
2295 7 : poDS->SetDescription(poOpenInfo->pszFilename);
2296 7 : poDS->TryLoadXML();
2297 :
2298 : /* -------------------------------------------------------------------- */
2299 : /* Open overviews. */
2300 : /* -------------------------------------------------------------------- */
2301 7 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
2302 :
2303 7 : return poDS.release();
2304 : }
2305 :
2306 : /************************************************************************/
2307 : /* ProcessData() */
2308 : /************************************************************************/
2309 18 : static int ProcessData(VSILFILE *fp, int fileid, CeosSARVolume_t *sar,
2310 : int max_records, vsi_l_offset max_bytes,
2311 : bool bSilentWrongRecordNumber)
2312 :
2313 : {
2314 : unsigned char temp_buffer[CEOS_HEADER_LENGTH];
2315 18 : unsigned char *temp_body = nullptr;
2316 18 : vsi_l_offset start = 0;
2317 18 : int CurrentBodyLength = 0;
2318 18 : int CurrentType = 0;
2319 18 : int CurrentSequence = 0;
2320 18 : int iThisRecord = 0;
2321 :
2322 120 : while (max_records != 0 && max_bytes != 0)
2323 : {
2324 104 : iThisRecord++;
2325 :
2326 208 : if (VSIFSeekL(fp, start, SEEK_SET) != 0 ||
2327 104 : VSIFReadL(temp_buffer, 1, CEOS_HEADER_LENGTH, fp) !=
2328 : CEOS_HEADER_LENGTH)
2329 : {
2330 0 : CPLError(CE_Failure, CPLE_AppDefined,
2331 : "Corrupt CEOS File - cannot read record %d.", iThisRecord);
2332 0 : CPLFree(temp_body);
2333 0 : return CE_Failure;
2334 : }
2335 104 : CeosRecord_t *record = (CeosRecord_t *)CPLMalloc(sizeof(CeosRecord_t));
2336 104 : record->Length = DetermineCeosRecordBodyLength(temp_buffer);
2337 :
2338 104 : CeosToNative(&(record->Sequence), temp_buffer, 4, 4);
2339 :
2340 104 : if (iThisRecord != record->Sequence)
2341 : {
2342 2 : if (fileid == CEOS_IMAGRY_OPT_FILE && iThisRecord == 2)
2343 : {
2344 0 : CPLDebug("SAR_CEOS",
2345 : "Ignoring CEOS file with wrong second record sequence "
2346 : "number - likely it has padded records.");
2347 0 : CPLFree(record);
2348 0 : CPLFree(temp_body);
2349 0 : return CE_Warning;
2350 : }
2351 2 : else if (bSilentWrongRecordNumber && iThisRecord == 2)
2352 : {
2353 2 : CPLFree(record);
2354 2 : CPLFree(temp_body);
2355 2 : return CE_Warning;
2356 : }
2357 : else
2358 : {
2359 0 : CPLError(CE_Warning, CPLE_AppDefined,
2360 : "Corrupted CEOS File - got record seq# %d instead of "
2361 : "the expected %d.",
2362 : record->Sequence, iThisRecord);
2363 0 : CPLFree(record);
2364 0 : CPLFree(temp_body);
2365 0 : return CE_Failure;
2366 : }
2367 : }
2368 :
2369 102 : if (record->Length <= CEOS_HEADER_LENGTH)
2370 : {
2371 0 : CPLError(CE_Failure, CPLE_AppDefined,
2372 : "Corrupt CEOS File - cannot read record %d.", iThisRecord);
2373 0 : CPLFree(record);
2374 0 : CPLFree(temp_body);
2375 0 : return CE_Failure;
2376 : }
2377 :
2378 102 : if (record->Length > CurrentBodyLength)
2379 : {
2380 : unsigned char *temp_body_new =
2381 47 : (unsigned char *)VSI_REALLOC_VERBOSE(temp_body, record->Length);
2382 47 : if (temp_body_new == nullptr)
2383 : {
2384 0 : CPLFree(record);
2385 0 : CPLFree(temp_body);
2386 0 : return CE_Failure;
2387 : }
2388 47 : temp_body = temp_body_new;
2389 47 : CurrentBodyLength = record->Length;
2390 : }
2391 :
2392 102 : int nToRead = record->Length - CEOS_HEADER_LENGTH;
2393 102 : if ((int)VSIFReadL(temp_body, 1, nToRead, fp) != nToRead)
2394 : {
2395 0 : CPLError(CE_Failure, CPLE_AppDefined,
2396 : "Corrupt CEOS File - cannot read record %d.", iThisRecord);
2397 0 : CPLFree(record);
2398 0 : CPLFree(temp_body);
2399 0 : return CE_Failure;
2400 : }
2401 :
2402 102 : InitCeosRecordWithHeader(record, temp_buffer, temp_body);
2403 102 : if (record->Length == 0)
2404 : {
2405 0 : CPLError(CE_Failure, CPLE_AppDefined,
2406 : "Corrupt CEOS File - invalid record %d.", iThisRecord);
2407 0 : CPLFree(record);
2408 0 : CPLFree(temp_body);
2409 0 : return CE_Failure;
2410 : }
2411 :
2412 102 : if (CurrentType == record->TypeCode.Int32Code)
2413 36 : record->Subsequence = ++CurrentSequence;
2414 : else
2415 : {
2416 66 : CurrentType = record->TypeCode.Int32Code;
2417 66 : record->Subsequence = 0;
2418 66 : CurrentSequence = 0;
2419 : }
2420 :
2421 102 : record->FileId = fileid;
2422 :
2423 102 : Link_t *TheLink = ceos2CreateLink(record);
2424 :
2425 102 : if (sar->RecordList == nullptr)
2426 7 : sar->RecordList = TheLink;
2427 : else
2428 95 : sar->RecordList = InsertLink(sar->RecordList, TheLink);
2429 :
2430 102 : start += record->Length;
2431 :
2432 102 : if (max_records > 0)
2433 28 : max_records--;
2434 102 : if (max_bytes > 0)
2435 : {
2436 102 : if ((vsi_l_offset)record->Length <= max_bytes)
2437 102 : max_bytes -= record->Length;
2438 : else
2439 : {
2440 0 : CPLDebug("SAR_CEOS",
2441 : "Partial record found. %d > " CPL_FRMT_GUIB,
2442 : record->Length, max_bytes);
2443 0 : max_bytes = 0;
2444 : }
2445 : }
2446 : }
2447 :
2448 16 : CPLFree(temp_body);
2449 :
2450 16 : return CE_None;
2451 : }
2452 :
2453 : /************************************************************************/
2454 : /* GDALRegister_SAR_CEOS() */
2455 : /************************************************************************/
2456 :
2457 2066 : void GDALRegister_SAR_CEOS()
2458 :
2459 : {
2460 2066 : if (GDALGetDriverByName("SAR_CEOS") != nullptr)
2461 263 : return;
2462 :
2463 1803 : GDALDriver *poDriver = new GDALDriver();
2464 :
2465 1803 : poDriver->SetDescription("SAR_CEOS");
2466 1803 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
2467 1803 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "CEOS SAR Image");
2468 1803 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
2469 1803 : "drivers/raster/sar_ceos.html");
2470 1803 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
2471 :
2472 1803 : poDriver->pfnOpen = SAR_CEOSDataset::Open;
2473 :
2474 1803 : GetGDALDriverManager()->RegisterDriver(poDriver);
2475 : }
2476 :
2477 : /************************************************************************/
2478 : /* GetFileList() */
2479 : /************************************************************************/
2480 :
2481 2 : char **SAR_CEOSDataset::GetFileList()
2482 :
2483 : {
2484 2 : char **papszFileList = GDALPamDataset::GetFileList();
2485 :
2486 2 : papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
2487 :
2488 2 : return papszFileList;
2489 : }
|