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