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