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