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