Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Arc/Info Binary Grid Driver
4 : * Purpose: Implements GDAL interface to underlying library.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "aigrid.h"
15 : #include "avc.h"
16 : #include "cpl_string.h"
17 : #include "gdal_frmts.h"
18 : #include "gdal_pam.h"
19 : #include "gdal_rat.h"
20 : #include "ogr_spatialref.h"
21 :
22 : #include <vector>
23 :
24 : static CPLString OSR_GDS(char **papszNV, const char *pszField,
25 : const char *pszDefaultValue);
26 :
27 : /************************************************************************/
28 : /* ==================================================================== */
29 : /* AIGDataset */
30 : /* ==================================================================== */
31 : /************************************************************************/
32 :
33 : class AIGRasterBand;
34 :
35 : class AIGDataset final : public GDALPamDataset
36 : {
37 : friend class AIGRasterBand;
38 :
39 : AIGInfo_t *psInfo;
40 :
41 : char **papszPrj;
42 : OGRSpatialReference m_oSRS{};
43 :
44 : GDALColorTable *poCT;
45 : bool bHasReadRat;
46 :
47 : void TranslateColorTable(const char *);
48 :
49 : void ReadRAT();
50 : GDALRasterAttributeTable *poRAT;
51 :
52 : public:
53 : AIGDataset();
54 : ~AIGDataset() override;
55 :
56 : static GDALDataset *Open(GDALOpenInfo *);
57 :
58 : CPLErr GetGeoTransform(double *) override;
59 : const OGRSpatialReference *GetSpatialRef() const override;
60 : char **GetFileList(void) override;
61 : };
62 :
63 : /************************************************************************/
64 : /* ==================================================================== */
65 : /* AIGRasterBand */
66 : /* ==================================================================== */
67 : /************************************************************************/
68 :
69 : class AIGRasterBand final : public GDALPamRasterBand
70 :
71 : {
72 : friend class AIGDataset;
73 :
74 : public:
75 : AIGRasterBand(AIGDataset *, int);
76 :
77 : CPLErr IReadBlock(int, int, void *) override;
78 : double GetMinimum(int *pbSuccess) override;
79 : double GetMaximum(int *pbSuccess) override;
80 : double GetNoDataValue(int *pbSuccess) override;
81 :
82 : GDALColorInterp GetColorInterpretation() override;
83 : GDALColorTable *GetColorTable() override;
84 : GDALRasterAttributeTable *GetDefaultRAT() override;
85 : };
86 :
87 : /************************************************************************/
88 : /* AIGRasterBand() */
89 : /************************************************************************/
90 :
91 9 : AIGRasterBand::AIGRasterBand(AIGDataset *poDSIn, int nBandIn)
92 :
93 : {
94 9 : poDS = poDSIn;
95 9 : nBand = nBandIn;
96 :
97 9 : nBlockXSize = poDSIn->psInfo->nBlockXSize;
98 9 : nBlockYSize = poDSIn->psInfo->nBlockYSize;
99 :
100 9 : if (poDSIn->psInfo->nCellType == AIG_CELLTYPE_INT &&
101 9 : poDSIn->psInfo->dfMin >= 0.0 && poDSIn->psInfo->dfMax <= 254.0)
102 : {
103 9 : eDataType = GDT_Byte;
104 : }
105 0 : else if (poDSIn->psInfo->nCellType == AIG_CELLTYPE_INT &&
106 0 : poDSIn->psInfo->dfMin >= -32767 && poDSIn->psInfo->dfMax <= 32767)
107 : {
108 0 : eDataType = GDT_Int16;
109 : }
110 0 : else if (poDSIn->psInfo->nCellType == AIG_CELLTYPE_INT)
111 : {
112 0 : eDataType = GDT_Int32;
113 : }
114 : else
115 : {
116 0 : eDataType = GDT_Float32;
117 : }
118 9 : }
119 :
120 : /************************************************************************/
121 : /* IReadBlock() */
122 : /************************************************************************/
123 :
124 4 : CPLErr AIGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
125 :
126 : {
127 4 : AIGDataset *poODS = (AIGDataset *)poDS;
128 : GInt32 *panGridRaster;
129 :
130 4 : if (poODS->psInfo->nCellType == AIG_CELLTYPE_INT)
131 : {
132 4 : panGridRaster = (GInt32 *)VSIMalloc3(4, nBlockXSize, nBlockYSize);
133 8 : if (panGridRaster == nullptr ||
134 4 : AIGReadTile(poODS->psInfo, nBlockXOff, nBlockYOff, panGridRaster) !=
135 : CE_None)
136 : {
137 2 : CPLFree(panGridRaster);
138 2 : return CE_Failure;
139 : }
140 :
141 2 : if (eDataType == GDT_Byte)
142 : {
143 2050 : for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
144 : {
145 2048 : if (panGridRaster[i] == ESRI_GRID_NO_DATA)
146 2042 : ((GByte *)pImage)[i] = 255;
147 : else
148 6 : ((GByte *)pImage)[i] = (GByte)panGridRaster[i];
149 : }
150 : }
151 0 : else if (eDataType == GDT_Int16)
152 : {
153 0 : for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
154 : {
155 0 : if (panGridRaster[i] == ESRI_GRID_NO_DATA)
156 0 : ((GInt16 *)pImage)[i] = -32768;
157 : else
158 0 : ((GInt16 *)pImage)[i] = (GInt16)panGridRaster[i];
159 : }
160 : }
161 : else
162 : {
163 0 : for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
164 0 : ((GInt32 *)pImage)[i] = panGridRaster[i];
165 : }
166 :
167 2 : CPLFree(panGridRaster);
168 :
169 2 : return CE_None;
170 : }
171 : else
172 : {
173 0 : return AIGReadFloatTile(poODS->psInfo, nBlockXOff, nBlockYOff,
174 0 : (float *)pImage);
175 : }
176 : }
177 :
178 : /************************************************************************/
179 : /* GetDefaultRAT() */
180 : /************************************************************************/
181 :
182 0 : GDALRasterAttributeTable *AIGRasterBand::GetDefaultRAT()
183 :
184 : {
185 0 : AIGDataset *poODS = (AIGDataset *)poDS;
186 :
187 : /* -------------------------------------------------------------------- */
188 : /* Read info raster attribute table, if present. */
189 : /* -------------------------------------------------------------------- */
190 0 : if (!poODS->bHasReadRat)
191 : {
192 0 : poODS->ReadRAT();
193 0 : poODS->bHasReadRat = true;
194 : }
195 :
196 0 : if (poODS->poRAT)
197 0 : return poODS->poRAT;
198 : else
199 0 : return GDALPamRasterBand::GetDefaultRAT();
200 : }
201 :
202 : /************************************************************************/
203 : /* GetMinimum() */
204 : /************************************************************************/
205 :
206 1 : double AIGRasterBand::GetMinimum(int *pbSuccess)
207 :
208 : {
209 1 : AIGDataset *poODS = (AIGDataset *)poDS;
210 :
211 1 : if (pbSuccess != nullptr)
212 1 : *pbSuccess = TRUE;
213 :
214 1 : return poODS->psInfo->dfMin;
215 : }
216 :
217 : /************************************************************************/
218 : /* GetMaximum() */
219 : /************************************************************************/
220 :
221 1 : double AIGRasterBand::GetMaximum(int *pbSuccess)
222 :
223 : {
224 1 : AIGDataset *poODS = (AIGDataset *)poDS;
225 :
226 1 : if (pbSuccess != nullptr)
227 1 : *pbSuccess = TRUE;
228 :
229 1 : return poODS->psInfo->dfMax;
230 : }
231 :
232 : /************************************************************************/
233 : /* GetNoDataValue() */
234 : /************************************************************************/
235 :
236 3 : double AIGRasterBand::GetNoDataValue(int *pbSuccess)
237 :
238 : {
239 3 : if (pbSuccess != nullptr)
240 3 : *pbSuccess = TRUE;
241 :
242 3 : if (eDataType == GDT_Float32)
243 0 : return ESRI_GRID_FLOAT_NO_DATA;
244 :
245 3 : if (eDataType == GDT_Int16)
246 0 : return -32768;
247 :
248 3 : if (eDataType == GDT_Byte)
249 3 : return 255;
250 :
251 0 : return ESRI_GRID_NO_DATA;
252 : }
253 :
254 : /************************************************************************/
255 : /* GetColorInterpretation() */
256 : /************************************************************************/
257 :
258 0 : GDALColorInterp AIGRasterBand::GetColorInterpretation()
259 :
260 : {
261 0 : AIGDataset *poODS = (AIGDataset *)poDS;
262 :
263 0 : if (poODS->poCT != nullptr)
264 0 : return GCI_PaletteIndex;
265 :
266 0 : return GDALPamRasterBand::GetColorInterpretation();
267 : }
268 :
269 : /************************************************************************/
270 : /* GetColorTable() */
271 : /************************************************************************/
272 :
273 2 : GDALColorTable *AIGRasterBand::GetColorTable()
274 :
275 : {
276 2 : AIGDataset *poODS = (AIGDataset *)poDS;
277 :
278 2 : if (poODS->poCT != nullptr)
279 2 : return poODS->poCT;
280 :
281 0 : return GDALPamRasterBand::GetColorTable();
282 : }
283 :
284 : /************************************************************************/
285 : /* ==================================================================== */
286 : /* AIGDataset */
287 : /* ==================================================================== */
288 : /************************************************************************/
289 :
290 : /************************************************************************/
291 : /* AIGDataset() */
292 : /************************************************************************/
293 :
294 9 : AIGDataset::AIGDataset()
295 : : psInfo(nullptr), papszPrj(nullptr), poCT(nullptr), bHasReadRat(false),
296 9 : poRAT(nullptr)
297 : {
298 9 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
299 9 : }
300 :
301 : /************************************************************************/
302 : /* ~AIGDataset() */
303 : /************************************************************************/
304 :
305 18 : AIGDataset::~AIGDataset()
306 :
307 : {
308 9 : FlushCache(true);
309 9 : CSLDestroy(papszPrj);
310 9 : if (psInfo != nullptr)
311 9 : AIGClose(psInfo);
312 :
313 9 : if (poCT != nullptr)
314 7 : delete poCT;
315 :
316 9 : if (poRAT != nullptr)
317 0 : delete poRAT;
318 18 : }
319 :
320 : /************************************************************************/
321 : /* GetFileList() */
322 : /************************************************************************/
323 :
324 2 : char **AIGDataset::GetFileList()
325 :
326 : {
327 2 : char **papszFileList = GDALPamDataset::GetFileList();
328 :
329 : // Add in all files in the cover directory.
330 2 : char **papszCoverFiles = VSIReadDir(GetDescription());
331 :
332 21 : for (int i = 0; papszCoverFiles != nullptr && papszCoverFiles[i] != nullptr;
333 : i++)
334 : {
335 19 : if (EQUAL(papszCoverFiles[i], ".") || EQUAL(papszCoverFiles[i], ".."))
336 4 : continue;
337 :
338 15 : papszFileList = CSLAddString(
339 : papszFileList,
340 15 : CPLFormFilename(GetDescription(), papszCoverFiles[i], nullptr));
341 : }
342 2 : CSLDestroy(papszCoverFiles);
343 :
344 2 : return papszFileList;
345 : }
346 :
347 : /************************************************************************/
348 : /* AIGErrorHandlerVATOpen() */
349 : /************************************************************************/
350 :
351 : class AIGErrorDescription
352 : {
353 : public:
354 : CPLErr eErr;
355 : CPLErrorNum no;
356 : CPLString osMsg;
357 : };
358 :
359 0 : static void CPL_STDCALL AIGErrorHandlerVATOpen(CPLErr eErr, CPLErrorNum no,
360 : const char *msg)
361 : {
362 : std::vector<AIGErrorDescription> *paoErrors =
363 0 : (std::vector<AIGErrorDescription> *)CPLGetErrorHandlerUserData();
364 0 : if (STARTS_WITH_CI(msg, "EOF encountered in") &&
365 0 : strstr(msg, "../info/arc.dir") != nullptr)
366 0 : return;
367 0 : if (STARTS_WITH_CI(msg, "Failed to open table "))
368 0 : return;
369 0 : AIGErrorDescription oError;
370 0 : oError.eErr = eErr;
371 0 : oError.no = no;
372 0 : oError.osMsg = msg;
373 0 : paoErrors->push_back(oError);
374 : }
375 :
376 : /************************************************************************/
377 : /* ReadRAT() */
378 : /************************************************************************/
379 :
380 0 : void AIGDataset::ReadRAT()
381 :
382 : {
383 : /* -------------------------------------------------------------------- */
384 : /* Check if we have an associated info directory. If not */
385 : /* return quietly. */
386 : /* -------------------------------------------------------------------- */
387 0 : CPLString osInfoPath, osTableName;
388 : VSIStatBufL sStatBuf;
389 :
390 0 : osInfoPath = psInfo->pszCoverName;
391 0 : osInfoPath += "/../info";
392 :
393 0 : if (VSIStatL(osInfoPath, &sStatBuf) != 0)
394 : {
395 0 : CPLDebug("AIG", "No associated info directory at: %s, skip RAT.",
396 : osInfoPath.c_str());
397 0 : return;
398 : }
399 :
400 0 : osInfoPath += "/";
401 :
402 : /* -------------------------------------------------------------------- */
403 : /* Attempt to open the VAT table associated with this coverage. */
404 : /* -------------------------------------------------------------------- */
405 0 : osTableName = CPLGetFilename(psInfo->pszCoverName);
406 0 : osTableName += ".VAT";
407 :
408 : /* Turn off errors that can be triggered if the info has no VAT */
409 : /* table related with this coverage */
410 0 : std::vector<AIGErrorDescription> aoErrors;
411 0 : CPLPushErrorHandlerEx(AIGErrorHandlerVATOpen, &aoErrors);
412 :
413 0 : AVCBinFile *psFile = AVCBinReadOpen(
414 : osInfoPath, osTableName, AVCCoverTypeUnknown, AVCFileTABLE, nullptr);
415 0 : CPLPopErrorHandler();
416 :
417 : /* Emit other errors */
418 0 : std::vector<AIGErrorDescription>::const_iterator oIter;
419 0 : for (oIter = aoErrors.begin(); oIter != aoErrors.end(); ++oIter)
420 : {
421 0 : const AIGErrorDescription &oError = *oIter;
422 0 : CPLError(oError.eErr, oError.no, "%s", oError.osMsg.c_str());
423 : }
424 :
425 0 : CPLErrorReset();
426 0 : if (psFile == nullptr)
427 0 : return;
428 :
429 0 : AVCTableDef *psTableDef = psFile->hdr.psTableDef;
430 :
431 : /* -------------------------------------------------------------------- */
432 : /* Setup columns in corresponding RAT. */
433 : /* -------------------------------------------------------------------- */
434 0 : poRAT = new GDALDefaultRasterAttributeTable();
435 :
436 0 : for (int iField = 0; iField < psTableDef->numFields; iField++)
437 : {
438 0 : AVCFieldInfo *psFDef = psTableDef->pasFieldDef + iField;
439 0 : GDALRATFieldUsage eFUsage = GFU_Generic;
440 0 : GDALRATFieldType eFType = GFT_String;
441 :
442 0 : CPLString osFName = psFDef->szName;
443 0 : osFName.Trim();
444 :
445 0 : if (EQUAL(osFName, "VALUE"))
446 0 : eFUsage = GFU_MinMax;
447 0 : else if (EQUAL(osFName, "COUNT"))
448 0 : eFUsage = GFU_PixelCount;
449 :
450 0 : if (psFDef->nType1 * 10 == AVC_FT_BININT)
451 0 : eFType = GFT_Integer;
452 0 : else if (psFDef->nType1 * 10 == AVC_FT_BINFLOAT)
453 0 : eFType = GFT_Real;
454 :
455 0 : poRAT->CreateColumn(osFName, eFType, eFUsage);
456 : }
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Process all records into RAT. */
460 : /* -------------------------------------------------------------------- */
461 0 : AVCField *pasFields = nullptr;
462 0 : int iRecord = 0;
463 :
464 0 : while ((pasFields = AVCBinReadNextTableRec(psFile)) != nullptr)
465 : {
466 0 : iRecord++;
467 :
468 0 : for (int iField = 0; iField < psTableDef->numFields; iField++)
469 : {
470 0 : switch (psTableDef->pasFieldDef[iField].nType1 * 10)
471 : {
472 0 : case AVC_FT_DATE:
473 : case AVC_FT_FIXINT:
474 : case AVC_FT_CHAR:
475 : case AVC_FT_FIXNUM:
476 : {
477 : // XXX - I bet mloskot would like to see const_cast +
478 : // static_cast :-)
479 0 : const char *pszTmp =
480 0 : (const char *)(pasFields[iField].pszStr);
481 0 : CPLString osStrValue(pszTmp);
482 0 : poRAT->SetValue(iRecord - 1, iField, osStrValue.Trim());
483 : }
484 0 : break;
485 :
486 0 : case AVC_FT_BININT:
487 0 : if (psTableDef->pasFieldDef[iField].nSize == 4)
488 0 : poRAT->SetValue(iRecord - 1, iField,
489 0 : pasFields[iField].nInt32);
490 : else
491 0 : poRAT->SetValue(iRecord - 1, iField,
492 0 : pasFields[iField].nInt16);
493 0 : break;
494 :
495 0 : case AVC_FT_BINFLOAT:
496 0 : if (psTableDef->pasFieldDef[iField].nSize == 4)
497 0 : poRAT->SetValue(iRecord - 1, iField,
498 0 : pasFields[iField].fFloat);
499 : else
500 0 : poRAT->SetValue(iRecord - 1, iField,
501 0 : pasFields[iField].dDouble);
502 0 : break;
503 : }
504 : }
505 : }
506 :
507 : /* -------------------------------------------------------------------- */
508 : /* Cleanup */
509 : /* -------------------------------------------------------------------- */
510 :
511 0 : AVCBinReadClose(psFile);
512 :
513 : /* Workaround against #2447 and #3031, to avoid binding languages */
514 : /* not being able to open the dataset */
515 0 : CPLErrorReset();
516 : }
517 :
518 : /************************************************************************/
519 : /* Open() */
520 : /************************************************************************/
521 :
522 35817 : GDALDataset *AIGDataset::Open(GDALOpenInfo *poOpenInfo)
523 :
524 : {
525 : /* -------------------------------------------------------------------- */
526 : /* If the pass name ends in .adf assume a file within the */
527 : /* coverage has been selected, and strip that off the coverage */
528 : /* name. */
529 : /* -------------------------------------------------------------------- */
530 71631 : CPLString osCoverName;
531 :
532 35813 : osCoverName = poOpenInfo->pszFilename;
533 71217 : if (osCoverName.size() > 4 &&
534 35400 : EQUAL(osCoverName.c_str() + osCoverName.size() - 4, ".adf"))
535 : {
536 1 : osCoverName = CPLGetDirname(poOpenInfo->pszFilename);
537 1 : if (osCoverName == "")
538 0 : osCoverName = ".";
539 : }
540 :
541 : /* -------------------------------------------------------------------- */
542 : /* Otherwise verify we were already given a directory. */
543 : /* -------------------------------------------------------------------- */
544 35812 : else if (!poOpenInfo->bIsDirectory)
545 : {
546 35401 : return nullptr;
547 : }
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Verify that a few of the "standard" files are available. */
551 : /* -------------------------------------------------------------------- */
552 : VSIStatBufL sStatBuf;
553 825 : CPLString osTestName;
554 :
555 413 : osTestName.Printf("%s/hdr.adf", osCoverName.c_str());
556 413 : if (VSIStatL(osTestName, &sStatBuf) != 0)
557 : {
558 407 : osTestName.Printf("%s/HDR.ADF", osCoverName.c_str());
559 407 : if (VSIStatL(osTestName, &sStatBuf) != 0)
560 404 : return nullptr;
561 : }
562 :
563 : /* -------------------------------------------------------------------- */
564 : /* Confirm we have at least one raster data file. These can be */
565 : /* sparse so we don't require particular ones to exists but if */
566 : /* there are none this is likely not a grid. */
567 : /* -------------------------------------------------------------------- */
568 9 : char **papszFileList = VSIReadDir(osCoverName);
569 9 : bool bGotOne = false;
570 :
571 9 : if (papszFileList == nullptr)
572 : {
573 : /* Useful when reading from /vsicurl/ on servers that don't */
574 : /* return a file list */
575 : /* such as
576 : * /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/NLCD/89110458
577 : */
578 : do
579 : {
580 0 : osTestName.Printf("%s/W001001.ADF", osCoverName.c_str());
581 0 : if (VSIStatL(osTestName, &sStatBuf) == 0)
582 : {
583 0 : bGotOne = true;
584 0 : break;
585 : }
586 :
587 0 : osTestName.Printf("%s/w001001.adf", osCoverName.c_str());
588 0 : if (VSIStatL(osTestName, &sStatBuf) == 0)
589 : {
590 0 : bGotOne = true;
591 0 : break;
592 : }
593 : } while (false);
594 : }
595 :
596 44 : for (int iFile = 0; papszFileList != nullptr &&
597 44 : papszFileList[iFile] != nullptr && !bGotOne;
598 : iFile++)
599 : {
600 35 : if (strlen(papszFileList[iFile]) != 11)
601 26 : continue;
602 :
603 : // looking for something like w001001.adf or z001013.adf
604 9 : if (papszFileList[iFile][0] != 'w' && papszFileList[iFile][0] != 'W' &&
605 0 : papszFileList[iFile][0] != 'z' && papszFileList[iFile][0] != 'Z')
606 0 : continue;
607 :
608 9 : if (!STARTS_WITH(papszFileList[iFile] + 1, "0010"))
609 0 : continue;
610 :
611 9 : if (!EQUAL(papszFileList[iFile] + 7, ".adf"))
612 0 : continue;
613 :
614 9 : bGotOne = true;
615 : }
616 9 : CSLDestroy(papszFileList);
617 :
618 9 : if (!bGotOne)
619 0 : return nullptr;
620 :
621 : /* -------------------------------------------------------------------- */
622 : /* Open the file. */
623 : /* -------------------------------------------------------------------- */
624 9 : AIGInfo_t *psInfo = AIGOpen(osCoverName.c_str(), "r");
625 :
626 9 : if (psInfo == nullptr)
627 : {
628 0 : CPLErrorReset();
629 0 : return nullptr;
630 : }
631 :
632 : /* -------------------------------------------------------------------- */
633 : /* Confirm the requested access is supported. */
634 : /* -------------------------------------------------------------------- */
635 9 : if (poOpenInfo->eAccess == GA_Update)
636 : {
637 0 : AIGClose(psInfo);
638 0 : CPLError(CE_Failure, CPLE_NotSupported,
639 : "The AIG driver does not support update access to existing"
640 : " datasets.\n");
641 0 : return nullptr;
642 : }
643 : /* -------------------------------------------------------------------- */
644 : /* Create a corresponding GDALDataset. */
645 : /* -------------------------------------------------------------------- */
646 9 : AIGDataset *poDS = new AIGDataset();
647 :
648 9 : poDS->psInfo = psInfo;
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* Try to read a color table (.clr). It seems it is legal to */
652 : /* have more than one so we just use the first one found. */
653 : /* -------------------------------------------------------------------- */
654 9 : char **papszFiles = VSIReadDir(psInfo->pszCoverName);
655 18 : CPLString osClrFilename;
656 9 : CPLString osCleanPath = CPLCleanTrailingSlash(psInfo->pszCoverName);
657 :
658 : // first check for any .clr in coverage dir.
659 43 : for (int iFile = 0; papszFiles != nullptr && papszFiles[iFile] != nullptr;
660 : iFile++)
661 : {
662 73 : if (!EQUAL(CPLGetExtension(papszFiles[iFile]), "clr") &&
663 34 : !EQUAL(CPLGetExtension(papszFiles[iFile]), "CLR"))
664 34 : continue;
665 :
666 : osClrFilename =
667 5 : CPLFormFilename(psInfo->pszCoverName, papszFiles[iFile], nullptr);
668 5 : break;
669 : }
670 :
671 9 : CSLDestroy(papszFiles);
672 :
673 : // Look in parent if we don't find a .clr in the coverage dir.
674 9 : if (osClrFilename.empty())
675 : {
676 8 : CPLString osTestClrFilename;
677 : osTestClrFilename.Printf("%s/../%s.clr", psInfo->pszCoverName,
678 4 : CPLGetFilename(osCleanPath));
679 :
680 4 : if (VSIStatL(osTestClrFilename, &sStatBuf) != 0)
681 : {
682 : osTestClrFilename.Printf("%s/../%s.CLR", psInfo->pszCoverName,
683 4 : CPLGetFilename(osCleanPath));
684 :
685 4 : if (!VSIStatL(osTestClrFilename, &sStatBuf))
686 2 : osClrFilename = std::move(osTestClrFilename);
687 : }
688 : else
689 0 : osClrFilename = std::move(osTestClrFilename);
690 : }
691 :
692 9 : if (!osClrFilename.empty())
693 7 : poDS->TranslateColorTable(osClrFilename);
694 :
695 : /* -------------------------------------------------------------------- */
696 : /* Establish raster info. */
697 : /* -------------------------------------------------------------------- */
698 9 : poDS->nRasterXSize = psInfo->nPixels;
699 9 : poDS->nRasterYSize = psInfo->nLines;
700 9 : poDS->nBands = 1;
701 :
702 : /* -------------------------------------------------------------------- */
703 : /* Create band information objects. */
704 : /* -------------------------------------------------------------------- */
705 9 : poDS->SetBand(1, new AIGRasterBand(poDS, 1));
706 :
707 : /* -------------------------------------------------------------------- */
708 : /* Try to read projection file. */
709 : /* -------------------------------------------------------------------- */
710 : const char *pszPrjFilename =
711 9 : CPLFormCIFilename(psInfo->pszCoverName, "prj", "adf");
712 9 : if (VSIStatL(pszPrjFilename, &sStatBuf) == 0)
713 : {
714 18 : OGRSpatialReference oSRS;
715 9 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
716 :
717 9 : poDS->papszPrj = CSLLoad(pszPrjFilename);
718 :
719 9 : if (oSRS.importFromESRI(poDS->papszPrj) == OGRERR_NONE)
720 : {
721 : // If geographic values are in seconds, we must transform.
722 : // Is there a code for minutes too?
723 10 : if (oSRS.IsGeographic() &&
724 10 : EQUAL(OSR_GDS(poDS->papszPrj, "Units", ""), "DS"))
725 : {
726 0 : psInfo->dfLLX /= 3600.0;
727 0 : psInfo->dfURY /= 3600.0;
728 0 : psInfo->dfCellSizeX /= 3600.0;
729 0 : psInfo->dfCellSizeY /= 3600.0;
730 : }
731 :
732 9 : poDS->m_oSRS = std::move(oSRS);
733 : }
734 : }
735 :
736 : /* -------------------------------------------------------------------- */
737 : /* Initialize any PAM information. */
738 : /* -------------------------------------------------------------------- */
739 9 : poDS->SetDescription(psInfo->pszCoverName);
740 9 : poDS->TryLoadXML();
741 :
742 : /* -------------------------------------------------------------------- */
743 : /* Open overviews. */
744 : /* -------------------------------------------------------------------- */
745 9 : poDS->oOvManager.Initialize(poDS, psInfo->pszCoverName);
746 :
747 9 : return poDS;
748 : }
749 :
750 : /************************************************************************/
751 : /* GetGeoTransform() */
752 : /************************************************************************/
753 :
754 1 : CPLErr AIGDataset::GetGeoTransform(double *padfTransform)
755 :
756 : {
757 1 : padfTransform[0] = psInfo->dfLLX;
758 1 : padfTransform[1] = psInfo->dfCellSizeX;
759 1 : padfTransform[2] = 0;
760 :
761 1 : padfTransform[3] = psInfo->dfURY;
762 1 : padfTransform[4] = 0;
763 1 : padfTransform[5] = -psInfo->dfCellSizeY;
764 :
765 1 : return CE_None;
766 : }
767 :
768 : /************************************************************************/
769 : /* GetSpatialRef() */
770 : /************************************************************************/
771 :
772 1 : const OGRSpatialReference *AIGDataset::GetSpatialRef() const
773 :
774 : {
775 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
776 : }
777 :
778 : /************************************************************************/
779 : /* TranslateColorTable() */
780 : /************************************************************************/
781 :
782 7 : void AIGDataset::TranslateColorTable(const char *pszClrFilename)
783 :
784 : {
785 7 : char **papszClrLines = CSLLoad(pszClrFilename);
786 7 : if (papszClrLines == nullptr)
787 0 : return;
788 :
789 7 : poCT = new GDALColorTable();
790 :
791 1813 : for (int iLine = 0; papszClrLines[iLine] != nullptr; iLine++)
792 : {
793 1806 : char **papszTokens = CSLTokenizeString(papszClrLines[iLine]);
794 :
795 1806 : if (CSLCount(papszTokens) >= 4 && papszTokens[0][0] != '#')
796 : {
797 : int nIndex;
798 : GDALColorEntry sEntry;
799 :
800 1792 : nIndex = atoi(papszTokens[0]);
801 1792 : sEntry.c1 = (short)atoi(papszTokens[1]);
802 1792 : sEntry.c2 = (short)atoi(papszTokens[2]);
803 1792 : sEntry.c3 = (short)atoi(papszTokens[3]);
804 1792 : sEntry.c4 = 255;
805 :
806 1792 : if ((nIndex < 0 || nIndex > 33000) ||
807 1792 : (sEntry.c1 < 0 || sEntry.c1 > 255) ||
808 1792 : (sEntry.c2 < 0 || sEntry.c2 > 255) ||
809 1792 : (sEntry.c3 < 0 || sEntry.c3 > 255))
810 : {
811 0 : CSLDestroy(papszTokens);
812 0 : CPLError(CE_Failure, CPLE_AppDefined,
813 : "Color table entry appears to be corrupt, skipping "
814 : "the rest. ");
815 0 : break;
816 : }
817 :
818 1792 : poCT->SetColorEntry(nIndex, &sEntry);
819 : }
820 :
821 1806 : CSLDestroy(papszTokens);
822 : }
823 :
824 7 : CSLDestroy(papszClrLines);
825 : }
826 :
827 : /************************************************************************/
828 : /* OSR_GDS() */
829 : /************************************************************************/
830 :
831 1 : static CPLString OSR_GDS(char **papszNV, const char *pszField,
832 : const char *pszDefaultValue)
833 :
834 : {
835 1 : if (papszNV == nullptr || papszNV[0] == nullptr)
836 0 : return pszDefaultValue;
837 :
838 1 : int iLine = 0;
839 4 : for (; papszNV[iLine] != nullptr &&
840 4 : !EQUALN(papszNV[iLine], pszField, strlen(pszField));
841 : iLine++)
842 : {
843 : }
844 :
845 1 : if (papszNV[iLine] == nullptr)
846 0 : return pszDefaultValue;
847 : else
848 : {
849 2 : CPLString osResult;
850 1 : char **papszTokens = CSLTokenizeString(papszNV[iLine]);
851 :
852 1 : if (CSLCount(papszTokens) > 1)
853 1 : osResult = papszTokens[1];
854 : else
855 0 : osResult = pszDefaultValue;
856 :
857 1 : CSLDestroy(papszTokens);
858 1 : return osResult;
859 : }
860 : }
861 :
862 : /************************************************************************/
863 : /* AIGRename() */
864 : /* */
865 : /* Custom renamer for AIG dataset. */
866 : /************************************************************************/
867 :
868 0 : static CPLErr AIGRename(const char *pszNewName, const char *pszOldName)
869 :
870 : {
871 : /* -------------------------------------------------------------------- */
872 : /* Make sure we are talking about paths to the coverage */
873 : /* directory. */
874 : /* -------------------------------------------------------------------- */
875 0 : CPLString osOldPath, osNewPath;
876 :
877 0 : if (strlen(CPLGetExtension(pszNewName)) > 0)
878 0 : osNewPath = CPLGetPath(pszNewName);
879 : else
880 0 : osNewPath = pszNewName;
881 :
882 0 : if (strlen(CPLGetExtension(pszOldName)) > 0)
883 0 : osOldPath = CPLGetPath(pszOldName);
884 : else
885 0 : osOldPath = pszOldName;
886 :
887 : /* -------------------------------------------------------------------- */
888 : /* Get file list. */
889 : /* -------------------------------------------------------------------- */
890 :
891 0 : GDALDatasetH hDS = GDALOpen(osOldPath, GA_ReadOnly);
892 0 : if (hDS == nullptr)
893 0 : return CE_Failure;
894 :
895 0 : char **papszFileList = GDALGetFileList(hDS);
896 0 : GDALClose(hDS);
897 :
898 0 : if (papszFileList == nullptr)
899 0 : return CE_Failure;
900 :
901 : /* -------------------------------------------------------------------- */
902 : /* Work out the corresponding new names. */
903 : /* -------------------------------------------------------------------- */
904 0 : char **papszNewFileList = nullptr;
905 :
906 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
907 : {
908 0 : CPLString osNewFilename;
909 :
910 0 : if (!EQUALN(papszFileList[i], osOldPath, osOldPath.size()))
911 : {
912 0 : CPLAssert(false);
913 : return CE_Failure;
914 : }
915 :
916 0 : osNewFilename = osNewPath + (papszFileList[i] + osOldPath.size());
917 :
918 0 : papszNewFileList = CSLAddString(papszNewFileList, osNewFilename);
919 : }
920 :
921 : /* -------------------------------------------------------------------- */
922 : /* Try renaming the directory. */
923 : /* -------------------------------------------------------------------- */
924 0 : if (VSIRename(osNewPath, osOldPath) != 0)
925 : {
926 0 : if (VSIMkdir(osNewPath, 0777) != 0)
927 : {
928 0 : CPLError(CE_Failure, CPLE_AppDefined,
929 : "Unable to create directory %s:\n%s", osNewPath.c_str(),
930 0 : VSIStrerror(errno));
931 0 : CSLDestroy(papszNewFileList);
932 0 : return CE_Failure;
933 : }
934 : }
935 :
936 : /* -------------------------------------------------------------------- */
937 : /* Copy/rename any remaining files. */
938 : /* -------------------------------------------------------------------- */
939 : VSIStatBufL sStatBuf;
940 :
941 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
942 : {
943 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
944 0 : VSI_ISREG(sStatBuf.st_mode))
945 : {
946 0 : if (CPLMoveFile(papszNewFileList[i], papszFileList[i]) != 0)
947 : {
948 0 : CPLError(CE_Failure, CPLE_AppDefined,
949 0 : "Unable to move %s to %s:\n%s", papszFileList[i],
950 0 : papszNewFileList[i], VSIStrerror(errno));
951 0 : CSLDestroy(papszNewFileList);
952 0 : return CE_Failure;
953 : }
954 : }
955 : }
956 :
957 0 : if (VSIStatL(osOldPath, &sStatBuf) == 0)
958 : {
959 0 : if (CPLUnlinkTree(osOldPath) != 0)
960 : {
961 0 : CPLError(CE_Warning, CPLE_AppDefined,
962 : "Unable to cleanup old path.");
963 : }
964 : }
965 :
966 0 : CSLDestroy(papszFileList);
967 0 : CSLDestroy(papszNewFileList);
968 0 : return CE_None;
969 : }
970 :
971 : /************************************************************************/
972 : /* AIGDelete() */
973 : /* */
974 : /* Custom dataset deleter for AIG dataset. */
975 : /************************************************************************/
976 :
977 0 : static CPLErr AIGDelete(const char *pszDatasetname)
978 :
979 : {
980 : /* -------------------------------------------------------------------- */
981 : /* Get file list. */
982 : /* -------------------------------------------------------------------- */
983 0 : GDALDatasetH hDS = GDALOpen(pszDatasetname, GA_ReadOnly);
984 0 : if (hDS == nullptr)
985 0 : return CE_Failure;
986 :
987 0 : char **papszFileList = GDALGetFileList(hDS);
988 0 : GDALClose(hDS);
989 :
990 0 : if (papszFileList == nullptr)
991 0 : return CE_Failure;
992 :
993 : /* -------------------------------------------------------------------- */
994 : /* Delete all regular files. */
995 : /* -------------------------------------------------------------------- */
996 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
997 : {
998 : VSIStatBufL sStatBuf;
999 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
1000 0 : VSI_ISREG(sStatBuf.st_mode))
1001 : {
1002 0 : if (VSIUnlink(papszFileList[i]) != 0)
1003 : {
1004 0 : CPLError(CE_Failure, CPLE_AppDefined,
1005 0 : "Unable to delete '%s':\n%s", papszFileList[i],
1006 0 : VSIStrerror(errno));
1007 0 : return CE_Failure;
1008 : }
1009 : }
1010 : }
1011 :
1012 : /* -------------------------------------------------------------------- */
1013 : /* Delete directories. */
1014 : /* -------------------------------------------------------------------- */
1015 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
1016 : {
1017 : VSIStatBufL sStatBuf;
1018 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
1019 0 : VSI_ISDIR(sStatBuf.st_mode))
1020 : {
1021 0 : if (CPLUnlinkTree(papszFileList[i]) != 0)
1022 0 : return CE_Failure;
1023 : }
1024 : }
1025 :
1026 0 : return CE_None;
1027 : }
1028 :
1029 : /************************************************************************/
1030 : /* GDALRegister_AIG() */
1031 : /************************************************************************/
1032 :
1033 1595 : void GDALRegister_AIGrid()
1034 :
1035 : {
1036 1595 : if (GDALGetDriverByName("AIG") != nullptr)
1037 302 : return;
1038 :
1039 1293 : GDALDriver *poDriver = new GDALDriver();
1040 :
1041 1293 : poDriver->SetDescription("AIG");
1042 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1043 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Arc/Info Binary Grid");
1044 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/aig.html");
1045 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1046 :
1047 1293 : poDriver->pfnOpen = AIGDataset::Open;
1048 :
1049 1293 : poDriver->pfnRename = AIGRename;
1050 1293 : poDriver->pfnDelete = AIGDelete;
1051 :
1052 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
1053 : }
|