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