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 36379 : 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 72756 : CPLString osCoverName;
532 :
533 36377 : osCoverName = poOpenInfo->pszFilename;
534 72321 : if (osCoverName.size() > 4 &&
535 35944 : 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 36377 : else if (!poOpenInfo->bIsDirectory)
546 : {
547 35956 : return nullptr;
548 : }
549 :
550 : /* -------------------------------------------------------------------- */
551 : /* Verify that a few of the "standard" files are available. */
552 : /* -------------------------------------------------------------------- */
553 : VSIStatBufL sStatBuf;
554 843 : CPLString osTestName;
555 :
556 421 : osTestName.Printf("%s/hdr.adf", osCoverName.c_str());
557 421 : if (VSIStatL(osTestName, &sStatBuf) != 0)
558 : {
559 415 : osTestName.Printf("%s/HDR.ADF", osCoverName.c_str());
560 415 : if (VSIStatL(osTestName, &sStatBuf) != 0)
561 412 : 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 67 : for (int iFile = 0; papszFileList != nullptr &&
598 67 : papszFileList[iFile] != nullptr && !bGotOne;
599 : iFile++)
600 : {
601 58 : if (strlen(papszFileList[iFile]) != 11)
602 49 : 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 : ReportUpdateNotSupportedByDriver("AIG");
640 0 : return nullptr;
641 : }
642 : /* -------------------------------------------------------------------- */
643 : /* Create a corresponding GDALDataset. */
644 : /* -------------------------------------------------------------------- */
645 9 : AIGDataset *poDS = new AIGDataset();
646 :
647 9 : poDS->psInfo = psInfo;
648 :
649 : /* -------------------------------------------------------------------- */
650 : /* Try to read a color table (.clr). It seems it is legal to */
651 : /* have more than one so we just use the first one found. */
652 : /* -------------------------------------------------------------------- */
653 9 : char **papszFiles = VSIReadDir(psInfo->pszCoverName);
654 18 : CPLString osClrFilename;
655 18 : CPLString osCleanPath = CPLCleanTrailingSlashSafe(psInfo->pszCoverName);
656 :
657 : // first check for any .clr in coverage dir.
658 43 : for (int iFile = 0; papszFiles != nullptr && papszFiles[iFile] != nullptr;
659 : iFile++)
660 : {
661 39 : const std::string osExt = CPLGetExtensionSafe(papszFiles[iFile]);
662 39 : if (!EQUAL(osExt.c_str(), "clr") && !EQUAL(osExt.c_str(), "CLR"))
663 34 : continue;
664 :
665 0 : osClrFilename = CPLFormFilenameSafe(psInfo->pszCoverName,
666 5 : papszFiles[iFile], nullptr);
667 5 : break;
668 : }
669 :
670 9 : CSLDestroy(papszFiles);
671 :
672 : // Look in parent if we don't find a .clr in the coverage dir.
673 9 : if (osClrFilename.empty())
674 : {
675 8 : CPLString osTestClrFilename;
676 : osTestClrFilename.Printf("%s/../%s.clr", psInfo->pszCoverName,
677 4 : CPLGetFilename(osCleanPath));
678 :
679 4 : if (VSIStatL(osTestClrFilename, &sStatBuf) != 0)
680 : {
681 : osTestClrFilename.Printf("%s/../%s.CLR", psInfo->pszCoverName,
682 4 : CPLGetFilename(osCleanPath));
683 :
684 4 : if (!VSIStatL(osTestClrFilename, &sStatBuf))
685 2 : osClrFilename = std::move(osTestClrFilename);
686 : }
687 : else
688 0 : osClrFilename = std::move(osTestClrFilename);
689 : }
690 :
691 9 : if (!osClrFilename.empty())
692 7 : poDS->TranslateColorTable(osClrFilename);
693 :
694 : /* -------------------------------------------------------------------- */
695 : /* Establish raster info. */
696 : /* -------------------------------------------------------------------- */
697 9 : poDS->nRasterXSize = psInfo->nPixels;
698 9 : poDS->nRasterYSize = psInfo->nLines;
699 9 : poDS->nBands = 1;
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* Create band information objects. */
703 : /* -------------------------------------------------------------------- */
704 9 : poDS->SetBand(1, new AIGRasterBand(poDS, 1));
705 :
706 : /* -------------------------------------------------------------------- */
707 : /* Try to read projection file. */
708 : /* -------------------------------------------------------------------- */
709 : const std::string osPrjFilename =
710 9 : CPLFormCIFilenameSafe(psInfo->pszCoverName, "prj", "adf");
711 9 : if (VSIStatL(osPrjFilename.c_str(), &sStatBuf) == 0)
712 : {
713 18 : OGRSpatialReference oSRS;
714 9 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
715 :
716 9 : poDS->papszPrj = CSLLoad(osPrjFilename.c_str());
717 :
718 9 : if (oSRS.importFromESRI(poDS->papszPrj) == OGRERR_NONE)
719 : {
720 : // If geographic values are in seconds, we must transform.
721 : // Is there a code for minutes too?
722 10 : if (oSRS.IsGeographic() &&
723 10 : EQUAL(OSR_GDS(poDS->papszPrj, "Units", ""), "DS"))
724 : {
725 0 : psInfo->dfLLX /= 3600.0;
726 0 : psInfo->dfURY /= 3600.0;
727 0 : psInfo->dfCellSizeX /= 3600.0;
728 0 : psInfo->dfCellSizeY /= 3600.0;
729 : }
730 :
731 9 : poDS->m_oSRS = std::move(oSRS);
732 : }
733 : }
734 :
735 : /* -------------------------------------------------------------------- */
736 : /* Initialize any PAM information. */
737 : /* -------------------------------------------------------------------- */
738 9 : poDS->SetDescription(psInfo->pszCoverName);
739 9 : poDS->TryLoadXML();
740 :
741 : /* -------------------------------------------------------------------- */
742 : /* Open overviews. */
743 : /* -------------------------------------------------------------------- */
744 9 : poDS->oOvManager.Initialize(poDS, psInfo->pszCoverName);
745 :
746 9 : return poDS;
747 : }
748 :
749 : /************************************************************************/
750 : /* GetGeoTransform() */
751 : /************************************************************************/
752 :
753 1 : CPLErr AIGDataset::GetGeoTransform(double *padfTransform)
754 :
755 : {
756 1 : padfTransform[0] = psInfo->dfLLX;
757 1 : padfTransform[1] = psInfo->dfCellSizeX;
758 1 : padfTransform[2] = 0;
759 :
760 1 : padfTransform[3] = psInfo->dfURY;
761 1 : padfTransform[4] = 0;
762 1 : padfTransform[5] = -psInfo->dfCellSizeY;
763 :
764 1 : return CE_None;
765 : }
766 :
767 : /************************************************************************/
768 : /* GetSpatialRef() */
769 : /************************************************************************/
770 :
771 1 : const OGRSpatialReference *AIGDataset::GetSpatialRef() const
772 :
773 : {
774 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
775 : }
776 :
777 : /************************************************************************/
778 : /* TranslateColorTable() */
779 : /************************************************************************/
780 :
781 7 : void AIGDataset::TranslateColorTable(const char *pszClrFilename)
782 :
783 : {
784 7 : char **papszClrLines = CSLLoad(pszClrFilename);
785 7 : if (papszClrLines == nullptr)
786 0 : return;
787 :
788 7 : poCT = new GDALColorTable();
789 :
790 1813 : for (int iLine = 0; papszClrLines[iLine] != nullptr; iLine++)
791 : {
792 1806 : char **papszTokens = CSLTokenizeString(papszClrLines[iLine]);
793 :
794 1806 : if (CSLCount(papszTokens) >= 4 && papszTokens[0][0] != '#')
795 : {
796 : int nIndex;
797 : GDALColorEntry sEntry;
798 :
799 1792 : nIndex = atoi(papszTokens[0]);
800 1792 : sEntry.c1 = (short)atoi(papszTokens[1]);
801 1792 : sEntry.c2 = (short)atoi(papszTokens[2]);
802 1792 : sEntry.c3 = (short)atoi(papszTokens[3]);
803 1792 : sEntry.c4 = 255;
804 :
805 1792 : if ((nIndex < 0 || nIndex > 33000) ||
806 1792 : (sEntry.c1 < 0 || sEntry.c1 > 255) ||
807 1792 : (sEntry.c2 < 0 || sEntry.c2 > 255) ||
808 1792 : (sEntry.c3 < 0 || sEntry.c3 > 255))
809 : {
810 0 : CSLDestroy(papszTokens);
811 0 : CPLError(CE_Failure, CPLE_AppDefined,
812 : "Color table entry appears to be corrupt, skipping "
813 : "the rest. ");
814 0 : break;
815 : }
816 :
817 1792 : poCT->SetColorEntry(nIndex, &sEntry);
818 : }
819 :
820 1806 : CSLDestroy(papszTokens);
821 : }
822 :
823 7 : CSLDestroy(papszClrLines);
824 : }
825 :
826 : /************************************************************************/
827 : /* OSR_GDS() */
828 : /************************************************************************/
829 :
830 1 : static CPLString OSR_GDS(char **papszNV, const char *pszField,
831 : const char *pszDefaultValue)
832 :
833 : {
834 1 : if (papszNV == nullptr || papszNV[0] == nullptr)
835 0 : return pszDefaultValue;
836 :
837 1 : int iLine = 0;
838 4 : for (; papszNV[iLine] != nullptr &&
839 4 : !EQUALN(papszNV[iLine], pszField, strlen(pszField));
840 : iLine++)
841 : {
842 : }
843 :
844 1 : if (papszNV[iLine] == nullptr)
845 0 : return pszDefaultValue;
846 : else
847 : {
848 2 : CPLString osResult;
849 1 : char **papszTokens = CSLTokenizeString(papszNV[iLine]);
850 :
851 1 : if (CSLCount(papszTokens) > 1)
852 1 : osResult = papszTokens[1];
853 : else
854 0 : osResult = pszDefaultValue;
855 :
856 1 : CSLDestroy(papszTokens);
857 1 : return osResult;
858 : }
859 : }
860 :
861 : /************************************************************************/
862 : /* AIGRename() */
863 : /* */
864 : /* Custom renamer for AIG dataset. */
865 : /************************************************************************/
866 :
867 0 : static CPLErr AIGRename(const char *pszNewName, const char *pszOldName)
868 :
869 : {
870 : /* -------------------------------------------------------------------- */
871 : /* Make sure we are talking about paths to the coverage */
872 : /* directory. */
873 : /* -------------------------------------------------------------------- */
874 0 : CPLString osOldPath, osNewPath;
875 :
876 0 : if (!CPLGetExtensionSafe(pszNewName).empty())
877 0 : osNewPath = CPLGetPathSafe(pszNewName);
878 : else
879 0 : osNewPath = pszNewName;
880 :
881 0 : if (!CPLGetExtensionSafe(pszOldName).empty())
882 0 : osOldPath = CPLGetPathSafe(pszOldName);
883 : else
884 0 : osOldPath = pszOldName;
885 :
886 : /* -------------------------------------------------------------------- */
887 : /* Get file list. */
888 : /* -------------------------------------------------------------------- */
889 :
890 0 : GDALDatasetH hDS = GDALOpen(osOldPath, GA_ReadOnly);
891 0 : if (hDS == nullptr)
892 0 : return CE_Failure;
893 :
894 0 : char **papszFileList = GDALGetFileList(hDS);
895 0 : GDALClose(hDS);
896 :
897 0 : if (papszFileList == nullptr)
898 0 : return CE_Failure;
899 :
900 : /* -------------------------------------------------------------------- */
901 : /* Work out the corresponding new names. */
902 : /* -------------------------------------------------------------------- */
903 0 : char **papszNewFileList = nullptr;
904 :
905 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
906 : {
907 0 : CPLString osNewFilename;
908 :
909 0 : if (!EQUALN(papszFileList[i], osOldPath, osOldPath.size()))
910 : {
911 0 : CPLAssert(false);
912 : return CE_Failure;
913 : }
914 :
915 0 : osNewFilename = osNewPath + (papszFileList[i] + osOldPath.size());
916 :
917 0 : papszNewFileList = CSLAddString(papszNewFileList, osNewFilename);
918 : }
919 :
920 : /* -------------------------------------------------------------------- */
921 : /* Try renaming the directory. */
922 : /* -------------------------------------------------------------------- */
923 0 : if (VSIRename(osNewPath, osOldPath) != 0)
924 : {
925 0 : if (VSIMkdir(osNewPath, 0777) != 0)
926 : {
927 0 : CPLError(CE_Failure, CPLE_AppDefined,
928 : "Unable to create directory %s:\n%s", osNewPath.c_str(),
929 0 : VSIStrerror(errno));
930 0 : CSLDestroy(papszNewFileList);
931 0 : return CE_Failure;
932 : }
933 : }
934 :
935 : /* -------------------------------------------------------------------- */
936 : /* Copy/rename any remaining files. */
937 : /* -------------------------------------------------------------------- */
938 : VSIStatBufL sStatBuf;
939 :
940 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
941 : {
942 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
943 0 : VSI_ISREG(sStatBuf.st_mode))
944 : {
945 0 : if (CPLMoveFile(papszNewFileList[i], papszFileList[i]) != 0)
946 : {
947 0 : CPLError(CE_Failure, CPLE_AppDefined,
948 0 : "Unable to move %s to %s:\n%s", papszFileList[i],
949 0 : papszNewFileList[i], VSIStrerror(errno));
950 0 : CSLDestroy(papszNewFileList);
951 0 : return CE_Failure;
952 : }
953 : }
954 : }
955 :
956 0 : if (VSIStatL(osOldPath, &sStatBuf) == 0)
957 : {
958 0 : if (CPLUnlinkTree(osOldPath) != 0)
959 : {
960 0 : CPLError(CE_Warning, CPLE_AppDefined,
961 : "Unable to cleanup old path.");
962 : }
963 : }
964 :
965 0 : CSLDestroy(papszFileList);
966 0 : CSLDestroy(papszNewFileList);
967 0 : return CE_None;
968 : }
969 :
970 : /************************************************************************/
971 : /* AIGDelete() */
972 : /* */
973 : /* Custom dataset deleter for AIG dataset. */
974 : /************************************************************************/
975 :
976 0 : static CPLErr AIGDelete(const char *pszDatasetname)
977 :
978 : {
979 : /* -------------------------------------------------------------------- */
980 : /* Get file list. */
981 : /* -------------------------------------------------------------------- */
982 0 : GDALDatasetH hDS = GDALOpen(pszDatasetname, GA_ReadOnly);
983 0 : if (hDS == nullptr)
984 0 : return CE_Failure;
985 :
986 0 : char **papszFileList = GDALGetFileList(hDS);
987 0 : GDALClose(hDS);
988 :
989 0 : if (papszFileList == nullptr)
990 0 : return CE_Failure;
991 :
992 : /* -------------------------------------------------------------------- */
993 : /* Delete all regular files. */
994 : /* -------------------------------------------------------------------- */
995 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
996 : {
997 : VSIStatBufL sStatBuf;
998 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
999 0 : VSI_ISREG(sStatBuf.st_mode))
1000 : {
1001 0 : if (VSIUnlink(papszFileList[i]) != 0)
1002 : {
1003 0 : CPLError(CE_Failure, CPLE_AppDefined,
1004 0 : "Unable to delete '%s':\n%s", papszFileList[i],
1005 0 : VSIStrerror(errno));
1006 0 : return CE_Failure;
1007 : }
1008 : }
1009 : }
1010 :
1011 : /* -------------------------------------------------------------------- */
1012 : /* Delete directories. */
1013 : /* -------------------------------------------------------------------- */
1014 0 : for (int i = 0; papszFileList[i] != nullptr; i++)
1015 : {
1016 : VSIStatBufL sStatBuf;
1017 0 : if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
1018 0 : VSI_ISDIR(sStatBuf.st_mode))
1019 : {
1020 0 : if (CPLUnlinkTree(papszFileList[i]) != 0)
1021 0 : return CE_Failure;
1022 : }
1023 : }
1024 :
1025 0 : return CE_None;
1026 : }
1027 :
1028 : /************************************************************************/
1029 : /* GDALRegister_AIG() */
1030 : /************************************************************************/
1031 :
1032 1686 : void GDALRegister_AIGrid()
1033 :
1034 : {
1035 1686 : if (GDALGetDriverByName("AIG") != nullptr)
1036 302 : return;
1037 :
1038 1384 : GDALDriver *poDriver = new GDALDriver();
1039 :
1040 1384 : poDriver->SetDescription("AIG");
1041 1384 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1042 1384 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Arc/Info Binary Grid");
1043 1384 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/aig.html");
1044 1384 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1045 :
1046 1384 : poDriver->pfnOpen = AIGDataset::Open;
1047 :
1048 1384 : poDriver->pfnRename = AIGRename;
1049 1384 : poDriver->pfnDelete = AIGDelete;
1050 :
1051 1384 : GetGDALDriverManager()->RegisterDriver(poDriver);
1052 : }
|