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