Line data Source code
1 : /******************************************************************************
2 : * Purpose: ASRP/USRP Reader
3 : * Author: Frank Warmerdam (warmerdam@pobox.com)
4 : *
5 : * Derived from ADRG driver by Even Rouault, even.rouault at spatialys.com.
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
9 : * Copyright (c) 2009, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_string.h"
31 : #include "gdal_pam.h"
32 : #include "gdal_frmts.h"
33 : #include "iso8211.h"
34 : #include "ogr_spatialref.h"
35 :
36 : #include <cstdlib>
37 : #include <algorithm>
38 : #include <limits>
39 :
40 : // Uncomment to recognize also .gen files in addition to .img files
41 : // #define OPEN_GEN
42 :
43 : class SRPDataset final : public GDALPamDataset
44 : {
45 : friend class SRPRasterBand;
46 :
47 : static CPLString ResetTo01(const char *str);
48 :
49 : VSILFILE *fdIMG;
50 : int *TILEINDEX;
51 : int offsetInIMG;
52 : CPLString osProduct;
53 : OGRSpatialReference m_oSRS{};
54 : CPLString osGENFileName;
55 : CPLString osQALFileName;
56 : CPLString osIMGFileName;
57 : int NFC;
58 : int NFL;
59 : int ZNA;
60 : double LSO;
61 : double PSO;
62 : double LOD;
63 : double LAD;
64 : int ARV;
65 : int BRV;
66 : int PCB;
67 : int PVB;
68 :
69 : char **papszSubDatasets;
70 :
71 : GDALColorTable oCT;
72 :
73 : static char **GetGENListFromTHF(const char *pszFileName);
74 : static char **GetIMGListFromGEN(const char *pszFileName,
75 : int *pnRecordIndex = nullptr);
76 : static SRPDataset *OpenDataset(const char *pszGENFileName,
77 : const char *pszIMGFileName,
78 : DDFRecord *record = nullptr);
79 : static DDFRecord *FindRecordInGENForIMG(DDFModule &module,
80 : const char *pszGENFileName,
81 : const char *pszIMGFileName);
82 :
83 : public:
84 : SRPDataset();
85 : ~SRPDataset() override;
86 :
87 : const OGRSpatialReference *GetSpatialRef() const override;
88 : CPLErr GetGeoTransform(double *padfGeoTransform) override;
89 :
90 : char **GetMetadata(const char *pszDomain = "") override;
91 :
92 : char **GetFileList() override;
93 :
94 : bool GetFromRecord(const char *pszFileName, DDFRecord *record);
95 : void AddSubDataset(const char *pszGENFileName, const char *pszIMGFileName);
96 : void AddMetadatafromFromTHF(const char *pszFileName);
97 :
98 : static GDALDataset *Open(GDALOpenInfo *);
99 : };
100 :
101 : /************************************************************************/
102 : /* ==================================================================== */
103 : /* SRPRasterBand */
104 : /* ==================================================================== */
105 : /************************************************************************/
106 :
107 : class SRPRasterBand final : public GDALPamRasterBand
108 : {
109 : friend class SRPDataset;
110 :
111 : public:
112 : SRPRasterBand(SRPDataset *, int);
113 :
114 : CPLErr IReadBlock(int, int, void *) override;
115 :
116 : double GetNoDataValue(int *pbSuccess = nullptr) override;
117 :
118 : GDALColorInterp GetColorInterpretation() override;
119 : GDALColorTable *GetColorTable() override;
120 : };
121 :
122 : /************************************************************************/
123 : /* SRPRasterBand() */
124 : /************************************************************************/
125 :
126 12 : SRPRasterBand::SRPRasterBand(SRPDataset *poDSIn, int nBandIn)
127 :
128 : {
129 12 : poDS = poDSIn;
130 12 : nBand = nBandIn;
131 :
132 12 : eDataType = GDT_Byte;
133 :
134 12 : nBlockXSize = 128;
135 12 : nBlockYSize = 128;
136 12 : }
137 :
138 : /************************************************************************/
139 : /* GetNoDataValue() */
140 : /************************************************************************/
141 :
142 0 : double SRPRasterBand::GetNoDataValue(int *pbSuccess)
143 : {
144 0 : if (pbSuccess)
145 0 : *pbSuccess = TRUE;
146 :
147 0 : return 0;
148 : }
149 :
150 : /************************************************************************/
151 : /* GetColorInterpretation() */
152 : /************************************************************************/
153 :
154 3 : GDALColorInterp SRPRasterBand::GetColorInterpretation()
155 :
156 : {
157 3 : SRPDataset *l_poDS = (SRPDataset *)this->poDS;
158 :
159 3 : if (l_poDS->oCT.GetColorEntryCount() > 0)
160 3 : return GCI_PaletteIndex;
161 : else
162 0 : return GCI_GrayIndex;
163 : }
164 :
165 : /************************************************************************/
166 : /* GetColorTable() */
167 : /************************************************************************/
168 :
169 3 : GDALColorTable *SRPRasterBand::GetColorTable()
170 :
171 : {
172 3 : SRPDataset *l_poDS = (SRPDataset *)this->poDS;
173 :
174 3 : if (l_poDS->oCT.GetColorEntryCount() > 0)
175 3 : return &(l_poDS->oCT);
176 : else
177 0 : return nullptr;
178 : }
179 :
180 : /************************************************************************/
181 : /* IReadBlock() */
182 : /************************************************************************/
183 :
184 5 : CPLErr SRPRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
185 :
186 : {
187 5 : SRPDataset *l_poDS = (SRPDataset *)this->poDS;
188 : vsi_l_offset offset;
189 5 : int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
190 5 : if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
191 : {
192 0 : CPLError(CE_Failure, CPLE_AppDefined,
193 : "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", nBlockXOff,
194 : l_poDS->NFC, nBlockYOff, l_poDS->NFL);
195 0 : return CE_Failure;
196 : }
197 :
198 : /* -------------------------------------------------------------------- */
199 : /* Is this a null block? */
200 : /* -------------------------------------------------------------------- */
201 5 : if (l_poDS->TILEINDEX && l_poDS->TILEINDEX[nBlock] <= 0)
202 : {
203 0 : memset(pImage, 0, 128 * 128);
204 0 : return CE_None;
205 : }
206 :
207 : /* -------------------------------------------------------------------- */
208 : /* Compute the offset to the block. */
209 : /* -------------------------------------------------------------------- */
210 5 : if (l_poDS->TILEINDEX)
211 : {
212 3 : if (l_poDS->PCB == 0) // uncompressed
213 0 : offset = l_poDS->offsetInIMG +
214 0 : static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1) *
215 0 : 128 * 128;
216 : else // compressed
217 3 : offset = l_poDS->offsetInIMG +
218 3 : static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1);
219 : }
220 : else
221 2 : offset =
222 2 : l_poDS->offsetInIMG + static_cast<vsi_l_offset>(nBlock) * 128 * 128;
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Seek to target location. */
226 : /* -------------------------------------------------------------------- */
227 5 : if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
228 : {
229 0 : CPLError(CE_Failure, CPLE_FileIO,
230 : "Cannot seek to offset " CPL_FRMT_GUIB, offset);
231 0 : return CE_Failure;
232 : }
233 :
234 : /* -------------------------------------------------------------------- */
235 : /* For uncompressed case we read the 128x128 and return with no */
236 : /* further processing. */
237 : /* -------------------------------------------------------------------- */
238 5 : if (l_poDS->PCB == 0)
239 : {
240 2 : if (VSIFReadL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128 * 128)
241 : {
242 0 : CPLError(CE_Failure, CPLE_FileIO,
243 : "Cannot read data at offset " CPL_FRMT_GUIB, offset);
244 0 : return CE_Failure;
245 : }
246 : }
247 :
248 : /* -------------------------------------------------------------------- */
249 : /* If this is compressed data, we read a goodly chunk of data */
250 : /* and then decode it. */
251 : /* -------------------------------------------------------------------- */
252 : else
253 : {
254 3 : const int nBufSize = 128 * 128 * 2;
255 3 : GByte *pabyCData = (GByte *)CPLCalloc(nBufSize, 1);
256 :
257 : const int nBytesRead =
258 3 : static_cast<int>(VSIFReadL(pabyCData, 1, nBufSize, l_poDS->fdIMG));
259 3 : if (nBytesRead == 0)
260 : {
261 0 : CPLError(CE_Failure, CPLE_FileIO,
262 : "Cannot read data at offset " CPL_FRMT_GUIB, offset);
263 0 : CPLFree(pabyCData);
264 0 : return CE_Failure;
265 : }
266 :
267 3 : CPLAssert(l_poDS->PVB == 8);
268 3 : CPLAssert(l_poDS->PCB == 4 || l_poDS->PCB == 8);
269 :
270 3 : bool bHalfByteUsed = false;
271 3201 : for (int iSrc = 0, iPixel = 0; iPixel < 128 * 128;)
272 : {
273 3198 : if (iSrc + 2 > nBytesRead)
274 : {
275 0 : CPLFree(pabyCData);
276 0 : CPLError(CE_Failure, CPLE_AppDefined,
277 : "Out of data decoding image block, only %d available.",
278 : iSrc);
279 0 : return CE_Failure;
280 : }
281 :
282 3198 : int nCount = 0;
283 3198 : int nValue = 0;
284 :
285 3198 : if (l_poDS->PCB == 8)
286 : {
287 640 : nCount = pabyCData[iSrc++];
288 640 : nValue = pabyCData[iSrc++];
289 : }
290 2558 : else if (l_poDS->PCB == 4)
291 : {
292 2558 : if ((iPixel % 128) == 0 && bHalfByteUsed)
293 : {
294 254 : iSrc++;
295 254 : bHalfByteUsed = false;
296 254 : continue;
297 : }
298 :
299 2304 : if (bHalfByteUsed)
300 : {
301 1024 : nCount = pabyCData[iSrc++] & 0xf;
302 1024 : nValue = pabyCData[iSrc++];
303 1024 : bHalfByteUsed = false;
304 : }
305 : else
306 : {
307 1280 : nCount = pabyCData[iSrc] >> 4;
308 1280 : nValue = ((pabyCData[iSrc] & 0xf) << 4) +
309 1280 : (pabyCData[iSrc + 1] >> 4);
310 1280 : bHalfByteUsed = true;
311 1280 : iSrc++;
312 : }
313 : }
314 :
315 2944 : if (iPixel + nCount > 128 * 128)
316 : {
317 0 : CPLFree(pabyCData);
318 0 : CPLError(CE_Failure, CPLE_AppDefined,
319 : "Too much data decoding image block, likely corrupt.");
320 0 : return CE_Failure;
321 : }
322 :
323 52096 : while (nCount > 0)
324 : {
325 49152 : ((GByte *)pImage)[iPixel++] = (GByte)nValue;
326 49152 : nCount--;
327 : }
328 : }
329 :
330 3 : CPLFree(pabyCData);
331 : }
332 :
333 5 : return CE_None;
334 : }
335 :
336 : /************************************************************************/
337 : /* SRPDataset() */
338 : /************************************************************************/
339 :
340 13 : SRPDataset::SRPDataset()
341 : : fdIMG(nullptr), TILEINDEX(nullptr), offsetInIMG(0), NFC(0), NFL(0),
342 : ZNA(0), LSO(0.0), PSO(0.0), LOD(0.0), LAD(0.0), ARV(0), BRV(0), PCB(0),
343 13 : PVB(0), papszSubDatasets(nullptr)
344 : {
345 13 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
346 13 : }
347 :
348 : /************************************************************************/
349 : /* ~SRPDataset() */
350 : /************************************************************************/
351 :
352 26 : SRPDataset::~SRPDataset()
353 : {
354 13 : CSLDestroy(papszSubDatasets);
355 :
356 13 : if (fdIMG)
357 : {
358 12 : VSIFCloseL(fdIMG);
359 : }
360 :
361 13 : if (TILEINDEX)
362 : {
363 7 : delete[] TILEINDEX;
364 : }
365 26 : }
366 :
367 : /************************************************************************/
368 : /* ResetTo01() */
369 : /* Replace the DD in ZZZZZZDD.XXX with 01. */
370 : /************************************************************************/
371 :
372 9 : CPLString SRPDataset::ResetTo01(const char *str)
373 : {
374 9 : CPLString osResult = str;
375 :
376 9 : osResult[6] = '0';
377 9 : osResult[7] = '1';
378 :
379 9 : return osResult;
380 : }
381 :
382 : /************************************************************************/
383 : /* GetSpatialRef() */
384 : /************************************************************************/
385 :
386 3 : const OGRSpatialReference *SRPDataset::GetSpatialRef() const
387 : {
388 3 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
389 : }
390 :
391 : /************************************************************************/
392 : /* GetGeoTransform() */
393 : /************************************************************************/
394 :
395 3 : CPLErr SRPDataset::GetGeoTransform(double *padfGeoTransform)
396 : {
397 3 : if (EQUAL(osProduct, "ASRP"))
398 : {
399 0 : if (ARV == 0)
400 0 : return CE_Failure;
401 0 : if (ZNA == 9)
402 : {
403 : // North Polar Case
404 0 : padfGeoTransform[0] = 111319.4907933 * (90.0 - PSO / 3600.0) *
405 0 : sin(LSO * M_PI / 648000.0);
406 0 : padfGeoTransform[1] = 40075016.68558 / ARV;
407 0 : padfGeoTransform[2] = 0.0;
408 0 : padfGeoTransform[3] = -111319.4907933 * (90.0 - PSO / 3600.0) *
409 0 : cos(LSO * M_PI / 648000.0);
410 0 : padfGeoTransform[4] = 0.0;
411 0 : padfGeoTransform[5] = -40075016.68558 / ARV;
412 : }
413 0 : else if (ZNA == 18)
414 : {
415 : // South Polar Case
416 0 : padfGeoTransform[0] = 111319.4907933 * (90.0 + PSO / 3600.0) *
417 0 : sin(LSO * M_PI / 648000.0);
418 0 : padfGeoTransform[1] = 40075016.68558 / ARV;
419 0 : padfGeoTransform[2] = 0.0;
420 0 : padfGeoTransform[3] = 111319.4907933 * (90.0 + PSO / 3600.0) *
421 0 : cos(LSO * M_PI / 648000.0);
422 0 : padfGeoTransform[4] = 0.0;
423 0 : padfGeoTransform[5] = -40075016.68558 / ARV;
424 : }
425 : else
426 : {
427 0 : if (BRV == 0)
428 0 : return CE_Failure;
429 0 : padfGeoTransform[0] = LSO / 3600.0;
430 0 : padfGeoTransform[1] = 360. / ARV;
431 0 : padfGeoTransform[2] = 0.0;
432 0 : padfGeoTransform[3] = PSO / 3600.0;
433 0 : padfGeoTransform[4] = 0.0;
434 0 : padfGeoTransform[5] = -360. / BRV;
435 : }
436 :
437 0 : return CE_None;
438 : }
439 3 : else if (EQUAL(osProduct, "USRP"))
440 : {
441 3 : padfGeoTransform[0] = LSO;
442 3 : padfGeoTransform[1] = LOD;
443 3 : padfGeoTransform[2] = 0.0;
444 3 : padfGeoTransform[3] = PSO;
445 3 : padfGeoTransform[4] = 0.0;
446 3 : padfGeoTransform[5] = -LAD;
447 3 : return CE_None;
448 : }
449 :
450 0 : return CE_Failure;
451 : }
452 :
453 : /************************************************************************/
454 : /* GetFromRecord() */
455 : /************************************************************************/
456 :
457 12 : bool SRPDataset::GetFromRecord(const char *pszFileName, DDFRecord *record)
458 : {
459 : int bSuccess;
460 :
461 : /* -------------------------------------------------------------------- */
462 : /* Read a variety of header fields of interest from the .GEN */
463 : /* file. */
464 : /* -------------------------------------------------------------------- */
465 12 : const int nSTR = record->GetIntSubfield("GEN", 0, "STR", 0, &bSuccess);
466 12 : if (!bSuccess || nSTR != 4)
467 : {
468 0 : CPLDebug("SRP", "Failed to extract STR, or not 4.");
469 0 : return false;
470 : }
471 :
472 12 : const int SCA = record->GetIntSubfield("GEN", 0, "SCA", 0, &bSuccess);
473 12 : CPLDebug("SRP", "SCA=%d", SCA);
474 :
475 12 : ZNA = record->GetIntSubfield("GEN", 0, "ZNA", 0, &bSuccess);
476 12 : CPLDebug("SRP", "ZNA=%d", ZNA);
477 :
478 12 : const double PSP = record->GetFloatSubfield("GEN", 0, "PSP", 0, &bSuccess);
479 12 : CPLDebug("SRP", "PSP=%f", PSP);
480 :
481 12 : ARV = record->GetIntSubfield("GEN", 0, "ARV", 0, &bSuccess);
482 12 : CPLDebug("SRP", "ARV=%d", ARV);
483 :
484 12 : BRV = record->GetIntSubfield("GEN", 0, "BRV", 0, &bSuccess);
485 12 : CPLDebug("SRP", "BRV=%d", BRV);
486 :
487 12 : LSO = record->GetFloatSubfield("GEN", 0, "LSO", 0, &bSuccess);
488 12 : CPLDebug("SRP", "LSO=%f", LSO);
489 :
490 12 : PSO = record->GetFloatSubfield("GEN", 0, "PSO", 0, &bSuccess);
491 12 : CPLDebug("SRP", "PSO=%f", PSO);
492 :
493 12 : LAD = record->GetFloatSubfield("GEN", 0, "LAD", 0);
494 12 : LOD = record->GetFloatSubfield("GEN", 0, "LOD", 0);
495 :
496 12 : NFL = record->GetIntSubfield("SPR", 0, "NFL", 0, &bSuccess);
497 12 : CPLDebug("SRP", "NFL=%d", NFL);
498 :
499 12 : NFC = record->GetIntSubfield("SPR", 0, "NFC", 0, &bSuccess);
500 12 : CPLDebug("SRP", "NFC=%d", NFC);
501 :
502 12 : const auto knIntMax = std::numeric_limits<int>::max();
503 12 : if (NFL <= 0 || NFC <= 0 || NFL > knIntMax / 128 || NFC > knIntMax / 128 ||
504 12 : NFL > knIntMax / NFC)
505 : {
506 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid NFL / NFC values");
507 0 : return false;
508 : }
509 :
510 12 : const int PNC = record->GetIntSubfield("SPR", 0, "PNC", 0, &bSuccess);
511 12 : CPLDebug("SRP", "PNC=%d", PNC);
512 :
513 12 : const int PNL = record->GetIntSubfield("SPR", 0, "PNL", 0, &bSuccess);
514 12 : CPLDebug("SRP", "PNL=%d", PNL);
515 :
516 12 : if (PNL != 128 || PNC != 128)
517 : {
518 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported PNL or PNC value.");
519 0 : return false;
520 : }
521 :
522 12 : PCB = record->GetIntSubfield("SPR", 0, "PCB", 0);
523 12 : PVB = record->GetIntSubfield("SPR", 0, "PVB", 0);
524 12 : if ((PCB != 8 && PCB != 4 && PCB != 0) || PVB != 8)
525 : {
526 0 : CPLError(CE_Failure, CPLE_AppDefined,
527 : "PCB(%d) or PVB(%d) value unsupported.", PCB, PVB);
528 0 : return false;
529 : }
530 :
531 : const char *pszBAD =
532 12 : record->GetStringSubfield("SPR", 0, "BAD", 0, &bSuccess);
533 12 : if (pszBAD == nullptr)
534 0 : return false;
535 24 : const CPLString osBAD = pszBAD;
536 : {
537 12 : char *c = (char *)strchr(osBAD, ' ');
538 12 : if (c)
539 0 : *c = 0;
540 : }
541 12 : CPLDebug("SRP", "BAD=%s", osBAD.c_str());
542 :
543 : /* -------------------------------------------------------------------- */
544 : /* Read the tile map if available. */
545 : /* -------------------------------------------------------------------- */
546 12 : const char *pszTIF = record->GetStringSubfield("SPR", 0, "TIF", 0);
547 12 : const bool TIF = pszTIF != nullptr && EQUAL(pszTIF, "Y");
548 12 : CPLDebug("SRP", "TIF=%s", TIF ? "true" : "false");
549 :
550 12 : if (TIF)
551 : {
552 7 : DDFField *field = record->FindField("TIM");
553 7 : if (field == nullptr)
554 0 : return false;
555 :
556 7 : DDFFieldDefn *fieldDefn = field->GetFieldDefn();
557 7 : DDFSubfieldDefn *subfieldDefn = fieldDefn->FindSubfieldDefn("TSI");
558 7 : if (subfieldDefn == nullptr)
559 0 : return false;
560 :
561 7 : const int nIndexValueWidth = subfieldDefn->GetWidth();
562 :
563 7 : char offset[30] = {0};
564 : /* Should be strict comparison, but apparently a few datasets */
565 : /* have GetDataSize() greater than the required minimum (#3862) */
566 14 : if (nIndexValueWidth <= 0 ||
567 7 : static_cast<size_t>(nIndexValueWidth) >= sizeof(offset) ||
568 21 : nIndexValueWidth > (INT_MAX - 1) / (NFL * NFC) ||
569 7 : field->GetDataSize() < nIndexValueWidth * NFL * NFC + 1)
570 : {
571 0 : return false;
572 : }
573 :
574 : try
575 : {
576 7 : TILEINDEX = new int[NFL * NFC];
577 : }
578 0 : catch (const std::exception &)
579 : {
580 0 : return false;
581 : }
582 7 : const char *ptr = field->GetData();
583 7 : offset[nIndexValueWidth] = '\0';
584 :
585 14 : for (int i = 0; i < NFL * NFC; i++)
586 : {
587 7 : strncpy(offset, ptr, nIndexValueWidth);
588 7 : ptr += nIndexValueWidth;
589 7 : TILEINDEX[i] = atoi(offset);
590 : // CPLDebug("SRP", "TSI[%d]=%d", i, TILEINDEX[i]);
591 : }
592 : }
593 :
594 : /* -------------------------------------------------------------------- */
595 : /* Open the .IMG file. Try to recover gracefully if the case */
596 : /* of the filename is wrong. */
597 : /* -------------------------------------------------------------------- */
598 24 : const CPLString osDirname = CPLGetDirname(pszFileName);
599 24 : const CPLString osImgName = CPLFormCIFilename(osDirname, osBAD, nullptr);
600 :
601 12 : fdIMG = VSIFOpenL(osImgName, "rb");
602 12 : if (fdIMG == nullptr)
603 : {
604 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
605 : osImgName.c_str());
606 0 : return false;
607 : }
608 :
609 : /* -------------------------------------------------------------------- */
610 : /* Establish the offset to the first byte of actual image data */
611 : /* in the IMG file, skipping the ISO8211 header. */
612 : /* */
613 : /* This code is awfully fragile! */
614 : /* -------------------------------------------------------------------- */
615 : char c;
616 12 : if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
617 : {
618 0 : return false;
619 : }
620 2604 : while (!VSIFEofL(fdIMG))
621 : {
622 2604 : if (c == 30)
623 : {
624 72 : char recordName[3] = {};
625 72 : if (VSIFReadL(recordName, 1, 3, fdIMG) != 3)
626 : {
627 0 : return false;
628 : }
629 72 : offsetInIMG += 3;
630 72 : if (STARTS_WITH(recordName, "IMG"))
631 : {
632 12 : offsetInIMG += 4;
633 12 : if (VSIFSeekL(fdIMG, 3, SEEK_CUR) != 0)
634 : {
635 0 : return false;
636 : }
637 12 : if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
638 : {
639 0 : return false;
640 : }
641 46296 : while (c != 30)
642 : {
643 46284 : offsetInIMG++;
644 46284 : if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
645 : {
646 0 : return false;
647 : }
648 : }
649 12 : offsetInIMG++;
650 12 : break;
651 : }
652 : }
653 :
654 2592 : offsetInIMG++;
655 2592 : if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
656 : {
657 0 : return false;
658 : }
659 : }
660 :
661 12 : if (VSIFEofL(fdIMG))
662 : {
663 0 : return false;
664 : }
665 :
666 12 : CPLDebug("SRP", "Img offset data = %d", offsetInIMG);
667 :
668 : /* -------------------------------------------------------------------- */
669 : /* Establish the SRP Dataset. */
670 : /* -------------------------------------------------------------------- */
671 12 : nRasterXSize = NFC * 128;
672 12 : nRasterYSize = NFL * 128;
673 :
674 12 : char szValue[32] = {};
675 12 : snprintf(szValue, sizeof(szValue), "%d", SCA);
676 12 : SetMetadataItem("SRP_SCA", szValue);
677 :
678 : // PSP Pixel Spacing, Microns at capture stage {000.0 - 100.0}
679 12 : snprintf(szValue, sizeof(szValue), "%3.1f", PSP);
680 12 : SetMetadataItem("SRP_PSP", szValue);
681 :
682 12 : nBands = 1;
683 24 : for (int i = 0; i < nBands; i++)
684 12 : SetBand(i + 1, new SRPRasterBand(this, i + 1));
685 :
686 : /* -------------------------------------------------------------------- */
687 : /* Try to collect a color map from the .QAL file. */
688 : /* -------------------------------------------------------------------- */
689 24 : const CPLString osBasename = CPLGetBasename(pszFileName);
690 12 : osQALFileName = CPLFormCIFilename(osDirname, osBasename, "QAL");
691 :
692 12 : DDFModule oQALModule;
693 :
694 12 : if (oQALModule.Open(osQALFileName, TRUE))
695 : {
696 48 : while ((record = oQALModule.ReadRecord()) != nullptr)
697 : {
698 36 : if (record->FindField("COL") != nullptr)
699 : {
700 : const int nColorCount =
701 12 : std::min(256, record->FindField("COL")->GetRepeatCount());
702 :
703 60 : for (int iColor = 0; iColor < nColorCount; iColor++)
704 : {
705 48 : const int nCCD = record->GetIntSubfield("COL", 0, "CCD",
706 : iColor, &bSuccess);
707 48 : if (!bSuccess || nCCD < 0 || nCCD > 255)
708 : break;
709 :
710 48 : int nNSR = record->GetIntSubfield("COL", 0, "NSR", iColor);
711 48 : int nNSG = record->GetIntSubfield("COL", 0, "NSG", iColor);
712 48 : int nNSB = record->GetIntSubfield("COL", 0, "NSB", iColor);
713 :
714 48 : GDALColorEntry sEntry = {static_cast<short>(nNSR),
715 : static_cast<short>(nNSG),
716 48 : static_cast<short>(nNSB), 255};
717 :
718 48 : oCT.SetColorEntry(nCCD, &sEntry);
719 : }
720 : }
721 :
722 36 : if (record->FindField("QUV") != nullptr)
723 : {
724 : // TODO: Translate to English or state why this should not be in
725 : // English.
726 : // Date de production du produit : QAL.QUV.DAT1
727 : // Numero d'edition du produit : QAL.QUV.EDN
728 :
729 : const int EDN =
730 12 : record->GetIntSubfield("QUV", 0, "EDN", 0, &bSuccess);
731 12 : if (bSuccess)
732 : {
733 12 : CPLDebug("SRP", "EDN=%d", EDN);
734 12 : snprintf(szValue, sizeof(szValue), "%d", EDN);
735 12 : SetMetadataItem("SRP_EDN", szValue);
736 : }
737 :
738 : const char *pszCDV07 =
739 12 : record->GetStringSubfield("QUV", 0, "CDV07", 0);
740 12 : if (pszCDV07 != nullptr)
741 0 : SetMetadataItem("SRP_CREATIONDATE", pszCDV07);
742 : else
743 : { /*USRP1.2*/
744 : const char *pszDAT =
745 12 : record->GetStringSubfield("QUV", 0, "DAT1", 0);
746 12 : if (pszDAT != nullptr && strlen(pszDAT) >= 12)
747 : {
748 : char dat[9];
749 12 : strncpy(dat, pszDAT + 4, 8);
750 12 : dat[8] = '\0';
751 12 : CPLDebug("SRP", "Record DAT %s", dat);
752 12 : SetMetadataItem("SRP_CREATIONDATE", dat);
753 : }
754 : }
755 :
756 : const char *pszCDV24 =
757 12 : record->GetStringSubfield("QUV", 0, "CDV24", 0);
758 12 : if (pszCDV24 != nullptr)
759 : {
760 0 : SetMetadataItem("SRP_REVISIONDATE", pszCDV24);
761 : }
762 : else
763 : { /*USRP1.2*/
764 : const char *pszDAT =
765 12 : record->GetStringSubfield("QUV", 0, "DAT2", 0);
766 12 : if (pszDAT != nullptr && strlen(pszDAT) >= 12)
767 : {
768 : char dat[9];
769 12 : strncpy(dat, pszDAT + 4, 8);
770 12 : dat[8] = '\0';
771 12 : CPLDebug("SRP", "Record DAT %s", dat);
772 12 : SetMetadataItem("SRP_REVISIONDATE", dat);
773 : }
774 : }
775 :
776 : const char *pszQSS =
777 12 : record->GetStringSubfield("QSR", 0, "QSS", 0);
778 12 : if (pszQSS != nullptr)
779 12 : SetMetadataItem("SRP_CLASSIFICATION", pszQSS);
780 : }
781 : }
782 : }
783 : else
784 : {
785 0 : osQALFileName = "";
786 0 : CPLError(CE_Warning, CPLE_AppDefined,
787 : "Unable to find .QAL file, no color table applied.");
788 : }
789 :
790 : /* -------------------------------------------------------------------- */
791 : /* Derive the coordinate system. */
792 : /* -------------------------------------------------------------------- */
793 12 : if (EQUAL(osProduct, "ASRP"))
794 : {
795 0 : m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
796 :
797 0 : if (ZNA == 9)
798 : {
799 0 : m_oSRS.importFromWkt(
800 : "PROJCS[\"ARC_System_Zone_09\",GEOGCS[\"GCS_Sphere\","
801 : "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
802 : "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
803 : "PROJECTION[\"Azimuthal_Equidistant\"],"
804 : "PARAMETER[\"latitude_of_center\",90],"
805 : "PARAMETER[\"longitude_of_center\",0],"
806 : "PARAMETER[\"false_easting\",0],"
807 : "PARAMETER[\"false_northing\",0],"
808 : "UNIT[\"metre\",1]]");
809 : }
810 :
811 0 : if (ZNA == 18)
812 : {
813 0 : m_oSRS.importFromWkt(
814 : "PROJCS[\"ARC_System_Zone_18\",GEOGCS[\"GCS_Sphere\","
815 : "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
816 : "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
817 : "PROJECTION[\"Azimuthal_Equidistant\"],"
818 : "PARAMETER[\"latitude_of_center\",-90],"
819 : "PARAMETER[\"longitude_of_center\",0],"
820 : "PARAMETER[\"false_easting\",0],"
821 : "PARAMETER[\"false_northing\",0],"
822 : "UNIT[\"metre\",1]]");
823 : }
824 : }
825 : else
826 : {
827 12 : if (std::abs(ZNA) >= 1 && std::abs(ZNA) <= 60)
828 : {
829 12 : m_oSRS.SetUTM(std::abs(ZNA), ZNA > 0);
830 12 : m_oSRS.SetWellKnownGeogCS("WGS84");
831 : }
832 0 : else if (ZNA == 61)
833 : {
834 0 : m_oSRS.importFromEPSG(32661); // WGS84 UPS North
835 : }
836 0 : else if (ZNA == -61)
837 : {
838 0 : m_oSRS.importFromEPSG(32761); // WGS84 UPS South
839 : }
840 : }
841 :
842 12 : snprintf(szValue, sizeof(szValue), "%d", ZNA);
843 12 : SetMetadataItem("SRP_ZNA", szValue);
844 :
845 12 : return true;
846 : }
847 :
848 : /************************************************************************/
849 : /* GetFileList() */
850 : /************************************************************************/
851 :
852 5 : char **SRPDataset::GetFileList()
853 :
854 : {
855 5 : char **papszFileList = GDALPamDataset::GetFileList();
856 5 : if (!osGENFileName.empty() && !osIMGFileName.empty())
857 : {
858 10 : CPLString osMainFilename = GetDescription();
859 : VSIStatBufL sStat;
860 :
861 5 : const bool bMainFileReal = VSIStatL(osMainFilename, &sStat) == 0;
862 5 : if (bMainFileReal)
863 : {
864 8 : CPLString osShortMainFilename = CPLGetFilename(osMainFilename);
865 8 : CPLString osShortGENFileName = CPLGetFilename(osGENFileName);
866 4 : if (!EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()))
867 : papszFileList =
868 4 : CSLAddString(papszFileList, osGENFileName.c_str());
869 : }
870 : else
871 : {
872 1 : papszFileList = CSLAddString(papszFileList, osGENFileName.c_str());
873 : }
874 :
875 5 : papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str());
876 :
877 5 : if (!osQALFileName.empty())
878 5 : papszFileList = CSLAddString(papszFileList, osQALFileName);
879 : }
880 5 : return papszFileList;
881 : }
882 :
883 : /************************************************************************/
884 : /* AddSubDataset() */
885 : /************************************************************************/
886 :
887 1 : void SRPDataset::AddSubDataset(const char *pszGENFileName,
888 : const char *pszIMGFileName)
889 : {
890 1 : const int nCount = CSLCount(papszSubDatasets) / 2;
891 :
892 1 : CPLString osSubDatasetName = "SRP:";
893 1 : osSubDatasetName += pszGENFileName;
894 1 : osSubDatasetName += ",";
895 1 : osSubDatasetName += pszIMGFileName;
896 :
897 : char szName[80];
898 1 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
899 1 : papszSubDatasets =
900 1 : CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
901 :
902 1 : snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
903 1 : papszSubDatasets =
904 1 : CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
905 1 : }
906 :
907 : /************************************************************************/
908 : /* GetMetadata() */
909 : /************************************************************************/
910 :
911 5 : char **SRPDataset::GetMetadata(const char *pszDomain)
912 :
913 : {
914 5 : if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
915 1 : return papszSubDatasets;
916 :
917 4 : return GDALPamDataset::GetMetadata(pszDomain);
918 : }
919 :
920 : /************************************************************************/
921 : /* FindRecordInGENForIMG() */
922 : /************************************************************************/
923 :
924 3 : DDFRecord *SRPDataset::FindRecordInGENForIMG(DDFModule &module,
925 : const char *pszGENFileName,
926 : const char *pszIMGFileName)
927 : {
928 : /* Finds the GEN file corresponding to the IMG file */
929 3 : if (!module.Open(pszGENFileName, TRUE))
930 0 : return nullptr;
931 :
932 6 : CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName);
933 :
934 3 : DDFField *field = nullptr;
935 3 : DDFFieldDefn *fieldDefn = nullptr;
936 :
937 : // Now finds the record.
938 : while (true)
939 : {
940 3 : CPLPushErrorHandler(CPLQuietErrorHandler);
941 3 : DDFRecord *record = module.ReadRecord();
942 3 : CPLPopErrorHandler();
943 3 : CPLErrorReset();
944 3 : if (record == nullptr)
945 0 : return nullptr;
946 :
947 3 : if (record->GetFieldCount() >= 5)
948 : {
949 3 : field = record->GetField(0);
950 3 : fieldDefn = field->GetFieldDefn();
951 6 : if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
952 3 : fieldDefn->GetSubfieldCount() == 2))
953 : {
954 0 : continue;
955 : }
956 :
957 3 : const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
958 3 : if (RTY == nullptr)
959 0 : continue;
960 : /* Ignore overviews */
961 3 : if (strcmp(RTY, "OVV") == 0)
962 0 : continue;
963 :
964 3 : if (strcmp(RTY, "GIN") != 0)
965 0 : continue;
966 :
967 3 : field = record->GetField(3);
968 3 : fieldDefn = field->GetFieldDefn();
969 :
970 6 : if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
971 3 : fieldDefn->GetSubfieldCount() == 15))
972 : {
973 0 : continue;
974 : }
975 :
976 3 : const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
977 3 : if (pszBAD == nullptr || strlen(pszBAD) != 12)
978 0 : continue;
979 3 : const CPLString osBAD = pszBAD;
980 : {
981 3 : char *c = (char *)strchr(osBAD.c_str(), ' ');
982 3 : if (c)
983 0 : *c = 0;
984 : }
985 :
986 3 : if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str()))
987 : {
988 3 : return record;
989 : }
990 : }
991 0 : }
992 : }
993 :
994 : /************************************************************************/
995 : /* OpenDataset() */
996 : /************************************************************************/
997 :
998 12 : SRPDataset *SRPDataset::OpenDataset(const char *pszGENFileName,
999 : const char *pszIMGFileName,
1000 : DDFRecord *record)
1001 : {
1002 24 : DDFModule module; // Don't move this line as it holds ownership of record.
1003 :
1004 12 : if (record == nullptr)
1005 : {
1006 3 : record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName);
1007 3 : if (record == nullptr)
1008 0 : return nullptr;
1009 : }
1010 :
1011 12 : DDFField *field = record->GetField(1);
1012 12 : if (field == nullptr)
1013 0 : return nullptr;
1014 12 : DDFFieldDefn *fieldDefn = field->GetFieldDefn();
1015 :
1016 24 : if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 &&
1017 12 : fieldDefn->GetSubfieldCount() == 2))
1018 : {
1019 0 : return nullptr;
1020 : }
1021 :
1022 12 : const char *pszPRT = record->GetStringSubfield("DSI", 0, "PRT", 0);
1023 12 : if (pszPRT == nullptr)
1024 0 : return nullptr;
1025 :
1026 24 : CPLString osPRT = pszPRT;
1027 12 : osPRT.resize(4);
1028 12 : CPLDebug("SRP", "osPRT=%s", osPRT.c_str());
1029 12 : if (!EQUAL(osPRT, "ASRP") && !EQUAL(osPRT, "USRP"))
1030 0 : return nullptr;
1031 :
1032 12 : const char *pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0);
1033 12 : if (pszNAM == nullptr)
1034 0 : return nullptr;
1035 :
1036 24 : const CPLString osNAM = pszNAM;
1037 12 : CPLDebug("SRP", "osNAM=%s", osNAM.c_str());
1038 12 : if (strlen(pszNAM) != 8)
1039 : {
1040 12 : CPLDebug("SRP", "Name Size=%d", (int)strlen(pszNAM));
1041 : }
1042 :
1043 12 : SRPDataset *poDS = new SRPDataset();
1044 :
1045 12 : poDS->osProduct = osPRT;
1046 12 : poDS->osGENFileName = pszGENFileName;
1047 12 : poDS->osIMGFileName = pszIMGFileName;
1048 :
1049 12 : poDS->SetMetadataItem("SRP_NAM", osNAM);
1050 12 : poDS->SetMetadataItem("SRP_PRODUCT", osPRT);
1051 :
1052 12 : if (!poDS->GetFromRecord(pszGENFileName, record))
1053 : {
1054 0 : delete poDS;
1055 0 : return nullptr;
1056 : }
1057 :
1058 12 : return poDS;
1059 : }
1060 :
1061 : /************************************************************************/
1062 : /* GetGENListFromTHF() */
1063 : /************************************************************************/
1064 :
1065 4 : char **SRPDataset::GetGENListFromTHF(const char *pszFileName)
1066 : {
1067 8 : DDFModule module;
1068 4 : DDFRecord *record = nullptr;
1069 4 : DDFField *field = nullptr;
1070 4 : DDFFieldDefn *fieldDefn = nullptr;
1071 4 : int nFilenames = 0;
1072 :
1073 4 : char **papszFileNames = nullptr;
1074 4 : if (!module.Open(pszFileName, TRUE))
1075 0 : return papszFileNames;
1076 :
1077 4 : CPLString osDirName(CPLGetDirname(pszFileName));
1078 :
1079 : while (true)
1080 : {
1081 14 : CPLPushErrorHandler(CPLQuietErrorHandler);
1082 14 : record = module.ReadRecord();
1083 14 : CPLPopErrorHandler();
1084 14 : CPLErrorReset();
1085 14 : if (record == nullptr)
1086 4 : break;
1087 10 : if (record->GetFieldCount() > 2)
1088 : {
1089 10 : field = record->GetField(0);
1090 10 : fieldDefn = field->GetFieldDefn();
1091 20 : if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
1092 10 : fieldDefn->GetSubfieldCount() == 2))
1093 : {
1094 0 : continue;
1095 : }
1096 :
1097 10 : const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1098 10 : if (RTY == nullptr)
1099 : {
1100 0 : continue;
1101 : }
1102 :
1103 10 : if (strcmp(RTY, "THF") == 0)
1104 : {
1105 3 : field = record->GetField(1);
1106 3 : fieldDefn = field->GetFieldDefn();
1107 6 : if (!(strcmp(fieldDefn->GetName(), "VDR") == 0 &&
1108 3 : fieldDefn->GetSubfieldCount() == 8))
1109 : {
1110 0 : continue;
1111 : }
1112 :
1113 3 : int iFDRFieldInstance = 0;
1114 6 : for (int i = 2; i < record->GetFieldCount(); i++)
1115 : {
1116 3 : field = record->GetField(i);
1117 3 : fieldDefn = field->GetFieldDefn();
1118 :
1119 6 : if (!(strcmp(fieldDefn->GetName(), "FDR") == 0 &&
1120 3 : fieldDefn->GetSubfieldCount() == 7))
1121 : {
1122 0 : CPLDebug("SRP", "Record FDR %d",
1123 : fieldDefn->GetSubfieldCount());
1124 0 : continue;
1125 : }
1126 :
1127 3 : const char *pszNAM = record->GetStringSubfield(
1128 : "FDR", iFDRFieldInstance++, "NAM", 0);
1129 3 : if (pszNAM == nullptr)
1130 0 : continue;
1131 :
1132 6 : CPLString osName = CPLString(pszNAM);
1133 :
1134 : /* Define a subdirectory from Dataset but with only 6
1135 : * characters */
1136 6 : CPLString osDirDataset = pszNAM;
1137 3 : osDirDataset.resize(6);
1138 : CPLString osDatasetDir = CPLFormFilename(
1139 6 : osDirName.c_str(), osDirDataset.c_str(), nullptr);
1140 :
1141 6 : CPLString osGENFileName = "";
1142 :
1143 3 : int bFound = 0;
1144 :
1145 : {
1146 : char **papszDirContent =
1147 3 : VSIReadDir(osDatasetDir.c_str());
1148 3 : char **ptrDir = papszDirContent;
1149 3 : if (ptrDir)
1150 : {
1151 0 : while (*ptrDir)
1152 : {
1153 0 : if (EQUAL(CPLGetExtension(*ptrDir), "GEN"))
1154 : {
1155 0 : bFound = 1;
1156 : osGENFileName = CPLFormFilename(
1157 0 : osDatasetDir.c_str(), *ptrDir, nullptr);
1158 0 : CPLDebug("SRP",
1159 : "Building GEN full file name : %s",
1160 : osGENFileName.c_str());
1161 0 : break;
1162 : }
1163 0 : ptrDir++;
1164 : }
1165 0 : CSLDestroy(papszDirContent);
1166 : }
1167 : }
1168 :
1169 : /* If not found in sub directory then search in the same
1170 : * directory of the THF file */
1171 3 : if (bFound == 0)
1172 : {
1173 3 : char **papszDirContent = VSIReadDir(osDirName.c_str());
1174 3 : char **ptrDir = papszDirContent;
1175 3 : if (ptrDir)
1176 : {
1177 5 : while (*ptrDir)
1178 : {
1179 8 : if (EQUAL(CPLGetExtension(*ptrDir), "GEN") &&
1180 3 : EQUALN(CPLGetBasename(*ptrDir), osName, 6))
1181 : {
1182 3 : bFound = 1;
1183 : osGENFileName = CPLFormFilename(
1184 3 : osDirName.c_str(), *ptrDir, nullptr);
1185 3 : CPLDebug("SRP",
1186 : "Building GEN full file name : %s",
1187 : osGENFileName.c_str());
1188 3 : break;
1189 : }
1190 2 : ptrDir++;
1191 : }
1192 3 : CSLDestroy(papszDirContent);
1193 : }
1194 : }
1195 :
1196 3 : if (bFound == 1)
1197 : {
1198 6 : papszFileNames = (char **)CPLRealloc(
1199 3 : papszFileNames, sizeof(char *) * (nFilenames + 2));
1200 6 : papszFileNames[nFilenames] =
1201 3 : CPLStrdup(osGENFileName.c_str());
1202 3 : papszFileNames[nFilenames + 1] = nullptr;
1203 3 : nFilenames++;
1204 : }
1205 : }
1206 : }
1207 : }
1208 10 : }
1209 4 : return papszFileNames;
1210 : }
1211 :
1212 : /************************************************************************/
1213 : /* AddMetadatafromFromTHF() */
1214 : /************************************************************************/
1215 :
1216 1 : void SRPDataset::AddMetadatafromFromTHF(const char *pszFileName)
1217 : {
1218 1 : DDFModule module;
1219 1 : DDFRecord *record = nullptr;
1220 1 : DDFField *field = nullptr;
1221 1 : DDFFieldDefn *fieldDefn = nullptr;
1222 :
1223 1 : int bSuccess = 0;
1224 1 : if (!module.Open(pszFileName, TRUE))
1225 0 : return;
1226 :
1227 : while (true)
1228 : {
1229 3 : CPLPushErrorHandler(CPLQuietErrorHandler);
1230 3 : record = module.ReadRecord();
1231 3 : CPLPopErrorHandler();
1232 3 : CPLErrorReset();
1233 3 : if (record == nullptr || record->GetFieldCount() <= 2)
1234 1 : break;
1235 :
1236 2 : field = record->GetField(0);
1237 2 : fieldDefn = field->GetFieldDefn();
1238 4 : if (!(strcmp(fieldDefn->GetName(), "001") == 0) ||
1239 2 : fieldDefn->GetSubfieldCount() != 2)
1240 0 : break;
1241 :
1242 2 : const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1243 2 : if (RTY != nullptr && strcmp(RTY, "THF") == 0)
1244 : {
1245 1 : field = record->GetField(1);
1246 1 : fieldDefn = field->GetFieldDefn();
1247 2 : if ((strcmp(fieldDefn->GetName(), "VDR") == 0 &&
1248 1 : fieldDefn->GetSubfieldCount() == 8))
1249 : {
1250 :
1251 : const char *pszVOO =
1252 1 : record->GetStringSubfield("VDR", 0, "VOO", 0);
1253 1 : if (pszVOO != nullptr)
1254 : {
1255 1 : CPLDebug("SRP", "Record VOO %s", pszVOO);
1256 1 : SetMetadataItem("SRP_VOO", pszVOO);
1257 : }
1258 :
1259 1 : int EDN = record->GetIntSubfield("VDR", 0, "EDN", 0, &bSuccess);
1260 1 : if (bSuccess)
1261 : {
1262 1 : CPLDebug("SRP", "Record EDN %d", EDN);
1263 : char szValue[5];
1264 1 : snprintf(szValue, sizeof(szValue), "%d", EDN);
1265 1 : SetMetadataItem("SRP_EDN", szValue);
1266 : }
1267 :
1268 : const char *pszCDV07 =
1269 1 : record->GetStringSubfield("VDR", 0, "CDV07", 0);
1270 1 : if (pszCDV07 != nullptr)
1271 : {
1272 0 : CPLDebug("SRP", "Record pszCDV07 %s", pszCDV07);
1273 0 : SetMetadataItem("SRP_CREATIONDATE", pszCDV07);
1274 : }
1275 : else
1276 : { /*USRP1.2*/
1277 : const char *pszDAT =
1278 1 : record->GetStringSubfield("VDR", 0, "DAT", 0);
1279 1 : if (pszDAT != nullptr)
1280 : {
1281 : char dat[9];
1282 1 : strncpy(dat, pszDAT + 4, 8);
1283 1 : dat[8] = '\0';
1284 1 : CPLDebug("SRP", "Record DAT %s", dat);
1285 1 : SetMetadataItem("SRP_CREATIONDATE", dat);
1286 : }
1287 : }
1288 : }
1289 : } /* End of THF part */
1290 :
1291 2 : if (RTY != nullptr && strcmp(RTY, "LCF") == 0)
1292 : {
1293 1 : field = record->GetField(1);
1294 1 : fieldDefn = field->GetFieldDefn();
1295 2 : if ((strcmp(fieldDefn->GetName(), "QSR") == 0 &&
1296 1 : fieldDefn->GetSubfieldCount() == 4))
1297 : {
1298 :
1299 : const char *pszQSS =
1300 1 : record->GetStringSubfield("QSR", 0, "QSS", 0);
1301 1 : if (pszQSS != nullptr)
1302 : {
1303 1 : CPLDebug("SRP", "Record Classification %s", pszQSS);
1304 1 : SetMetadataItem("SRP_CLASSIFICATION", pszQSS);
1305 : }
1306 : }
1307 :
1308 1 : field = record->GetField(2);
1309 1 : fieldDefn = field->GetFieldDefn();
1310 2 : if ((strcmp(fieldDefn->GetName(), "QUV") == 0 &&
1311 1 : fieldDefn->GetSubfieldCount() == 6))
1312 : {
1313 : const char *pszSRC2 =
1314 0 : record->GetStringSubfield("QUV", 0, "SRC1", 0);
1315 0 : if (pszSRC2 != nullptr)
1316 : {
1317 0 : SetMetadataItem("SRP_PRODUCTVERSION", pszSRC2);
1318 : }
1319 : else
1320 : {
1321 : const char *pszSRC =
1322 0 : record->GetStringSubfield("QUV", 0, "SRC", 0);
1323 0 : if (pszSRC != nullptr)
1324 : {
1325 0 : SetMetadataItem("SRP_PRODUCTVERSION", pszSRC);
1326 : }
1327 : }
1328 : }
1329 : } /* End of LCF part */
1330 2 : }
1331 : }
1332 :
1333 : /************************************************************************/
1334 : /* GetIMGListFromGEN() */
1335 : /************************************************************************/
1336 :
1337 3 : char **SRPDataset::GetIMGListFromGEN(const char *pszFileName,
1338 : int *pnRecordIndex)
1339 : {
1340 3 : DDFRecord *record = nullptr;
1341 3 : DDFField *field = nullptr;
1342 3 : DDFFieldDefn *fieldDefn = nullptr;
1343 3 : int nFilenames = 0;
1344 3 : char **papszFileNames = nullptr;
1345 3 : int nRecordIndex = -1;
1346 :
1347 3 : if (pnRecordIndex)
1348 2 : *pnRecordIndex = -1;
1349 :
1350 6 : DDFModule module;
1351 3 : if (!module.Open(pszFileName, TRUE))
1352 0 : return nullptr;
1353 :
1354 : while (true)
1355 : {
1356 9 : nRecordIndex++;
1357 :
1358 9 : CPLPushErrorHandler(CPLQuietErrorHandler);
1359 9 : record = module.ReadRecord();
1360 9 : CPLPopErrorHandler();
1361 9 : CPLErrorReset();
1362 9 : if (record == nullptr)
1363 3 : break;
1364 :
1365 6 : if (record->GetFieldCount() >= 5)
1366 : {
1367 3 : field = record->GetField(0);
1368 3 : fieldDefn = field->GetFieldDefn();
1369 6 : if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
1370 3 : fieldDefn->GetSubfieldCount() == 2))
1371 : {
1372 0 : continue;
1373 : }
1374 :
1375 3 : const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
1376 3 : if (RTY == nullptr)
1377 0 : continue;
1378 : /* Ignore overviews */
1379 3 : if (strcmp(RTY, "OVV") == 0)
1380 0 : continue;
1381 :
1382 3 : if (strcmp(RTY, "GIN") != 0)
1383 0 : continue;
1384 :
1385 : /* make sure that the GEN file is part of a SRP dataset, not an ADRG
1386 : * dataset, by checking that the GEN field does not contain a NOW
1387 : * subfield */
1388 3 : const char *NWO = record->GetStringSubfield("GEN", 0, "NWO", 0);
1389 3 : if (NWO)
1390 : {
1391 0 : CSLDestroy(papszFileNames);
1392 0 : return nullptr;
1393 : }
1394 :
1395 3 : field = record->GetField(3);
1396 3 : if (field == nullptr)
1397 0 : continue;
1398 3 : fieldDefn = field->GetFieldDefn();
1399 :
1400 6 : if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
1401 3 : fieldDefn->GetSubfieldCount() == 15))
1402 : {
1403 0 : continue;
1404 : }
1405 :
1406 3 : const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
1407 3 : if (pszBAD == nullptr || strlen(pszBAD) != 12)
1408 0 : continue;
1409 6 : CPLString osBAD = pszBAD;
1410 : {
1411 3 : char *c = (char *)strchr(osBAD.c_str(), ' ');
1412 3 : if (c)
1413 0 : *c = 0;
1414 : }
1415 3 : CPLDebug("SRP", "BAD=%s", osBAD.c_str());
1416 :
1417 : /* Build full IMG file name from BAD value */
1418 6 : const CPLString osGENDir(CPLGetDirname(pszFileName));
1419 :
1420 : const CPLString osFileName =
1421 3 : CPLFormFilename(osGENDir.c_str(), osBAD.c_str(), nullptr);
1422 : VSIStatBufL sStatBuf;
1423 3 : if (VSIStatL(osFileName, &sStatBuf) == 0)
1424 : {
1425 3 : osBAD = osFileName;
1426 3 : CPLDebug("SRP", "Building IMG full file name : %s",
1427 : osBAD.c_str());
1428 : }
1429 : else
1430 : {
1431 0 : char **papszDirContent = nullptr;
1432 0 : if (strcmp(osGENDir.c_str(), "/vsimem") == 0)
1433 : {
1434 0 : CPLString osTmp = osGENDir + "/";
1435 0 : papszDirContent = VSIReadDir(osTmp);
1436 : }
1437 : else
1438 0 : papszDirContent = VSIReadDir(osGENDir);
1439 0 : char **ptrDir = papszDirContent;
1440 0 : while (ptrDir && *ptrDir)
1441 : {
1442 0 : if (EQUAL(*ptrDir, osBAD.c_str()))
1443 : {
1444 : osBAD =
1445 0 : CPLFormFilename(osGENDir.c_str(), *ptrDir, nullptr);
1446 0 : CPLDebug("SRP", "Building IMG full file name : %s",
1447 : osBAD.c_str());
1448 0 : break;
1449 : }
1450 0 : ptrDir++;
1451 : }
1452 0 : CSLDestroy(papszDirContent);
1453 : }
1454 :
1455 3 : if (nFilenames == 0 && pnRecordIndex)
1456 2 : *pnRecordIndex = nRecordIndex;
1457 :
1458 6 : papszFileNames = (char **)CPLRealloc(
1459 3 : papszFileNames, sizeof(char *) * (nFilenames + 2));
1460 3 : papszFileNames[nFilenames] = CPLStrdup(osBAD.c_str());
1461 3 : papszFileNames[nFilenames + 1] = nullptr;
1462 3 : nFilenames++;
1463 : }
1464 6 : }
1465 :
1466 3 : return papszFileNames;
1467 : }
1468 :
1469 : /************************************************************************/
1470 : /* Open() */
1471 : /************************************************************************/
1472 :
1473 28416 : GDALDataset *SRPDataset::Open(GDALOpenInfo *poOpenInfo)
1474 : {
1475 28416 : int nRecordIndex = -1;
1476 56831 : CPLString osGENFileName;
1477 56831 : CPLString osIMGFileName;
1478 28417 : int bFromSubdataset = FALSE;
1479 28417 : int bTHFWithSingleGEN = FALSE;
1480 :
1481 28417 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "SRP:"))
1482 : {
1483 : char **papszTokens =
1484 1 : CSLTokenizeString2(poOpenInfo->pszFilename + 4, ",", 0);
1485 1 : if (CSLCount(papszTokens) == 2)
1486 : {
1487 1 : osGENFileName = papszTokens[0];
1488 1 : osIMGFileName = papszTokens[1];
1489 1 : bFromSubdataset = TRUE;
1490 : }
1491 1 : CSLDestroy(papszTokens);
1492 : }
1493 : else
1494 : {
1495 28416 : if (poOpenInfo->nHeaderBytes < 500)
1496 26527 : return nullptr;
1497 1931 : CPLString osFileName(poOpenInfo->pszFilename);
1498 :
1499 1931 : if (EQUAL(CPLGetExtension(osFileName.c_str()), "THF"))
1500 : {
1501 :
1502 4 : CPLDebug("SRP", "Read THF");
1503 :
1504 4 : char **papszFileNames = GetGENListFromTHF(osFileName.c_str());
1505 4 : if (papszFileNames == nullptr)
1506 1 : return nullptr;
1507 6 : if (papszFileNames[1] == nullptr &&
1508 3 : CPLTestBool(CPLGetConfigOption(
1509 : "SRP_SINGLE_GEN_IN_THF_AS_DATASET", "TRUE")))
1510 : {
1511 2 : osFileName = papszFileNames[0];
1512 2 : CSLDestroy(papszFileNames);
1513 2 : bTHFWithSingleGEN = TRUE;
1514 : }
1515 : else
1516 : {
1517 1 : char **ptr = papszFileNames;
1518 1 : SRPDataset *poDS = new SRPDataset();
1519 1 : poDS->AddMetadatafromFromTHF(osFileName.c_str());
1520 2 : while (*ptr)
1521 : {
1522 1 : char **papszIMGFileNames = GetIMGListFromGEN(*ptr);
1523 1 : char **papszIMGIter = papszIMGFileNames;
1524 2 : while (papszIMGIter && *papszIMGIter)
1525 : {
1526 1 : poDS->AddSubDataset(*ptr, *papszIMGIter);
1527 1 : papszIMGIter++;
1528 : }
1529 1 : CSLDestroy(papszIMGFileNames);
1530 :
1531 1 : ptr++;
1532 : }
1533 1 : CSLDestroy(papszFileNames);
1534 1 : return poDS;
1535 : }
1536 : }
1537 :
1538 1929 : if (bTHFWithSingleGEN
1539 : #ifdef OPEN_GEN
1540 : || EQUAL(CPLGetExtension(osFileName.c_str()), "GEN")
1541 : #endif
1542 : )
1543 : {
1544 2 : osGENFileName = osFileName;
1545 :
1546 : char **papszFileNames =
1547 2 : GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex);
1548 2 : if (papszFileNames == nullptr)
1549 0 : return nullptr;
1550 2 : if (papszFileNames[1] == nullptr)
1551 : {
1552 2 : osIMGFileName = papszFileNames[0];
1553 2 : CSLDestroy(papszFileNames);
1554 : }
1555 : else
1556 : {
1557 0 : char **ptr = papszFileNames;
1558 0 : SRPDataset *poDS = new SRPDataset();
1559 0 : while (*ptr)
1560 : {
1561 0 : poDS->AddSubDataset(osFileName.c_str(), *ptr);
1562 0 : ptr++;
1563 : }
1564 0 : CSLDestroy(papszFileNames);
1565 0 : return poDS;
1566 : }
1567 : }
1568 :
1569 1929 : if (EQUAL(CPLGetExtension(osFileName.c_str()), "IMG"))
1570 : {
1571 :
1572 49 : osIMGFileName = osFileName;
1573 :
1574 49 : constexpr int nLeaderSize = 24;
1575 :
1576 49 : int i = 0; // Used after for.
1577 511 : for (; i < nLeaderSize; i++)
1578 : {
1579 498 : if (poOpenInfo->pabyHeader[i] < 32 ||
1580 472 : poOpenInfo->pabyHeader[i] > 126)
1581 40 : return nullptr;
1582 : }
1583 :
1584 13 : if (poOpenInfo->pabyHeader[5] != '1' &&
1585 13 : poOpenInfo->pabyHeader[5] != '2' &&
1586 4 : poOpenInfo->pabyHeader[5] != '3')
1587 4 : return nullptr;
1588 :
1589 9 : if (poOpenInfo->pabyHeader[6] != 'L')
1590 0 : return nullptr;
1591 9 : if (poOpenInfo->pabyHeader[8] != '1' &&
1592 9 : poOpenInfo->pabyHeader[8] != ' ')
1593 0 : return nullptr;
1594 :
1595 : // --------------------------------------------------------------------
1596 : // Find and open the .GEN file.
1597 : // --------------------------------------------------------------------
1598 : VSIStatBufL sStatBuf;
1599 :
1600 9 : CPLString basename = CPLGetBasename(osFileName);
1601 9 : if (basename.size() != 8)
1602 : {
1603 0 : CPLDebug("SRP", "Invalid basename file");
1604 0 : return nullptr;
1605 : }
1606 :
1607 9 : nRecordIndex = static_cast<int>(CPLScanLong(basename + 6, 2));
1608 :
1609 9 : CPLString path = CPLGetDirname(osFileName);
1610 9 : CPLString basename01 = ResetTo01(basename);
1611 9 : osFileName = CPLFormFilename(path, basename01, ".IMG");
1612 :
1613 9 : osFileName = CPLResetExtension(osFileName, "GEN");
1614 9 : if (VSIStatL(osFileName, &sStatBuf) != 0)
1615 : {
1616 0 : osFileName = CPLResetExtension(osFileName, "gen");
1617 0 : if (VSIStatL(osFileName, &sStatBuf) != 0)
1618 0 : return nullptr;
1619 : }
1620 :
1621 9 : osGENFileName = std::move(osFileName);
1622 : }
1623 : }
1624 :
1625 1890 : if (!osGENFileName.empty() && !osIMGFileName.empty())
1626 : {
1627 :
1628 12 : if (poOpenInfo->eAccess == GA_Update)
1629 : {
1630 0 : CPLError(CE_Failure, CPLE_NotSupported,
1631 : "The SRP driver does not support update access to existing"
1632 : " datasets.\n");
1633 12 : return nullptr;
1634 : }
1635 :
1636 12 : DDFModule module;
1637 12 : DDFRecord *record = nullptr;
1638 12 : if (nRecordIndex >= 0 && module.Open(osGENFileName.c_str(), TRUE))
1639 : {
1640 20 : for (int i = 0; i < nRecordIndex; i++)
1641 : {
1642 9 : CPLPushErrorHandler(CPLQuietErrorHandler);
1643 9 : record = module.ReadRecord();
1644 9 : CPLPopErrorHandler();
1645 9 : CPLErrorReset();
1646 9 : if (record == nullptr)
1647 0 : break;
1648 : }
1649 : }
1650 : SRPDataset *poDS =
1651 12 : OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record);
1652 :
1653 12 : if (poDS)
1654 : {
1655 : /* ---------------------------------------------------------- */
1656 : /* Initialize any PAM information. */
1657 : /* ---------------------------------------------------------- */
1658 12 : poDS->SetDescription(poOpenInfo->pszFilename);
1659 12 : poDS->TryLoadXML();
1660 :
1661 : /* ---------------------------------------------------------- */
1662 : /* Check for external overviews. */
1663 : /* ---------------------------------------------------------- */
1664 12 : if (bFromSubdataset)
1665 1 : poDS->oOvManager.Initialize(poDS, osIMGFileName.c_str());
1666 : else
1667 11 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1668 :
1669 12 : return poDS;
1670 : }
1671 : }
1672 :
1673 1878 : return nullptr;
1674 : }
1675 :
1676 : /************************************************************************/
1677 : /* GDALRegister_SRP() */
1678 : /************************************************************************/
1679 :
1680 1511 : void GDALRegister_SRP()
1681 :
1682 : {
1683 1511 : if (GDALGetDriverByName("SRP") != nullptr)
1684 295 : return;
1685 :
1686 1216 : GDALDriver *poDriver = new GDALDriver();
1687 :
1688 1216 : poDriver->SetDescription("SRP");
1689 1216 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1690 1216 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1691 1216 : "Standard Raster Product (ASRP/USRP)");
1692 1216 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/srp.html");
1693 1216 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "img");
1694 1216 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1695 1216 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1696 :
1697 1216 : poDriver->pfnOpen = SRPDataset::Open;
1698 :
1699 1216 : GetGDALDriverManager()->RegisterDriver(poDriver);
1700 : }
|