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