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