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