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 30 : CPLFormFilenameSafe(GetDescription(), papszCoverFiles[i], nullptr)
341 : .c_str());
342 : }
343 2 : CSLDestroy(papszCoverFiles);
344 :
345 2 : return papszFileList;
346 : }
347 :
348 : /************************************************************************/
349 : /* AIGErrorHandlerVATOpen() */
350 : /************************************************************************/
351 :
352 : class AIGErrorDescription
353 : {
354 : public:
355 : CPLErr eErr;
356 : CPLErrorNum no;
357 : CPLString osMsg;
358 : };
359 :
360 0 : static void CPL_STDCALL AIGErrorHandlerVATOpen(CPLErr eErr, CPLErrorNum no,
361 : const char *msg)
362 : {
363 : std::vector<AIGErrorDescription> *paoErrors =
364 0 : (std::vector<AIGErrorDescription> *)CPLGetErrorHandlerUserData();
365 0 : if (STARTS_WITH_CI(msg, "EOF encountered in") &&
366 0 : strstr(msg, "../info/arc.dir") != nullptr)
367 0 : return;
368 0 : if (STARTS_WITH_CI(msg, "Failed to open table "))
369 0 : return;
370 0 : AIGErrorDescription oError;
371 0 : oError.eErr = eErr;
372 0 : oError.no = no;
373 0 : oError.osMsg = msg;
374 0 : paoErrors->push_back(oError);
375 : }
376 :
377 : /************************************************************************/
378 : /* ReadRAT() */
379 : /************************************************************************/
380 :
381 0 : void AIGDataset::ReadRAT()
382 :
383 : {
384 : /* -------------------------------------------------------------------- */
385 : /* Check if we have an associated info directory. If not */
386 : /* return quietly. */
387 : /* -------------------------------------------------------------------- */
388 0 : CPLString osInfoPath, osTableName;
389 : VSIStatBufL sStatBuf;
390 :
391 0 : osInfoPath = psInfo->pszCoverName;
392 0 : osInfoPath += "/../info";
393 :
394 0 : if (VSIStatL(osInfoPath, &sStatBuf) != 0)
395 : {
396 0 : CPLDebug("AIG", "No associated info directory at: %s, skip RAT.",
397 : osInfoPath.c_str());
398 0 : return;
399 : }
400 :
401 0 : osInfoPath += "/";
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* Attempt to open the VAT table associated with this coverage. */
405 : /* -------------------------------------------------------------------- */
406 0 : osTableName = CPLGetFilename(psInfo->pszCoverName);
407 0 : osTableName += ".VAT";
408 :
409 : /* Turn off errors that can be triggered if the info has no VAT */
410 : /* table related with this coverage */
411 0 : std::vector<AIGErrorDescription> aoErrors;
412 0 : CPLPushErrorHandlerEx(AIGErrorHandlerVATOpen, &aoErrors);
413 :
414 0 : AVCBinFile *psFile = AVCBinReadOpen(
415 : osInfoPath, osTableName, AVCCoverTypeUnknown, AVCFileTABLE, nullptr);
416 0 : CPLPopErrorHandler();
417 :
418 : /* Emit other errors */
419 0 : std::vector<AIGErrorDescription>::const_iterator oIter;
420 0 : for (oIter = aoErrors.begin(); oIter != aoErrors.end(); ++oIter)
421 : {
422 0 : const AIGErrorDescription &oError = *oIter;
423 0 : CPLError(oError.eErr, oError.no, "%s", oError.osMsg.c_str());
424 : }
425 :
426 0 : CPLErrorReset();
427 0 : if (psFile == nullptr)
428 0 : return;
429 :
430 0 : AVCTableDef *psTableDef = psFile->hdr.psTableDef;
431 :
432 : /* -------------------------------------------------------------------- */
433 : /* Setup columns in corresponding RAT. */
434 : /* -------------------------------------------------------------------- */
435 0 : poRAT = new GDALDefaultRasterAttributeTable();
436 :
437 0 : for (int iField = 0; iField < psTableDef->numFields; iField++)
438 : {
439 0 : AVCFieldInfo *psFDef = psTableDef->pasFieldDef + iField;
440 0 : GDALRATFieldUsage eFUsage = GFU_Generic;
441 0 : GDALRATFieldType eFType = GFT_String;
442 :
443 0 : CPLString osFName = psFDef->szName;
444 0 : osFName.Trim();
445 :
446 0 : if (EQUAL(osFName, "VALUE"))
447 0 : eFUsage = GFU_MinMax;
448 0 : else if (EQUAL(osFName, "COUNT"))
449 0 : eFUsage = GFU_PixelCount;
450 :
451 0 : if (psFDef->nType1 * 10 == AVC_FT_BININT)
452 0 : eFType = GFT_Integer;
453 0 : else if (psFDef->nType1 * 10 == AVC_FT_BINFLOAT)
454 0 : eFType = GFT_Real;
455 :
456 0 : poRAT->CreateColumn(osFName, eFType, eFUsage);
457 : }
458 :
459 : /* -------------------------------------------------------------------- */
460 : /* Process all records into RAT. */
461 : /* -------------------------------------------------------------------- */
462 0 : AVCField *pasFields = nullptr;
463 0 : int iRecord = 0;
464 :
465 0 : while ((pasFields = AVCBinReadNextTableRec(psFile)) != nullptr)
466 : {
467 0 : iRecord++;
468 :
469 0 : for (int iField = 0; iField < psTableDef->numFields; iField++)
470 : {
471 0 : switch (psTableDef->pasFieldDef[iField].nType1 * 10)
472 : {
473 0 : case AVC_FT_DATE:
474 : case AVC_FT_FIXINT:
475 : case AVC_FT_CHAR:
476 : case AVC_FT_FIXNUM:
477 : {
478 : // XXX - I bet mloskot would like to see const_cast +
479 : // static_cast :-)
480 0 : const char *pszTmp =
481 0 : (const char *)(pasFields[iField].pszStr);
482 0 : CPLString osStrValue(pszTmp);
483 0 : poRAT->SetValue(iRecord - 1, iField, osStrValue.Trim());
484 : }
485 0 : break;
486 :
487 0 : case AVC_FT_BININT:
488 0 : if (psTableDef->pasFieldDef[iField].nSize == 4)
489 0 : poRAT->SetValue(iRecord - 1, iField,
490 0 : pasFields[iField].nInt32);
491 : else
492 0 : poRAT->SetValue(iRecord - 1, iField,
493 0 : pasFields[iField].nInt16);
494 0 : break;
495 :
496 0 : case AVC_FT_BINFLOAT:
497 0 : if (psTableDef->pasFieldDef[iField].nSize == 4)
498 0 : poRAT->SetValue(iRecord - 1, iField,
499 0 : pasFields[iField].fFloat);
500 : else
501 0 : poRAT->SetValue(iRecord - 1, iField,
502 0 : pasFields[iField].dDouble);
503 0 : break;
504 : }
505 : }
506 : }
507 :
508 : /* -------------------------------------------------------------------- */
509 : /* Cleanup */
510 : /* -------------------------------------------------------------------- */
511 :
512 0 : AVCBinReadClose(psFile);
513 :
514 : /* Workaround against #2447 and #3031, to avoid binding languages */
515 : /* not being able to open the dataset */
516 0 : CPLErrorReset();
517 : }
518 :
519 : /************************************************************************/
520 : /* Open() */
521 : /************************************************************************/
522 :
523 36138 : GDALDataset *AIGDataset::Open(GDALOpenInfo *poOpenInfo)
524 :
525 : {
526 : /* -------------------------------------------------------------------- */
527 : /* If the pass name ends in .adf assume a file within the */
528 : /* coverage has been selected, and strip that off the coverage */
529 : /* name. */
530 : /* -------------------------------------------------------------------- */
531 72274 : CPLString osCoverName;
532 :
533 36137 : osCoverName = poOpenInfo->pszFilename;
534 71836 : if (osCoverName.size() > 4 &&
535 35698 : EQUAL(osCoverName.c_str() + osCoverName.size() - 4, ".adf"))
536 : {
537 1 : osCoverName = CPLGetDirnameSafe(poOpenInfo->pszFilename);
538 1 : if (osCoverName == "")
539 0 : osCoverName = ".";
540 : }
541 :
542 : /* -------------------------------------------------------------------- */
543 : /* Otherwise verify we were already given a directory. */
544 : /* -------------------------------------------------------------------- */
545 36135 : else if (!poOpenInfo->bIsDirectory)
546 : {
547 35718 : return nullptr;
548 : }
549 :
550 : /* -------------------------------------------------------------------- */
551 : /* Verify that a few of the "standard" files are available. */
552 : /* -------------------------------------------------------------------- */
553 : VSIStatBufL sStatBuf;
554 836 : CPLString osTestName;
555 :
556 418 : osTestName.Printf("%s/hdr.adf", osCoverName.c_str());
557 418 : if (VSIStatL(osTestName, &sStatBuf) != 0)
558 : {
559 412 : osTestName.Printf("%s/HDR.ADF", osCoverName.c_str());
560 412 : if (VSIStatL(osTestName, &sStatBuf) != 0)
561 409 : return nullptr;
562 : }
563 :
564 : /* -------------------------------------------------------------------- */
565 : /* Confirm we have at least one raster data file. These can be */
566 : /* sparse so we don't require particular ones to exists but if */
567 : /* there are none this is likely not a grid. */
568 : /* -------------------------------------------------------------------- */
569 9 : char **papszFileList = VSIReadDir(osCoverName);
570 9 : bool bGotOne = false;
571 :
572 9 : if (papszFileList == nullptr)
573 : {
574 : /* Useful when reading from /vsicurl/ on servers that don't */
575 : /* return a file list */
576 : /* such as
577 : * /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/NLCD/89110458
578 : */
579 : do
580 : {
581 0 : osTestName.Printf("%s/W001001.ADF", osCoverName.c_str());
582 0 : if (VSIStatL(osTestName, &sStatBuf) == 0)
583 : {
584 0 : bGotOne = true;
585 0 : break;
586 : }
587 :
588 0 : osTestName.Printf("%s/w001001.adf", osCoverName.c_str());
589 0 : if (VSIStatL(osTestName, &sStatBuf) == 0)
590 : {
591 0 : bGotOne = true;
592 0 : break;
593 : }
594 : } while (false);
595 : }
596 :
597 44 : for (int iFile = 0; papszFileList != nullptr &&
598 44 : papszFileList[iFile] != nullptr && !bGotOne;
599 : iFile++)
600 : {
601 35 : if (strlen(papszFileList[iFile]) != 11)
602 26 : continue;
603 :
604 : // looking for something like w001001.adf or z001013.adf
605 9 : if (papszFileList[iFile][0] != 'w' && papszFileList[iFile][0] != 'W' &&
606 0 : papszFileList[iFile][0] != 'z' && papszFileList[iFile][0] != 'Z')
607 0 : continue;
608 :
609 9 : if (!STARTS_WITH(papszFileList[iFile] + 1, "0010"))
610 0 : continue;
611 :
612 9 : if (!EQUAL(papszFileList[iFile] + 7, ".adf"))
613 0 : continue;
614 :
615 9 : bGotOne = true;
616 : }
617 9 : CSLDestroy(papszFileList);
618 :
619 9 : if (!bGotOne)
620 0 : return nullptr;
621 :
622 : /* -------------------------------------------------------------------- */
623 : /* Open the file. */
624 : /* -------------------------------------------------------------------- */
625 9 : AIGInfo_t *psInfo = AIGOpen(osCoverName.c_str(), "r");
626 :
627 9 : if (psInfo == nullptr)
628 : {
629 0 : CPLErrorReset();
630 0 : return nullptr;
631 : }
632 :
633 : /* -------------------------------------------------------------------- */
634 : /* Confirm the requested access is supported. */
635 : /* -------------------------------------------------------------------- */
636 9 : if (poOpenInfo->eAccess == GA_Update)
637 : {
638 0 : AIGClose(psInfo);
639 0 : CPLError(CE_Failure, CPLE_NotSupported,
640 : "The AIG driver does not support update access to existing"
641 : " datasets.\n");
642 0 : return nullptr;
643 : }
644 : /* -------------------------------------------------------------------- */
645 : /* Create a corresponding GDALDataset. */
646 : /* -------------------------------------------------------------------- */
647 9 : AIGDataset *poDS = new AIGDataset();
648 :
649 9 : poDS->psInfo = psInfo;
650 :
651 : /* -------------------------------------------------------------------- */
652 : /* Try to read a color table (.clr). It seems it is legal to */
653 : /* have more than one so we just use the first one found. */
654 : /* -------------------------------------------------------------------- */
655 9 : char **papszFiles = VSIReadDir(psInfo->pszCoverName);
656 18 : CPLString osClrFilename;
657 18 : CPLString osCleanPath = CPLCleanTrailingSlashSafe(psInfo->pszCoverName);
658 :
659 : // first check for any .clr in coverage dir.
660 43 : for (int iFile = 0; papszFiles != nullptr && papszFiles[iFile] != nullptr;
661 : iFile++)
662 : {
663 39 : const std::string osExt = CPLGetExtensionSafe(papszFiles[iFile]);
664 39 : if (!EQUAL(osExt.c_str(), "clr") && !EQUAL(osExt.c_str(), "CLR"))
665 34 : continue;
666 :
667 0 : osClrFilename = CPLFormFilenameSafe(psInfo->pszCoverName,
668 5 : papszFiles[iFile], nullptr);
669 5 : break;
670 : }
671 :
672 9 : CSLDestroy(papszFiles);
673 :
674 : // Look in parent if we don't find a .clr in the coverage dir.
675 9 : if (osClrFilename.empty())
676 : {
677 8 : CPLString osTestClrFilename;
678 : osTestClrFilename.Printf("%s/../%s.clr", psInfo->pszCoverName,
679 4 : CPLGetFilename(osCleanPath));
680 :
681 4 : if (VSIStatL(osTestClrFilename, &sStatBuf) != 0)
682 : {
683 : osTestClrFilename.Printf("%s/../%s.CLR", psInfo->pszCoverName,
684 4 : CPLGetFilename(osCleanPath));
685 :
686 4 : if (!VSIStatL(osTestClrFilename, &sStatBuf))
687 2 : osClrFilename = std::move(osTestClrFilename);
688 : }
689 : else
690 0 : osClrFilename = std::move(osTestClrFilename);
691 : }
692 :
693 9 : if (!osClrFilename.empty())
694 7 : poDS->TranslateColorTable(osClrFilename);
695 :
696 : /* -------------------------------------------------------------------- */
697 : /* Establish raster info. */
698 : /* -------------------------------------------------------------------- */
699 9 : poDS->nRasterXSize = psInfo->nPixels;
700 9 : poDS->nRasterYSize = psInfo->nLines;
701 9 : poDS->nBands = 1;
702 :
703 : /* -------------------------------------------------------------------- */
704 : /* Create band information objects. */
705 : /* -------------------------------------------------------------------- */
706 9 : poDS->SetBand(1, new AIGRasterBand(poDS, 1));
707 :
708 : /* -------------------------------------------------------------------- */
709 : /* Try to read projection file. */
710 : /* -------------------------------------------------------------------- */
711 : const std::string osPrjFilename =
712 9 : CPLFormCIFilenameSafe(psInfo->pszCoverName, "prj", "adf");
713 9 : if (VSIStatL(osPrjFilename.c_str(), &sStatBuf) == 0)
714 : {
715 18 : OGRSpatialReference oSRS;
716 9 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
717 :
718 9 : poDS->papszPrj = CSLLoad(osPrjFilename.c_str());
719 :
720 9 : if (oSRS.importFromESRI(poDS->papszPrj) == OGRERR_NONE)
721 : {
722 : // If geographic values are in seconds, we must transform.
723 : // Is there a code for minutes too?
724 10 : if (oSRS.IsGeographic() &&
725 10 : EQUAL(OSR_GDS(poDS->papszPrj, "Units", ""), "DS"))
726 : {
727 0 : psInfo->dfLLX /= 3600.0;
728 0 : psInfo->dfURY /= 3600.0;
729 0 : psInfo->dfCellSizeX /= 3600.0;
730 0 : psInfo->dfCellSizeY /= 3600.0;
731 : }
732 :
733 9 : poDS->m_oSRS = std::move(oSRS);
734 : }
735 : }
736 :
737 : /* -------------------------------------------------------------------- */
738 : /* Initialize any PAM information. */
739 : /* -------------------------------------------------------------------- */
740 9 : poDS->SetDescription(psInfo->pszCoverName);
741 9 : poDS->TryLoadXML();
742 :
743 : /* -------------------------------------------------------------------- */
744 : /* Open overviews. */
745 : /* -------------------------------------------------------------------- */
746 9 : poDS->oOvManager.Initialize(poDS, psInfo->pszCoverName);
747 :
748 9 : return poDS;
749 : }
750 :
751 : /************************************************************************/
752 : /* GetGeoTransform() */
753 : /************************************************************************/
754 :
755 1 : CPLErr AIGDataset::GetGeoTransform(double *padfTransform)
756 :
757 : {
758 1 : padfTransform[0] = psInfo->dfLLX;
759 1 : padfTransform[1] = psInfo->dfCellSizeX;
760 1 : padfTransform[2] = 0;
761 :
762 1 : padfTransform[3] = psInfo->dfURY;
763 1 : padfTransform[4] = 0;
764 1 : padfTransform[5] = -psInfo->dfCellSizeY;
765 :
766 1 : return CE_None;
767 : }
768 :
769 : /************************************************************************/
770 : /* GetSpatialRef() */
771 : /************************************************************************/
772 :
773 1 : const OGRSpatialReference *AIGDataset::GetSpatialRef() const
774 :
775 : {
776 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
777 : }
778 :
779 : /************************************************************************/
780 : /* TranslateColorTable() */
781 : /************************************************************************/
782 :
783 7 : void AIGDataset::TranslateColorTable(const char *pszClrFilename)
784 :
785 : {
786 7 : char **papszClrLines = CSLLoad(pszClrFilename);
787 7 : if (papszClrLines == nullptr)
788 0 : return;
789 :
790 7 : poCT = new GDALColorTable();
791 :
792 1813 : for (int iLine = 0; papszClrLines[iLine] != nullptr; iLine++)
793 : {
794 1806 : char **papszTokens = CSLTokenizeString(papszClrLines[iLine]);
795 :
796 1806 : if (CSLCount(papszTokens) >= 4 && papszTokens[0][0] != '#')
797 : {
798 : int nIndex;
799 : GDALColorEntry sEntry;
800 :
801 1792 : nIndex = atoi(papszTokens[0]);
802 1792 : sEntry.c1 = (short)atoi(papszTokens[1]);
803 1792 : sEntry.c2 = (short)atoi(papszTokens[2]);
804 1792 : sEntry.c3 = (short)atoi(papszTokens[3]);
805 1792 : sEntry.c4 = 255;
806 :
807 1792 : if ((nIndex < 0 || nIndex > 33000) ||
808 1792 : (sEntry.c1 < 0 || sEntry.c1 > 255) ||
809 1792 : (sEntry.c2 < 0 || sEntry.c2 > 255) ||
810 1792 : (sEntry.c3 < 0 || sEntry.c3 > 255))
811 : {
812 0 : CSLDestroy(papszTokens);
813 0 : CPLError(CE_Failure, CPLE_AppDefined,
814 : "Color table entry appears to be corrupt, skipping "
815 : "the rest. ");
816 0 : break;
817 : }
818 :
819 1792 : poCT->SetColorEntry(nIndex, &sEntry);
820 : }
821 :
822 1806 : CSLDestroy(papszTokens);
823 : }
824 :
825 7 : CSLDestroy(papszClrLines);
826 : }
827 :
828 : /************************************************************************/
829 : /* OSR_GDS() */
830 : /************************************************************************/
831 :
832 1 : static CPLString OSR_GDS(char **papszNV, const char *pszField,
833 : const char *pszDefaultValue)
834 :
835 : {
836 1 : if (papszNV == nullptr || papszNV[0] == nullptr)
837 0 : return pszDefaultValue;
838 :
839 1 : int iLine = 0;
840 4 : for (; papszNV[iLine] != nullptr &&
841 4 : !EQUALN(papszNV[iLine], pszField, strlen(pszField));
842 : iLine++)
843 : {
844 : }
845 :
846 1 : if (papszNV[iLine] == nullptr)
847 0 : return pszDefaultValue;
848 : else
849 : {
850 2 : CPLString osResult;
851 1 : char **papszTokens = CSLTokenizeString(papszNV[iLine]);
852 :
853 1 : if (CSLCount(papszTokens) > 1)
854 1 : osResult = papszTokens[1];
855 : else
856 0 : osResult = pszDefaultValue;
857 :
858 1 : CSLDestroy(papszTokens);
859 1 : return osResult;
860 : }
861 : }
862 :
863 : /************************************************************************/
864 : /* AIGRename() */
865 : /* */
866 : /* Custom renamer for AIG dataset. */
867 : /************************************************************************/
868 :
869 0 : static CPLErr AIGRename(const char *pszNewName, const char *pszOldName)
870 :
871 : {
872 : /* -------------------------------------------------------------------- */
873 : /* Make sure we are talking about paths to the coverage */
874 : /* directory. */
875 : /* -------------------------------------------------------------------- */
876 0 : CPLString osOldPath, osNewPath;
877 :
878 0 : if (!CPLGetExtensionSafe(pszNewName).empty())
879 0 : osNewPath = CPLGetPathSafe(pszNewName);
880 : else
881 0 : osNewPath = pszNewName;
882 :
883 0 : if (!CPLGetExtensionSafe(pszOldName).empty())
884 0 : osOldPath = CPLGetPathSafe(pszOldName);
885 : else
886 0 : osOldPath = pszOldName;
887 :
888 : /* -------------------------------------------------------------------- */
889 : /* Get file list. */
890 : /* -------------------------------------------------------------------- */
891 :
892 0 : GDALDatasetH hDS = GDALOpen(osOldPath, GA_ReadOnly);
893 0 : if (hDS == nullptr)
894 0 : return CE_Failure;
895 :
896 0 : char **papszFileList = GDALGetFileList(hDS);
897 0 : GDALClose(hDS);
898 :
899 0 : if (papszFileList == nullptr)
900 0 : return CE_Failure;
901 :
902 : /* -------------------------------------------------------------------- */
903 : /* Work out the corresponding new names. */
904 : /* -------------------------------------------------------------------- */
905 0 : char **papszNewFileList = nullptr;
906 :
907 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
908 : {
909 0 : CPLString osNewFilename;
910 :
911 0 : if (!EQUALN(papszFileList[i], osOldPath, osOldPath.size()))
912 : {
913 0 : CPLAssert(false);
914 : return CE_Failure;
915 : }
916 :
917 0 : osNewFilename = osNewPath + (papszFileList[i] + osOldPath.size());
918 :
919 0 : papszNewFileList = CSLAddString(papszNewFileList, osNewFilename);
920 : }
921 :
922 : /* -------------------------------------------------------------------- */
923 : /* Try renaming the directory. */
924 : /* -------------------------------------------------------------------- */
925 0 : if (VSIRename(osNewPath, osOldPath) != 0)
926 : {
927 0 : if (VSIMkdir(osNewPath, 0777) != 0)
928 : {
929 0 : CPLError(CE_Failure, CPLE_AppDefined,
930 : "Unable to create directory %s:\n%s", osNewPath.c_str(),
931 0 : VSIStrerror(errno));
932 0 : CSLDestroy(papszNewFileList);
933 0 : return CE_Failure;
934 : }
935 : }
936 :
937 : /* -------------------------------------------------------------------- */
938 : /* Copy/rename any remaining files. */
939 : /* -------------------------------------------------------------------- */
940 : VSIStatBufL sStatBuf;
941 :
942 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
943 : {
944 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
945 0 : VSI_ISREG(sStatBuf.st_mode))
946 : {
947 0 : if (CPLMoveFile(papszNewFileList[i], papszFileList[i]) != 0)
948 : {
949 0 : CPLError(CE_Failure, CPLE_AppDefined,
950 0 : "Unable to move %s to %s:\n%s", papszFileList[i],
951 0 : papszNewFileList[i], VSIStrerror(errno));
952 0 : CSLDestroy(papszNewFileList);
953 0 : return CE_Failure;
954 : }
955 : }
956 : }
957 :
958 0 : if (VSIStatL(osOldPath, &sStatBuf) == 0)
959 : {
960 0 : if (CPLUnlinkTree(osOldPath) != 0)
961 : {
962 0 : CPLError(CE_Warning, CPLE_AppDefined,
963 : "Unable to cleanup old path.");
964 : }
965 : }
966 :
967 0 : CSLDestroy(papszFileList);
968 0 : CSLDestroy(papszNewFileList);
969 0 : return CE_None;
970 : }
971 :
972 : /************************************************************************/
973 : /* AIGDelete() */
974 : /* */
975 : /* Custom dataset deleter for AIG dataset. */
976 : /************************************************************************/
977 :
978 0 : static CPLErr AIGDelete(const char *pszDatasetname)
979 :
980 : {
981 : /* -------------------------------------------------------------------- */
982 : /* Get file list. */
983 : /* -------------------------------------------------------------------- */
984 0 : GDALDatasetH hDS = GDALOpen(pszDatasetname, GA_ReadOnly);
985 0 : if (hDS == nullptr)
986 0 : return CE_Failure;
987 :
988 0 : char **papszFileList = GDALGetFileList(hDS);
989 0 : GDALClose(hDS);
990 :
991 0 : if (papszFileList == nullptr)
992 0 : return CE_Failure;
993 :
994 : /* -------------------------------------------------------------------- */
995 : /* Delete all regular files. */
996 : /* -------------------------------------------------------------------- */
997 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
998 : {
999 : VSIStatBufL sStatBuf;
1000 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
1001 0 : VSI_ISREG(sStatBuf.st_mode))
1002 : {
1003 0 : if (VSIUnlink(papszFileList[i]) != 0)
1004 : {
1005 0 : CPLError(CE_Failure, CPLE_AppDefined,
1006 0 : "Unable to delete '%s':\n%s", papszFileList[i],
1007 0 : VSIStrerror(errno));
1008 0 : return CE_Failure;
1009 : }
1010 : }
1011 : }
1012 :
1013 : /* -------------------------------------------------------------------- */
1014 : /* Delete directories. */
1015 : /* -------------------------------------------------------------------- */
1016 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
1017 : {
1018 : VSIStatBufL sStatBuf;
1019 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
1020 0 : VSI_ISDIR(sStatBuf.st_mode))
1021 : {
1022 0 : if (CPLUnlinkTree(papszFileList[i]) != 0)
1023 0 : return CE_Failure;
1024 : }
1025 : }
1026 :
1027 0 : return CE_None;
1028 : }
1029 :
1030 : /************************************************************************/
1031 : /* GDALRegister_AIG() */
1032 : /************************************************************************/
1033 :
1034 1682 : void GDALRegister_AIGrid()
1035 :
1036 : {
1037 1682 : if (GDALGetDriverByName("AIG") != nullptr)
1038 301 : return;
1039 :
1040 1381 : GDALDriver *poDriver = new GDALDriver();
1041 :
1042 1381 : poDriver->SetDescription("AIG");
1043 1381 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1044 1381 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Arc/Info Binary Grid");
1045 1381 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/aig.html");
1046 1381 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1047 :
1048 1381 : poDriver->pfnOpen = AIGDataset::Open;
1049 :
1050 1381 : poDriver->pfnRename = AIGRename;
1051 1381 : poDriver->pfnDelete = AIGDelete;
1052 :
1053 1381 : GetGDALDriverManager()->RegisterDriver(poDriver);
1054 : }
|