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