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