Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: EOSAT FAST Format reader
4 : * Purpose: Reads Landsat FAST-L7A, IRS 1C/1D
5 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Andrey Kiselev <dron@ak4719.spb.edu>
9 : * Copyright (c) 2007-2011, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 : #include "cpl_conv.h"
14 : #include "cpl_string.h"
15 : #include "gdal_frmts.h"
16 : #include "ogr_spatialref.h"
17 : #include "rawdataset.h"
18 :
19 : #include <algorithm>
20 :
21 : // constexpr int ADM_STD_HEADER_SIZE = 4608; // Format specification says it
22 : constexpr int ADM_HEADER_SIZE = 5000; // Should be 4608, but some vendors
23 : // ship broken large datasets.
24 : constexpr size_t ADM_MIN_HEADER_SIZE = 1536; // And sometimes it can be
25 : // even 1/3 of standard size.
26 :
27 : static const char ACQUISITION_DATE[] = "ACQUISITION DATE";
28 : constexpr int ACQUISITION_DATE_SIZE = 8;
29 :
30 : static const char SATELLITE_NAME[] = "SATELLITE";
31 : constexpr int SATELLITE_NAME_SIZE = 10;
32 :
33 : static const char SENSOR_NAME[] = "SENSOR";
34 : constexpr int SENSOR_NAME_SIZE = 10;
35 :
36 : static const char BANDS_PRESENT[] = "BANDS PRESENT";
37 : constexpr int BANDS_PRESENT_SIZE = 32;
38 :
39 : static const char FILENAME[] = "FILENAME";
40 : constexpr int FILENAME_SIZE = 29;
41 :
42 : static const char PIXELS[] = "PIXELS PER LINE";
43 : constexpr int PIXELS_SIZE = 5;
44 :
45 : static const char LINES1[] = "LINES PER BAND";
46 : static const char LINES2[] = "LINES PER IMAGE";
47 : constexpr int LINES_SIZE = 5;
48 :
49 : static const char BITS_PER_PIXEL[] = "OUTPUT BITS PER PIXEL";
50 : constexpr int BITS_PER_PIXEL_SIZE = 2;
51 :
52 : static const char PROJECTION_NAME[] = "MAP PROJECTION";
53 : constexpr int PROJECTION_NAME_SIZE = 4;
54 :
55 : static const char ELLIPSOID_NAME[] = "ELLIPSOID";
56 : constexpr int ELLIPSOID_NAME_SIZE = 18;
57 :
58 : static const char DATUM_NAME[] = "DATUM";
59 : constexpr int DATUM_NAME_SIZE = 6;
60 :
61 : static const char ZONE_NUMBER[] = "USGS MAP ZONE";
62 : constexpr int ZONE_NUMBER_SIZE = 6;
63 :
64 : static const char USGS_PARAMETERS[] = "USGS PROJECTION PARAMETERS";
65 :
66 : static const char CORNER_UPPER_LEFT[] = "UL ";
67 : static const char CORNER_UPPER_RIGHT[] = "UR ";
68 : static const char CORNER_LOWER_LEFT[] = "LL ";
69 : static const char CORNER_LOWER_RIGHT[] = "LR ";
70 : constexpr int CORNER_VALUE_SIZE = 13;
71 :
72 : constexpr int VALUE_SIZE = 24;
73 :
74 : enum FASTSatellite // Satellites:
75 : {
76 : LANDSAT, // Landsat 7
77 : IRS, // IRS 1C/1D
78 : FAST_UNKNOWN
79 : };
80 :
81 : constexpr int MAX_FILES = 7;
82 :
83 : /************************************************************************/
84 : /* ==================================================================== */
85 : /* FASTDataset */
86 : /* ==================================================================== */
87 : /************************************************************************/
88 :
89 : class FASTDataset final : public GDALPamDataset
90 : {
91 : double adfGeoTransform[6];
92 : OGRSpatialReference m_oSRS{};
93 :
94 : VSILFILE *fpHeader;
95 : CPLString apoChannelFilenames[MAX_FILES];
96 : VSILFILE *fpChannels[MAX_FILES];
97 : const char *pszFilename;
98 : char *pszDirname;
99 : GDALDataType eDataType;
100 : FASTSatellite iSatellite;
101 :
102 : int OpenChannel(const char *pszFilename, int iBand);
103 :
104 : CPL_DISALLOW_COPY_ASSIGN(FASTDataset)
105 :
106 : public:
107 : FASTDataset();
108 : ~FASTDataset() override;
109 :
110 : static GDALDataset *Open(GDALOpenInfo *);
111 :
112 : CPLErr GetGeoTransform(double *) override;
113 :
114 4 : const OGRSpatialReference *GetSpatialRef() const override
115 : {
116 4 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
117 : }
118 :
119 : VSILFILE *FOpenChannel(const char *, int iBand, int iFASTBand);
120 : void TryEuromap_IRS_1C_1D_ChannelNameConvention(int &l_nBands);
121 :
122 : char **GetFileList() override;
123 : };
124 :
125 : /************************************************************************/
126 : /* ==================================================================== */
127 : /* FASTDataset */
128 : /* ==================================================================== */
129 : /************************************************************************/
130 :
131 : /************************************************************************/
132 : /* FASTDataset() */
133 : /************************************************************************/
134 :
135 13 : FASTDataset::FASTDataset()
136 : : fpHeader(nullptr), pszFilename(nullptr), pszDirname(nullptr),
137 104 : eDataType(GDT_Unknown), iSatellite(FAST_UNKNOWN)
138 : {
139 13 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
140 13 : adfGeoTransform[0] = 0.0;
141 13 : adfGeoTransform[1] = 1.0;
142 13 : adfGeoTransform[2] = 0.0;
143 13 : adfGeoTransform[3] = 0.0;
144 13 : adfGeoTransform[4] = 0.0;
145 13 : adfGeoTransform[5] = 1.0;
146 : // TODO: Why does this not work?
147 : // fill( fpChannels, fpChannels + CPL_ARRAYSIZE(fpChannels), NULL );
148 104 : for (int i = 0; i < MAX_FILES; ++i)
149 91 : fpChannels[i] = nullptr;
150 13 : }
151 :
152 : /************************************************************************/
153 : /* ~FASTDataset() */
154 : /************************************************************************/
155 :
156 117 : FASTDataset::~FASTDataset()
157 :
158 : {
159 13 : FlushCache(true);
160 :
161 13 : CPLFree(pszDirname);
162 104 : for (int i = 0; i < MAX_FILES; i++)
163 91 : if (fpChannels[i])
164 29 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpChannels[i]));
165 13 : if (fpHeader != nullptr)
166 13 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpHeader));
167 26 : }
168 :
169 : /************************************************************************/
170 : /* GetGeoTransform() */
171 : /************************************************************************/
172 :
173 5 : CPLErr FASTDataset::GetGeoTransform(double *padfTransform)
174 :
175 : {
176 5 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
177 5 : return CE_None;
178 : }
179 :
180 : /************************************************************************/
181 : /* GetFileList() */
182 : /************************************************************************/
183 :
184 5 : char **FASTDataset::GetFileList()
185 : {
186 5 : char **papszFileList = GDALPamDataset::GetFileList();
187 :
188 35 : for (int i = 0; i < 6; i++)
189 : {
190 30 : if (!apoChannelFilenames[i].empty())
191 : papszFileList =
192 10 : CSLAddString(papszFileList, apoChannelFilenames[i].c_str());
193 : }
194 :
195 5 : return papszFileList;
196 : }
197 :
198 : /************************************************************************/
199 : /* OpenChannel() */
200 : /************************************************************************/
201 :
202 78 : int FASTDataset::OpenChannel(const char *pszFilenameIn, int iBand)
203 : {
204 78 : CPLAssert(fpChannels[iBand] == nullptr);
205 78 : fpChannels[iBand] = VSIFOpenL(pszFilenameIn, "rb");
206 78 : if (fpChannels[iBand])
207 29 : apoChannelFilenames[iBand] = pszFilenameIn;
208 78 : return fpChannels[iBand] != nullptr;
209 : }
210 :
211 : /************************************************************************/
212 : /* FOpenChannel() */
213 : /************************************************************************/
214 :
215 49 : VSILFILE *FASTDataset::FOpenChannel(const char *pszBandname, int iBand,
216 : int iFASTBand)
217 : {
218 49 : const char *pszChannelFilename = nullptr;
219 49 : char *pszPrefix = CPLStrdup(CPLGetBasename(pszFilename));
220 49 : char *pszSuffix = CPLStrdup(CPLGetExtension(pszFilename));
221 :
222 49 : fpChannels[iBand] = nullptr;
223 :
224 49 : switch (iSatellite)
225 : {
226 42 : case LANDSAT:
227 42 : if (pszBandname && !EQUAL(pszBandname, ""))
228 : {
229 : pszChannelFilename =
230 8 : CPLFormCIFilename(pszDirname, pszBandname, nullptr);
231 8 : if (OpenChannel(pszChannelFilename, iBand))
232 8 : break;
233 0 : pszChannelFilename = CPLFormFilename(
234 0 : pszDirname, CPLSPrintf("%s.b%02d", pszPrefix, iFASTBand),
235 : nullptr);
236 0 : CPL_IGNORE_RET_VAL(OpenChannel(pszChannelFilename, iBand));
237 : }
238 34 : break;
239 7 : case IRS:
240 : default:
241 7 : pszChannelFilename = CPLFormFilename(
242 7 : pszDirname, CPLSPrintf("%s.%d", pszPrefix, iFASTBand),
243 : pszSuffix);
244 7 : if (OpenChannel(pszChannelFilename, iBand))
245 0 : break;
246 7 : pszChannelFilename = CPLFormFilename(
247 7 : pszDirname, CPLSPrintf("IMAGERY%d", iFASTBand), pszSuffix);
248 7 : if (OpenChannel(pszChannelFilename, iBand))
249 0 : break;
250 7 : pszChannelFilename = CPLFormFilename(
251 7 : pszDirname, CPLSPrintf("imagery%d", iFASTBand), pszSuffix);
252 7 : if (OpenChannel(pszChannelFilename, iBand))
253 0 : break;
254 7 : pszChannelFilename = CPLFormFilename(
255 7 : pszDirname, CPLSPrintf("IMAGERY%d.DAT", iFASTBand), nullptr);
256 7 : if (OpenChannel(pszChannelFilename, iBand))
257 0 : break;
258 7 : pszChannelFilename = CPLFormFilename(
259 7 : pszDirname, CPLSPrintf("imagery%d.dat", iFASTBand), nullptr);
260 7 : if (OpenChannel(pszChannelFilename, iBand))
261 0 : break;
262 7 : pszChannelFilename = CPLFormFilename(
263 7 : pszDirname, CPLSPrintf("IMAGERY%d.dat", iFASTBand), nullptr);
264 7 : if (OpenChannel(pszChannelFilename, iBand))
265 0 : break;
266 7 : pszChannelFilename = CPLFormFilename(
267 7 : pszDirname, CPLSPrintf("imagery%d.DAT", iFASTBand), nullptr);
268 7 : if (OpenChannel(pszChannelFilename, iBand))
269 0 : break;
270 7 : pszChannelFilename = CPLFormFilename(
271 7 : pszDirname, CPLSPrintf("BAND%d", iFASTBand), pszSuffix);
272 7 : if (OpenChannel(pszChannelFilename, iBand))
273 7 : break;
274 0 : pszChannelFilename = CPLFormFilename(
275 0 : pszDirname, CPLSPrintf("band%d", iFASTBand), pszSuffix);
276 0 : if (OpenChannel(pszChannelFilename, iBand))
277 0 : break;
278 0 : pszChannelFilename = CPLFormFilename(
279 0 : pszDirname, CPLSPrintf("BAND%d.DAT", iFASTBand), nullptr);
280 0 : if (OpenChannel(pszChannelFilename, iBand))
281 0 : break;
282 0 : pszChannelFilename = CPLFormFilename(
283 0 : pszDirname, CPLSPrintf("band%d.dat", iFASTBand), nullptr);
284 0 : if (OpenChannel(pszChannelFilename, iBand))
285 0 : break;
286 0 : pszChannelFilename = CPLFormFilename(
287 0 : pszDirname, CPLSPrintf("BAND%d.dat", iFASTBand), nullptr);
288 0 : if (OpenChannel(pszChannelFilename, iBand))
289 0 : break;
290 0 : pszChannelFilename = CPLFormFilename(
291 0 : pszDirname, CPLSPrintf("band%d.DAT", iFASTBand), nullptr);
292 0 : CPL_IGNORE_RET_VAL(OpenChannel(pszChannelFilename, iBand));
293 0 : break;
294 : }
295 :
296 49 : CPLDebug("FAST", "Band %d filename=%s", iBand + 1,
297 : pszChannelFilename ? pszChannelFilename : "(null)");
298 :
299 49 : CPLFree(pszPrefix);
300 49 : CPLFree(pszSuffix);
301 49 : return fpChannels[iBand];
302 : }
303 :
304 : /************************************************************************/
305 : /* TryEuromap_IRS_1C_1D_ChannelNameConvention() */
306 : /************************************************************************/
307 :
308 6 : void FASTDataset::TryEuromap_IRS_1C_1D_ChannelNameConvention(int &l_nBands)
309 : {
310 : // Filename convention explained in:
311 : // http://www.euromap.de/download/em_names.pdf
312 :
313 6 : char chLastLetterHeader = pszFilename[strlen(pszFilename) - 1];
314 6 : if (EQUAL(GetMetadataItem("SENSOR"), "PAN"))
315 : {
316 : /* Converting upper-case to lower case */
317 2 : if (chLastLetterHeader >= 'A' && chLastLetterHeader <= 'M')
318 0 : chLastLetterHeader += 'a' - 'A';
319 :
320 2 : if (chLastLetterHeader >= 'a' && chLastLetterHeader <= 'j')
321 : {
322 2 : const char chLastLetterData = chLastLetterHeader - 'a' + '0';
323 2 : char *pszChannelFilename = CPLStrdup(pszFilename);
324 2 : pszChannelFilename[strlen(pszChannelFilename) - 1] =
325 : chLastLetterData;
326 2 : if (OpenChannel(pszChannelFilename, 0))
327 2 : l_nBands++;
328 : else
329 0 : CPLDebug("FAST", "Could not find %s", pszChannelFilename);
330 2 : CPLFree(pszChannelFilename);
331 : }
332 0 : else if (chLastLetterHeader >= 'k' && chLastLetterHeader <= 'm')
333 : {
334 0 : const char chLastLetterData = chLastLetterHeader - 'k' + 'n';
335 0 : char *pszChannelFilename = CPLStrdup(pszFilename);
336 0 : pszChannelFilename[strlen(pszChannelFilename) - 1] =
337 : chLastLetterData;
338 0 : if (OpenChannel(pszChannelFilename, 0))
339 : {
340 0 : l_nBands++;
341 : }
342 : else
343 : {
344 : /* Trying upper-case */
345 0 : pszChannelFilename[strlen(pszChannelFilename) - 1] =
346 0 : chLastLetterData - 'a' + 'A';
347 0 : if (OpenChannel(pszChannelFilename, 0))
348 0 : l_nBands++;
349 : else
350 0 : CPLDebug("FAST", "Could not find %s", pszChannelFilename);
351 : }
352 0 : CPLFree(pszChannelFilename);
353 : }
354 : else
355 : {
356 0 : CPLDebug(
357 : "FAST",
358 : "Unknown last letter (%c) for a IRS PAN Euromap FAST dataset",
359 : chLastLetterHeader);
360 : }
361 : }
362 4 : else if (EQUAL(GetMetadataItem("SENSOR"), "LISS3"))
363 : {
364 2 : const char apchLISSFilenames[7][5] = {
365 : {'0', '2', '3', '4', '5'}, {'6', '7', '8', '9', 'a'},
366 : {'b', 'c', 'd', 'e', 'f'}, {'g', 'h', 'i', 'j', 'k'},
367 : {'l', 'm', 'n', 'o', 'p'}, {'q', 'r', 's', 't', 'u'},
368 : {'v', 'w', 'x', 'y', 'z'}};
369 :
370 2 : int i = 0;
371 10 : for (; i < 7; i++)
372 : {
373 10 : if (chLastLetterHeader == apchLISSFilenames[i][0] ||
374 8 : (apchLISSFilenames[i][0] >= 'a' &&
375 4 : apchLISSFilenames[i][0] <= 'z' &&
376 4 : (apchLISSFilenames[i][0] - chLastLetterHeader == 0 ||
377 4 : apchLISSFilenames[i][0] - chLastLetterHeader == 32)))
378 : {
379 10 : for (int j = 0; j < 4; j++)
380 : {
381 8 : char *pszChannelFilename = CPLStrdup(pszFilename);
382 8 : pszChannelFilename[strlen(pszChannelFilename) - 1] =
383 8 : apchLISSFilenames[i][j + 1];
384 8 : if (OpenChannel(pszChannelFilename, l_nBands))
385 8 : l_nBands++;
386 0 : else if (apchLISSFilenames[i][j + 1] >= 'a' &&
387 0 : apchLISSFilenames[i][j + 1] <= 'z')
388 : {
389 : /* Trying upper-case */
390 0 : pszChannelFilename[strlen(pszChannelFilename) - 1] =
391 0 : apchLISSFilenames[i][j + 1] - 'a' + 'A';
392 0 : if (OpenChannel(pszChannelFilename, l_nBands))
393 : {
394 0 : l_nBands++;
395 : }
396 : else
397 : {
398 0 : CPLDebug("FAST", "Could not find %s",
399 : pszChannelFilename);
400 : }
401 : }
402 : else
403 : {
404 0 : CPLDebug("FAST", "Could not find %s",
405 : pszChannelFilename);
406 : }
407 8 : CPLFree(pszChannelFilename);
408 : }
409 2 : break;
410 : }
411 : }
412 2 : if (i == 7)
413 : {
414 0 : CPLDebug(
415 : "FAST",
416 : "Unknown last letter (%c) for a IRS LISS3 Euromap FAST dataset",
417 : chLastLetterHeader);
418 : }
419 : }
420 2 : else if (EQUAL(GetMetadataItem("SENSOR"), "WIFS"))
421 : {
422 2 : if (chLastLetterHeader == '0')
423 : {
424 6 : for (int j = 0; j < 2; j++)
425 : {
426 4 : char *pszChannelFilename = CPLStrdup(pszFilename);
427 4 : pszChannelFilename[strlen(pszChannelFilename) - 1] =
428 4 : static_cast<char>('1' + j);
429 4 : if (OpenChannel(pszChannelFilename, l_nBands))
430 : {
431 4 : l_nBands++;
432 : }
433 : else
434 : {
435 0 : CPLDebug("FAST", "Could not find %s", pszChannelFilename);
436 : }
437 4 : CPLFree(pszChannelFilename);
438 : }
439 : }
440 : else
441 : {
442 0 : CPLDebug(
443 : "FAST",
444 : "Unknown last letter (%c) for a IRS WIFS Euromap FAST dataset",
445 : chLastLetterHeader);
446 : }
447 : }
448 : else
449 : {
450 0 : CPLAssert(false);
451 : }
452 6 : }
453 :
454 : /************************************************************************/
455 : /* GetValue() */
456 : /************************************************************************/
457 :
458 131 : static char *GetValue(const char *pszString, const char *pszName,
459 : int iValueSize, int bNormalize)
460 : {
461 131 : char *pszTemp = strstr(const_cast<char *>(pszString), pszName);
462 131 : if (pszTemp)
463 : {
464 : // Skip the parameter name
465 120 : pszTemp += strlen(pszName);
466 : // Skip whitespaces and equal signs
467 238 : while (*pszTemp == ' ')
468 118 : pszTemp++;
469 240 : while (*pszTemp == '=')
470 120 : pszTemp++;
471 :
472 120 : pszTemp = CPLScanString(pszTemp, iValueSize, TRUE, bNormalize);
473 : }
474 :
475 131 : return pszTemp;
476 : }
477 :
478 : /************************************************************************/
479 : /* USGSMnemonicToCode() */
480 : /************************************************************************/
481 :
482 12 : static long USGSMnemonicToCode(const char *pszMnemonic)
483 : {
484 12 : if (EQUAL(pszMnemonic, "UTM"))
485 2 : return 1L;
486 10 : else if (EQUAL(pszMnemonic, "LCC"))
487 2 : return 4L;
488 8 : else if (EQUAL(pszMnemonic, "PS"))
489 0 : return 6L;
490 8 : else if (EQUAL(pszMnemonic, "PC"))
491 0 : return 7L;
492 8 : else if (EQUAL(pszMnemonic, "TM"))
493 6 : return 9L;
494 2 : else if (EQUAL(pszMnemonic, "OM"))
495 0 : return 20L;
496 2 : else if (EQUAL(pszMnemonic, "SOM"))
497 2 : return 22L;
498 : else
499 0 : return 1L; // UTM by default
500 : }
501 :
502 : /************************************************************************/
503 : /* USGSEllipsoidToCode() */
504 : /************************************************************************/
505 :
506 13 : static long USGSEllipsoidToCode(const char *pszMnemonic)
507 : {
508 13 : if (EQUAL(pszMnemonic, "CLARKE_1866"))
509 0 : return 0L;
510 13 : else if (EQUAL(pszMnemonic, "CLARKE_1880"))
511 0 : return 1L;
512 13 : else if (EQUAL(pszMnemonic, "BESSEL"))
513 0 : return 2L;
514 13 : else if (EQUAL(pszMnemonic, "INTERNATL_1967"))
515 0 : return 3L;
516 13 : else if (EQUAL(pszMnemonic, "INTERNATL_1909"))
517 4 : return 4L;
518 9 : else if (EQUAL(pszMnemonic, "WGS72") || EQUAL(pszMnemonic, "WGS_72"))
519 0 : return 5L;
520 9 : else if (EQUAL(pszMnemonic, "EVEREST"))
521 0 : return 6L;
522 9 : else if (EQUAL(pszMnemonic, "WGS66") || EQUAL(pszMnemonic, "WGS_66"))
523 0 : return 7L;
524 9 : else if (EQUAL(pszMnemonic, "GRS_80"))
525 0 : return 8L;
526 9 : else if (EQUAL(pszMnemonic, "AIRY"))
527 0 : return 9L;
528 9 : else if (EQUAL(pszMnemonic, "MODIFIED_EVEREST"))
529 0 : return 10L;
530 9 : else if (EQUAL(pszMnemonic, "MODIFIED_AIRY"))
531 0 : return 11L;
532 9 : else if (EQUAL(pszMnemonic, "WGS84") || EQUAL(pszMnemonic, "WGS_84"))
533 8 : return 12L;
534 1 : else if (EQUAL(pszMnemonic, "SOUTHEAST_ASIA"))
535 0 : return 13L;
536 1 : else if (EQUAL(pszMnemonic, "AUSTRALIAN_NATL"))
537 0 : return 14L;
538 1 : else if (EQUAL(pszMnemonic, "KRASSOVSKY"))
539 0 : return 15L;
540 1 : else if (EQUAL(pszMnemonic, "HOUGH"))
541 0 : return 16L;
542 1 : else if (EQUAL(pszMnemonic, "MERCURY_1960"))
543 0 : return 17L;
544 1 : else if (EQUAL(pszMnemonic, "MOD_MERC_1968"))
545 0 : return 18L;
546 1 : else if (EQUAL(pszMnemonic, "6370997_M_SPHERE"))
547 0 : return 19L;
548 : else
549 1 : return 0L;
550 : }
551 :
552 : /************************************************************************/
553 : /* Open() */
554 : /************************************************************************/
555 :
556 30625 : GDALDataset *FASTDataset::Open(GDALOpenInfo *poOpenInfo)
557 :
558 : {
559 30625 : if (poOpenInfo->nHeaderBytes < 1024 || poOpenInfo->fpL == nullptr)
560 28625 : return nullptr;
561 :
562 2000 : if (!EQUALN((const char *)poOpenInfo->pabyHeader + 52,
563 1988 : "ACQUISITION DATE =", 18) &&
564 1988 : !EQUALN((const char *)poOpenInfo->pabyHeader + 36,
565 : "ACQUISITION DATE =", 18))
566 1987 : return nullptr;
567 :
568 : /* -------------------------------------------------------------------- */
569 : /* Create a corresponding GDALDataset. */
570 : /* -------------------------------------------------------------------- */
571 26 : auto poDS = std::make_unique<FASTDataset>();
572 :
573 13 : std::swap(poDS->fpHeader, poOpenInfo->fpL);
574 :
575 13 : poDS->pszFilename = poOpenInfo->pszFilename;
576 13 : poDS->pszDirname = CPLStrdup(CPLGetDirname(poOpenInfo->pszFilename));
577 :
578 : /* -------------------------------------------------------------------- */
579 : /* Read the administrative record. */
580 : /* -------------------------------------------------------------------- */
581 26 : std::string osHeader;
582 13 : osHeader.resize(ADM_HEADER_SIZE);
583 :
584 13 : size_t nBytesRead = 0;
585 13 : if (VSIFSeekL(poDS->fpHeader, 0, SEEK_SET) >= 0)
586 : nBytesRead =
587 13 : VSIFReadL(&osHeader[0], 1, ADM_HEADER_SIZE, poDS->fpHeader);
588 13 : if (nBytesRead < ADM_MIN_HEADER_SIZE)
589 : {
590 0 : CPLDebug("FAST", "Header file too short. Reading failed");
591 0 : return nullptr;
592 : }
593 13 : osHeader.resize(nBytesRead);
594 13 : const char *pszHeader = osHeader.c_str();
595 :
596 : // Read acquisition date
597 : {
598 : char *pszTemp =
599 13 : GetValue(pszHeader, ACQUISITION_DATE, ACQUISITION_DATE_SIZE, TRUE);
600 13 : if (pszTemp == nullptr)
601 : {
602 0 : CPLDebug("FAST", "Cannot get ACQUISITION_DATE, using empty value.");
603 0 : pszTemp = CPLStrdup("");
604 : }
605 13 : poDS->SetMetadataItem("ACQUISITION_DATE", pszTemp);
606 13 : CPLFree(pszTemp);
607 : }
608 :
609 : // Read satellite name (will read the first one only)
610 : {
611 : char *pszTemp =
612 13 : GetValue(pszHeader, SATELLITE_NAME, SATELLITE_NAME_SIZE, TRUE);
613 13 : if (pszTemp == nullptr)
614 : {
615 0 : CPLDebug("FAST", "Cannot get SATELLITE_NAME, using empty value.");
616 0 : pszTemp = CPLStrdup("");
617 : }
618 13 : poDS->SetMetadataItem("SATELLITE", pszTemp);
619 13 : if (STARTS_WITH_CI(pszTemp, "LANDSAT"))
620 6 : poDS->iSatellite = LANDSAT;
621 : // TODO(schwehr): Was this a bug that both are IRS?
622 : // else if ( STARTS_WITH_CI(pszTemp, "IRS") )
623 : // poDS->iSatellite = IRS;
624 : else
625 7 : poDS->iSatellite =
626 : IRS; // TODO(schwehr): Should this be FAST_UNKNOWN?
627 13 : CPLFree(pszTemp);
628 : }
629 :
630 : // Read sensor name (will read the first one only)
631 : {
632 : char *pszTemp =
633 13 : GetValue(pszHeader, SENSOR_NAME, SENSOR_NAME_SIZE, TRUE);
634 13 : if (pszTemp == nullptr)
635 : {
636 1 : CPLDebug("FAST", "Cannot get SENSOR_NAME, using empty value.");
637 1 : pszTemp = CPLStrdup("");
638 : }
639 13 : poDS->SetMetadataItem("SENSOR", pszTemp);
640 13 : CPLFree(pszTemp);
641 : }
642 :
643 : // Read filenames
644 13 : int l_nBands = 0;
645 :
646 13 : if (strstr(pszHeader, FILENAME) == nullptr)
647 : {
648 7 : if (strstr(pszHeader, "GENERATING AGENCY =EUROMAP"))
649 : {
650 : // If we don't find the FILENAME field, let's try with the Euromap
651 : // PAN / LISS3 / WIFS IRS filename convention.
652 6 : if ((EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1C") ||
653 12 : EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1D")) &&
654 6 : (EQUAL(poDS->GetMetadataItem("SENSOR"), "PAN") ||
655 4 : EQUAL(poDS->GetMetadataItem("SENSOR"), "LISS3") ||
656 2 : EQUAL(poDS->GetMetadataItem("SENSOR"), "WIFS")))
657 : {
658 6 : poDS->TryEuromap_IRS_1C_1D_ChannelNameConvention(l_nBands);
659 : }
660 0 : else if (EQUAL(poDS->GetMetadataItem("SATELLITE"), "CARTOSAT-1") &&
661 0 : (EQUAL(poDS->GetMetadataItem("SENSOR"), "FORE") ||
662 0 : EQUAL(poDS->GetMetadataItem("SENSOR"), "AFT")))
663 : {
664 : // See appendix F in
665 : // http://www.euromap.de/download/p5fast_20050301.pdf
666 0 : const CPLString osSuffix = CPLGetExtension(poDS->pszFilename);
667 0 : const char *papszBasenames[] = {"BANDF", "bandf", "BANDA",
668 : "banda"};
669 0 : for (int i = 0; i < 4; i++)
670 : {
671 : const CPLString osChannelFilename = CPLFormFilename(
672 0 : poDS->pszDirname, papszBasenames[i], osSuffix);
673 0 : if (poDS->OpenChannel(osChannelFilename, 0))
674 : {
675 0 : l_nBands = 1;
676 0 : break;
677 : }
678 : }
679 : }
680 0 : else if (EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS P6"))
681 : {
682 : // If BANDS_PRESENT="2345", the file bands are "BAND2.DAT",
683 : // "BAND3.DAT", etc.
684 0 : char *pszTemp = GetValue(pszHeader, BANDS_PRESENT,
685 : BANDS_PRESENT_SIZE, TRUE);
686 0 : if (pszTemp)
687 : {
688 0 : for (int i = 0; pszTemp[i] != '\0'; i++)
689 : {
690 0 : if (pszTemp[i] >= '2' && pszTemp[i] <= '5')
691 : {
692 0 : if (poDS->FOpenChannel(poDS->pszFilename, l_nBands,
693 0 : pszTemp[i] - '0'))
694 0 : l_nBands++;
695 : }
696 : }
697 0 : CPLFree(pszTemp);
698 : }
699 : }
700 : }
701 : }
702 :
703 : // If the previous lookup for band files didn't success, fallback to the
704 : // standard way of finding them, either by the FILENAME field, either with
705 : // the usual patterns like bandX.dat, etc.
706 13 : if (!l_nBands)
707 : {
708 7 : const char *pszTemp = pszHeader;
709 56 : for (int i = 0; i < 7; i++)
710 : {
711 49 : char *pszFilename = nullptr;
712 49 : if (pszTemp)
713 43 : pszTemp = strstr(pszTemp, FILENAME);
714 49 : if (pszTemp)
715 : {
716 : // Skip the parameter name
717 36 : pszTemp += strlen(FILENAME);
718 : // Skip whitespaces and equal signs
719 72 : while (*pszTemp == ' ')
720 36 : pszTemp++;
721 72 : while (*pszTemp == '=')
722 36 : pszTemp++;
723 : pszFilename =
724 36 : CPLScanString(pszTemp, FILENAME_SIZE, TRUE, FALSE);
725 : }
726 : else
727 13 : pszTemp = nullptr;
728 49 : if (poDS->FOpenChannel(pszFilename, l_nBands, l_nBands + 1))
729 15 : l_nBands++;
730 49 : if (pszFilename)
731 36 : CPLFree(pszFilename);
732 : }
733 : }
734 :
735 13 : if (!l_nBands)
736 : {
737 0 : CPLError(CE_Failure, CPLE_NotSupported,
738 : "Failed to find and open band data files.");
739 0 : return nullptr;
740 : }
741 :
742 : // Read number of pixels/lines and bit depth
743 : {
744 13 : char *pszTemp = GetValue(pszHeader, PIXELS, PIXELS_SIZE, FALSE);
745 13 : if (pszTemp)
746 : {
747 13 : poDS->nRasterXSize = atoi(pszTemp);
748 13 : CPLFree(pszTemp);
749 : }
750 : else
751 : {
752 0 : CPLDebug("FAST", "Failed to find number of pixels in line.");
753 0 : return nullptr;
754 : }
755 : }
756 :
757 : {
758 13 : char *pszTemp = GetValue(pszHeader, LINES1, LINES_SIZE, FALSE);
759 13 : if (!pszTemp)
760 1 : pszTemp = GetValue(pszHeader, LINES2, LINES_SIZE, FALSE);
761 13 : if (pszTemp)
762 : {
763 13 : poDS->nRasterYSize = atoi(pszTemp);
764 13 : CPLFree(pszTemp);
765 : }
766 : else
767 : {
768 0 : CPLDebug("FAST", "Failed to find number of lines in raster.");
769 0 : return nullptr;
770 : }
771 : }
772 :
773 13 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
774 : {
775 0 : return nullptr;
776 : }
777 :
778 : {
779 : char *pszTemp =
780 13 : GetValue(pszHeader, BITS_PER_PIXEL, BITS_PER_PIXEL_SIZE, FALSE);
781 13 : if (pszTemp)
782 : {
783 12 : switch (atoi(pszTemp))
784 : {
785 12 : case 8:
786 : default:
787 12 : poDS->eDataType = GDT_Byte;
788 12 : break;
789 : // For a strange reason, some Euromap products declare 10 bits
790 : // output, but are 16 bits.
791 0 : case 10:
792 : case 16:
793 0 : poDS->eDataType = GDT_UInt16;
794 0 : break;
795 : }
796 12 : CPLFree(pszTemp);
797 : }
798 : else
799 : {
800 1 : poDS->eDataType = GDT_Byte;
801 : }
802 : }
803 :
804 : /* -------------------------------------------------------------------- */
805 : /* Read radiometric record. */
806 : /* -------------------------------------------------------------------- */
807 : {
808 13 : const char *pszFirst = nullptr;
809 13 : const char *pszSecond = nullptr;
810 :
811 : // Read gains and biases. This is a trick!
812 : const char *pszTemp =
813 13 : strstr(pszHeader, "BIASES"); // It may be "BIASES AND GAINS"
814 : // or "GAINS AND BIASES"
815 13 : const char *pszGains = strstr(pszHeader, "GAINS");
816 13 : if (pszTemp == nullptr || pszGains == nullptr)
817 : {
818 0 : CPLDebug("FAST", "No BIASES and/or GAINS");
819 0 : return nullptr;
820 : }
821 13 : if (pszTemp > pszGains)
822 : {
823 5 : pszFirst = "GAIN%d";
824 5 : pszSecond = "BIAS%d";
825 : }
826 : else
827 : {
828 8 : pszFirst = "BIAS%d";
829 8 : pszSecond = "GAIN%d";
830 : }
831 :
832 : // Now search for the first number occurrence after that string.
833 42 : for (int i = 1; i <= l_nBands; i++)
834 : {
835 29 : char *pszValue = nullptr;
836 29 : size_t nValueLen = VALUE_SIZE;
837 :
838 29 : pszTemp = strpbrk(pszTemp, "-.0123456789");
839 29 : if (pszTemp)
840 : {
841 29 : nValueLen = strspn(pszTemp, "+-.0123456789");
842 29 : pszValue = CPLScanString(pszTemp, static_cast<int>(nValueLen),
843 : TRUE, TRUE);
844 29 : poDS->SetMetadataItem(CPLSPrintf(pszFirst, i), pszValue);
845 29 : CPLFree(pszValue);
846 : }
847 : else
848 : {
849 0 : return nullptr;
850 : }
851 29 : pszTemp += nValueLen;
852 29 : pszTemp = strpbrk(pszTemp, "-.0123456789");
853 29 : if (pszTemp)
854 : {
855 29 : nValueLen = strspn(pszTemp, "+-.0123456789");
856 29 : pszValue = CPLScanString(pszTemp, static_cast<int>(nValueLen),
857 : TRUE, TRUE);
858 29 : poDS->SetMetadataItem(CPLSPrintf(pszSecond, i), pszValue);
859 29 : CPLFree(pszValue);
860 : }
861 : else
862 : {
863 0 : return nullptr;
864 : }
865 29 : pszTemp += nValueLen;
866 : }
867 : }
868 :
869 : /* -------------------------------------------------------------------- */
870 : /* Read geometric record. */
871 : /* -------------------------------------------------------------------- */
872 : // Coordinates of pixel's centers
873 13 : double dfULX = 0.0;
874 13 : double dfULY = 0.0;
875 13 : double dfURX = 0.0;
876 13 : double dfURY = 0.0;
877 13 : double dfLLX = 0.0;
878 13 : double dfLLY = 0.0;
879 13 : double dfLRX = 0.0;
880 13 : double dfLRY = 0.0;
881 :
882 : // Read projection name
883 13 : long iProjSys = 0;
884 : {
885 : char *pszTemp =
886 13 : GetValue(pszHeader, PROJECTION_NAME, PROJECTION_NAME_SIZE, FALSE);
887 13 : if (pszTemp && !EQUAL(pszTemp, ""))
888 12 : iProjSys = USGSMnemonicToCode(pszTemp);
889 : else
890 1 : iProjSys = 1L; // UTM by default
891 13 : CPLFree(pszTemp);
892 : }
893 :
894 : // Read ellipsoid name
895 13 : long iDatum = 0; // Clarke, 1866 (NAD1927) by default.
896 : {
897 : char *pszTemp =
898 13 : GetValue(pszHeader, ELLIPSOID_NAME, ELLIPSOID_NAME_SIZE, FALSE);
899 13 : if (pszTemp && !EQUAL(pszTemp, ""))
900 13 : iDatum = USGSEllipsoidToCode(pszTemp);
901 13 : CPLFree(pszTemp);
902 : }
903 :
904 : // Read zone number.
905 13 : long iZone = 0;
906 : {
907 : char *pszTemp =
908 13 : GetValue(pszHeader, ZONE_NUMBER, ZONE_NUMBER_SIZE, FALSE);
909 13 : if (pszTemp && !EQUAL(pszTemp, ""))
910 7 : iZone = atoi(pszTemp);
911 13 : CPLFree(pszTemp);
912 : }
913 :
914 : // Read 15 USGS projection parameters
915 13 : double adfProjParams[15] = {0.0};
916 : {
917 13 : const char *pszTemp = strstr(pszHeader, USGS_PARAMETERS);
918 13 : if (pszTemp && !EQUAL(pszTemp, ""))
919 : {
920 13 : pszTemp += strlen(USGS_PARAMETERS);
921 208 : for (int i = 0; i < 15; i++)
922 : {
923 195 : pszTemp = strpbrk(pszTemp, "-.0123456789");
924 195 : if (pszTemp)
925 : {
926 195 : adfProjParams[i] = CPLScanDouble(pszTemp, VALUE_SIZE);
927 195 : pszTemp = strpbrk(pszTemp, " \t");
928 : }
929 195 : if (pszTemp == nullptr)
930 : {
931 0 : return nullptr;
932 : }
933 : }
934 : }
935 : }
936 :
937 : // Coordinates should follow the word "PROJECTION", otherwise we can
938 : // be confused by other occurrences of the corner keywords.
939 13 : const char *pszGeomRecord = strstr(pszHeader, "PROJECTION");
940 13 : if (pszGeomRecord)
941 : {
942 : // Read corner coordinates
943 13 : const char *pszTemp = strstr(pszGeomRecord, CORNER_UPPER_LEFT);
944 13 : if (pszTemp && !EQUAL(pszTemp, "") &&
945 13 : strlen(pszTemp) >=
946 : strlen(CORNER_UPPER_LEFT) + 28 + CORNER_VALUE_SIZE + 1)
947 : {
948 13 : pszTemp += strlen(CORNER_UPPER_LEFT) + 28;
949 13 : dfULX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
950 13 : pszTemp += CORNER_VALUE_SIZE + 1;
951 13 : dfULY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
952 : }
953 :
954 13 : pszTemp = strstr(pszGeomRecord, CORNER_UPPER_RIGHT);
955 13 : if (pszTemp && !EQUAL(pszTemp, "") &&
956 13 : strlen(pszTemp) >=
957 : strlen(CORNER_UPPER_RIGHT) + 28 + CORNER_VALUE_SIZE + 1)
958 : {
959 13 : pszTemp += strlen(CORNER_UPPER_RIGHT) + 28;
960 13 : dfURX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
961 13 : pszTemp += CORNER_VALUE_SIZE + 1;
962 13 : dfURY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
963 : }
964 :
965 13 : pszTemp = strstr(pszGeomRecord, CORNER_LOWER_LEFT);
966 13 : if (pszTemp && !EQUAL(pszTemp, "") &&
967 13 : strlen(pszTemp) >=
968 : strlen(CORNER_LOWER_LEFT) + 28 + CORNER_VALUE_SIZE + 1)
969 : {
970 13 : pszTemp += strlen(CORNER_LOWER_LEFT) + 28;
971 13 : dfLLX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
972 13 : pszTemp += CORNER_VALUE_SIZE + 1;
973 13 : dfLLY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
974 : }
975 :
976 13 : pszTemp = strstr(pszGeomRecord, CORNER_LOWER_RIGHT);
977 13 : if (pszTemp && !EQUAL(pszTemp, "") &&
978 13 : strlen(pszTemp) >=
979 : strlen(CORNER_LOWER_RIGHT) + 28 + CORNER_VALUE_SIZE + 1)
980 : {
981 13 : pszTemp += strlen(CORNER_LOWER_RIGHT) + 28;
982 13 : dfLRX = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
983 13 : pszTemp += CORNER_VALUE_SIZE + 1;
984 13 : dfLRY = CPLScanDouble(pszTemp, CORNER_VALUE_SIZE);
985 : }
986 : }
987 :
988 13 : if (dfULX != 0.0 && dfULY != 0.0 && dfURX != 0.0 && dfURY != 0.0 &&
989 13 : dfLLX != 0.0 && dfLLY != 0.0 && dfLRX != 0.0 && dfLRY != 0.0)
990 : {
991 : // Strip out zone number from the easting values, if either
992 13 : if (dfULX >= 1000000.0)
993 4 : dfULX -= static_cast<double>(iZone) * 1000000.0;
994 13 : if (dfURX >= 1000000.0)
995 4 : dfURX -= static_cast<double>(iZone) * 1000000.0;
996 13 : if (dfLLX >= 1000000.0)
997 4 : dfLLX -= static_cast<double>(iZone) * 1000000.0;
998 13 : if (dfLRX >= 1000000.0)
999 4 : dfLRX -= static_cast<double>(iZone) * 1000000.0;
1000 :
1001 : // In EOSAT FAST Rev C, the angles are in decimal degrees
1002 : // otherwise they are in packed DMS format.
1003 13 : const int bAnglesInPackedDMSFormat =
1004 13 : strstr(pszHeader, "REV C") == nullptr;
1005 :
1006 : // Create projection definition
1007 13 : OGRErr eErr = poDS->m_oSRS.importFromUSGS(
1008 : iProjSys, iZone, adfProjParams, iDatum, bAnglesInPackedDMSFormat);
1009 13 : if (eErr != OGRERR_NONE)
1010 0 : CPLDebug("FAST", "Import projection from USGS failed: %d", eErr);
1011 : else
1012 : {
1013 13 : poDS->m_oSRS.SetLinearUnits(SRS_UL_METER, 1.0);
1014 :
1015 : // Read datum name
1016 : char *pszTemp =
1017 13 : GetValue(pszHeader, DATUM_NAME, DATUM_NAME_SIZE, FALSE);
1018 13 : if (pszTemp)
1019 : {
1020 12 : if (EQUAL(pszTemp, "WGS84"))
1021 6 : poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
1022 6 : else if (EQUAL(pszTemp, "NAD27"))
1023 0 : poDS->m_oSRS.SetWellKnownGeogCS("NAD27");
1024 6 : else if (EQUAL(pszTemp, "NAD83"))
1025 0 : poDS->m_oSRS.SetWellKnownGeogCS("NAD83");
1026 12 : CPLFree(pszTemp);
1027 : }
1028 : else
1029 : {
1030 : // Reasonable fallback
1031 1 : poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
1032 : }
1033 : }
1034 :
1035 : // Generate GCPs
1036 : GDAL_GCP *pasGCPList =
1037 13 : static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), 4));
1038 13 : GDALInitGCPs(4, pasGCPList);
1039 13 : CPLFree(pasGCPList[0].pszId);
1040 13 : CPLFree(pasGCPList[1].pszId);
1041 13 : CPLFree(pasGCPList[2].pszId);
1042 13 : CPLFree(pasGCPList[3].pszId);
1043 :
1044 : /* Let's order the GCP in TL, TR, BR, BL order to benefit from the */
1045 : /* GDALGCPsToGeoTransform optimization */
1046 13 : pasGCPList[0].pszId = CPLStrdup("UPPER_LEFT");
1047 13 : pasGCPList[0].dfGCPX = dfULX;
1048 13 : pasGCPList[0].dfGCPY = dfULY;
1049 13 : pasGCPList[0].dfGCPZ = 0.0;
1050 13 : pasGCPList[0].dfGCPPixel = 0.5;
1051 13 : pasGCPList[0].dfGCPLine = 0.5;
1052 13 : pasGCPList[1].pszId = CPLStrdup("UPPER_RIGHT");
1053 13 : pasGCPList[1].dfGCPX = dfURX;
1054 13 : pasGCPList[1].dfGCPY = dfURY;
1055 13 : pasGCPList[1].dfGCPZ = 0.0;
1056 13 : pasGCPList[1].dfGCPPixel = poDS->nRasterXSize - 0.5;
1057 13 : pasGCPList[1].dfGCPLine = 0.5;
1058 13 : pasGCPList[2].pszId = CPLStrdup("LOWER_RIGHT");
1059 13 : pasGCPList[2].dfGCPX = dfLRX;
1060 13 : pasGCPList[2].dfGCPY = dfLRY;
1061 13 : pasGCPList[2].dfGCPZ = 0.0;
1062 13 : pasGCPList[2].dfGCPPixel = poDS->nRasterXSize - 0.5;
1063 13 : pasGCPList[2].dfGCPLine = poDS->nRasterYSize - 0.5;
1064 13 : pasGCPList[3].pszId = CPLStrdup("LOWER_LEFT");
1065 13 : pasGCPList[3].dfGCPX = dfLLX;
1066 13 : pasGCPList[3].dfGCPY = dfLLY;
1067 13 : pasGCPList[3].dfGCPZ = 0.0;
1068 13 : pasGCPList[3].dfGCPPixel = 0.5;
1069 13 : pasGCPList[3].dfGCPLine = poDS->nRasterYSize - 0.5;
1070 :
1071 : // Calculate transformation matrix, if accurate
1072 13 : const bool transform_ok = CPL_TO_BOOL(
1073 13 : GDALGCPsToGeoTransform(4, pasGCPList, poDS->adfGeoTransform, 0));
1074 13 : if (!transform_ok)
1075 : {
1076 0 : poDS->adfGeoTransform[0] = 0.0;
1077 0 : poDS->adfGeoTransform[1] = 1.0;
1078 0 : poDS->adfGeoTransform[2] = 0.0;
1079 0 : poDS->adfGeoTransform[3] = 0.0;
1080 0 : poDS->adfGeoTransform[4] = 0.0;
1081 0 : poDS->adfGeoTransform[5] = 1.0;
1082 0 : poDS->m_oSRS.Clear();
1083 : }
1084 :
1085 13 : GDALDeinitGCPs(4, pasGCPList);
1086 13 : CPLFree(pasGCPList);
1087 : }
1088 :
1089 : /* -------------------------------------------------------------------- */
1090 : /* Create band information objects. */
1091 : /* -------------------------------------------------------------------- */
1092 13 : const int nPixelOffset = GDALGetDataTypeSizeBytes(poDS->eDataType);
1093 13 : const int nLineOffset = poDS->nRasterXSize * nPixelOffset;
1094 :
1095 42 : for (int i = 1; i <= l_nBands; i++)
1096 : {
1097 : auto poBand = RawRasterBand::Create(
1098 58 : poDS.get(), i, poDS->fpChannels[i - 1], 0, nPixelOffset,
1099 29 : nLineOffset, poDS->eDataType, RawRasterBand::NATIVE_BYTE_ORDER,
1100 29 : RawRasterBand::OwnFP::NO);
1101 29 : if (!poBand)
1102 0 : return nullptr;
1103 29 : poDS->SetBand(i, std::move(poBand));
1104 : }
1105 :
1106 : /* -------------------------------------------------------------------- */
1107 : /* Initialize any PAM information. */
1108 : /* -------------------------------------------------------------------- */
1109 13 : poDS->SetDescription(poOpenInfo->pszFilename);
1110 13 : poDS->TryLoadXML();
1111 :
1112 : // opens overviews.
1113 13 : poDS->oOvManager.Initialize(poDS.get(), poDS->pszFilename);
1114 :
1115 : /* -------------------------------------------------------------------- */
1116 : /* Confirm the requested access is supported. */
1117 : /* -------------------------------------------------------------------- */
1118 13 : if (poOpenInfo->eAccess == GA_Update)
1119 : {
1120 0 : CPLError(CE_Failure, CPLE_NotSupported,
1121 : "The FAST driver does not support update access to existing"
1122 : " datasets.");
1123 0 : return nullptr;
1124 : }
1125 :
1126 13 : return poDS.release();
1127 : }
1128 :
1129 : /************************************************************************/
1130 : /* GDALRegister_FAST() */
1131 : /************************************************************************/
1132 :
1133 1595 : void GDALRegister_FAST()
1134 :
1135 : {
1136 1595 : if (GDALGetDriverByName("FAST") != nullptr)
1137 302 : return;
1138 :
1139 1293 : GDALDriver *poDriver = new GDALDriver();
1140 :
1141 1293 : poDriver->SetDescription("FAST");
1142 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1143 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "EOSAT FAST Format");
1144 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/fast.html");
1145 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1146 :
1147 1293 : poDriver->pfnOpen = FASTDataset::Open;
1148 :
1149 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
1150 : }
|