/src/gdal/frmts/raw/landataset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: eCognition |
4 | | * Purpose: Implementation of Erdas .LAN / .GIS format. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2004, Frank Warmerdam |
9 | | * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_string.h" |
15 | | #include "gdal_frmts.h" |
16 | | #include "ogr_spatialref.h" |
17 | | #include "rawdataset.h" |
18 | | |
19 | | #include <cmath> |
20 | | |
21 | | #include <algorithm> |
22 | | |
23 | | /** |
24 | | |
25 | | Erdas Header format: "HEAD74" |
26 | | |
27 | | Offset Size Type Description |
28 | | ------ ---- ---- ----------- |
29 | | 0 6 char magic cookie / version (i.e. HEAD74). |
30 | | 6 2 Int16 Pixel type, 0=8bit, 1=4bit, 2=16bit |
31 | | 8 2 Int16 Number of Bands. |
32 | | 10 6 char Unknown. |
33 | | 16 4 Int32 Width |
34 | | 20 4 Int32 Height |
35 | | 24 4 Int32 X Start (offset in original file?) |
36 | | 28 4 Int32 Y Start (offset in original file?) |
37 | | 32 56 char Unknown. |
38 | | 88 2 Int16 0=LAT, 1=UTM, 2=StatePlane, 3- are projections? |
39 | | 90 2 Int16 Classes in coverage. |
40 | | 92 14 char Unknown. |
41 | | 106 2 Int16 Area Unit (0=none, 1=Acre, 2=Hectare, 3=Other) |
42 | | 108 4 Float32 Pixel area. |
43 | | 112 4 Float32 Upper Left corner X (center of pixel?) |
44 | | 116 4 Float32 Upper Left corner Y (center of pixel?) |
45 | | 120 4 Float32 Width of a pixel. |
46 | | 124 4 Float32 Height of a pixel. |
47 | | |
48 | | Erdas Header format: "HEADER" |
49 | | |
50 | | Offset Size Type Description |
51 | | ------ ---- ---- ----------- |
52 | | 0 6 char magic cookie / version (i.e. HEAD74). |
53 | | 6 2 Int16 Pixel type, 0=8bit, 1=4bit, 2=16bit |
54 | | 8 2 Int16 Number of Bands. |
55 | | 10 6 char Unknown. |
56 | | 16 4 Float32 Width |
57 | | 20 4 Float32 Height |
58 | | 24 4 Int32 X Start (offset in original file?) |
59 | | 28 4 Int32 Y Start (offset in original file?) |
60 | | 32 56 char Unknown. |
61 | | 88 2 Int16 0=LAT, 1=UTM, 2=StatePlane, 3- are projections? |
62 | | 90 2 Int16 Classes in coverage. |
63 | | 92 14 char Unknown. |
64 | | 106 2 Int16 Area Unit (0=none, 1=Acre, 2=Hectare, 3=Other) |
65 | | 108 4 Float32 Pixel area. |
66 | | 112 4 Float32 Upper Left corner X (center of pixel?) |
67 | | 116 4 Float32 Upper Left corner Y (center of pixel?) |
68 | | 120 4 Float32 Width of a pixel. |
69 | | 124 4 Float32 Height of a pixel. |
70 | | |
71 | | All binary fields are in the same byte order but it may be big endian or |
72 | | little endian depending on what platform the file was written on. Usually |
73 | | this can be checked against the number of bands though this test won't work |
74 | | if there are more than 255 bands. |
75 | | |
76 | | There is also some information on .STA and .TRL files at: |
77 | | |
78 | | http://www.pcigeomatics.com/cgi-bin/pcihlp/ERDASWR%7CTRAILER+FORMAT |
79 | | |
80 | | **/ |
81 | | |
82 | | constexpr int ERD_HEADER_SIZE = 128; |
83 | | |
84 | | /************************************************************************/ |
85 | | /* ==================================================================== */ |
86 | | /* LAN4BitRasterBand */ |
87 | | /* ==================================================================== */ |
88 | | /************************************************************************/ |
89 | | |
90 | | class LANDataset; |
91 | | |
92 | | class LAN4BitRasterBand final : public GDALPamRasterBand |
93 | | { |
94 | | GDALColorTable *poCT; |
95 | | GDALColorInterp eInterp; |
96 | | |
97 | | CPL_DISALLOW_COPY_ASSIGN(LAN4BitRasterBand) |
98 | | |
99 | | public: |
100 | | LAN4BitRasterBand(LANDataset *, int); |
101 | | ~LAN4BitRasterBand() override; |
102 | | |
103 | | GDALColorTable *GetColorTable() override; |
104 | | GDALColorInterp GetColorInterpretation() override; |
105 | | CPLErr SetColorTable(GDALColorTable *) override; |
106 | | CPLErr SetColorInterpretation(GDALColorInterp) override; |
107 | | |
108 | | CPLErr IReadBlock(int, int, void *) override; |
109 | | }; |
110 | | |
111 | | /************************************************************************/ |
112 | | /* ==================================================================== */ |
113 | | /* LANDataset */ |
114 | | /* ==================================================================== */ |
115 | | /************************************************************************/ |
116 | | |
117 | | class LANDataset final : public RawDataset |
118 | | { |
119 | | CPL_DISALLOW_COPY_ASSIGN(LANDataset) |
120 | | |
121 | | public: |
122 | | VSILFILE *fpImage; // Image data file. |
123 | | |
124 | | char pachHeader[ERD_HEADER_SIZE]; |
125 | | |
126 | | OGRSpatialReference *m_poSRS = nullptr; |
127 | | |
128 | | double adfGeoTransform[6]; |
129 | | |
130 | | CPLString osSTAFilename{}; |
131 | | void CheckForStatistics(void); |
132 | | |
133 | | char **GetFileList() override; |
134 | | |
135 | | CPLErr Close() override; |
136 | | |
137 | | public: |
138 | | LANDataset(); |
139 | | ~LANDataset() override; |
140 | | |
141 | | CPLErr GetGeoTransform(double *padfTransform) override; |
142 | | |
143 | | const OGRSpatialReference *GetSpatialRef() const override; |
144 | | |
145 | | static GDALDataset *Open(GDALOpenInfo *); |
146 | | }; |
147 | | |
148 | | /************************************************************************/ |
149 | | /* ==================================================================== */ |
150 | | /* LAN4BitRasterBand */ |
151 | | /* ==================================================================== */ |
152 | | /************************************************************************/ |
153 | | |
154 | | /************************************************************************/ |
155 | | /* LAN4BitRasterBand() */ |
156 | | /************************************************************************/ |
157 | | |
158 | | LAN4BitRasterBand::LAN4BitRasterBand(LANDataset *poDSIn, int nBandIn) |
159 | 29.4k | : poCT(nullptr), eInterp(GCI_Undefined) |
160 | 29.4k | { |
161 | 29.4k | poDS = poDSIn; |
162 | 29.4k | nBand = nBandIn; |
163 | 29.4k | eDataType = GDT_Byte; |
164 | | |
165 | 29.4k | nBlockXSize = poDSIn->GetRasterXSize(); |
166 | 29.4k | nBlockYSize = 1; |
167 | 29.4k | } |
168 | | |
169 | | /************************************************************************/ |
170 | | /* ~LAN4BitRasterBand() */ |
171 | | /************************************************************************/ |
172 | | |
173 | | LAN4BitRasterBand::~LAN4BitRasterBand() |
174 | | |
175 | 29.4k | { |
176 | 29.4k | if (poCT) |
177 | 0 | delete poCT; |
178 | 29.4k | } |
179 | | |
180 | | /************************************************************************/ |
181 | | /* IReadBlock() */ |
182 | | /************************************************************************/ |
183 | | |
184 | | CPLErr LAN4BitRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff, |
185 | | void *pImage) |
186 | | |
187 | 33.2k | { |
188 | 33.2k | LANDataset *poLAN_DS = reinterpret_cast<LANDataset *>(poDS); |
189 | 33.2k | CPLAssert(nBlockXOff == 0); |
190 | | |
191 | | /* -------------------------------------------------------------------- */ |
192 | | /* Seek to profile. */ |
193 | | /* -------------------------------------------------------------------- */ |
194 | 33.2k | const vsi_l_offset nOffset = |
195 | 33.2k | ERD_HEADER_SIZE + |
196 | 33.2k | (static_cast<vsi_l_offset>(nBlockYOff) * nRasterXSize * |
197 | 33.2k | poLAN_DS->GetRasterCount()) / |
198 | 33.2k | 2 + |
199 | 33.2k | (static_cast<vsi_l_offset>(nBand - 1) * nRasterXSize) / 2; |
200 | | |
201 | 33.2k | if (VSIFSeekL(poLAN_DS->fpImage, nOffset, SEEK_SET) != 0) |
202 | 0 | { |
203 | 0 | CPLError(CE_Failure, CPLE_FileIO, "LAN Seek failed:%s", |
204 | 0 | VSIStrerror(errno)); |
205 | 0 | return CE_Failure; |
206 | 0 | } |
207 | | |
208 | | /* -------------------------------------------------------------------- */ |
209 | | /* Read the profile. */ |
210 | | /* -------------------------------------------------------------------- */ |
211 | 33.2k | if (VSIFReadL(pImage, 1, nRasterXSize / 2, poLAN_DS->fpImage) != |
212 | 33.2k | static_cast<size_t>(nRasterXSize) / 2) |
213 | 835 | { |
214 | 835 | CPLError(CE_Failure, CPLE_FileIO, "LAN Read failed:%s", |
215 | 835 | VSIStrerror(errno)); |
216 | 835 | return CE_Failure; |
217 | 835 | } |
218 | | |
219 | | /* -------------------------------------------------------------------- */ |
220 | | /* Convert 4bit to 8bit. */ |
221 | | /* -------------------------------------------------------------------- */ |
222 | 153k | for (int i = nRasterXSize - 1; i >= 0; i--) |
223 | 121k | { |
224 | 121k | if ((i & 0x01) != 0) |
225 | 60.7k | reinterpret_cast<GByte *>(pImage)[i] = |
226 | 60.7k | reinterpret_cast<GByte *>(pImage)[i / 2] & 0x0f; |
227 | 60.7k | else |
228 | 60.7k | reinterpret_cast<GByte *>(pImage)[i] = |
229 | 60.7k | (reinterpret_cast<GByte *>(pImage)[i / 2] & 0xf0) / 16; |
230 | 121k | } |
231 | | |
232 | 32.4k | return CE_None; |
233 | 33.2k | } |
234 | | |
235 | | /************************************************************************/ |
236 | | /* SetColorTable() */ |
237 | | /************************************************************************/ |
238 | | |
239 | | CPLErr LAN4BitRasterBand::SetColorTable(GDALColorTable *poNewCT) |
240 | | |
241 | 0 | { |
242 | 0 | if (poCT) |
243 | 0 | delete poCT; |
244 | 0 | if (poNewCT == nullptr) |
245 | 0 | poCT = nullptr; |
246 | 0 | else |
247 | 0 | poCT = poNewCT->Clone(); |
248 | |
|
249 | 0 | return CE_None; |
250 | 0 | } |
251 | | |
252 | | /************************************************************************/ |
253 | | /* GetColorTable() */ |
254 | | /************************************************************************/ |
255 | | |
256 | | GDALColorTable *LAN4BitRasterBand::GetColorTable() |
257 | | |
258 | 641 | { |
259 | 641 | if (poCT != nullptr) |
260 | 0 | return poCT; |
261 | | |
262 | 641 | return GDALPamRasterBand::GetColorTable(); |
263 | 641 | } |
264 | | |
265 | | /************************************************************************/ |
266 | | /* SetColorInterpretation() */ |
267 | | /************************************************************************/ |
268 | | |
269 | | CPLErr LAN4BitRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp) |
270 | | |
271 | 0 | { |
272 | 0 | eInterp = eNewInterp; |
273 | |
|
274 | 0 | return CE_None; |
275 | 0 | } |
276 | | |
277 | | /************************************************************************/ |
278 | | /* GetColorInterpretation() */ |
279 | | /************************************************************************/ |
280 | | |
281 | | GDALColorInterp LAN4BitRasterBand::GetColorInterpretation() |
282 | | |
283 | 684 | { |
284 | 684 | return eInterp; |
285 | 684 | } |
286 | | |
287 | | /************************************************************************/ |
288 | | /* ==================================================================== */ |
289 | | /* LANDataset */ |
290 | | /* ==================================================================== */ |
291 | | /************************************************************************/ |
292 | | |
293 | | /************************************************************************/ |
294 | | /* LANDataset() */ |
295 | | /************************************************************************/ |
296 | | |
297 | 511 | LANDataset::LANDataset() : fpImage(nullptr) |
298 | 511 | { |
299 | 511 | memset(pachHeader, 0, sizeof(pachHeader)); |
300 | 511 | adfGeoTransform[0] = 0.0; |
301 | 511 | adfGeoTransform[1] = 0.0; // TODO(schwehr): Should this be 1.0? |
302 | 511 | adfGeoTransform[2] = 0.0; |
303 | 511 | adfGeoTransform[3] = 0.0; |
304 | 511 | adfGeoTransform[4] = 0.0; |
305 | 511 | adfGeoTransform[5] = 0.0; // TODO(schwehr): Should this be 1.0? |
306 | 511 | } |
307 | | |
308 | | /************************************************************************/ |
309 | | /* ~LANDataset() */ |
310 | | /************************************************************************/ |
311 | | |
312 | | LANDataset::~LANDataset() |
313 | | |
314 | 511 | { |
315 | 511 | LANDataset::Close(); |
316 | 511 | } |
317 | | |
318 | | /************************************************************************/ |
319 | | /* Close() */ |
320 | | /************************************************************************/ |
321 | | |
322 | | CPLErr LANDataset::Close() |
323 | 1.01k | { |
324 | 1.01k | CPLErr eErr = CE_None; |
325 | 1.01k | if (nOpenFlags != OPEN_FLAGS_CLOSED) |
326 | 511 | { |
327 | 511 | if (LANDataset::FlushCache(true) != CE_None) |
328 | 5 | eErr = CE_Failure; |
329 | | |
330 | 511 | if (fpImage) |
331 | 511 | { |
332 | 511 | if (VSIFCloseL(fpImage) != 0) |
333 | 0 | { |
334 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
335 | 0 | eErr = CE_Failure; |
336 | 0 | } |
337 | 511 | } |
338 | | |
339 | 511 | if (m_poSRS) |
340 | 499 | m_poSRS->Release(); |
341 | | |
342 | 511 | if (GDALPamDataset::Close() != CE_None) |
343 | 0 | eErr = CE_Failure; |
344 | 511 | } |
345 | 1.01k | return eErr; |
346 | 1.01k | } |
347 | | |
348 | | /************************************************************************/ |
349 | | /* Open() */ |
350 | | /************************************************************************/ |
351 | | |
352 | | GDALDataset *LANDataset::Open(GDALOpenInfo *poOpenInfo) |
353 | | |
354 | 847k | { |
355 | | /* -------------------------------------------------------------------- */ |
356 | | /* We assume the user is pointing to the header (.pcb) file. */ |
357 | | /* Does this appear to be a pcb file? */ |
358 | | /* -------------------------------------------------------------------- */ |
359 | 847k | if (poOpenInfo->nHeaderBytes < ERD_HEADER_SIZE || |
360 | 847k | poOpenInfo->fpL == nullptr) |
361 | 775k | return nullptr; |
362 | | |
363 | 71.5k | if (!STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader), |
364 | 71.5k | "HEADER") && |
365 | 71.5k | !STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader), |
366 | 71.5k | "HEAD74")) |
367 | 71.0k | return nullptr; |
368 | | |
369 | 511 | if (memcmp(poOpenInfo->pabyHeader + 16, "S LAT ", 8) == 0) |
370 | 0 | { |
371 | | // NTV1 format |
372 | 0 | return nullptr; |
373 | 0 | } |
374 | | |
375 | | /* -------------------------------------------------------------------- */ |
376 | | /* Create a corresponding GDALDataset. */ |
377 | | /* -------------------------------------------------------------------- */ |
378 | 511 | auto poDS = std::make_unique<LANDataset>(); |
379 | | |
380 | 511 | poDS->eAccess = poOpenInfo->eAccess; |
381 | 511 | std::swap(poDS->fpImage, poOpenInfo->fpL); |
382 | | |
383 | | /* -------------------------------------------------------------------- */ |
384 | | /* Do we need to byte swap the headers to local machine order? */ |
385 | | /* -------------------------------------------------------------------- */ |
386 | 511 | const RawRasterBand::ByteOrder eByteOrder = |
387 | 511 | poOpenInfo->pabyHeader[8] == 0 |
388 | 511 | ? RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN |
389 | 511 | : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN; |
390 | | |
391 | 511 | memcpy(poDS->pachHeader, poOpenInfo->pabyHeader, ERD_HEADER_SIZE); |
392 | | |
393 | 511 | if (eByteOrder != RawRasterBand::NATIVE_BYTE_ORDER) |
394 | 498 | { |
395 | 498 | CPL_SWAP16PTR(poDS->pachHeader + 6); |
396 | 498 | CPL_SWAP16PTR(poDS->pachHeader + 8); |
397 | | |
398 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 16); |
399 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 20); |
400 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 24); |
401 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 28); |
402 | | |
403 | 498 | CPL_SWAP16PTR(poDS->pachHeader + 88); |
404 | 498 | CPL_SWAP16PTR(poDS->pachHeader + 90); |
405 | | |
406 | 498 | CPL_SWAP16PTR(poDS->pachHeader + 106); |
407 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 108); |
408 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 112); |
409 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 116); |
410 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 120); |
411 | 498 | CPL_SWAP32PTR(poDS->pachHeader + 124); |
412 | 498 | } |
413 | | |
414 | | /* -------------------------------------------------------------------- */ |
415 | | /* Capture some information from the file that is of interest. */ |
416 | | /* -------------------------------------------------------------------- */ |
417 | 511 | if (STARTS_WITH_CI(poDS->pachHeader, "HEADER")) |
418 | 508 | { |
419 | 508 | float fTmp = 0.0; |
420 | 508 | memcpy(&fTmp, poDS->pachHeader + 16, 4); |
421 | 508 | poDS->nRasterXSize = static_cast<int>(fTmp); |
422 | 508 | memcpy(&fTmp, poDS->pachHeader + 20, 4); |
423 | 508 | poDS->nRasterYSize = static_cast<int>(fTmp); |
424 | 508 | } |
425 | 3 | else |
426 | 3 | { |
427 | 3 | GInt32 nTmp = 0; |
428 | 3 | memcpy(&nTmp, poDS->pachHeader + 16, 4); |
429 | 3 | poDS->nRasterXSize = nTmp; |
430 | 3 | memcpy(&nTmp, poDS->pachHeader + 20, 4); |
431 | 3 | poDS->nRasterYSize = nTmp; |
432 | 3 | } |
433 | | |
434 | 511 | GInt16 nTmp16 = 0; |
435 | 511 | memcpy(&nTmp16, poDS->pachHeader + 6, 2); |
436 | | |
437 | 511 | int nPixelOffset = 0; |
438 | 511 | GDALDataType eDataType = GDT_Unknown; |
439 | 511 | if (nTmp16 == 0) |
440 | 88 | { |
441 | 88 | eDataType = GDT_Byte; |
442 | 88 | nPixelOffset = 1; |
443 | 88 | } |
444 | 423 | else if (nTmp16 == 1) // 4 bit |
445 | 110 | { |
446 | 110 | eDataType = GDT_Byte; |
447 | 110 | nPixelOffset = -1; |
448 | 110 | } |
449 | 313 | else if (nTmp16 == 2) |
450 | 306 | { |
451 | 306 | nPixelOffset = 2; |
452 | 306 | eDataType = GDT_Int16; |
453 | 306 | } |
454 | 7 | else |
455 | 7 | { |
456 | 7 | CPLError(CE_Failure, CPLE_AppDefined, "Unsupported pixel type (%d).", |
457 | 7 | nTmp16); |
458 | 7 | return nullptr; |
459 | 7 | } |
460 | | |
461 | 504 | memcpy(&nTmp16, poDS->pachHeader + 8, 2); |
462 | 504 | const int nBandCount = nTmp16; |
463 | | |
464 | 504 | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || |
465 | 504 | !GDALCheckBandCount(nBandCount, FALSE)) |
466 | 4 | { |
467 | 4 | return nullptr; |
468 | 4 | } |
469 | | |
470 | | // cppcheck-suppress knownConditionTrueFalse |
471 | 500 | if (nPixelOffset != -1 && |
472 | 500 | poDS->nRasterXSize > INT_MAX / (nPixelOffset * nBandCount)) |
473 | 1 | { |
474 | 1 | CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); |
475 | 1 | return nullptr; |
476 | 1 | } |
477 | | |
478 | | /* -------------------------------------------------------------------- */ |
479 | | /* Create band information object. */ |
480 | | /* -------------------------------------------------------------------- */ |
481 | 49.0k | for (int iBand = 1; iBand <= nBandCount; iBand++) |
482 | 48.5k | { |
483 | 48.5k | if (nPixelOffset == -1) /* 4 bit case */ |
484 | 29.4k | poDS->SetBand(iBand, new LAN4BitRasterBand(poDS.get(), iBand)); |
485 | 19.0k | else |
486 | 19.0k | { |
487 | 19.0k | auto poBand = RawRasterBand::Create( |
488 | 19.0k | poDS.get(), iBand, poDS->fpImage, |
489 | 19.0k | ERD_HEADER_SIZE + |
490 | 19.0k | (iBand - 1) * nPixelOffset * poDS->nRasterXSize, |
491 | 19.0k | nPixelOffset, poDS->nRasterXSize * nPixelOffset * nBandCount, |
492 | 19.0k | eDataType, eByteOrder, RawRasterBand::OwnFP::NO); |
493 | 19.0k | if (!poBand) |
494 | 0 | return nullptr; |
495 | 19.0k | poDS->SetBand(iBand, std::move(poBand)); |
496 | 19.0k | } |
497 | 48.5k | } |
498 | | |
499 | | /* -------------------------------------------------------------------- */ |
500 | | /* Initialize any PAM information. */ |
501 | | /* -------------------------------------------------------------------- */ |
502 | 499 | poDS->SetDescription(poOpenInfo->pszFilename); |
503 | 499 | poDS->CheckForStatistics(); |
504 | 499 | poDS->TryLoadXML(); |
505 | | |
506 | | /* -------------------------------------------------------------------- */ |
507 | | /* Check for overviews. */ |
508 | | /* -------------------------------------------------------------------- */ |
509 | 499 | poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename); |
510 | | |
511 | | /* -------------------------------------------------------------------- */ |
512 | | /* Try to interpret georeferencing. */ |
513 | | /* -------------------------------------------------------------------- */ |
514 | 499 | float fTmp = 0.0; |
515 | | |
516 | 499 | memcpy(&fTmp, poDS->pachHeader + 112, 4); |
517 | 499 | poDS->adfGeoTransform[0] = fTmp; |
518 | 499 | memcpy(&fTmp, poDS->pachHeader + 120, 4); |
519 | 499 | poDS->adfGeoTransform[1] = fTmp; |
520 | 499 | poDS->adfGeoTransform[2] = 0.0; |
521 | 499 | memcpy(&fTmp, poDS->pachHeader + 116, 4); |
522 | 499 | poDS->adfGeoTransform[3] = fTmp; |
523 | 499 | poDS->adfGeoTransform[4] = 0.0; |
524 | 499 | memcpy(&fTmp, poDS->pachHeader + 124, 4); |
525 | 499 | poDS->adfGeoTransform[5] = -fTmp; |
526 | | |
527 | | // adjust for center of pixel vs. top left corner of pixel. |
528 | 499 | poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5; |
529 | 499 | poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5; |
530 | | |
531 | | /* -------------------------------------------------------------------- */ |
532 | | /* If we didn't get any georeferencing, try for a worldfile. */ |
533 | | /* -------------------------------------------------------------------- */ |
534 | 499 | if (poDS->adfGeoTransform[1] == 0.0 || poDS->adfGeoTransform[5] == 0.0) |
535 | 65 | { |
536 | 65 | if (!GDALReadWorldFile(poOpenInfo->pszFilename, nullptr, |
537 | 65 | poDS->adfGeoTransform)) |
538 | 64 | GDALReadWorldFile(poOpenInfo->pszFilename, ".wld", |
539 | 64 | poDS->adfGeoTransform); |
540 | 65 | } |
541 | | |
542 | | /* -------------------------------------------------------------------- */ |
543 | | /* Try to come up with something for the coordinate system. */ |
544 | | /* -------------------------------------------------------------------- */ |
545 | 499 | memcpy(&nTmp16, poDS->pachHeader + 88, 2); |
546 | 499 | int nCoordSys = nTmp16; |
547 | | |
548 | 499 | poDS->m_poSRS = new OGRSpatialReference(); |
549 | 499 | poDS->m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
550 | 499 | if (nCoordSys == 0) |
551 | 249 | { |
552 | 249 | poDS->m_poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG); |
553 | 249 | } |
554 | 250 | else if (nCoordSys == 1) |
555 | 5 | { |
556 | 5 | poDS->m_poSRS->SetFromUserInput( |
557 | 5 | "LOCAL_CS[\"UTM - Zone Unknown\",UNIT[\"Meter\",1]]"); |
558 | 5 | } |
559 | 245 | else if (nCoordSys == 2) |
560 | 2 | { |
561 | 2 | poDS->m_poSRS->SetFromUserInput( |
562 | 2 | "LOCAL_CS[\"State Plane - Zone Unknown\"," |
563 | 2 | "UNIT[\"US survey foot\",0.3048006096012192]]"); |
564 | 2 | } |
565 | 243 | else |
566 | 243 | { |
567 | 243 | poDS->m_poSRS->SetFromUserInput( |
568 | 243 | "LOCAL_CS[\"Unknown\",UNIT[\"Meter\",1]]"); |
569 | 243 | } |
570 | | |
571 | | /* -------------------------------------------------------------------- */ |
572 | | /* Check for a trailer file with a colormap in it. */ |
573 | | /* -------------------------------------------------------------------- */ |
574 | 499 | char *pszPath = CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str()); |
575 | 499 | char *pszBasename = |
576 | 499 | CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str()); |
577 | 499 | const std::string osTRLFilename = |
578 | 499 | CPLFormCIFilenameSafe(pszPath, pszBasename, "trl"); |
579 | 499 | VSILFILE *fpTRL = VSIFOpenL(osTRLFilename.c_str(), "rb"); |
580 | 499 | if (fpTRL != nullptr) |
581 | 0 | { |
582 | 0 | char szTRLData[896] = {'\0'}; |
583 | |
|
584 | 0 | CPL_IGNORE_RET_VAL(VSIFReadL(szTRLData, 1, 896, fpTRL)); |
585 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fpTRL)); |
586 | |
|
587 | 0 | GDALColorTable oCT; |
588 | 0 | for (int iColor = 0; iColor < 256; iColor++) |
589 | 0 | { |
590 | 0 | GDALColorEntry sEntry = {0, 0, 0, 0}; |
591 | |
|
592 | 0 | sEntry.c2 = reinterpret_cast<GByte *>(szTRLData)[iColor + 128]; |
593 | 0 | sEntry.c1 = |
594 | 0 | reinterpret_cast<GByte *>(szTRLData)[iColor + 128 + 256]; |
595 | 0 | sEntry.c3 = |
596 | 0 | reinterpret_cast<GByte *>(szTRLData)[iColor + 128 + 512]; |
597 | 0 | sEntry.c4 = 255; |
598 | 0 | oCT.SetColorEntry(iColor, &sEntry); |
599 | | |
600 | | // Only 16 colors in 4bit files. |
601 | 0 | if (nPixelOffset == -1 && iColor == 15) |
602 | 0 | break; |
603 | 0 | } |
604 | |
|
605 | 0 | poDS->GetRasterBand(1)->SetColorTable(&oCT); |
606 | 0 | poDS->GetRasterBand(1)->SetColorInterpretation(GCI_PaletteIndex); |
607 | 0 | } |
608 | | |
609 | 499 | CPLFree(pszPath); |
610 | 499 | CPLFree(pszBasename); |
611 | | |
612 | 499 | return poDS.release(); |
613 | 499 | } |
614 | | |
615 | | /************************************************************************/ |
616 | | /* GetGeoTransform() */ |
617 | | /************************************************************************/ |
618 | | |
619 | | CPLErr LANDataset::GetGeoTransform(double *padfTransform) |
620 | | |
621 | 720 | { |
622 | 720 | if (adfGeoTransform[1] != 0.0 && adfGeoTransform[5] != 0.0) |
623 | 588 | { |
624 | 588 | memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6); |
625 | 588 | return CE_None; |
626 | 588 | } |
627 | | |
628 | 132 | return GDALPamDataset::GetGeoTransform(padfTransform); |
629 | 720 | } |
630 | | |
631 | | /************************************************************************/ |
632 | | /* GetSpatialRef() */ |
633 | | /* */ |
634 | | /* Use PAM coordinate system if available in preference to the */ |
635 | | /* generally poor value derived from the file itself. */ |
636 | | /************************************************************************/ |
637 | | |
638 | | const OGRSpatialReference *LANDataset::GetSpatialRef() const |
639 | | |
640 | 631 | { |
641 | 631 | const auto poSRS = GDALPamDataset::GetSpatialRef(); |
642 | 631 | if (poSRS) |
643 | 0 | return poSRS; |
644 | | |
645 | 631 | return m_poSRS; |
646 | 631 | } |
647 | | |
648 | | /************************************************************************/ |
649 | | /* GetFileList() */ |
650 | | /************************************************************************/ |
651 | | |
652 | | char **LANDataset::GetFileList() |
653 | | |
654 | 110 | { |
655 | | // Main data file, etc. |
656 | 110 | char **papszFileList = GDALPamDataset::GetFileList(); |
657 | | |
658 | 110 | if (!osSTAFilename.empty()) |
659 | 0 | papszFileList = CSLAddString(papszFileList, osSTAFilename); |
660 | | |
661 | 110 | return papszFileList; |
662 | 110 | } |
663 | | |
664 | | /************************************************************************/ |
665 | | /* CheckForStatistics() */ |
666 | | /************************************************************************/ |
667 | | |
668 | | void LANDataset::CheckForStatistics() |
669 | | |
670 | 499 | { |
671 | | /* -------------------------------------------------------------------- */ |
672 | | /* Do we have a statistics file? */ |
673 | | /* -------------------------------------------------------------------- */ |
674 | 499 | osSTAFilename = CPLResetExtensionSafe(GetDescription(), "sta"); |
675 | | |
676 | 499 | VSILFILE *fpSTA = VSIFOpenL(osSTAFilename, "r"); |
677 | | |
678 | 499 | if (fpSTA == nullptr && VSIIsCaseSensitiveFS(osSTAFilename)) |
679 | 499 | { |
680 | 499 | osSTAFilename = CPLResetExtensionSafe(GetDescription(), "STA"); |
681 | 499 | fpSTA = VSIFOpenL(osSTAFilename, "r"); |
682 | 499 | } |
683 | | |
684 | 499 | if (fpSTA == nullptr) |
685 | 499 | { |
686 | 499 | osSTAFilename = ""; |
687 | 499 | return; |
688 | 499 | } |
689 | | |
690 | | /* -------------------------------------------------------------------- */ |
691 | | /* Read it one band at a time. */ |
692 | | /* -------------------------------------------------------------------- */ |
693 | 0 | GByte abyBandInfo[1152] = {'\0'}; |
694 | |
|
695 | 0 | for (int iBand = 0; iBand < nBands; iBand++) |
696 | 0 | { |
697 | 0 | if (VSIFReadL(abyBandInfo, 1152, 1, fpSTA) != 1) |
698 | 0 | break; |
699 | | |
700 | 0 | const int nBandNumber = abyBandInfo[7]; |
701 | 0 | GDALRasterBand *poBand = GetRasterBand(nBandNumber); |
702 | 0 | if (poBand == nullptr) |
703 | 0 | break; |
704 | | |
705 | 0 | GInt16 nMin = 0; |
706 | 0 | GInt16 nMax = 0; |
707 | |
|
708 | 0 | if (poBand->GetRasterDataType() != GDT_Byte) |
709 | 0 | { |
710 | 0 | memcpy(&nMin, abyBandInfo + 28, 2); |
711 | 0 | memcpy(&nMax, abyBandInfo + 30, 2); |
712 | 0 | CPL_LSBPTR16(&nMin); |
713 | 0 | CPL_LSBPTR16(&nMax); |
714 | 0 | } |
715 | 0 | else |
716 | 0 | { |
717 | 0 | nMin = abyBandInfo[9]; |
718 | 0 | nMax = abyBandInfo[8]; |
719 | 0 | } |
720 | |
|
721 | 0 | float fMean = 0.0; |
722 | 0 | float fStdDev = 0.0; |
723 | 0 | memcpy(&fMean, abyBandInfo + 12, 4); |
724 | 0 | memcpy(&fStdDev, abyBandInfo + 24, 4); |
725 | 0 | CPL_LSBPTR32(&fMean); |
726 | 0 | CPL_LSBPTR32(&fStdDev); |
727 | |
|
728 | 0 | poBand->SetStatistics(nMin, nMax, fMean, fStdDev); |
729 | 0 | } |
730 | |
|
731 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fpSTA)); |
732 | 0 | } |
733 | | |
734 | | /************************************************************************/ |
735 | | /* GDALRegister_LAN() */ |
736 | | /************************************************************************/ |
737 | | |
738 | | void GDALRegister_LAN() |
739 | | |
740 | 24 | { |
741 | 24 | if (GDALGetDriverByName("LAN") != nullptr) |
742 | 0 | return; |
743 | | |
744 | 24 | GDALDriver *poDriver = new GDALDriver(); |
745 | | |
746 | 24 | poDriver->SetDescription("LAN"); |
747 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
748 | 24 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Erdas .LAN/.GIS"); |
749 | 24 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/lan.html"); |
750 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
751 | | |
752 | 24 | poDriver->pfnOpen = LANDataset::Open; |
753 | | |
754 | 24 | GetGDALDriverManager()->RegisterDriver(poDriver); |
755 | 24 | } |