LCOV - code coverage report
Current view: top level - frmts/hfa - hfaopen.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1817 2207 82.3 %
Date: 2026-01-18 23:37:47 Functions: 55 64 85.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Erdas Imagine (.img) Translator
       4             :  * Purpose:  Supporting functions for HFA (.img) ... main (C callable) API
       5             :  *           that is not dependent on GDAL (just CPL).
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Intergraph Corporation
      10             :  * Copyright (c) 2007-2011, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ******************************************************************************
      14             :  *
      15             :  * hfaopen.cpp
      16             :  *
      17             :  * Supporting routines for reading Erdas Imagine (.imf) Hierarchical
      18             :  * File Architecture files.  This is intended to be a library independent
      19             :  * of the GDAL core, but dependent on the Common Portability Library.
      20             :  *
      21             :  */
      22             : 
      23             : #include "cpl_port.h"
      24             : #include "hfa_p.h"
      25             : 
      26             : #include <cerrno>
      27             : #include <climits>
      28             : #include <cmath>
      29             : #include <cstddef>
      30             : #include <cstdio>
      31             : #include <cstdlib>
      32             : #include <cstring>
      33             : #include <algorithm>
      34             : #include <memory>
      35             : #include <string>
      36             : #include <vector>
      37             : 
      38             : #include "cpl_conv.h"
      39             : #include "cpl_error.h"
      40             : #include "cpl_string.h"
      41             : #include "cpl_vsi.h"
      42             : #include "gdal_priv.h"
      43             : #include "hfa.h"
      44             : #include "ogr_proj_p.h"
      45             : #include "proj.h"
      46             : 
      47             : constexpr double R2D = 180.0 / M_PI;
      48             : 
      49             : constexpr double RAD2ARCSEC = 648000.0 / M_PI;
      50             : 
      51             : static const char *const apszAuxMetadataItems[] = {
      52             :     // node/entry            field_name                  metadata_key       type
      53             :     "Statistics",
      54             :     "dminimum",
      55             :     "STATISTICS_MINIMUM",
      56             :     "Esta_Statistics",
      57             :     "Statistics",
      58             :     "dmaximum",
      59             :     "STATISTICS_MAXIMUM",
      60             :     "Esta_Statistics",
      61             :     "Statistics",
      62             :     "dmean",
      63             :     "STATISTICS_MEAN",
      64             :     "Esta_Statistics",
      65             :     "Statistics",
      66             :     "dmedian",
      67             :     "STATISTICS_MEDIAN",
      68             :     "Esta_Statistics",
      69             :     "Statistics",
      70             :     "dmode",
      71             :     "STATISTICS_MODE",
      72             :     "Esta_Statistics",
      73             :     "Statistics",
      74             :     "dstddev",
      75             :     "STATISTICS_STDDEV",
      76             :     "Esta_Statistics",
      77             :     "HistogramParameters",
      78             :     "lBinFunction.numBins",
      79             :     "STATISTICS_HISTONUMBINS",
      80             :     "Eimg_StatisticsParameters830",
      81             :     "HistogramParameters",
      82             :     "dBinFunction.minLimit",
      83             :     "STATISTICS_HISTOMIN",
      84             :     "Eimg_StatisticsParameters830",
      85             :     "HistogramParameters",
      86             :     "dBinFunction.maxLimit",
      87             :     "STATISTICS_HISTOMAX",
      88             :     "Eimg_StatisticsParameters830",
      89             :     "StatisticsParameters",
      90             :     "lSkipFactorX",
      91             :     "STATISTICS_SKIPFACTORX",
      92             :     "",
      93             :     "StatisticsParameters",
      94             :     "lSkipFactorY",
      95             :     "STATISTICS_SKIPFACTORY",
      96             :     "",
      97             :     "StatisticsParameters",
      98             :     "dExcludedValues",
      99             :     "STATISTICS_EXCLUDEDVALUES",
     100             :     "",
     101             :     "",
     102             :     "elayerType",
     103             :     "LAYER_TYPE",
     104             :     "",
     105             :     "RRDInfoList",
     106             :     "salgorithm.string",
     107             :     "OVERVIEWS_ALGORITHM",
     108             :     "Emif_String",
     109             :     nullptr};
     110             : 
     111         688 : const char *const *GetHFAAuxMetaDataList()
     112             : {
     113         688 :     return apszAuxMetadataItems;
     114             : }
     115             : 
     116             : /************************************************************************/
     117             : /*                          HFAGetDictionary()                          */
     118             : /************************************************************************/
     119             : 
     120         562 : static char *HFAGetDictionary(HFAHandle hHFA)
     121             : 
     122             : {
     123         562 :     int nDictMax = 100;
     124         562 :     char *pszDictionary = static_cast<char *>(CPLMalloc(nDictMax));
     125         562 :     int nDictSize = 0;
     126             : 
     127         562 :     if (VSIFSeekL(hHFA->fp, static_cast<vsi_l_offset>(hHFA->nDictionaryPos),
     128         562 :                   SEEK_SET) < 0)
     129             :     {
     130           0 :         pszDictionary[nDictSize] = '\0';
     131           0 :         return pszDictionary;
     132             :     }
     133             : 
     134             :     while (true)
     135             :     {
     136     2075220 :         if (nDictSize >= nDictMax - 1)
     137             :         {
     138        2791 :             nDictMax = nDictSize * 2 + 100;
     139             :             pszDictionary =
     140        2791 :                 static_cast<char *>(CPLRealloc(pszDictionary, nDictMax));
     141             :         }
     142             : 
     143     2075220 :         if (VSIFReadL(pszDictionary + nDictSize, 1, 1, hHFA->fp) < 1 ||
     144     4148190 :             pszDictionary[nDictSize] == '\0' ||
     145     2072970 :             (nDictSize > 2 && pszDictionary[nDictSize - 2] == ',' &&
     146      161989 :              pszDictionary[nDictSize - 1] == '.'))
     147         562 :             break;
     148             : 
     149     2074650 :         nDictSize++;
     150             :     }
     151             : 
     152         562 :     pszDictionary[nDictSize] = '\0';
     153             : 
     154         562 :     return pszDictionary;
     155             : }
     156             : 
     157             : /************************************************************************/
     158             : /*                              HFAOpen()                               */
     159             : /************************************************************************/
     160             : 
     161         562 : HFAHandle HFAOpen(const char *pszFilename, const char *pszAccess)
     162             : 
     163             : {
     164         562 :     VSILFILE *fp = VSIFOpenL(
     165             :         pszFilename,
     166         562 :         (EQUAL(pszAccess, "r") || EQUAL(pszAccess, "rb")) ? "rb" : "r+b");
     167             : 
     168             :     // Should this be changed to use some sort of CPLFOpen() which will
     169             :     // set the error?
     170         562 :     if (fp == nullptr)
     171             :     {
     172           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "File open of %s failed.",
     173             :                  pszFilename);
     174             : 
     175           0 :         return nullptr;
     176             :     }
     177             : 
     178             :     // Read and verify the header.
     179         562 :     char szHeader[16] = {};
     180         562 :     if (VSIFReadL(szHeader, 16, 1, fp) < 1)
     181             :     {
     182           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     183             :                  "Attempt to read 16 byte header failed for\n%s.", pszFilename);
     184           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     185           0 :         return nullptr;
     186             :     }
     187             : 
     188         562 :     if (!STARTS_WITH_CI(szHeader, "EHFA_HEADER_TAG"))
     189             :     {
     190           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     191             :                  "File %s is not an Imagine HFA file ... header wrong.",
     192             :                  pszFilename);
     193           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     194           0 :         return nullptr;
     195             :     }
     196             : 
     197             :     // Create the HFAInfo_t.
     198             :     HFAInfo_t *psInfo =
     199         562 :         static_cast<HFAInfo_t *>(CPLCalloc(sizeof(HFAInfo_t), 1));
     200             : 
     201         562 :     psInfo->pszFilename = CPLStrdup(CPLGetFilename(pszFilename));
     202         562 :     psInfo->pszPath = CPLStrdup(CPLGetPathSafe(pszFilename).c_str());
     203         562 :     psInfo->fp = fp;
     204         562 :     if (EQUAL(pszAccess, "r") || EQUAL(pszAccess, "rb"))
     205         343 :         psInfo->eAccess = HFA_ReadOnly;
     206             :     else
     207         219 :         psInfo->eAccess = HFA_Update;
     208         562 :     psInfo->bTreeDirty = false;
     209             : 
     210             :     // Where is the header?
     211         562 :     GUInt32 nHeaderPos = 0;
     212         562 :     bool bRet = VSIFReadL(&nHeaderPos, sizeof(GInt32), 1, fp) > 0;
     213             :     HFAStandard(4, &nHeaderPos);
     214             : 
     215             :     // Read the header.
     216         562 :     bRet &= VSIFSeekL(fp, static_cast<vsi_l_offset>(nHeaderPos), SEEK_SET) >= 0;
     217             : 
     218         562 :     bRet &= VSIFReadL(&(psInfo->nVersion), sizeof(GInt32), 1, fp) > 0;
     219             :     HFAStandard(4, &(psInfo->nVersion));
     220             : 
     221         562 :     bRet &= VSIFReadL(szHeader, 4, 1, fp) > 0;  // Skip freeList.
     222             : 
     223         562 :     bRet &= VSIFReadL(&(psInfo->nRootPos), sizeof(GInt32), 1, fp) > 0;
     224             :     HFAStandard(4, &(psInfo->nRootPos));
     225             : 
     226         562 :     bRet &= VSIFReadL(&(psInfo->nEntryHeaderLength), sizeof(GInt16), 1, fp) > 0;
     227             :     HFAStandard(2, &(psInfo->nEntryHeaderLength));
     228             : 
     229         562 :     bRet &= VSIFReadL(&(psInfo->nDictionaryPos), sizeof(GInt32), 1, fp) > 0;
     230             :     HFAStandard(4, &(psInfo->nDictionaryPos));
     231             : 
     232             :     // Collect file size.
     233         562 :     bRet &= VSIFSeekL(fp, 0, SEEK_END) >= 0;
     234         562 :     if (!bRet)
     235             :     {
     236           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     237           0 :         CPLFree(psInfo->pszFilename);
     238           0 :         CPLFree(psInfo->pszPath);
     239           0 :         CPLFree(psInfo);
     240           0 :         return nullptr;
     241             :     }
     242         562 :     psInfo->nEndOfFile = static_cast<GUInt32>(VSIFTellL(fp));
     243             : 
     244             :     // Instantiate the root entry.
     245         562 :     psInfo->poRoot = HFAEntry::New(psInfo, psInfo->nRootPos, nullptr, nullptr);
     246         562 :     if (psInfo->poRoot == nullptr)
     247             :     {
     248           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     249           0 :         CPLFree(psInfo->pszFilename);
     250           0 :         CPLFree(psInfo->pszPath);
     251           0 :         CPLFree(psInfo);
     252           0 :         return nullptr;
     253             :     }
     254             : 
     255             :     // Read the dictionary.
     256         562 :     psInfo->pszDictionary = HFAGetDictionary(psInfo);
     257         562 :     psInfo->poDictionary = new HFADictionary(psInfo->pszDictionary);
     258             : 
     259             :     // Collect band definitions.
     260         562 :     HFAParseBandInfo(psInfo);
     261             : 
     262         562 :     return psInfo;
     263             : }
     264             : 
     265             : /************************************************************************/
     266             : /*                         HFACreateDependent()                         */
     267             : /*                                                                      */
     268             : /*      Create a .rrd file for the named file if it does not exist,     */
     269             : /*      or return the existing dependent if it already exists.          */
     270             : /************************************************************************/
     271             : 
     272           2 : HFAInfo_t *HFACreateDependent(HFAInfo_t *psBase)
     273             : 
     274             : {
     275           2 :     if (psBase->psDependent != nullptr)
     276           1 :         return psBase->psDependent;
     277             : 
     278             :     // Create desired RRD filename.
     279           2 :     const CPLString oBasename = CPLGetBasenameSafe(psBase->pszFilename);
     280             :     const CPLString oRRDFilename =
     281           2 :         CPLFormFilenameSafe(psBase->pszPath, oBasename, "rrd");
     282             : 
     283             :     // Does this file already exist?  If so, re-use it.
     284           1 :     VSILFILE *fp = VSIFOpenL(oRRDFilename, "rb");
     285           1 :     if (fp != nullptr)
     286             :     {
     287           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     288           0 :         psBase->psDependent = HFAOpen(oRRDFilename, "rb");
     289             :         // FIXME? this is not going to be reused but recreated...
     290             :     }
     291             : 
     292             :     // Otherwise create it now.
     293           1 :     HFAInfo_t *psDep = HFACreateLL(oRRDFilename);
     294           1 :     psBase->psDependent = psDep;
     295           1 :     if (psDep == nullptr)
     296           0 :         return nullptr;
     297             : 
     298             :     /* -------------------------------------------------------------------- */
     299             :     /*      Add the DependentFile node with the pointer back to the         */
     300             :     /*      parent.  When working from an .aux file we really want the      */
     301             :     /*      .rrd to point back to the original file, not the .aux file.     */
     302             :     /* -------------------------------------------------------------------- */
     303           1 :     HFAEntry *poEntry = psBase->poRoot->GetNamedChild("DependentFile");
     304           1 :     const char *pszDependentFile = nullptr;
     305           1 :     if (poEntry != nullptr)
     306           0 :         pszDependentFile = poEntry->GetStringField("dependent.string");
     307           1 :     if (pszDependentFile == nullptr)
     308           1 :         pszDependentFile = psBase->pszFilename;
     309             : 
     310           1 :     HFAEntry *poDF = HFAEntry::New(psDep, "DependentFile", "Eimg_DependentFile",
     311             :                                    psDep->poRoot);
     312             : 
     313           1 :     poDF->MakeData(static_cast<int>(strlen(pszDependentFile) + 50));
     314           1 :     poDF->SetPosition();
     315           1 :     poDF->SetStringField("dependent.string", pszDependentFile);
     316             : 
     317           1 :     return psDep;
     318             : }
     319             : 
     320             : /************************************************************************/
     321             : /*                          HFAGetDependent()                           */
     322             : /************************************************************************/
     323             : 
     324          50 : HFAInfo_t *HFAGetDependent(HFAInfo_t *psBase, const char *pszFilename)
     325             : 
     326             : {
     327          50 :     if (EQUAL(pszFilename, psBase->pszFilename))
     328          31 :         return psBase;
     329             : 
     330          19 :     if (psBase->psDependent != nullptr)
     331             :     {
     332           2 :         if (EQUAL(pszFilename, psBase->psDependent->pszFilename))
     333           2 :             return psBase->psDependent;
     334             :         else
     335           0 :             return nullptr;
     336             :     }
     337             : 
     338             :     // Try to open the dependent file.
     339          17 :     const char *pszMode = psBase->eAccess == HFA_Update ? "r+b" : "rb";
     340             : 
     341          17 :     char *pszDependent = CPLStrdup(
     342          34 :         CPLFormFilenameSafe(psBase->pszPath, pszFilename, nullptr).c_str());
     343             : 
     344          17 :     VSILFILE *fp = VSIFOpenL(pszDependent, pszMode);
     345          17 :     if (fp != nullptr)
     346             :     {
     347          13 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     348          13 :         psBase->psDependent = HFAOpen(pszDependent, pszMode);
     349             :     }
     350             : 
     351          17 :     CPLFree(pszDependent);
     352             : 
     353          17 :     return psBase->psDependent;
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                          HFAParseBandInfo()                          */
     358             : /*                                                                      */
     359             : /*      This is used by HFAOpen() and HFACreate() to initialize the     */
     360             : /*      band structures.                                                */
     361             : /************************************************************************/
     362             : 
     363         754 : CPLErr HFAParseBandInfo(HFAInfo_t *psInfo)
     364             : 
     365             : {
     366             :     // Find the first band node.
     367         754 :     psInfo->nBands = 0;
     368         754 :     HFAEntry *poNode = psInfo->poRoot->GetChild();
     369        2463 :     while (poNode != nullptr)
     370             :     {
     371        1709 :         if (EQUAL(poNode->GetType(), "Eimg_Layer") &&
     372        2563 :             poNode->GetIntField("width") > 0 &&
     373         854 :             poNode->GetIntField("height") > 0)
     374             :         {
     375         854 :             if (psInfo->nBands == 0)
     376             :             {
     377         736 :                 psInfo->nXSize = poNode->GetIntField("width");
     378         736 :                 psInfo->nYSize = poNode->GetIntField("height");
     379             :             }
     380         236 :             else if (poNode->GetIntField("width") != psInfo->nXSize ||
     381         118 :                      poNode->GetIntField("height") != psInfo->nYSize)
     382             :             {
     383           0 :                 return CE_Failure;
     384             :             }
     385             : 
     386        1708 :             psInfo->papoBand = static_cast<HFABand **>(CPLRealloc(
     387         854 :                 psInfo->papoBand, sizeof(HFABand *) * (psInfo->nBands + 1)));
     388         854 :             psInfo->papoBand[psInfo->nBands] = new HFABand(psInfo, poNode);
     389         854 :             if (psInfo->papoBand[psInfo->nBands]->nWidth == 0)
     390             :             {
     391           0 :                 delete psInfo->papoBand[psInfo->nBands];
     392           0 :                 return CE_Failure;
     393             :             }
     394         854 :             psInfo->nBands++;
     395             :         }
     396             : 
     397        1709 :         poNode = poNode->GetNext();
     398             :     }
     399             : 
     400         754 :     return CE_None;
     401             : }
     402             : 
     403             : /************************************************************************/
     404             : /*                              HFAClose()                              */
     405             : /************************************************************************/
     406             : 
     407         763 : int HFAClose(HFAHandle hHFA)
     408             : 
     409             : {
     410         763 :     if (hHFA->eAccess == HFA_Update &&
     411         420 :         (hHFA->bTreeDirty || (hHFA->poDictionary != nullptr &&
     412          16 :                               hHFA->poDictionary->bDictionaryTextDirty)))
     413         400 :         HFAFlush(hHFA);
     414             : 
     415         763 :     int nRet = 0;
     416         763 :     if (hHFA->psDependent != nullptr)
     417             :     {
     418          13 :         if (HFAClose(hHFA->psDependent) != 0)
     419           0 :             nRet = -1;
     420             :     }
     421             : 
     422         763 :     delete hHFA->poRoot;
     423             : 
     424         763 :     if (VSIFCloseL(hHFA->fp) != 0)
     425           0 :         nRet = -1;
     426             : 
     427         763 :     if (hHFA->poDictionary != nullptr)
     428         759 :         delete hHFA->poDictionary;
     429             : 
     430         763 :     CPLFree(hHFA->pszDictionary);
     431         763 :     CPLFree(hHFA->pszFilename);
     432         763 :     CPLFree(hHFA->pszIGEFilename);
     433         763 :     CPLFree(hHFA->pszPath);
     434             : 
     435        1617 :     for (int i = 0; i < hHFA->nBands; i++)
     436             :     {
     437         854 :         delete hHFA->papoBand[i];
     438             :     }
     439             : 
     440         763 :     CPLFree(hHFA->papoBand);
     441             : 
     442         763 :     if (hHFA->pProParameters != nullptr)
     443             :     {
     444         203 :         Eprj_ProParameters *psProParams =
     445             :             (Eprj_ProParameters *)hHFA->pProParameters;
     446             : 
     447         203 :         CPLFree(psProParams->proExeName);
     448         203 :         CPLFree(psProParams->proName);
     449         203 :         CPLFree(psProParams->proSpheroid.sphereName);
     450             : 
     451         203 :         CPLFree(psProParams);
     452             :     }
     453             : 
     454         763 :     if (hHFA->pDatum != nullptr)
     455             :     {
     456         203 :         CPLFree(((Eprj_Datum *)hHFA->pDatum)->datumname);
     457         203 :         CPLFree(((Eprj_Datum *)hHFA->pDatum)->gridname);
     458         203 :         CPLFree(hHFA->pDatum);
     459             :     }
     460             : 
     461         763 :     if (hHFA->pMapInfo != nullptr)
     462             :     {
     463         246 :         CPLFree(((Eprj_MapInfo *)hHFA->pMapInfo)->proName);
     464         246 :         CPLFree(((Eprj_MapInfo *)hHFA->pMapInfo)->units);
     465         246 :         CPLFree(hHFA->pMapInfo);
     466             :     }
     467             : 
     468         763 :     CPLFree(hHFA);
     469         763 :     return nRet;
     470             : }
     471             : 
     472             : /************************************************************************/
     473             : /*                              HFARemove()                             */
     474             : /*  Used from HFADelete() function.                                     */
     475             : /************************************************************************/
     476             : 
     477           0 : static CPLErr HFARemove(const char *pszFilename)
     478             : 
     479             : {
     480             :     VSIStatBufL sStat;
     481             : 
     482           0 :     if (VSIStatL(pszFilename, &sStat) == 0 && VSI_ISREG(sStat.st_mode))
     483             :     {
     484           0 :         if (VSIUnlink(pszFilename) == 0)
     485           0 :             return CE_None;
     486             :         else
     487             :         {
     488           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     489             :                      "Attempt to unlink %s failed.", pszFilename);
     490           0 :             return CE_Failure;
     491             :         }
     492             :     }
     493             : 
     494           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unable to delete %s, not a file.",
     495             :              pszFilename);
     496           0 :     return CE_Failure;
     497             : }
     498             : 
     499             : /************************************************************************/
     500             : /*                              HFADelete()                             */
     501             : /************************************************************************/
     502             : 
     503           0 : CPLErr HFADelete(const char *pszFilename)
     504             : 
     505             : {
     506           0 :     HFAInfo_t *psInfo = HFAOpen(pszFilename, "rb");
     507           0 :     HFAEntry *poDMS = nullptr;
     508           0 :     HFAEntry *poLayer = nullptr;
     509           0 :     HFAEntry *poNode = nullptr;
     510             : 
     511           0 :     if (psInfo != nullptr)
     512             :     {
     513           0 :         poNode = psInfo->poRoot->GetChild();
     514           0 :         while ((poNode != nullptr) && (poLayer == nullptr))
     515             :         {
     516           0 :             if (EQUAL(poNode->GetType(), "Eimg_Layer"))
     517             :             {
     518           0 :                 poLayer = poNode;
     519             :             }
     520           0 :             poNode = poNode->GetNext();
     521             :         }
     522             : 
     523           0 :         if (poLayer != nullptr)
     524           0 :             poDMS = poLayer->GetNamedChild("ExternalRasterDMS");
     525             : 
     526           0 :         if (poDMS)
     527             :         {
     528             :             const char *pszRawFilename =
     529           0 :                 poDMS->GetStringField("fileName.string");
     530             : 
     531           0 :             if (pszRawFilename != nullptr)
     532             :             {
     533           0 :                 if (CPLHasPathTraversal(pszRawFilename))
     534             :                 {
     535           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     536             :                              "Path traversal detected in %s", pszRawFilename);
     537             :                 }
     538             :                 else
     539             :                 {
     540           0 :                     HFARemove(CPLFormFilenameSafe(psInfo->pszPath,
     541             :                                                   pszRawFilename, nullptr)
     542             :                                   .c_str());
     543             :                 }
     544             :             }
     545             :         }
     546             : 
     547           0 :         CPL_IGNORE_RET_VAL(HFAClose(psInfo));
     548             :     }
     549           0 :     return HFARemove(pszFilename);
     550             : }
     551             : 
     552             : /************************************************************************/
     553             : /*                          HFAGetRasterInfo()                          */
     554             : /************************************************************************/
     555             : 
     556         547 : CPLErr HFAGetRasterInfo(HFAHandle hHFA, int *pnXSize, int *pnYSize,
     557             :                         int *pnBands)
     558             : 
     559             : {
     560         547 :     if (pnXSize != nullptr)
     561         547 :         *pnXSize = hHFA->nXSize;
     562         547 :     if (pnYSize != nullptr)
     563         547 :         *pnYSize = hHFA->nYSize;
     564         547 :     if (pnBands != nullptr)
     565         547 :         *pnBands = hHFA->nBands;
     566         547 :     return CE_None;
     567             : }
     568             : 
     569             : /************************************************************************/
     570             : /*                           HFAGetBandInfo()                           */
     571             : /************************************************************************/
     572             : 
     573         679 : CPLErr HFAGetBandInfo(HFAHandle hHFA, int nBand, EPTType *peDataType,
     574             :                       int *pnBlockXSize, int *pnBlockYSize,
     575             :                       int *pnCompressionType)
     576             : 
     577             : {
     578         679 :     if (nBand < 0 || nBand > hHFA->nBands)
     579             :     {
     580           0 :         CPLAssert(false);
     581             :         return CE_Failure;
     582             :     }
     583             : 
     584         679 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
     585             : 
     586         679 :     if (peDataType != nullptr)
     587         679 :         *peDataType = poBand->eDataType;
     588             : 
     589         679 :     if (pnBlockXSize != nullptr)
     590         679 :         *pnBlockXSize = poBand->nBlockXSize;
     591             : 
     592         679 :     if (pnBlockYSize != nullptr)
     593         679 :         *pnBlockYSize = poBand->nBlockYSize;
     594             : 
     595             :     // Get compression code from RasterDMS.
     596         679 :     if (pnCompressionType != nullptr)
     597             :     {
     598         679 :         *pnCompressionType = 0;
     599             : 
     600         679 :         HFAEntry *poDMS = poBand->poNode->GetNamedChild("RasterDMS");
     601             : 
     602         679 :         if (poDMS != nullptr)
     603         585 :             *pnCompressionType = poDMS->GetIntField("compressionType");
     604             :     }
     605             : 
     606         679 :     return CE_None;
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*                          HFAGetBandNoData()                          */
     611             : /*                                                                      */
     612             : /*      returns TRUE if value is set, otherwise FALSE.                  */
     613             : /************************************************************************/
     614             : 
     615         223 : int HFAGetBandNoData(HFAHandle hHFA, int nBand, double *pdfNoData)
     616             : 
     617             : {
     618         223 :     if (nBand < 0 || nBand > hHFA->nBands)
     619             :     {
     620           0 :         CPLAssert(false);
     621             :         return CE_Failure;
     622             :     }
     623             : 
     624         223 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
     625             : 
     626         223 :     if (!poBand->bNoDataSet && poBand->nOverviews > 0)
     627             :     {
     628          13 :         poBand = poBand->papoOverviews[0];
     629          13 :         if (poBand == nullptr)
     630           0 :             return FALSE;
     631             :     }
     632             : 
     633         223 :     *pdfNoData = poBand->dfNoData;
     634         223 :     return poBand->bNoDataSet;
     635             : }
     636             : 
     637             : /************************************************************************/
     638             : /*                          HFASetBandNoData()                          */
     639             : /*                                                                      */
     640             : /*      attempts to set a no-data value on the given band               */
     641             : /************************************************************************/
     642             : 
     643           9 : CPLErr HFASetBandNoData(HFAHandle hHFA, int nBand, double dfValue)
     644             : 
     645             : {
     646           9 :     if (nBand < 0 || nBand > hHFA->nBands)
     647             :     {
     648           0 :         CPLAssert(false);
     649             :         return CE_Failure;
     650             :     }
     651             : 
     652           9 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
     653             : 
     654           9 :     return poBand->SetNoDataValue(dfValue);
     655             : }
     656             : 
     657             : /************************************************************************/
     658             : /*                        HFAGetOverviewCount()                         */
     659             : /************************************************************************/
     660             : 
     661         127 : int HFAGetOverviewCount(HFAHandle hHFA, int nBand)
     662             : 
     663             : {
     664         127 :     if (nBand < 0 || nBand > hHFA->nBands)
     665             :     {
     666           0 :         CPLAssert(false);
     667             :         return CE_Failure;
     668             :     }
     669             : 
     670         127 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
     671         127 :     poBand->LoadOverviews();
     672             : 
     673         127 :     return poBand->nOverviews;
     674             : }
     675             : 
     676             : /************************************************************************/
     677             : /*                         HFAGetOverviewInfo()                         */
     678             : /************************************************************************/
     679             : 
     680          56 : CPLErr HFAGetOverviewInfo(HFAHandle hHFA, int nBand, int iOverview,
     681             :                           int *pnXSize, int *pnYSize, int *pnBlockXSize,
     682             :                           int *pnBlockYSize, EPTType *peHFADataType)
     683             : 
     684             : {
     685          56 :     if (nBand < 0 || nBand > hHFA->nBands)
     686             :     {
     687           0 :         CPLAssert(false);
     688             :         return CE_Failure;
     689             :     }
     690             : 
     691          56 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
     692          56 :     poBand->LoadOverviews();
     693             : 
     694          56 :     if (iOverview < 0 || iOverview >= poBand->nOverviews)
     695             :     {
     696           0 :         CPLAssert(false);
     697             :         return CE_Failure;
     698             :     }
     699          56 :     poBand = poBand->papoOverviews[iOverview];
     700          56 :     if (poBand == nullptr)
     701             :     {
     702           0 :         return CE_Failure;
     703             :     }
     704             : 
     705          56 :     if (pnXSize != nullptr)
     706          56 :         *pnXSize = poBand->nWidth;
     707             : 
     708          56 :     if (pnYSize != nullptr)
     709          56 :         *pnYSize = poBand->nHeight;
     710             : 
     711          56 :     if (pnBlockXSize != nullptr)
     712          56 :         *pnBlockXSize = poBand->nBlockXSize;
     713             : 
     714          56 :     if (pnBlockYSize != nullptr)
     715          56 :         *pnBlockYSize = poBand->nBlockYSize;
     716             : 
     717          56 :     if (peHFADataType != nullptr)
     718          56 :         *peHFADataType = poBand->eDataType;
     719             : 
     720          56 :     return CE_None;
     721             : }
     722             : 
     723             : /************************************************************************/
     724             : /*                         HFAGetRasterBlock()                          */
     725             : /************************************************************************/
     726             : 
     727           0 : CPLErr HFAGetRasterBlock(HFAHandle hHFA, int nBand, int nXBlock, int nYBlock,
     728             :                          void *pData)
     729             : 
     730             : {
     731           0 :     return HFAGetRasterBlockEx(hHFA, nBand, nXBlock, nYBlock, pData, -1);
     732             : }
     733             : 
     734             : /************************************************************************/
     735             : /*                        HFAGetRasterBlockEx()                         */
     736             : /************************************************************************/
     737             : 
     738        1330 : CPLErr HFAGetRasterBlockEx(HFAHandle hHFA, int nBand, int nXBlock, int nYBlock,
     739             :                            void *pData, int nDataSize)
     740             : 
     741             : {
     742        1330 :     if (nBand < 1 || nBand > hHFA->nBands)
     743           0 :         return CE_Failure;
     744             : 
     745        1330 :     return hHFA->papoBand[nBand - 1]->GetRasterBlock(nXBlock, nYBlock, pData,
     746        1330 :                                                      nDataSize);
     747             : }
     748             : 
     749             : /************************************************************************/
     750             : /*                     HFAGetOverviewRasterBlock()                      */
     751             : /************************************************************************/
     752             : 
     753           0 : CPLErr HFAGetOverviewRasterBlock(HFAHandle hHFA, int nBand, int iOverview,
     754             :                                  int nXBlock, int nYBlock, void *pData)
     755             : 
     756             : {
     757           0 :     return HFAGetOverviewRasterBlockEx(hHFA, nBand, iOverview, nXBlock, nYBlock,
     758           0 :                                        pData, -1);
     759             : }
     760             : 
     761             : /************************************************************************/
     762             : /*                   HFAGetOverviewRasterBlockEx()                      */
     763             : /************************************************************************/
     764             : 
     765          24 : CPLErr HFAGetOverviewRasterBlockEx(HFAHandle hHFA, int nBand, int iOverview,
     766             :                                    int nXBlock, int nYBlock, void *pData,
     767             :                                    int nDataSize)
     768             : 
     769             : {
     770          24 :     if (nBand < 1 || nBand > hHFA->nBands)
     771           0 :         return CE_Failure;
     772             : 
     773          24 :     if (iOverview < 0 || iOverview >= hHFA->papoBand[nBand - 1]->nOverviews)
     774           0 :         return CE_Failure;
     775             : 
     776          24 :     return hHFA->papoBand[nBand - 1]->papoOverviews[iOverview]->GetRasterBlock(
     777          24 :         nXBlock, nYBlock, pData, nDataSize);
     778             : }
     779             : 
     780             : /************************************************************************/
     781             : /*                         HFASetRasterBlock()                          */
     782             : /************************************************************************/
     783             : 
     784         105 : CPLErr HFASetRasterBlock(HFAHandle hHFA, int nBand, int nXBlock, int nYBlock,
     785             :                          void *pData)
     786             : 
     787             : {
     788         105 :     if (nBand < 1 || nBand > hHFA->nBands)
     789           0 :         return CE_Failure;
     790             : 
     791         105 :     return hHFA->papoBand[nBand - 1]->SetRasterBlock(nXBlock, nYBlock, pData);
     792             : }
     793             : 
     794             : /************************************************************************/
     795             : /*                         HFASetRasterBlock()                          */
     796             : /************************************************************************/
     797             : 
     798          29 : CPLErr HFASetOverviewRasterBlock(HFAHandle hHFA, int nBand, int iOverview,
     799             :                                  int nXBlock, int nYBlock, void *pData)
     800             : 
     801             : {
     802          29 :     if (nBand < 1 || nBand > hHFA->nBands)
     803           0 :         return CE_Failure;
     804             : 
     805          29 :     if (iOverview < 0 || iOverview >= hHFA->papoBand[nBand - 1]->nOverviews)
     806           0 :         return CE_Failure;
     807             : 
     808          29 :     return hHFA->papoBand[nBand - 1]->papoOverviews[iOverview]->SetRasterBlock(
     809          29 :         nXBlock, nYBlock, pData);
     810             : }
     811             : 
     812             : /************************************************************************/
     813             : /*                         HFAGetBandName()                             */
     814             : /************************************************************************/
     815             : 
     816         214 : const char *HFAGetBandName(HFAHandle hHFA, int nBand)
     817             : {
     818         214 :     if (nBand < 1 || nBand > hHFA->nBands)
     819           0 :         return "";
     820             : 
     821         214 :     return hHFA->papoBand[nBand - 1]->GetBandName();
     822             : }
     823             : 
     824             : /************************************************************************/
     825             : /*                         HFASetBandName()                             */
     826             : /************************************************************************/
     827             : 
     828           7 : void HFASetBandName(HFAHandle hHFA, int nBand, const char *pszName)
     829             : {
     830           7 :     if (nBand < 1 || nBand > hHFA->nBands)
     831           0 :         return;
     832             : 
     833           7 :     hHFA->papoBand[nBand - 1]->SetBandName(pszName);
     834             : }
     835             : 
     836             : /************************************************************************/
     837             : /*                         HFAGetDataTypeBits()                         */
     838             : /************************************************************************/
     839             : 
     840        4730 : int HFAGetDataTypeBits(EPTType eDataType)
     841             : 
     842             : {
     843        4730 :     switch (eDataType)
     844             :     {
     845          76 :         case EPT_u1:
     846          76 :             return 1;
     847             : 
     848          42 :         case EPT_u2:
     849          42 :             return 2;
     850             : 
     851          18 :         case EPT_u4:
     852          18 :             return 4;
     853             : 
     854        1345 :         case EPT_u8:
     855             :         case EPT_s8:
     856        1345 :             return 8;
     857             : 
     858        1149 :         case EPT_u16:
     859             :         case EPT_s16:
     860        1149 :             return 16;
     861             : 
     862         268 :         case EPT_u32:
     863             :         case EPT_s32:
     864             :         case EPT_f32:
     865         268 :             return 32;
     866             : 
     867        1793 :         case EPT_f64:
     868             :         case EPT_c64:
     869        1793 :             return 64;
     870             : 
     871          39 :         case EPT_c128:
     872          39 :             return 128;
     873             :     }
     874             : 
     875           0 :     CPLAssert(false);
     876             :     return 1;
     877             : }
     878             : 
     879             : /************************************************************************/
     880             : /*                         HFAGetDataTypeName()                         */
     881             : /************************************************************************/
     882             : 
     883           0 : const char *HFAGetDataTypeName(EPTType eDataType)
     884             : 
     885             : {
     886           0 :     switch (eDataType)
     887             :     {
     888           0 :         case EPT_u1:
     889           0 :             return "u1";
     890             : 
     891           0 :         case EPT_u2:
     892           0 :             return "u2";
     893             : 
     894           0 :         case EPT_u4:
     895           0 :             return "u4";
     896             : 
     897           0 :         case EPT_u8:
     898           0 :             return "u8";
     899             : 
     900           0 :         case EPT_s8:
     901           0 :             return "s8";
     902             : 
     903           0 :         case EPT_u16:
     904           0 :             return "u16";
     905             : 
     906           0 :         case EPT_s16:
     907           0 :             return "s16";
     908             : 
     909           0 :         case EPT_u32:
     910           0 :             return "u32";
     911             : 
     912           0 :         case EPT_s32:
     913           0 :             return "s32";
     914             : 
     915           0 :         case EPT_f32:
     916           0 :             return "f32";
     917             : 
     918           0 :         case EPT_f64:
     919           0 :             return "f64";
     920             : 
     921           0 :         case EPT_c64:
     922           0 :             return "c64";
     923             : 
     924           0 :         case EPT_c128:
     925           0 :             return "c128";
     926             : 
     927           0 :         default:
     928           0 :             CPLAssert(false);
     929             :             return "unknown";
     930             :     }
     931             : }
     932             : 
     933             : /************************************************************************/
     934             : /*                           HFAGetMapInfo()                            */
     935             : /************************************************************************/
     936             : 
     937        1086 : const Eprj_MapInfo *HFAGetMapInfo(HFAHandle hHFA)
     938             : 
     939             : {
     940        1086 :     if (hHFA->nBands < 1)
     941           0 :         return nullptr;
     942             : 
     943             :     // Do we already have it?
     944        1086 :     if (hHFA->pMapInfo != nullptr)
     945         246 :         return (Eprj_MapInfo *)hHFA->pMapInfo;
     946             : 
     947             :     // Get the HFA node.  If we don't find it under the usual name
     948             :     // we search for any node of the right type (#3338).
     949         840 :     HFAEntry *poMIEntry = hHFA->papoBand[0]->poNode->GetNamedChild("Map_Info");
     950         840 :     if (poMIEntry == nullptr)
     951             :     {
     952         594 :         for (HFAEntry *poChild = hHFA->papoBand[0]->poNode->GetChild();
     953        2060 :              poChild != nullptr && poMIEntry == nullptr;
     954        1466 :              poChild = poChild->GetNext())
     955             :         {
     956        1466 :             if (EQUAL(poChild->GetType(), "Eprj_MapInfo"))
     957           0 :                 poMIEntry = poChild;
     958             :         }
     959             :     }
     960             : 
     961         840 :     if (poMIEntry == nullptr)
     962             :     {
     963         594 :         return nullptr;
     964             :     }
     965             : 
     966             :     // Allocate the structure.
     967             :     Eprj_MapInfo *psMapInfo =
     968         246 :         static_cast<Eprj_MapInfo *>(CPLCalloc(sizeof(Eprj_MapInfo), 1));
     969             : 
     970             :     // Fetch the fields.
     971         246 :     psMapInfo->proName = CPLStrdup(poMIEntry->GetStringField("proName"));
     972             : 
     973         246 :     psMapInfo->upperLeftCenter.x =
     974         246 :         poMIEntry->GetDoubleField("upperLeftCenter.x");
     975         246 :     psMapInfo->upperLeftCenter.y =
     976         246 :         poMIEntry->GetDoubleField("upperLeftCenter.y");
     977             : 
     978         246 :     psMapInfo->lowerRightCenter.x =
     979         246 :         poMIEntry->GetDoubleField("lowerRightCenter.x");
     980         246 :     psMapInfo->lowerRightCenter.y =
     981         246 :         poMIEntry->GetDoubleField("lowerRightCenter.y");
     982             : 
     983         246 :     CPLErr eErr = CE_None;
     984         246 :     psMapInfo->pixelSize.width =
     985         246 :         poMIEntry->GetDoubleField("pixelSize.width", &eErr);
     986         246 :     psMapInfo->pixelSize.height =
     987         246 :         poMIEntry->GetDoubleField("pixelSize.height", &eErr);
     988             : 
     989             :     // The following is basically a hack to get files with
     990             :     // non-standard MapInfo's that misname the pixelSize fields. (#3338)
     991         246 :     if (eErr != CE_None)
     992             :     {
     993           0 :         psMapInfo->pixelSize.width = poMIEntry->GetDoubleField("pixelSize.x");
     994           0 :         psMapInfo->pixelSize.height = poMIEntry->GetDoubleField("pixelSize.y");
     995             :     }
     996             : 
     997         246 :     psMapInfo->units = CPLStrdup(poMIEntry->GetStringField("units"));
     998             : 
     999         246 :     hHFA->pMapInfo = (void *)psMapInfo;
    1000             : 
    1001         246 :     return psMapInfo;
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                        HFAInvGeoTransform()                          */
    1006             : /************************************************************************/
    1007             : 
    1008           6 : static bool HFAInvGeoTransform(const double *gt_in, double *gt_out)
    1009             : 
    1010             : {
    1011             :     // Assume a 3rd row that is [1 0 0].
    1012             :     // Compute determinate.
    1013           6 :     const double det = gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4];
    1014             : 
    1015           6 :     if (fabs(det) < 1.0e-15)
    1016           0 :         return false;
    1017             : 
    1018           6 :     const double inv_det = 1.0 / det;
    1019             : 
    1020             :     // Compute adjoint, and divide by determinate.
    1021           6 :     gt_out[1] = gt_in[5] * inv_det;
    1022           6 :     gt_out[4] = -gt_in[4] * inv_det;
    1023             : 
    1024           6 :     gt_out[2] = -gt_in[2] * inv_det;
    1025           6 :     gt_out[5] = gt_in[1] * inv_det;
    1026             : 
    1027           6 :     gt_out[0] = (gt_in[2] * gt_in[3] - gt_in[0] * gt_in[5]) * inv_det;
    1028           6 :     gt_out[3] = (-gt_in[1] * gt_in[3] + gt_in[0] * gt_in[4]) * inv_det;
    1029             : 
    1030           6 :     return true;
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                         HFAGetGeoTransform()                         */
    1035             : /************************************************************************/
    1036             : 
    1037         543 : int HFAGetGeoTransform(HFAHandle hHFA, double *padfGeoTransform)
    1038             : 
    1039             : {
    1040         543 :     const Eprj_MapInfo *psMapInfo = HFAGetMapInfo(hHFA);
    1041             : 
    1042         543 :     padfGeoTransform[0] = 0.0;
    1043         543 :     padfGeoTransform[1] = 1.0;
    1044         543 :     padfGeoTransform[2] = 0.0;
    1045         543 :     padfGeoTransform[3] = 0.0;
    1046         543 :     padfGeoTransform[4] = 0.0;
    1047         543 :     padfGeoTransform[5] = 1.0;
    1048             : 
    1049             :     // Simple (north up) MapInfo approach.
    1050         543 :     if (psMapInfo != nullptr)
    1051             :     {
    1052         246 :         padfGeoTransform[0] =
    1053         246 :             psMapInfo->upperLeftCenter.x - psMapInfo->pixelSize.width * 0.5;
    1054         246 :         padfGeoTransform[1] = psMapInfo->pixelSize.width;
    1055         246 :         if (padfGeoTransform[1] == 0.0)
    1056           0 :             padfGeoTransform[1] = 1.0;
    1057         246 :         padfGeoTransform[2] = 0.0;
    1058         246 :         if (psMapInfo->upperLeftCenter.y >= psMapInfo->lowerRightCenter.y)
    1059         246 :             padfGeoTransform[5] = -psMapInfo->pixelSize.height;
    1060             :         else
    1061           0 :             padfGeoTransform[5] = psMapInfo->pixelSize.height;
    1062         246 :         if (padfGeoTransform[5] == 0.0)
    1063           0 :             padfGeoTransform[5] = 1.0;
    1064             : 
    1065         246 :         padfGeoTransform[3] =
    1066         246 :             psMapInfo->upperLeftCenter.y - padfGeoTransform[5] * 0.5;
    1067         246 :         padfGeoTransform[4] = 0.0;
    1068             : 
    1069             :         // Special logic to fixup odd angular units.
    1070         246 :         if (EQUAL(psMapInfo->units, "ds"))
    1071             :         {
    1072           0 :             padfGeoTransform[0] /= 3600.0;
    1073           0 :             padfGeoTransform[1] /= 3600.0;
    1074           0 :             padfGeoTransform[2] /= 3600.0;
    1075           0 :             padfGeoTransform[3] /= 3600.0;
    1076           0 :             padfGeoTransform[4] /= 3600.0;
    1077           0 :             padfGeoTransform[5] /= 3600.0;
    1078             :         }
    1079             : 
    1080         246 :         return TRUE;
    1081             :     }
    1082             : 
    1083             :     // Try for a MapToPixelXForm affine polynomial supporting
    1084             :     // rotated and sheared affine transformations.
    1085         297 :     if (hHFA->nBands == 0)
    1086           0 :         return FALSE;
    1087             : 
    1088             :     HFAEntry *poXForm0 =
    1089         297 :         hHFA->papoBand[0]->poNode->GetNamedChild("MapToPixelXForm.XForm0");
    1090             : 
    1091         297 :     if (poXForm0 == nullptr)
    1092         291 :         return FALSE;
    1093             : 
    1094           6 :     if (poXForm0->GetIntField("order") != 1 ||
    1095           5 :         poXForm0->GetIntField("numdimtransform") != 2 ||
    1096          16 :         poXForm0->GetIntField("numdimpolynomial") != 2 ||
    1097           5 :         poXForm0->GetIntField("termcount") != 3)
    1098           1 :         return FALSE;
    1099             : 
    1100             :     // Verify that there aren't any further xform steps.
    1101           5 :     if (hHFA->papoBand[0]->poNode->GetNamedChild("MapToPixelXForm.XForm1") !=
    1102             :         nullptr)
    1103           1 :         return FALSE;
    1104             : 
    1105             :     // We should check that the exponent list is 0 0 1 0 0 1, but
    1106             :     // we don't because we are lazy.
    1107             : 
    1108             :     // Fetch geotransform values.
    1109           4 :     double adfXForm[6] = {poXForm0->GetDoubleField("polycoefvector[0]"),
    1110           8 :                           poXForm0->GetDoubleField("polycoefmtx[0]"),
    1111           8 :                           poXForm0->GetDoubleField("polycoefmtx[2]"),
    1112           8 :                           poXForm0->GetDoubleField("polycoefvector[1]"),
    1113           8 :                           poXForm0->GetDoubleField("polycoefmtx[1]"),
    1114           4 :                           poXForm0->GetDoubleField("polycoefmtx[3]")};
    1115             : 
    1116             :     // Invert.
    1117             : 
    1118           4 :     if (!HFAInvGeoTransform(adfXForm, padfGeoTransform))
    1119           0 :         memset(padfGeoTransform, 0, 6 * sizeof(double));
    1120             : 
    1121             :     // Adjust origin from center of top left pixel to top left corner
    1122             :     // of top left pixel.
    1123           4 :     padfGeoTransform[0] -= padfGeoTransform[1] * 0.5;
    1124           4 :     padfGeoTransform[0] -= padfGeoTransform[2] * 0.5;
    1125           4 :     padfGeoTransform[3] -= padfGeoTransform[4] * 0.5;
    1126           4 :     padfGeoTransform[3] -= padfGeoTransform[5] * 0.5;
    1127             : 
    1128           4 :     return TRUE;
    1129             : }
    1130             : 
    1131             : /************************************************************************/
    1132             : /*                           HFASetMapInfo()                            */
    1133             : /************************************************************************/
    1134             : 
    1135         145 : CPLErr HFASetMapInfo(HFAHandle hHFA, const Eprj_MapInfo *poMapInfo)
    1136             : 
    1137             : {
    1138             :     // Loop over bands, setting information on each one.
    1139         328 :     for (int iBand = 0; iBand < hHFA->nBands; iBand++)
    1140             :     {
    1141             :         // Create a new Map_Info if there isn't one present already.
    1142             :         HFAEntry *poMIEntry =
    1143         183 :             hHFA->papoBand[iBand]->poNode->GetNamedChild("Map_Info");
    1144         183 :         if (poMIEntry == nullptr)
    1145             :         {
    1146         182 :             poMIEntry = HFAEntry::New(hHFA, "Map_Info", "Eprj_MapInfo",
    1147         182 :                                       hHFA->papoBand[iBand]->poNode);
    1148             :         }
    1149             : 
    1150         183 :         poMIEntry->MarkDirty();
    1151             : 
    1152             :         // Ensure we have enough space for all the data.
    1153             :         // TODO(schwehr): Explain 48 and 40 constants.
    1154         183 :         const int nSize =
    1155         183 :             static_cast<int>(48 + 40 + strlen(poMapInfo->proName) + 1 +
    1156         183 :                              strlen(poMapInfo->units) + 1);
    1157             : 
    1158         183 :         GByte *pabyData = poMIEntry->MakeData(nSize);
    1159         183 :         memset(pabyData, 0, nSize);
    1160             : 
    1161         183 :         poMIEntry->SetPosition();
    1162             : 
    1163             :         // Write the various fields.
    1164         183 :         poMIEntry->SetStringField("proName", poMapInfo->proName);
    1165             : 
    1166         183 :         poMIEntry->SetDoubleField("upperLeftCenter.x",
    1167         183 :                                   poMapInfo->upperLeftCenter.x);
    1168         183 :         poMIEntry->SetDoubleField("upperLeftCenter.y",
    1169         183 :                                   poMapInfo->upperLeftCenter.y);
    1170             : 
    1171         183 :         poMIEntry->SetDoubleField("lowerRightCenter.x",
    1172         183 :                                   poMapInfo->lowerRightCenter.x);
    1173         183 :         poMIEntry->SetDoubleField("lowerRightCenter.y",
    1174         183 :                                   poMapInfo->lowerRightCenter.y);
    1175             : 
    1176         183 :         poMIEntry->SetDoubleField("pixelSize.width",
    1177         183 :                                   poMapInfo->pixelSize.width);
    1178         183 :         poMIEntry->SetDoubleField("pixelSize.height",
    1179         183 :                                   poMapInfo->pixelSize.height);
    1180             : 
    1181         183 :         poMIEntry->SetStringField("units", poMapInfo->units);
    1182             :     }
    1183             : 
    1184         145 :     return CE_None;
    1185             : }
    1186             : 
    1187             : /************************************************************************/
    1188             : /*                           HFAGetPEString()                           */
    1189             : /*                                                                      */
    1190             : /*      Some files have a ProjectionX node containing the ESRI style    */
    1191             : /*      PE_STRING.  This function allows fetching from it.              */
    1192             : /************************************************************************/
    1193             : 
    1194          81 : char *HFAGetPEString(HFAHandle hHFA)
    1195             : 
    1196             : {
    1197          81 :     if (hHFA->nBands == 0)
    1198           0 :         return nullptr;
    1199             : 
    1200             :     // Get the HFA node.
    1201          81 :     HFAEntry *poProX = hHFA->papoBand[0]->poNode->GetNamedChild("ProjectionX");
    1202          81 :     if (poProX == nullptr)
    1203          62 :         return nullptr;
    1204             : 
    1205          19 :     const char *pszType = poProX->GetStringField("projection.type.string");
    1206          19 :     if (pszType == nullptr || !EQUAL(pszType, "PE_COORDSYS"))
    1207           0 :         return nullptr;
    1208             : 
    1209             :     // Use a gross hack to scan ahead to the actual projection
    1210             :     // string. We do it this way because we don't have general
    1211             :     // handling for MIFObjects.
    1212          19 :     GByte *pabyData = poProX->GetData();
    1213          19 :     int nDataSize = poProX->GetDataSize();
    1214             : 
    1215        1767 :     while (nDataSize > 10 &&
    1216        1767 :            !STARTS_WITH_CI((const char *)pabyData, "PE_COORDSYS,."))
    1217             :     {
    1218        1748 :         pabyData++;
    1219        1748 :         nDataSize--;
    1220             :     }
    1221             : 
    1222          19 :     if (nDataSize < 31)
    1223           0 :         return nullptr;
    1224             : 
    1225             :     // Skip ahead to the actual string.
    1226          19 :     pabyData += 30;
    1227             :     // nDataSize -= 30;
    1228             : 
    1229          19 :     return CPLStrdup((const char *)pabyData);
    1230             : }
    1231             : 
    1232             : /************************************************************************/
    1233             : /*                           HFASetPEString()                           */
    1234             : /************************************************************************/
    1235             : 
    1236         133 : CPLErr HFASetPEString(HFAHandle hHFA, const char *pszPEString)
    1237             : 
    1238             : {
    1239         133 :     if (!CPLTestBool(CPLGetConfigOption("HFA_WRITE_PE_STRING", "YES")))
    1240           0 :         return CE_None;
    1241             : 
    1242             :     // Loop over bands, setting information on each one.
    1243         304 :     for (int iBand = 0; iBand < hHFA->nBands; iBand++)
    1244             :     {
    1245             :         // Verify we don't already have the node, since update-in-place
    1246             :         // is likely to be more complicated.
    1247             :         HFAEntry *poProX =
    1248         171 :             hHFA->papoBand[iBand]->poNode->GetNamedChild("ProjectionX");
    1249             : 
    1250             :         // If we are setting an empty string then a missing entry is equivalent.
    1251         171 :         if (strlen(pszPEString) == 0 && poProX == nullptr)
    1252           1 :             continue;
    1253             : 
    1254             :         // Create the node.
    1255         170 :         if (poProX == nullptr)
    1256             :         {
    1257         338 :             poProX = HFAEntry::New(hHFA, "ProjectionX", "Eprj_MapProjection842",
    1258         169 :                                    hHFA->papoBand[iBand]->poNode);
    1259         169 :             if (poProX->GetTypeObject() == nullptr)
    1260           0 :                 return CE_Failure;
    1261             :         }
    1262             : 
    1263             :         // Prepare the data area with some extra space just in case.
    1264             :         GByte *pabyData =
    1265         170 :             poProX->MakeData(static_cast<int>(700 + strlen(pszPEString)));
    1266         170 :         if (!pabyData)
    1267           0 :             return CE_Failure;
    1268             : 
    1269         170 :         memset(pabyData, 0, 250 + strlen(pszPEString));
    1270             : 
    1271         170 :         poProX->SetPosition();
    1272             : 
    1273         170 :         poProX->SetStringField("projection.type.string", "PE_COORDSYS");
    1274         170 :         poProX->SetStringField("projection.MIFDictionary.string",
    1275             :                                "{0:pcstring,}Emif_String,{1:x{0:pcstring,}"
    1276             :                                "Emif_String,coordSys,}PE_COORDSYS,.");
    1277             : 
    1278             :         // Use a gross hack to scan ahead to the actual projection
    1279             :         // string. We do it this way because we don't have general
    1280             :         // handling for MIFObjects
    1281         170 :         pabyData = poProX->GetData();
    1282         170 :         int nDataSize = poProX->GetDataSize();
    1283         170 :         GUInt32 iOffset = poProX->GetDataPos();
    1284             : 
    1285       15810 :         while (nDataSize > 10 &&
    1286       15810 :                !STARTS_WITH_CI((const char *)pabyData, "PE_COORDSYS,."))
    1287             :         {
    1288       15640 :             pabyData++;
    1289       15640 :             nDataSize--;
    1290       15640 :             iOffset++;
    1291             :         }
    1292             : 
    1293         170 :         CPLAssert(nDataSize > static_cast<int>(strlen(pszPEString)) + 10);
    1294             : 
    1295         170 :         pabyData += 14;
    1296         170 :         iOffset += 14;
    1297             : 
    1298             :         // Set the size and offset of the mifobject.
    1299         170 :         iOffset += 8;
    1300             : 
    1301         170 :         GUInt32 nSize = static_cast<GUInt32>(strlen(pszPEString) + 9);
    1302             : 
    1303             :         HFAStandard(4, &nSize);
    1304         170 :         memcpy(pabyData, &nSize, 4);
    1305         170 :         pabyData += 4;
    1306             : 
    1307             :         HFAStandard(4, &iOffset);
    1308         170 :         memcpy(pabyData, &iOffset, 4);
    1309         170 :         pabyData += 4;
    1310             : 
    1311             :         // Set the size and offset of the string value.
    1312         170 :         nSize = static_cast<GUInt32>(strlen(pszPEString) + 1);
    1313             : 
    1314             :         HFAStandard(4, &nSize);
    1315         170 :         memcpy(pabyData, &nSize, 4);
    1316         170 :         pabyData += 4;
    1317             : 
    1318         170 :         iOffset = 8;
    1319             :         HFAStandard(4, &iOffset);
    1320         170 :         memcpy(pabyData, &iOffset, 4);
    1321         170 :         pabyData += 4;
    1322             : 
    1323             :         // Place the string itself.
    1324         170 :         memcpy(pabyData, pszPEString, strlen(pszPEString) + 1);
    1325             : 
    1326         170 :         poProX->SetStringField("title.string", "PE");
    1327             :     }
    1328             : 
    1329         133 :     return CE_None;
    1330             : }
    1331             : 
    1332             : /************************************************************************/
    1333             : /*                        HFAGetProParameters()                         */
    1334             : /************************************************************************/
    1335             : 
    1336         543 : const Eprj_ProParameters *HFAGetProParameters(HFAHandle hHFA)
    1337             : 
    1338             : {
    1339         543 :     if (hHFA->nBands < 1)
    1340           0 :         return nullptr;
    1341             : 
    1342             :     // Do we already have it?
    1343         543 :     if (hHFA->pProParameters != nullptr)
    1344           0 :         return (Eprj_ProParameters *)hHFA->pProParameters;
    1345             : 
    1346             :     // Get the HFA node.
    1347             :     HFAEntry *poMIEntry =
    1348         543 :         hHFA->papoBand[0]->poNode->GetNamedChild("Projection");
    1349         543 :     if (poMIEntry == nullptr)
    1350         340 :         return nullptr;
    1351             : 
    1352             :     // Allocate the structure.
    1353             :     Eprj_ProParameters *psProParams = static_cast<Eprj_ProParameters *>(
    1354         203 :         CPLCalloc(sizeof(Eprj_ProParameters), 1));
    1355             : 
    1356             :     // Fetch the fields.
    1357         203 :     const int proType = poMIEntry->GetIntField("proType");
    1358         203 :     if (proType != EPRJ_INTERNAL && proType != EPRJ_EXTERNAL)
    1359             :     {
    1360           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for proType");
    1361           0 :         CPLFree(psProParams);
    1362           0 :         return nullptr;
    1363             :     }
    1364         203 :     psProParams->proType = static_cast<Eprj_ProType>(proType);
    1365         203 :     psProParams->proNumber = poMIEntry->GetIntField("proNumber");
    1366         203 :     psProParams->proExeName =
    1367         203 :         CPLStrdup(poMIEntry->GetStringField("proExeName"));
    1368         203 :     psProParams->proName = CPLStrdup(poMIEntry->GetStringField("proName"));
    1369         203 :     psProParams->proZone = poMIEntry->GetIntField("proZone");
    1370             : 
    1371        3248 :     for (int i = 0; i < 15; i++)
    1372             :     {
    1373        3045 :         char szFieldName[40] = {};
    1374             : 
    1375        3045 :         snprintf(szFieldName, sizeof(szFieldName), "proParams[%d]", i);
    1376        3045 :         psProParams->proParams[i] = poMIEntry->GetDoubleField(szFieldName);
    1377             :     }
    1378             : 
    1379         203 :     psProParams->proSpheroid.sphereName =
    1380         203 :         CPLStrdup(poMIEntry->GetStringField("proSpheroid.sphereName"));
    1381         203 :     psProParams->proSpheroid.a = poMIEntry->GetDoubleField("proSpheroid.a");
    1382         203 :     psProParams->proSpheroid.b = poMIEntry->GetDoubleField("proSpheroid.b");
    1383         203 :     psProParams->proSpheroid.eSquared =
    1384         203 :         poMIEntry->GetDoubleField("proSpheroid.eSquared");
    1385         203 :     psProParams->proSpheroid.radius =
    1386         203 :         poMIEntry->GetDoubleField("proSpheroid.radius");
    1387             : 
    1388         203 :     hHFA->pProParameters = (void *)psProParams;
    1389             : 
    1390         203 :     return psProParams;
    1391             : }
    1392             : 
    1393             : /************************************************************************/
    1394             : /*                        HFASetProParameters()                         */
    1395             : /************************************************************************/
    1396             : 
    1397         131 : CPLErr HFASetProParameters(HFAHandle hHFA, const Eprj_ProParameters *poPro)
    1398             : 
    1399             : {
    1400             :     // Loop over bands, setting information on each one.
    1401         300 :     for (int iBand = 0; iBand < hHFA->nBands; iBand++)
    1402             :     {
    1403             :         // Create a new Projection if there isn't one present already.
    1404             :         HFAEntry *poMIEntry =
    1405         169 :             hHFA->papoBand[iBand]->poNode->GetNamedChild("Projection");
    1406         169 :         if (poMIEntry == nullptr)
    1407             :         {
    1408         168 :             poMIEntry = HFAEntry::New(hHFA, "Projection", "Eprj_ProParameters",
    1409         168 :                                       hHFA->papoBand[iBand]->poNode);
    1410             :         }
    1411             : 
    1412         169 :         poMIEntry->MarkDirty();
    1413             : 
    1414             :         // Ensure we have enough space for all the data.
    1415             :         // TODO(schwehr): Explain all these constants.
    1416         169 :         int nSize =
    1417         169 :             static_cast<int>(34 + 15 * 8 + 8 + strlen(poPro->proName) + 1 + 32 +
    1418         169 :                              8 + strlen(poPro->proSpheroid.sphereName) + 1);
    1419             : 
    1420         169 :         if (poPro->proExeName != nullptr)
    1421           1 :             nSize += static_cast<int>(strlen(poPro->proExeName) + 1);
    1422             : 
    1423         169 :         GByte *pabyData = poMIEntry->MakeData(nSize);
    1424         169 :         if (!pabyData)
    1425           0 :             return CE_Failure;
    1426             : 
    1427         169 :         poMIEntry->SetPosition();
    1428             : 
    1429             :         // Initialize the whole thing to zeros for a clean start.
    1430         169 :         memset(poMIEntry->GetData(), 0, poMIEntry->GetDataSize());
    1431             : 
    1432             :         // Write the various fields.
    1433         169 :         poMIEntry->SetIntField("proType", poPro->proType);
    1434             : 
    1435         169 :         poMIEntry->SetIntField("proNumber", poPro->proNumber);
    1436             : 
    1437         169 :         poMIEntry->SetStringField("proExeName", poPro->proExeName);
    1438         169 :         poMIEntry->SetStringField("proName", poPro->proName);
    1439         169 :         poMIEntry->SetIntField("proZone", poPro->proZone);
    1440         169 :         poMIEntry->SetDoubleField("proParams[0]", poPro->proParams[0]);
    1441         169 :         poMIEntry->SetDoubleField("proParams[1]", poPro->proParams[1]);
    1442         169 :         poMIEntry->SetDoubleField("proParams[2]", poPro->proParams[2]);
    1443         169 :         poMIEntry->SetDoubleField("proParams[3]", poPro->proParams[3]);
    1444         169 :         poMIEntry->SetDoubleField("proParams[4]", poPro->proParams[4]);
    1445         169 :         poMIEntry->SetDoubleField("proParams[5]", poPro->proParams[5]);
    1446         169 :         poMIEntry->SetDoubleField("proParams[6]", poPro->proParams[6]);
    1447         169 :         poMIEntry->SetDoubleField("proParams[7]", poPro->proParams[7]);
    1448         169 :         poMIEntry->SetDoubleField("proParams[8]", poPro->proParams[8]);
    1449         169 :         poMIEntry->SetDoubleField("proParams[9]", poPro->proParams[9]);
    1450         169 :         poMIEntry->SetDoubleField("proParams[10]", poPro->proParams[10]);
    1451         169 :         poMIEntry->SetDoubleField("proParams[11]", poPro->proParams[11]);
    1452         169 :         poMIEntry->SetDoubleField("proParams[12]", poPro->proParams[12]);
    1453         169 :         poMIEntry->SetDoubleField("proParams[13]", poPro->proParams[13]);
    1454         169 :         poMIEntry->SetDoubleField("proParams[14]", poPro->proParams[14]);
    1455         169 :         poMIEntry->SetStringField("proSpheroid.sphereName",
    1456         169 :                                   poPro->proSpheroid.sphereName);
    1457         169 :         poMIEntry->SetDoubleField("proSpheroid.a", poPro->proSpheroid.a);
    1458         169 :         poMIEntry->SetDoubleField("proSpheroid.b", poPro->proSpheroid.b);
    1459         169 :         poMIEntry->SetDoubleField("proSpheroid.eSquared",
    1460         169 :                                   poPro->proSpheroid.eSquared);
    1461         169 :         poMIEntry->SetDoubleField("proSpheroid.radius",
    1462         169 :                                   poPro->proSpheroid.radius);
    1463             :     }
    1464             : 
    1465         131 :     return CE_None;
    1466             : }
    1467             : 
    1468             : /************************************************************************/
    1469             : /*                            HFAGetDatum()                             */
    1470             : /************************************************************************/
    1471             : 
    1472         543 : const Eprj_Datum *HFAGetDatum(HFAHandle hHFA)
    1473             : 
    1474             : {
    1475         543 :     if (hHFA->nBands < 1)
    1476           0 :         return nullptr;
    1477             : 
    1478             :     // Do we already have it?
    1479         543 :     if (hHFA->pDatum != nullptr)
    1480           0 :         return (Eprj_Datum *)hHFA->pDatum;
    1481             : 
    1482             :     // Get the HFA node.
    1483             :     HFAEntry *poMIEntry =
    1484         543 :         hHFA->papoBand[0]->poNode->GetNamedChild("Projection.Datum");
    1485         543 :     if (poMIEntry == nullptr)
    1486         340 :         return nullptr;
    1487             : 
    1488             :     // Allocate the structure.
    1489             :     Eprj_Datum *psDatum =
    1490         203 :         static_cast<Eprj_Datum *>(CPLCalloc(sizeof(Eprj_Datum), 1));
    1491             : 
    1492             :     // Fetch the fields.
    1493         203 :     psDatum->datumname = CPLStrdup(poMIEntry->GetStringField("datumname"));
    1494         203 :     const int nDatumType = poMIEntry->GetIntField("type");
    1495         203 :     if (nDatumType < 0 || nDatumType > EPRJ_DATUM_NONE)
    1496             :     {
    1497           1 :         CPLDebug("HFA", "Invalid value for datum type: %d", nDatumType);
    1498           1 :         psDatum->type = EPRJ_DATUM_NONE;
    1499             :     }
    1500             :     else
    1501         202 :         psDatum->type = static_cast<Eprj_DatumType>(nDatumType);
    1502             : 
    1503        1624 :     for (int i = 0; i < 7; i++)
    1504             :     {
    1505        1421 :         char szFieldName[30] = {};
    1506        1421 :         snprintf(szFieldName, sizeof(szFieldName), "params[%d]", i);
    1507        1421 :         psDatum->params[i] = poMIEntry->GetDoubleField(szFieldName);
    1508             :     }
    1509             : 
    1510         203 :     psDatum->gridname = CPLStrdup(poMIEntry->GetStringField("gridname"));
    1511             : 
    1512         203 :     hHFA->pDatum = (void *)psDatum;
    1513             : 
    1514         203 :     return psDatum;
    1515             : }
    1516             : 
    1517             : /************************************************************************/
    1518             : /*                            HFASetDatum()                             */
    1519             : /************************************************************************/
    1520             : 
    1521         131 : CPLErr HFASetDatum(HFAHandle hHFA, const Eprj_Datum *poDatum)
    1522             : 
    1523             : {
    1524             :     // Loop over bands, setting information on each one.
    1525         300 :     for (int iBand = 0; iBand < hHFA->nBands; iBand++)
    1526             :     {
    1527             :         // Create a new Projection if there isn't one present already.
    1528             :         HFAEntry *poProParams =
    1529         169 :             hHFA->papoBand[iBand]->poNode->GetNamedChild("Projection");
    1530         169 :         if (poProParams == nullptr)
    1531             :         {
    1532           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1533             :                      "Can't add Eprj_Datum with no Eprj_ProjParameters.");
    1534           0 :             return CE_Failure;
    1535             :         }
    1536             : 
    1537         169 :         HFAEntry *poDatumEntry = poProParams->GetNamedChild("Datum");
    1538         169 :         if (poDatumEntry == nullptr)
    1539             :         {
    1540             :             poDatumEntry =
    1541         168 :                 HFAEntry::New(hHFA, "Datum", "Eprj_Datum", poProParams);
    1542             :         }
    1543             : 
    1544         169 :         poDatumEntry->MarkDirty();
    1545             : 
    1546             :         // Ensure we have enough space for all the data.
    1547             :         // TODO(schwehr): Explain constants.
    1548         169 :         int nSize =
    1549         169 :             static_cast<int>(26 + strlen(poDatum->datumname) + 1 + 7 * 8);
    1550             : 
    1551         169 :         if (poDatum->gridname != nullptr)
    1552          30 :             nSize += static_cast<int>(strlen(poDatum->gridname) + 1);
    1553             : 
    1554         169 :         GByte *pabyData = poDatumEntry->MakeData(nSize);
    1555         169 :         if (!pabyData)
    1556           0 :             return CE_Failure;
    1557             : 
    1558         169 :         poDatumEntry->SetPosition();
    1559             : 
    1560             :         // Initialize the whole thing to zeros for a clean start.
    1561         169 :         memset(poDatumEntry->GetData(), 0, poDatumEntry->GetDataSize());
    1562             : 
    1563             :         // Write the various fields.
    1564         169 :         poDatumEntry->SetStringField("datumname", poDatum->datumname);
    1565         169 :         poDatumEntry->SetIntField("type", poDatum->type);
    1566             : 
    1567         169 :         poDatumEntry->SetDoubleField("params[0]", poDatum->params[0]);
    1568         169 :         poDatumEntry->SetDoubleField("params[1]", poDatum->params[1]);
    1569         169 :         poDatumEntry->SetDoubleField("params[2]", poDatum->params[2]);
    1570         169 :         poDatumEntry->SetDoubleField("params[3]", poDatum->params[3]);
    1571         169 :         poDatumEntry->SetDoubleField("params[4]", poDatum->params[4]);
    1572         169 :         poDatumEntry->SetDoubleField("params[5]", poDatum->params[5]);
    1573         169 :         poDatumEntry->SetDoubleField("params[6]", poDatum->params[6]);
    1574             : 
    1575         169 :         poDatumEntry->SetStringField("gridname", poDatum->gridname);
    1576             :     }
    1577             : 
    1578         131 :     return CE_None;
    1579             : }
    1580             : 
    1581             : /************************************************************************/
    1582             : /*                             HFAGetPCT()                              */
    1583             : /*                                                                      */
    1584             : /*      Read the PCT from a band, if it has one.                        */
    1585             : /************************************************************************/
    1586             : 
    1587         623 : CPLErr HFAGetPCT(HFAHandle hHFA, int nBand, int *pnColors, double **ppadfRed,
    1588             :                  double **ppadfGreen, double **ppadfBlue, double **ppadfAlpha,
    1589             :                  double **ppadfBins)
    1590             : 
    1591             : {
    1592         623 :     if (nBand < 1 || nBand > hHFA->nBands)
    1593           0 :         return CE_Failure;
    1594             : 
    1595         623 :     return hHFA->papoBand[nBand - 1]->GetPCT(pnColors, ppadfRed, ppadfGreen,
    1596         623 :                                              ppadfBlue, ppadfAlpha, ppadfBins);
    1597             : }
    1598             : 
    1599             : /************************************************************************/
    1600             : /*                             HFASetPCT()                              */
    1601             : /*                                                                      */
    1602             : /*      Set the PCT on a band.                                          */
    1603             : /************************************************************************/
    1604             : 
    1605           3 : CPLErr HFASetPCT(HFAHandle hHFA, int nBand, int nColors, double *padfRed,
    1606             :                  double *padfGreen, double *padfBlue, double *padfAlpha)
    1607             : 
    1608             : {
    1609           3 :     if (nBand < 1 || nBand > hHFA->nBands)
    1610           0 :         return CE_Failure;
    1611             : 
    1612           3 :     return hHFA->papoBand[nBand - 1]->SetPCT(nColors, padfRed, padfGreen,
    1613           3 :                                              padfBlue, padfAlpha);
    1614             : }
    1615             : 
    1616             : /************************************************************************/
    1617             : /*                          HFAGetDataRange()                           */
    1618             : /************************************************************************/
    1619             : 
    1620           0 : CPLErr HFAGetDataRange(HFAHandle hHFA, int nBand, double *pdfMin,
    1621             :                        double *pdfMax)
    1622             : 
    1623             : {
    1624           0 :     if (nBand < 1 || nBand > hHFA->nBands)
    1625           0 :         return CE_Failure;
    1626             : 
    1627             :     HFAEntry *poBinInfo =
    1628           0 :         hHFA->papoBand[nBand - 1]->poNode->GetNamedChild("Statistics");
    1629             : 
    1630           0 :     if (poBinInfo == nullptr)
    1631           0 :         return CE_Failure;
    1632             : 
    1633           0 :     *pdfMin = poBinInfo->GetDoubleField("minimum");
    1634           0 :     *pdfMax = poBinInfo->GetDoubleField("maximum");
    1635             : 
    1636           0 :     if (*pdfMax > *pdfMin)
    1637           0 :         return CE_None;
    1638             :     else
    1639           0 :         return CE_Failure;
    1640             : }
    1641             : 
    1642             : /************************************************************************/
    1643             : /*                            HFADumpNode()                             */
    1644             : /************************************************************************/
    1645             : 
    1646           0 : static void HFADumpNode(HFAEntry *poEntry, int nIndent, bool bVerbose, FILE *fp)
    1647             : 
    1648             : {
    1649           0 :     std::string osSpaces(nIndent * 2, ' ');
    1650             : 
    1651           0 :     fprintf(fp, "%s%s(%s) @ %u + %u @ %u\n", osSpaces.c_str(),
    1652             :             poEntry->GetName(), poEntry->GetType(), poEntry->GetFilePos(),
    1653             :             poEntry->GetDataSize(), poEntry->GetDataPos());
    1654             : 
    1655           0 :     if (bVerbose)
    1656             :     {
    1657           0 :         osSpaces += "+ ";
    1658           0 :         poEntry->DumpFieldValues(fp, osSpaces.c_str());
    1659           0 :         fprintf(fp, "\n");
    1660             :     }
    1661             : 
    1662           0 :     if (poEntry->GetChild() != nullptr)
    1663           0 :         HFADumpNode(poEntry->GetChild(), nIndent + 1, bVerbose, fp);
    1664             : 
    1665           0 :     if (poEntry->GetNext() != nullptr)
    1666           0 :         HFADumpNode(poEntry->GetNext(), nIndent, bVerbose, fp);
    1667           0 : }
    1668             : 
    1669             : /************************************************************************/
    1670             : /*                            HFADumpTree()                             */
    1671             : /*                                                                      */
    1672             : /*      Dump the tree of information in a HFA file.                     */
    1673             : /************************************************************************/
    1674             : 
    1675           0 : void HFADumpTree(HFAHandle hHFA, FILE *fpOut)
    1676             : 
    1677             : {
    1678           0 :     HFADumpNode(hHFA->poRoot, 0, true, fpOut);
    1679           0 : }
    1680             : 
    1681             : /************************************************************************/
    1682             : /*                         HFADumpDictionary()                          */
    1683             : /*                                                                      */
    1684             : /*      Dump the dictionary (in raw, and parsed form) to the named      */
    1685             : /*      device.                                                         */
    1686             : /************************************************************************/
    1687             : 
    1688           0 : void HFADumpDictionary(HFAHandle hHFA, FILE *fpOut)
    1689             : 
    1690             : {
    1691           0 :     fprintf(fpOut, "%s\n", hHFA->pszDictionary);
    1692             : 
    1693           0 :     hHFA->poDictionary->Dump(fpOut);
    1694           0 : }
    1695             : 
    1696             : /************************************************************************/
    1697             : /*                            HFAStandard()                             */
    1698             : /*                                                                      */
    1699             : /*      Swap byte order on MSB systems.                                 */
    1700             : /************************************************************************/
    1701             : 
    1702             : #ifdef CPL_MSB
    1703             : void HFAStandard(int nBytes, void *pData)
    1704             : 
    1705             : {
    1706             :     GByte *pabyData = static_cast<GByte *>(pData);
    1707             : 
    1708             :     for (int i = nBytes / 2 - 1; i >= 0; i--)
    1709             :     {
    1710             :         GByte byTemp = pabyData[i];
    1711             :         pabyData[i] = pabyData[nBytes - i - 1];
    1712             :         pabyData[nBytes - i - 1] = byTemp;
    1713             :     }
    1714             : }
    1715             : #endif
    1716             : 
    1717             : /* ==================================================================== */
    1718             : /*      Default data dictionary.  Emitted verbatim into the imagine     */
    1719             : /*      file.                                                           */
    1720             : /* ==================================================================== */
    1721             : 
    1722             : static const char *const aszDefaultDD[] = {
    1723             :     "{1:lversion,1:LfreeList,1:LrootEntryPtr,1:sentryHeaderLength,1:"
    1724             :     "LdictionaryPtr,}Ehfa_File,{1:Lnext,1:Lprev,1:Lparent,1:Lchild,1:Ldata,1:"
    1725             :     "ldataSize,64:cname,32:ctype,1:tmodTime,}Ehfa_Entry,{16:clabel,1:"
    1726             :     "LheaderPtr,}Ehfa_HeaderTag,{1:LfreeList,1:lfreeSize,}Ehfa_FreeListNode,{1:"
    1727             :     "lsize,1:Lptr,}Ehfa_Data,{1:lwidth,1:lheight,1:e3:thematic,athematic,fft "
    1728             :     "of real-valued data,layerType,",
    1729             :     "1:e13:u1,u2,u4,u8,s8,u16,s16,u32,s32,f32,f64,c64,c128,pixelType,1:"
    1730             :     "lblockWidth,1:lblockHeight,}Eimg_Layer,{1:lwidth,1:lheight,1:e3:thematic,"
    1731             :     "athematic,fft of real-valued "
    1732             :     "data,layerType,1:e13:u1,u2,u4,u8,s8,u16,s16,u32,s32,f32,f64,c64,c128,"
    1733             :     "pixelType,1:lblockWidth,1:lblockHeight,}Eimg_Layer_SubSample,{1:e2:raster,"
    1734             :     "vector,type,1:LdictionaryPtr,}Ehfa_Layer,{1:LspaceUsedForRasterData,}"
    1735             :     "ImgFormatInfo831,{1:sfileCode,1:Loffset,1:lsize,1:e2:false,true,logvalid,",
    1736             :     "1:e2:no compression,ESRI GRID "
    1737             :     "compression,compressionType,}Edms_VirtualBlockInfo,{1:lmin,1:lmax,}Edms_"
    1738             :     "FreeIDList,{1:lnumvirtualblocks,1:lnumobjectsperblock,1:lnextobjectnum,1:"
    1739             :     "e2:no compression,RLC "
    1740             :     "compression,compressionType,0:poEdms_VirtualBlockInfo,blockinfo,0:poEdms_"
    1741             :     "FreeIDList,freelist,1:tmodTime,}Edms_State,{0:pcstring,}Emif_String,{1:"
    1742             :     "oEmif_String,fileName,2:LlayerStackValidFlagsOffset,2:"
    1743             :     "LlayerStackDataOffset,1:LlayerStackCount,1:LlayerStackIndex,}"
    1744             :     "ImgExternalRaster,{1:oEmif_String,algorithm,0:poEmif_String,nameList,}"
    1745             :     "Eimg_RRDNamesList,{1:oEmif_String,projection,1:oEmif_String,units,}Eimg_"
    1746             :     "MapInformation,",
    1747             :     "{1:oEmif_String,dependent,}Eimg_DependentFile,{1:oEmif_String,"
    1748             :     "ImageLayerName,}Eimg_DependentLayerName,{1:lnumrows,1:lnumcolumns,1:e13:"
    1749             :     "EGDA_TYPE_U1,EGDA_TYPE_U2,EGDA_TYPE_U4,EGDA_TYPE_U8,EGDA_TYPE_S8,EGDA_"
    1750             :     "TYPE_U16,EGDA_TYPE_S16,EGDA_TYPE_U32,EGDA_TYPE_S32,EGDA_TYPE_F32,EGDA_"
    1751             :     "TYPE_F64,EGDA_TYPE_C64,EGDA_TYPE_C128,datatype,1:e4:EGDA_SCALAR_OBJECT,"
    1752             :     "EGDA_TABLE_OBJECT,EGDA_MATRIX_OBJECT,EGDA_RASTER_OBJECT,objecttype,}Egda_"
    1753             :     "BaseData,{1:*bvalueBD,}Eimg_NonInitializedValue,{1:dx,1:dy,}Eprj_"
    1754             :     "Coordinate,{1:dwidth,1:dheight,}Eprj_Size,{0:pcproName,1:*oEprj_"
    1755             :     "Coordinate,upperLeftCenter,",
    1756             :     "1:*oEprj_Coordinate,lowerRightCenter,1:*oEprj_Size,pixelSize,0:pcunits,}"
    1757             :     "Eprj_MapInfo,{0:pcdatumname,1:e3:EPRJ_DATUM_PARAMETRIC,EPRJ_DATUM_GRID,"
    1758             :     "EPRJ_DATUM_REGRESSION,type,0:pdparams,0:pcgridname,}Eprj_Datum,{0:"
    1759             :     "pcsphereName,1:da,1:db,1:deSquared,1:dradius,}Eprj_Spheroid,{1:e2:EPRJ_"
    1760             :     "INTERNAL,EPRJ_EXTERNAL,proType,1:lproNumber,0:pcproExeName,0:pcproName,1:"
    1761             :     "lproZone,0:pdproParams,1:*oEprj_Spheroid,proSpheroid,}Eprj_ProParameters,{"
    1762             :     "1:dminimum,1:dmaximum,1:dmean,1:dmedian,1:dmode,1:dstddev,}Esta_"
    1763             :     "Statistics,{1:lnumBins,1:e4:direct,linear,logarithmic,explicit,"
    1764             :     "binFunctionType,1:dminLimit,1:dmaxLimit,1:*bbinLimits,}Edsc_BinFunction,{"
    1765             :     "0:poEmif_String,LayerNames,1:*bExcludedValues,1:oEmif_String,AOIname,",
    1766             :     "1:lSkipFactorX,1:lSkipFactorY,1:*oEdsc_BinFunction,BinFunction,}Eimg_"
    1767             :     "StatisticsParameters830,{1:lnumrows,}Edsc_Table,{1:lnumRows,1:"
    1768             :     "LcolumnDataPtr,1:e4:integer,real,complex,string,dataType,1:lmaxNumChars,}"
    1769             :     "Edsc_Column,{1:lposition,0:pcname,1:e2:EMSC_FALSE,EMSC_TRUE,editable,1:e3:"
    1770             :     "LEFT,CENTER,RIGHT,alignment,0:pcformat,1:e3:DEFAULT,APPLY,AUTO-APPLY,"
    1771             :     "formulamode,0:pcformula,1:dcolumnwidth,0:pcunits,1:e5:NO_COLOR,RED,GREEN,"
    1772             :     "BLUE,COLOR,colorflag,0:pcgreenname,0:pcbluename,}Eded_ColumnAttributes_1,{"
    1773             :     "1:lversion,1:lnumobjects,1:e2:EAOI_UNION,EAOI_INTERSECTION,operation,}"
    1774             :     "Eaoi_AreaOfInterest,",
    1775             :     "{1:x{0:pcstring,}Emif_String,type,1:x{0:pcstring,}Emif_String,"
    1776             :     "MIFDictionary,0:pCMIFObject,}Emif_MIFObject,",
    1777             :     "{1:x{1:x{0:pcstring,}Emif_String,type,1:x{0:pcstring,}Emif_String,"
    1778             :     "MIFDictionary,0:pCMIFObject,}Emif_MIFObject,projection,1:x{0:pcstring,}"
    1779             :     "Emif_String,title,}Eprj_MapProjection842,",
    1780             :     "{0:poEmif_String,titleList,}Exfr_GenericXFormHeader,{1:lorder,1:"
    1781             :     "lnumdimtransform,1:lnumdimpolynomial,1:ltermcount,0:plexponentlist,1:*"
    1782             :     "bpolycoefmtx,1:*bpolycoefvector,}Efga_Polynomial,",
    1783             :     ".",
    1784             :     nullptr};
    1785             : 
    1786             : /************************************************************************/
    1787             : /*                            HFACreateLL()                             */
    1788             : /*                                                                      */
    1789             : /*      Low level creation of an Imagine file.  Writes out the          */
    1790             : /*      Ehfa_HeaderTag, dictionary and Ehfa_File.                       */
    1791             : /************************************************************************/
    1792             : 
    1793         204 : HFAHandle HFACreateLL(const char *pszFilename)
    1794             : 
    1795             : {
    1796             :     // Create the file in the file system.
    1797         204 :     VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
    1798         204 :     if (fp == nullptr)
    1799             :     {
    1800           3 :         CPLError(CE_Failure, CPLE_OpenFailed, "Creation of file %s failed.",
    1801             :                  pszFilename);
    1802           3 :         return nullptr;
    1803             :     }
    1804             : 
    1805             :     // Create the HFAInfo_t.
    1806             :     HFAInfo_t *psInfo =
    1807         201 :         static_cast<HFAInfo_t *>(CPLCalloc(sizeof(HFAInfo_t), 1));
    1808             : 
    1809         201 :     psInfo->fp = fp;
    1810         201 :     psInfo->eAccess = HFA_Update;
    1811         201 :     psInfo->nXSize = 0;
    1812         201 :     psInfo->nYSize = 0;
    1813         201 :     psInfo->nBands = 0;
    1814         201 :     psInfo->papoBand = nullptr;
    1815         201 :     psInfo->pMapInfo = nullptr;
    1816         201 :     psInfo->pDatum = nullptr;
    1817         201 :     psInfo->pProParameters = nullptr;
    1818         201 :     psInfo->bTreeDirty = false;
    1819         201 :     psInfo->pszFilename = CPLStrdup(CPLGetFilename(pszFilename));
    1820         201 :     psInfo->pszPath = CPLStrdup(CPLGetPathSafe(pszFilename).c_str());
    1821             : 
    1822             :     // Write out the Ehfa_HeaderTag.
    1823         201 :     bool bRet = VSIFWriteL((void *)"EHFA_HEADER_TAG", 1, 16, fp) > 0;
    1824             : 
    1825         201 :     GInt32 nHeaderPos = 20;
    1826             :     HFAStandard(4, &nHeaderPos);
    1827         201 :     bRet &= VSIFWriteL(&nHeaderPos, 4, 1, fp) > 0;
    1828             : 
    1829             :     // Write the Ehfa_File node, locked in at offset 20.
    1830         201 :     GInt32 nVersion = 1;
    1831         201 :     GInt32 nFreeList = 0;
    1832         201 :     GInt32 nRootEntry = 0;
    1833         201 :     GInt16 nEntryHeaderLength = 128;
    1834         201 :     GInt32 nDictionaryPtr = 38;
    1835             : 
    1836         201 :     psInfo->nEntryHeaderLength = nEntryHeaderLength;
    1837         201 :     psInfo->nRootPos = 0;
    1838         201 :     psInfo->nDictionaryPos = nDictionaryPtr;
    1839         201 :     psInfo->nVersion = nVersion;
    1840             : 
    1841             :     HFAStandard(4, &nVersion);
    1842             :     HFAStandard(4, &nFreeList);
    1843             :     HFAStandard(4, &nRootEntry);
    1844             :     HFAStandard(2, &nEntryHeaderLength);
    1845             :     HFAStandard(4, &nDictionaryPtr);
    1846             : 
    1847         201 :     bRet &= VSIFWriteL(&nVersion, 4, 1, fp) > 0;
    1848         201 :     bRet &= VSIFWriteL(&nFreeList, 4, 1, fp) > 0;
    1849         201 :     bRet &= VSIFWriteL(&nRootEntry, 4, 1, fp) > 0;
    1850         201 :     bRet &= VSIFWriteL(&nEntryHeaderLength, 2, 1, fp) > 0;
    1851         201 :     bRet &= VSIFWriteL(&nDictionaryPtr, 4, 1, fp) > 0;
    1852             : 
    1853             :     // Write the dictionary, locked in at location 38.  Note that
    1854             :     // we jump through a bunch of hoops to operate on the
    1855             :     // dictionary in chunks because some compiles (such as VC++)
    1856             :     // don't allow particularly large static strings.
    1857         201 :     int nDictLen = 0;
    1858             : 
    1859        2211 :     for (int iChunk = 0; aszDefaultDD[iChunk] != nullptr; iChunk++)
    1860        2010 :         nDictLen += static_cast<int>(strlen(aszDefaultDD[iChunk]));
    1861             : 
    1862         201 :     psInfo->pszDictionary = static_cast<char *>(CPLMalloc(nDictLen + 1));
    1863         201 :     psInfo->pszDictionary[0] = '\0';
    1864             : 
    1865        2211 :     for (int iChunk = 0; aszDefaultDD[iChunk] != nullptr; iChunk++)
    1866        2010 :         strcat(psInfo->pszDictionary, aszDefaultDD[iChunk]);
    1867             : 
    1868         402 :     bRet &= VSIFWriteL((void *)psInfo->pszDictionary,
    1869         201 :                        strlen(psInfo->pszDictionary) + 1, 1, fp) > 0;
    1870         201 :     if (!bRet)
    1871             :     {
    1872           4 :         CPL_IGNORE_RET_VAL(HFAClose(psInfo));
    1873           4 :         return nullptr;
    1874             :     }
    1875             : 
    1876         197 :     psInfo->poDictionary = new HFADictionary(psInfo->pszDictionary);
    1877             : 
    1878         197 :     psInfo->nEndOfFile = static_cast<GUInt32>(VSIFTellL(fp));
    1879             : 
    1880             :     // Create a root entry.
    1881         197 :     psInfo->poRoot = new HFAEntry(psInfo, "root", "root", nullptr);
    1882             : 
    1883             :     // If an .ige or .rrd file exists with the same base name,
    1884             :     // delete them.  (#1784)
    1885         197 :     CPLString osExtension = CPLGetExtensionSafe(pszFilename);
    1886         197 :     if (!EQUAL(osExtension, "rrd") && !EQUAL(osExtension, "aux"))
    1887             :     {
    1888         382 :         CPLString osPath = CPLGetPathSafe(pszFilename);
    1889         382 :         CPLString osBasename = CPLGetBasenameSafe(pszFilename);
    1890             :         VSIStatBufL sStatBuf;
    1891         382 :         CPLString osSupFile = CPLFormCIFilenameSafe(osPath, osBasename, "rrd");
    1892             : 
    1893         191 :         if (VSIStatL(osSupFile, &sStatBuf) == 0)
    1894           0 :             VSIUnlink(osSupFile);
    1895             : 
    1896         191 :         osSupFile = CPLFormCIFilenameSafe(osPath, osBasename, "ige");
    1897             : 
    1898         191 :         if (VSIStatL(osSupFile, &sStatBuf) == 0)
    1899           0 :             VSIUnlink(osSupFile);
    1900             :     }
    1901             : 
    1902         197 :     return psInfo;
    1903             : }
    1904             : 
    1905             : /************************************************************************/
    1906             : /*                          HFAAllocateSpace()                          */
    1907             : /*                                                                      */
    1908             : /*      Return an area in the file to the caller to write the           */
    1909             : /*      requested number of bytes.  Currently this is always at the     */
    1910             : /*      end of the file, but eventually we might actually keep track    */
    1911             : /*      of free space.  The HFAInfo_t's concept of file size is         */
    1912             : /*      updated, even if nothing ever gets written to this region.      */
    1913             : /*                                                                      */
    1914             : /*      Returns the offset to the requested space, or zero one          */
    1915             : /*      failure.                                                        */
    1916             : /************************************************************************/
    1917             : 
    1918        2880 : vsi_l_offset HFAAllocateSpace(HFAInfo_t *psInfo, GUInt32 nBytes)
    1919             : 
    1920             : {
    1921        2880 :     psInfo->nEndOfFile += nBytes;
    1922        2880 :     return psInfo->nEndOfFile - nBytes;
    1923             : }
    1924             : 
    1925             : /************************************************************************/
    1926             : /*                              HFAFlush()                              */
    1927             : /*                                                                      */
    1928             : /*      Write out any dirty tree information to disk, putting the       */
    1929             : /*      disk file in a consistent state.                                */
    1930             : /************************************************************************/
    1931             : 
    1932         400 : CPLErr HFAFlush(HFAHandle hHFA)
    1933             : 
    1934             : {
    1935         400 :     if (!hHFA->bTreeDirty && !hHFA->poDictionary->bDictionaryTextDirty)
    1936           0 :         return CE_None;
    1937             : 
    1938         400 :     CPLAssert(hHFA->poRoot != nullptr);
    1939             : 
    1940             :     // Flush HFAEntry tree to disk.
    1941         400 :     if (hHFA->bTreeDirty)
    1942             :     {
    1943         400 :         const CPLErr eErr = hHFA->poRoot->FlushToDisk();
    1944         400 :         if (eErr != CE_None)
    1945           6 :             return eErr;
    1946             : 
    1947         394 :         hHFA->bTreeDirty = false;
    1948             :     }
    1949             : 
    1950             :     // Flush Dictionary to disk.
    1951         394 :     GUInt32 nNewDictionaryPos = hHFA->nDictionaryPos;
    1952         394 :     bool bRet = true;
    1953         394 :     if (hHFA->poDictionary->bDictionaryTextDirty)
    1954             :     {
    1955           0 :         bRet &= VSIFSeekL(hHFA->fp, 0, SEEK_END) >= 0;
    1956           0 :         nNewDictionaryPos = static_cast<GUInt32>(VSIFTellL(hHFA->fp));
    1957           0 :         bRet &=
    1958           0 :             VSIFWriteL(hHFA->poDictionary->osDictionaryText.c_str(),
    1959           0 :                        strlen(hHFA->poDictionary->osDictionaryText.c_str()) + 1,
    1960           0 :                        1, hHFA->fp) > 0;
    1961           0 :         hHFA->poDictionary->bDictionaryTextDirty = false;
    1962             :     }
    1963             : 
    1964             :     // Do we need to update the Ehfa_File pointer to the root node?
    1965         596 :     if (hHFA->nRootPos != hHFA->poRoot->GetFilePos() ||
    1966         202 :         nNewDictionaryPos != hHFA->nDictionaryPos)
    1967             :     {
    1968         192 :         GUInt32 nHeaderPos = 0;
    1969             : 
    1970         192 :         bRet &= VSIFSeekL(hHFA->fp, 16, SEEK_SET) >= 0;
    1971         192 :         bRet &= VSIFReadL(&nHeaderPos, sizeof(GInt32), 1, hHFA->fp) > 0;
    1972             :         HFAStandard(4, &nHeaderPos);
    1973             : 
    1974         192 :         GUInt32 nOffset = hHFA->poRoot->GetFilePos();
    1975         192 :         hHFA->nRootPos = nOffset;
    1976             :         HFAStandard(4, &nOffset);
    1977         192 :         bRet &= VSIFSeekL(hHFA->fp, static_cast<vsi_l_offset>(nHeaderPos) + 8,
    1978         192 :                           SEEK_SET) >= 0;
    1979         192 :         bRet &= VSIFWriteL(&nOffset, 4, 1, hHFA->fp) > 0;
    1980             : 
    1981         192 :         nOffset = nNewDictionaryPos;
    1982         192 :         hHFA->nDictionaryPos = nNewDictionaryPos;
    1983             :         HFAStandard(4, &nOffset);
    1984         192 :         bRet &= VSIFSeekL(hHFA->fp, static_cast<vsi_l_offset>(nHeaderPos) + 14,
    1985         192 :                           SEEK_SET) >= 0;
    1986         192 :         bRet &= VSIFWriteL(&nOffset, 4, 1, hHFA->fp) > 0;
    1987             :     }
    1988             : 
    1989         394 :     return bRet ? CE_None : CE_Failure;
    1990             : }
    1991             : 
    1992             : /************************************************************************/
    1993             : /*                           HFACreateLayer()                           */
    1994             : /*                                                                      */
    1995             : /*      Create a layer object, and corresponding RasterDMS.             */
    1996             : /*      Suitable for use with primary layers, and overviews.            */
    1997             : /************************************************************************/
    1998             : 
    1999         245 : int HFACreateLayer(HFAHandle psInfo, HFAEntry *poParent,
    2000             :                    const char *pszLayerName, int bOverview, int nBlockSize,
    2001             :                    int bCreateCompressed, int bCreateLargeRaster,
    2002             :                    int bDependentLayer, int nXSize, int nYSize,
    2003             :                    EPTType eDataType, char ** /* papszOptions */,
    2004             :                    // These are only related to external (large) files.
    2005             :                    GIntBig nStackValidFlagsOffset, GIntBig nStackDataOffset,
    2006             :                    int nStackCount, int nStackIndex)
    2007             : 
    2008             : {
    2009         245 :     const char *pszLayerType =
    2010         245 :         bOverview ? "Eimg_Layer_SubSample" : "Eimg_Layer";
    2011             : 
    2012         245 :     if (nBlockSize <= 0)
    2013             :     {
    2014           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2015             :                  "HFACreateLayer: nBlockXSize < 0");
    2016           0 :         return FALSE;
    2017             :     }
    2018             : 
    2019             :     // Work out some details about the tiling scheme.
    2020         245 :     const int nBlocksPerRow = DIV_ROUND_UP(nXSize, nBlockSize);
    2021         245 :     const int nBlocksPerColumn = DIV_ROUND_UP(nYSize, nBlockSize);
    2022         245 :     const int nBlocks = nBlocksPerRow * nBlocksPerColumn;
    2023             :     const int nBytesPerBlock =
    2024         245 :         (nBlockSize * nBlockSize * HFAGetDataTypeBits(eDataType) + 7) / 8;
    2025             : 
    2026             :     // Create the Eimg_Layer for the band.
    2027             :     HFAEntry *poEimg_Layer =
    2028         245 :         HFAEntry::New(psInfo, pszLayerName, pszLayerType, poParent);
    2029             : 
    2030         245 :     poEimg_Layer->SetIntField("width", nXSize);
    2031         245 :     poEimg_Layer->SetIntField("height", nYSize);
    2032         245 :     poEimg_Layer->SetStringField("layerType", "athematic");
    2033         245 :     poEimg_Layer->SetIntField("pixelType", eDataType);
    2034         245 :     poEimg_Layer->SetIntField("blockWidth", nBlockSize);
    2035         245 :     poEimg_Layer->SetIntField("blockHeight", nBlockSize);
    2036             : 
    2037             :     // Create the RasterDMS (block list).  This is a complex type
    2038             :     // with pointers, and variable size.  We set the superstructure
    2039             :     // ourselves rather than trying to have the HFA type management
    2040             :     // system do it for us (since this would be hard to implement).
    2041         245 :     if (!bCreateLargeRaster && !bDependentLayer)
    2042             :     {
    2043             :         HFAEntry *poEdms_State =
    2044         233 :             HFAEntry::New(psInfo, "RasterDMS", "Edms_State", poEimg_Layer);
    2045             : 
    2046             :         // TODO(schwehr): Explain constants.
    2047         233 :         const int nDmsSize = 14 * nBlocks + 38;
    2048         233 :         GByte *pabyData = poEdms_State->MakeData(nDmsSize);
    2049             : 
    2050             :         // Set some simple values.
    2051         233 :         poEdms_State->SetIntField("numvirtualblocks", nBlocks);
    2052         233 :         poEdms_State->SetIntField("numobjectsperblock",
    2053             :                                   nBlockSize * nBlockSize);
    2054         233 :         poEdms_State->SetIntField("nextobjectnum",
    2055         233 :                                   nBlockSize * nBlockSize * nBlocks);
    2056             : 
    2057             :         // Is file compressed or not?
    2058         233 :         if (bCreateCompressed)
    2059             :         {
    2060          10 :             poEdms_State->SetStringField("compressionType", "RLC compression");
    2061             :         }
    2062             :         else
    2063             :         {
    2064         223 :             poEdms_State->SetStringField("compressionType", "no compression");
    2065             :         }
    2066             : 
    2067             :         // We need to hardcode file offset into the data, so locate it now.
    2068         233 :         poEdms_State->SetPosition();
    2069             : 
    2070             :         // Set block info headers.
    2071             : 
    2072             :         // Blockinfo count.
    2073         233 :         GUInt32 nValue = nBlocks;
    2074             :         HFAStandard(4, &nValue);
    2075         233 :         memcpy(pabyData + 14, &nValue, 4);
    2076             : 
    2077             :         // Blockinfo position.
    2078         233 :         nValue = poEdms_State->GetDataPos() + 22;
    2079             :         HFAStandard(4, &nValue);
    2080         233 :         memcpy(pabyData + 18, &nValue, 4);
    2081             : 
    2082             :         // Set each blockinfo.
    2083         734 :         for (int iBlock = 0; iBlock < nBlocks; iBlock++)
    2084             :         {
    2085         501 :             int nOffset = 22 + 14 * iBlock;
    2086             : 
    2087             :             // fileCode.
    2088         501 :             GInt16 nValue16 = 0;
    2089             :             HFAStandard(2, &nValue16);
    2090         501 :             memcpy(pabyData + nOffset, &nValue16, 2);
    2091             : 
    2092             :             // Offset.
    2093         501 :             if (bCreateCompressed)
    2094             :             {
    2095             :                 // Flag it with zero offset. Allocate space when we compress it.
    2096          18 :                 nValue = 0;
    2097             :             }
    2098             :             else
    2099             :             {
    2100         483 :                 const auto nValue64 = HFAAllocateSpace(psInfo, nBytesPerBlock);
    2101         483 :                 if (nValue64 > UINT32_MAX)
    2102           0 :                     return FALSE;
    2103         483 :                 nValue = static_cast<GUInt32>(nValue64);
    2104             :             }
    2105             :             HFAStandard(4, &nValue);
    2106         501 :             memcpy(pabyData + nOffset + 2, &nValue, 4);
    2107             : 
    2108             :             // Size.
    2109         501 :             if (bCreateCompressed)
    2110             :             {
    2111             :                 // Flag with zero size. Don't know until we compress it.
    2112          18 :                 nValue = 0;
    2113             :             }
    2114             :             else
    2115             :             {
    2116         483 :                 nValue = nBytesPerBlock;
    2117             :             }
    2118             :             HFAStandard(4, &nValue);
    2119         501 :             memcpy(pabyData + nOffset + 6, &nValue, 4);
    2120             : 
    2121             :             // logValid (false).
    2122         501 :             nValue16 = 0;
    2123             :             HFAStandard(2, &nValue16);
    2124         501 :             memcpy(pabyData + nOffset + 10, &nValue16, 2);
    2125             : 
    2126             :             // compressionType.
    2127         501 :             if (bCreateCompressed)
    2128          18 :                 nValue16 = 1;
    2129             :             else
    2130         483 :                 nValue16 = 0;
    2131             : 
    2132             :             HFAStandard(2, &nValue16);
    2133         501 :             memcpy(pabyData + nOffset + 12, &nValue16, 2);
    2134         233 :         }
    2135             :     }
    2136             : 
    2137             :     // Create ExternalRasterDMS object.
    2138          12 :     else if (bCreateLargeRaster)
    2139             :     {
    2140           7 :         HFAEntry *poEdms_State = HFAEntry::New(
    2141             :             psInfo, "ExternalRasterDMS", "ImgExternalRaster", poEimg_Layer);
    2142           7 :         poEdms_State->MakeData(
    2143           7 :             static_cast<int>(8 + strlen(psInfo->pszIGEFilename) + 1 + 6 * 4));
    2144             : 
    2145           7 :         poEdms_State->SetStringField("fileName.string", psInfo->pszIGEFilename);
    2146             : 
    2147           7 :         poEdms_State->SetIntField(
    2148             :             "layerStackValidFlagsOffset[0]",
    2149             :             static_cast<int>(nStackValidFlagsOffset & 0xFFFFFFFF));
    2150           7 :         poEdms_State->SetIntField(
    2151             :             "layerStackValidFlagsOffset[1]",
    2152           7 :             static_cast<int>(nStackValidFlagsOffset >> 32));
    2153             : 
    2154           7 :         poEdms_State->SetIntField(
    2155             :             "layerStackDataOffset[0]",
    2156             :             static_cast<int>(nStackDataOffset & 0xFFFFFFFF));
    2157           7 :         poEdms_State->SetIntField("layerStackDataOffset[1]",
    2158           7 :                                   static_cast<int>(nStackDataOffset >> 32));
    2159           7 :         poEdms_State->SetIntField("layerStackCount", nStackCount);
    2160           7 :         poEdms_State->SetIntField("layerStackIndex", nStackIndex);
    2161             :     }
    2162             :     // Dependent...
    2163           5 :     else if (bDependentLayer)
    2164             :     {
    2165             :         HFAEntry *poDepLayerName =
    2166           5 :             HFAEntry::New(psInfo, "DependentLayerName",
    2167             :                           "Eimg_DependentLayerName", poEimg_Layer);
    2168           5 :         poDepLayerName->MakeData(
    2169           5 :             static_cast<int>(8 + strlen(pszLayerName) + 2));
    2170             : 
    2171           5 :         poDepLayerName->SetStringField("ImageLayerName.string", pszLayerName);
    2172             :     }
    2173             : 
    2174             :     // Create the Ehfa_Layer.
    2175         245 :     char chBandType = '\0';
    2176             : 
    2177         245 :     if (eDataType == EPT_u1)
    2178           2 :         chBandType = '1';
    2179         243 :     else if (eDataType == EPT_u2)
    2180           2 :         chBandType = '2';
    2181         241 :     else if (eDataType == EPT_u4)
    2182           2 :         chBandType = '4';
    2183         239 :     else if (eDataType == EPT_u8)
    2184         151 :         chBandType = 'c';
    2185          88 :     else if (eDataType == EPT_s8)
    2186           4 :         chBandType = 'C';
    2187          84 :     else if (eDataType == EPT_u16)
    2188          13 :         chBandType = 's';
    2189          71 :     else if (eDataType == EPT_s16)
    2190           9 :         chBandType = 'S';
    2191          62 :     else if (eDataType == EPT_u32)
    2192             :         // For some reason erdas imagine expects an L for unsigned 32 bit ints
    2193             :         // otherwise it gives strange "out of memory errors".
    2194          10 :         chBandType = 'L';
    2195          52 :     else if (eDataType == EPT_s32)
    2196          11 :         chBandType = 'L';
    2197          41 :     else if (eDataType == EPT_f32)
    2198          11 :         chBandType = 'f';
    2199          30 :     else if (eDataType == EPT_f64)
    2200          12 :         chBandType = 'd';
    2201          18 :     else if (eDataType == EPT_c64)
    2202           9 :         chBandType = 'm';
    2203           9 :     else if (eDataType == EPT_c128)
    2204           9 :         chBandType = 'M';
    2205             :     else
    2206             :     {
    2207           0 :         CPLAssert(false);
    2208             :         chBandType = 'c';
    2209             :     }
    2210             : 
    2211             :     // The first value in the entry below gives the number of pixels
    2212             :     // within a block.
    2213         245 :     char szLDict[128] = {};
    2214         245 :     snprintf(szLDict, sizeof(szLDict), "{%d:%cdata,}RasterDMS,.",
    2215             :              nBlockSize * nBlockSize, chBandType);
    2216             : 
    2217             :     HFAEntry *poEhfa_Layer =
    2218         245 :         HFAEntry::New(psInfo, "Ehfa_Layer", "Ehfa_Layer", poEimg_Layer);
    2219         245 :     poEhfa_Layer->MakeData();
    2220         245 :     poEhfa_Layer->SetPosition();
    2221             :     const auto nLDict =
    2222         245 :         HFAAllocateSpace(psInfo, static_cast<GUInt32>(strlen(szLDict) + 1));
    2223         245 :     if (nLDict > static_cast<unsigned>(INT_MAX))
    2224           0 :         return FALSE;
    2225             : 
    2226         245 :     poEhfa_Layer->SetStringField("type", "raster");
    2227         245 :     poEhfa_Layer->SetIntField("dictionaryPtr", static_cast<int>(nLDict));
    2228             : 
    2229         245 :     bool bRet = VSIFSeekL(psInfo->fp, nLDict, SEEK_SET) >= 0;
    2230         245 :     bRet &= VSIFWriteL((void *)szLDict, strlen(szLDict) + 1, 1, psInfo->fp) > 0;
    2231             : 
    2232         245 :     return bRet;
    2233             : }
    2234             : 
    2235             : /************************************************************************/
    2236             : /*                             HFACreate()                              */
    2237             : /************************************************************************/
    2238             : 
    2239         204 : HFAHandle HFACreate(const char *pszFilename, int nXSize, int nYSize, int nBands,
    2240             :                     EPTType eDataType, char **papszOptions)
    2241             : 
    2242             : {
    2243         204 :     if (nXSize == 0 || nYSize == 0)
    2244             :     {
    2245           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    2246             :                  "nXSize == 0 || nYSize == 0 not supported");
    2247           1 :         return nullptr;
    2248             :     }
    2249         203 :     int nBlockSize = 64;
    2250         203 :     const char *pszValue = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
    2251             : 
    2252         203 :     if (pszValue != nullptr)
    2253             :     {
    2254           1 :         nBlockSize = atoi(pszValue);
    2255             :         // Check for sane values.
    2256           2 :         if (nBlockSize == 0 ||
    2257           1 :             (((nBlockSize < 32) || (nBlockSize > 2048)) &&
    2258           0 :              !CPLTestBool(CPLGetConfigOption("FORCE_BLOCKSIZE", "NO"))))
    2259             :         {
    2260           0 :             if (nBlockSize != 0)
    2261           0 :                 CPLError(CE_Warning, CPLE_AppDefined, "Forcing BLOCKSIZE to %d",
    2262             :                          64);
    2263           0 :             nBlockSize = 64;
    2264             :         }
    2265             :     }
    2266         203 :     bool bCreateLargeRaster = CPLFetchBool(papszOptions, "USE_SPILL", false);
    2267         402 :     bool bCreateCompressed = CPLFetchBool(papszOptions, "COMPRESS", false) ||
    2268         199 :                              CPLFetchBool(papszOptions, "COMPRESSED", false);
    2269         203 :     const bool bCreateAux = CPLFetchBool(papszOptions, "AUX", false);
    2270             : 
    2271         203 :     char *pszFullFilename = nullptr;
    2272         203 :     char *pszRawFilename = nullptr;
    2273             : 
    2274             :     // Work out some details about the tiling scheme.
    2275         203 :     const int nBlocksPerRow = DIV_ROUND_UP(nXSize, nBlockSize);
    2276         203 :     const int nBlocksPerColumn = DIV_ROUND_UP(nYSize, nBlockSize);
    2277         203 :     if (nBlocksPerRow > INT_MAX / nBlocksPerColumn)
    2278             :     {
    2279           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too many blocks");
    2280           0 :         return nullptr;
    2281             :     }
    2282         203 :     const int nBlocks = nBlocksPerRow * nBlocksPerColumn;
    2283             :     const GInt64 nBytesPerBlock64 =
    2284         406 :         (static_cast<GInt64>(nBlockSize) * nBlockSize *
    2285         203 :              HFAGetDataTypeBits(eDataType) +
    2286             :          7) /
    2287         203 :         8;
    2288         203 :     if (nBytesPerBlock64 > INT_MAX)
    2289             :     {
    2290           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too large block");
    2291           0 :         return nullptr;
    2292             :     }
    2293         203 :     const int nBytesPerBlock = static_cast<int>(nBytesPerBlock64);
    2294             : 
    2295             :     // Create the low level structure.
    2296         203 :     HFAHandle psInfo = HFACreateLL(pszFilename);
    2297         203 :     if (psInfo == nullptr)
    2298           7 :         return nullptr;
    2299             : 
    2300             :     // Create the DependentFile node if requested.
    2301             :     const char *pszDependentFile =
    2302         196 :         CSLFetchNameValue(papszOptions, "DEPENDENT_FILE");
    2303             : 
    2304         196 :     if (pszDependentFile != nullptr)
    2305             :     {
    2306           5 :         HFAEntry *poDF = HFAEntry::New(psInfo, "DependentFile",
    2307             :                                        "Eimg_DependentFile", psInfo->poRoot);
    2308             : 
    2309           5 :         poDF->MakeData(static_cast<int>(strlen(pszDependentFile) + 50));
    2310           5 :         poDF->SetPosition();
    2311           5 :         poDF->SetStringField("dependent.string", pszDependentFile);
    2312             :     }
    2313             : 
    2314         196 :     CPLDebug("HFACreate",
    2315             :              "Blocks per row %d, blocks per column %d, "
    2316             :              "total number of blocks %d, bytes per block %d.",
    2317             :              nBlocksPerRow, nBlocksPerColumn, nBlocks, nBytesPerBlock);
    2318             : 
    2319             :     // Check whether we should create external large file with
    2320             :     // image.  We create a spill file if the amount of imagery is
    2321             :     // close to 2GB.  We don't check the amount of auxiliary
    2322             :     // information, so in theory if there were an awful lot of
    2323             :     // non-imagery data our approximate size could be smaller than
    2324             :     // the file will actually we be.  We leave room for 10MB of
    2325             :     // auxiliary data.
    2326             :     // We can also force spill file creation using option
    2327             :     // SPILL_FILE=YES.
    2328         196 :     const double dfApproxSize = static_cast<double>(nBytesPerBlock) *
    2329         196 :                                     static_cast<double>(nBlocks) *
    2330         196 :                                     static_cast<double>(nBands) +
    2331             :                                 10000000.0;
    2332             : 
    2333         196 :     if (dfApproxSize > 2147483648.0 && !bCreateAux)
    2334           0 :         bCreateLargeRaster = true;
    2335             : 
    2336             :     // Erdas Imagine creates this entry even if an external spill file is used.
    2337         196 :     if (!bCreateAux)
    2338             :     {
    2339         191 :         HFAEntry *poImgFormat = HFAEntry::New(
    2340             :             psInfo, "IMGFormatInfo", "ImgFormatInfo831", psInfo->poRoot);
    2341         191 :         poImgFormat->MakeData();
    2342         191 :         if (bCreateLargeRaster)
    2343             :         {
    2344           7 :             poImgFormat->SetIntField("spaceUsedForRasterData", 0);
    2345             :             // Can't be compressed if we are creating a spillfile.
    2346           7 :             bCreateCompressed = false;
    2347             :         }
    2348             :         else
    2349             :         {
    2350         184 :             poImgFormat->SetIntField("spaceUsedForRasterData",
    2351         184 :                                      nBytesPerBlock * nBlocks * nBands);
    2352             :         }
    2353             :     }
    2354             : 
    2355             :     // Create external file and write its header.
    2356         196 :     GIntBig nValidFlagsOffset = 0;
    2357         196 :     GIntBig nDataOffset = 0;
    2358             : 
    2359         196 :     if (bCreateLargeRaster)
    2360             :     {
    2361           7 :         if (!HFACreateSpillStack(psInfo, nXSize, nYSize, nBands, nBlockSize,
    2362             :                                  eDataType, &nValidFlagsOffset, &nDataOffset))
    2363             :         {
    2364           0 :             CPLFree(pszRawFilename);
    2365           0 :             CPLFree(pszFullFilename);
    2366           0 :             return nullptr;
    2367             :         }
    2368             :     }
    2369             : 
    2370             :     // Create each band (layer).
    2371         425 :     for (int iBand = 0; iBand < nBands; iBand++)
    2372             :     {
    2373         233 :         char szName[128] = {};
    2374             : 
    2375         233 :         snprintf(szName, sizeof(szName), "Layer_%d", iBand + 1);
    2376             : 
    2377         233 :         if (!HFACreateLayer(psInfo, psInfo->poRoot, szName, FALSE, nBlockSize,
    2378             :                             bCreateCompressed, bCreateLargeRaster, bCreateAux,
    2379             :                             nXSize, nYSize, eDataType, papszOptions,
    2380             :                             nValidFlagsOffset, nDataOffset, nBands, iBand))
    2381             :         {
    2382           4 :             CPL_IGNORE_RET_VAL(HFAClose(psInfo));
    2383           4 :             return nullptr;
    2384             :         }
    2385             :     }
    2386             : 
    2387             :     // Initialize the band information.
    2388         192 :     HFAParseBandInfo(psInfo);
    2389             : 
    2390         192 :     return psInfo;
    2391             : }
    2392             : 
    2393             : /************************************************************************/
    2394             : /*                         HFACreateOverview()                          */
    2395             : /*                                                                      */
    2396             : /*      Create an overview layer object for a band.                     */
    2397             : /************************************************************************/
    2398             : 
    2399          12 : int HFACreateOverview(HFAHandle hHFA, int nBand, int nOverviewLevel,
    2400             :                       const char *pszResampling)
    2401             : 
    2402             : {
    2403          12 :     if (nBand < 1 || nBand > hHFA->nBands)
    2404           0 :         return -1;
    2405             : 
    2406          12 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
    2407          12 :     return poBand->CreateOverview(nOverviewLevel, pszResampling);
    2408             : }
    2409             : 
    2410             : /************************************************************************/
    2411             : /*                           HFAGetMetadata()                           */
    2412             : /*                                                                      */
    2413             : /*      Read metadata structured in a table called GDAL_MetaData.       */
    2414             : /************************************************************************/
    2415             : 
    2416        1166 : char **HFAGetMetadata(HFAHandle hHFA, int nBand)
    2417             : 
    2418             : {
    2419        1166 :     HFAEntry *poTable = nullptr;
    2420             : 
    2421        1166 :     if (nBand > 0 && nBand <= hHFA->nBands)
    2422         623 :         poTable = hHFA->papoBand[nBand - 1]->poNode->GetChild();
    2423         543 :     else if (nBand == 0)
    2424         543 :         poTable = hHFA->poRoot->GetChild();
    2425             :     else
    2426           0 :         return nullptr;
    2427             : 
    2428        4777 :     for (; poTable != nullptr && !EQUAL(poTable->GetName(), "GDAL_MetaData");
    2429        3611 :          poTable = poTable->GetNext())
    2430             :     {
    2431             :     }
    2432             : 
    2433        1166 :     if (poTable == nullptr || !EQUAL(poTable->GetType(), "Edsc_Table"))
    2434        1071 :         return nullptr;
    2435             : 
    2436          95 :     if (poTable->GetIntField("numRows") != 1)
    2437             :     {
    2438           0 :         CPLDebug("HFADataset", "GDAL_MetaData.numRows = %d, expected 1!",
    2439             :                  poTable->GetIntField("numRows"));
    2440           0 :         return nullptr;
    2441             :     }
    2442             : 
    2443             :     // Loop over each column.  Each column will be one metadata
    2444             :     // entry, with the title being the key, and the row value being
    2445             :     // the value.  There is only ever one row in GDAL_MetaData tables.
    2446          95 :     char **papszMD = nullptr;
    2447             : 
    2448         286 :     for (HFAEntry *poColumn = poTable->GetChild(); poColumn != nullptr;
    2449         191 :          poColumn = poColumn->GetNext())
    2450             :     {
    2451             :         // Skip the #Bin_Function# entry.
    2452         191 :         if (STARTS_WITH_CI(poColumn->GetName(), "#"))
    2453          95 :             continue;
    2454             : 
    2455          96 :         const char *pszValue = poColumn->GetStringField("dataType");
    2456          96 :         if (pszValue == nullptr || !EQUAL(pszValue, "string"))
    2457           0 :             continue;
    2458             : 
    2459          96 :         const int columnDataPtr = poColumn->GetIntField("columnDataPtr");
    2460          96 :         if (columnDataPtr <= 0)
    2461           0 :             continue;
    2462             : 
    2463             :         // Read up to nMaxNumChars bytes from the indicated location.
    2464             :         // allocate required space temporarily nMaxNumChars should have been
    2465             :         // set by GDAL originally so we should trust it, but who knows.
    2466          96 :         const int nMaxNumChars = poColumn->GetIntField("maxNumChars");
    2467             : 
    2468          96 :         if (nMaxNumChars <= 0)
    2469             :         {
    2470           0 :             papszMD = CSLSetNameValue(papszMD, poColumn->GetName(), "");
    2471             :         }
    2472             :         else
    2473             :         {
    2474             :             char *pszMDValue =
    2475          96 :                 static_cast<char *>(VSI_MALLOC_VERBOSE(nMaxNumChars));
    2476          96 :             if (pszMDValue == nullptr)
    2477             :             {
    2478           0 :                 continue;
    2479             :             }
    2480             : 
    2481          96 :             if (VSIFSeekL(hHFA->fp, static_cast<vsi_l_offset>(columnDataPtr),
    2482          96 :                           SEEK_SET) != 0)
    2483             :             {
    2484           0 :                 CPLFree(pszMDValue);
    2485           0 :                 continue;
    2486             :             }
    2487             : 
    2488             :             const int nMDBytes = static_cast<int>(
    2489          96 :                 VSIFReadL(pszMDValue, 1, nMaxNumChars, hHFA->fp));
    2490          96 :             if (nMDBytes == 0)
    2491             :             {
    2492           0 :                 CPLFree(pszMDValue);
    2493           0 :                 continue;
    2494             :             }
    2495             : 
    2496          96 :             pszMDValue[nMaxNumChars - 1] = '\0';
    2497             : 
    2498          96 :             papszMD = CSLSetNameValue(papszMD, poColumn->GetName(), pszMDValue);
    2499          96 :             CPLFree(pszMDValue);
    2500             :         }
    2501             :     }
    2502             : 
    2503          95 :     return papszMD;
    2504             : }
    2505             : 
    2506             : /************************************************************************/
    2507             : /*                         HFASetGDALMetadata()                         */
    2508             : /*                                                                      */
    2509             : /*      This function is used to set metadata in a table called         */
    2510             : /*      GDAL_MetaData.  It is called by HFASetMetadata() for all        */
    2511             : /*      metadata items that aren't some specific supported              */
    2512             : /*      information (like histogram or stats info).                     */
    2513             : /************************************************************************/
    2514             : 
    2515          42 : static CPLErr HFASetGDALMetadata(HFAHandle hHFA, int nBand, char **papszMD)
    2516             : 
    2517             : {
    2518          42 :     if (papszMD == nullptr)
    2519           0 :         return CE_None;
    2520             : 
    2521          42 :     HFAEntry *poNode = nullptr;
    2522             : 
    2523          42 :     if (nBand > 0 && nBand <= hHFA->nBands)
    2524           6 :         poNode = hHFA->papoBand[nBand - 1]->poNode;
    2525          36 :     else if (nBand == 0)
    2526          36 :         poNode = hHFA->poRoot;
    2527             :     else
    2528           0 :         return CE_Failure;
    2529             : 
    2530             :     // Create the Descriptor table.
    2531             :     // Check we have no table with this name already.
    2532          42 :     HFAEntry *poEdsc_Table = poNode->GetNamedChild("GDAL_MetaData");
    2533             : 
    2534          43 :     if (poEdsc_Table == nullptr ||
    2535           1 :         !EQUAL(poEdsc_Table->GetType(), "Edsc_Table"))
    2536             :         poEdsc_Table =
    2537          41 :             HFAEntry::New(hHFA, "GDAL_MetaData", "Edsc_Table", poNode);
    2538             : 
    2539          42 :     poEdsc_Table->SetIntField("numrows", 1);
    2540             : 
    2541             :     // Create the Binning function node.  Do we really need this though?
    2542             :     // Check it doesn't exist already.
    2543             :     HFAEntry *poEdsc_BinFunction =
    2544          42 :         poEdsc_Table->GetNamedChild("#Bin_Function#");
    2545             : 
    2546          43 :     if (poEdsc_BinFunction == nullptr ||
    2547           1 :         !EQUAL(poEdsc_BinFunction->GetType(), "Edsc_BinFunction"))
    2548          41 :         poEdsc_BinFunction = HFAEntry::New(hHFA, "#Bin_Function#",
    2549             :                                            "Edsc_BinFunction", poEdsc_Table);
    2550             : 
    2551             :     // Because of the BaseData we have to hardcode the size.
    2552          42 :     poEdsc_BinFunction->MakeData(30);
    2553             : 
    2554          42 :     poEdsc_BinFunction->SetIntField("numBins", 1);
    2555          42 :     poEdsc_BinFunction->SetStringField("binFunction", "direct");
    2556          42 :     poEdsc_BinFunction->SetDoubleField("minLimit", 0.0);
    2557          42 :     poEdsc_BinFunction->SetDoubleField("maxLimit", 0.0);
    2558             : 
    2559             :     // Process each metadata item as a separate column.
    2560          42 :     bool bRet = true;
    2561          85 :     for (int iColumn = 0; papszMD[iColumn] != nullptr; iColumn++)
    2562             :     {
    2563          43 :         char *pszKey = nullptr;
    2564          43 :         const char *pszValue = CPLParseNameValue(papszMD[iColumn], &pszKey);
    2565          43 :         if (pszValue == nullptr)
    2566           0 :             continue;
    2567             : 
    2568             :         // Create the Edsc_Column.
    2569             :         // Check it doesn't exist already.
    2570          43 :         HFAEntry *poEdsc_Column = poEdsc_Table->GetNamedChild(pszKey);
    2571             : 
    2572          44 :         if (poEdsc_Column == nullptr ||
    2573           1 :             !EQUAL(poEdsc_Column->GetType(), "Edsc_Column"))
    2574             :             poEdsc_Column =
    2575          42 :                 HFAEntry::New(hHFA, pszKey, "Edsc_Column", poEdsc_Table);
    2576             : 
    2577          43 :         CPLFree(pszKey);
    2578             : 
    2579          43 :         poEdsc_Column->SetIntField("numRows", 1);
    2580          43 :         poEdsc_Column->SetStringField("dataType", "string");
    2581          43 :         poEdsc_Column->SetIntField("maxNumChars",
    2582          43 :                                    static_cast<GUInt32>(strlen(pszValue) + 1));
    2583             : 
    2584             :         // Write the data out.
    2585             :         const auto nOffset =
    2586          43 :             HFAAllocateSpace(hHFA, static_cast<GUInt32>(strlen(pszValue) + 1));
    2587          43 :         if (nOffset > static_cast<unsigned>(INT_MAX))
    2588             :         {
    2589           0 :             return CE_Failure;
    2590             :         }
    2591             : 
    2592          43 :         poEdsc_Column->SetIntField("columnDataPtr", static_cast<int>(nOffset));
    2593             : 
    2594          43 :         bRet &= VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) >= 0;
    2595          43 :         bRet &=
    2596          43 :             VSIFWriteL((void *)pszValue, strlen(pszValue) + 1, 1, hHFA->fp) > 0;
    2597             :     }
    2598             : 
    2599          42 :     return bRet ? CE_None : CE_Failure;
    2600             : }
    2601             : 
    2602             : /************************************************************************/
    2603             : /*                           HFASetMetadata()                           */
    2604             : /************************************************************************/
    2605             : 
    2606          65 : CPLErr HFASetMetadata(HFAHandle hHFA, int nBand, CSLConstList papszMD)
    2607             : 
    2608             : {
    2609          65 :     char **papszGDALMD = nullptr;
    2610             : 
    2611          65 :     if (CSLCount(papszMD) == 0)
    2612           0 :         return CE_None;
    2613             : 
    2614          65 :     HFAEntry *poNode = nullptr;
    2615             : 
    2616          65 :     if (nBand > 0 && nBand <= hHFA->nBands)
    2617          15 :         poNode = hHFA->papoBand[nBand - 1]->poNode;
    2618          50 :     else if (nBand == 0)
    2619          50 :         poNode = hHFA->poRoot;
    2620             :     else
    2621           0 :         return CE_Failure;
    2622             : #ifdef DEBUG
    2623             :     // To please Clang Static Analyzer (CSA).
    2624          65 :     if (poNode == nullptr)
    2625             :     {
    2626           0 :         CPLAssert(false);
    2627             :         return CE_Failure;
    2628             :     }
    2629             : #endif
    2630             : 
    2631             :     // Check if the Metadata is an "known" entity which should be
    2632             :     // stored in a better place.
    2633          65 :     char *pszBinValues = nullptr;
    2634          65 :     bool bCreatedHistogramParameters = false;
    2635          65 :     bool bCreatedStatistics = false;
    2636          65 :     const char *const *pszAuxMetaData = GetHFAAuxMetaDataList();
    2637             :     // Check each metadata item.
    2638         229 :     for (int iColumn = 0; papszMD[iColumn] != nullptr; iColumn++)
    2639             :     {
    2640         164 :         char *pszKey = nullptr;
    2641         164 :         const char *pszValue = CPLParseNameValue(papszMD[iColumn], &pszKey);
    2642         164 :         if (pszValue == nullptr)
    2643          28 :             continue;
    2644             : 
    2645             :         // Know look if its known.
    2646         150 :         int i = 0;  // Used after for.
    2647        1408 :         for (; pszAuxMetaData[i] != nullptr; i += 4)
    2648             :         {
    2649        1358 :             if (EQUALN(pszAuxMetaData[i + 2], pszKey, strlen(pszKey)))
    2650         100 :                 break;
    2651             :         }
    2652         150 :         if (pszAuxMetaData[i] != nullptr)
    2653             :         {
    2654             :             // Found one, get the right entry.
    2655         100 :             HFAEntry *poEntry = nullptr;
    2656             : 
    2657         100 :             if (strlen(pszAuxMetaData[i]) > 0)
    2658          90 :                 poEntry = poNode->GetNamedChild(pszAuxMetaData[i]);
    2659             :             else
    2660          10 :                 poEntry = poNode;
    2661             : 
    2662         100 :             if (poEntry == nullptr && strlen(pszAuxMetaData[i + 3]) > 0)
    2663             :             {
    2664             :                 // Child does not yet exist --> create it,
    2665          28 :                 poEntry = HFAEntry::New(hHFA, pszAuxMetaData[i],
    2666          14 :                                         pszAuxMetaData[i + 3], poNode);
    2667             : 
    2668          14 :                 if (STARTS_WITH_CI(pszAuxMetaData[i], "Statistics"))
    2669           8 :                     bCreatedStatistics = true;
    2670             : 
    2671          14 :                 if (STARTS_WITH_CI(pszAuxMetaData[i], "HistogramParameters"))
    2672             :                 {
    2673             :                     // A bit nasty.  Need to set the string field for the object
    2674             :                     // first because the SetStringField sets the count for the
    2675             :                     // object BinFunction to the length of the string.
    2676           6 :                     poEntry->MakeData(70);
    2677           6 :                     poEntry->SetStringField("BinFunction.binFunctionType",
    2678             :                                             "direct");
    2679             : 
    2680           6 :                     bCreatedHistogramParameters = true;
    2681             :                 }
    2682             :             }
    2683         100 :             if (poEntry == nullptr)
    2684             :             {
    2685          14 :                 CPLFree(pszKey);
    2686          14 :                 continue;
    2687             :             }
    2688             : 
    2689          86 :             const char *pszFieldName = pszAuxMetaData[i + 1] + 1;
    2690          86 :             switch (pszAuxMetaData[i + 1][0])
    2691             :             {
    2692          67 :                 case 'd':
    2693             :                 {
    2694          67 :                     double dfValue = CPLAtof(pszValue);
    2695          67 :                     poEntry->SetDoubleField(pszFieldName, dfValue);
    2696             :                 }
    2697          67 :                 break;
    2698           9 :                 case 'i':
    2699             :                 case 'l':
    2700             :                 {
    2701           9 :                     int nValue = atoi(pszValue);
    2702           9 :                     poEntry->SetIntField(pszFieldName, nValue);
    2703             :                 }
    2704           9 :                 break;
    2705          10 :                 case 's':
    2706             :                 case 'e':
    2707             :                 {
    2708          10 :                     poEntry->SetStringField(pszFieldName, pszValue);
    2709             :                 }
    2710          10 :                 break;
    2711           0 :                 default:
    2712           0 :                     CPLAssert(false);
    2713             :             }
    2714             :         }
    2715          50 :         else if (STARTS_WITH_CI(pszKey, "STATISTICS_HISTOBINVALUES"))
    2716             :         {
    2717           7 :             CPLFree(pszBinValues);
    2718           7 :             pszBinValues = CPLStrdup(pszValue);
    2719             :         }
    2720             :         else
    2721             :         {
    2722          43 :             papszGDALMD = CSLAddString(papszGDALMD, papszMD[iColumn]);
    2723             :         }
    2724             : 
    2725         136 :         CPLFree(pszKey);
    2726             :     }
    2727             : 
    2728             :     // Special case to write out the histogram.
    2729          65 :     bool bRet = true;
    2730          65 :     if (pszBinValues != nullptr)
    2731             :     {
    2732           7 :         HFAEntry *poEntry = poNode->GetNamedChild("HistogramParameters");
    2733           7 :         if (poEntry != nullptr && bCreatedHistogramParameters)
    2734             :         {
    2735             :             // If this node exists we have added Histogram data -- complete with
    2736             :             // some defaults.
    2737           6 :             poEntry->SetIntField("SkipFactorX", 1);
    2738           6 :             poEntry->SetIntField("SkipFactorY", 1);
    2739             : 
    2740           6 :             const int nNumBins = poEntry->GetIntField("BinFunction.numBins");
    2741             :             const double dMinLimit =
    2742           6 :                 poEntry->GetDoubleField("BinFunction.minLimit");
    2743             :             const double dMaxLimit =
    2744           6 :                 poEntry->GetDoubleField("BinFunction.maxLimit");
    2745             : 
    2746             :             // Fill the descriptor table - check it isn't there already.
    2747           6 :             poEntry = poNode->GetNamedChild("Descriptor_Table");
    2748           6 :             if (poEntry == nullptr || !EQUAL(poEntry->GetType(), "Edsc_Table"))
    2749           1 :                 poEntry = HFAEntry::New(hHFA, "Descriptor_Table", "Edsc_Table",
    2750             :                                         poNode);
    2751             : 
    2752           6 :             poEntry->SetIntField("numRows", nNumBins);
    2753             : 
    2754             :             // Bin function.
    2755           6 :             HFAEntry *poBinFunc = poEntry->GetNamedChild("#Bin_Function#");
    2756          10 :             if (poBinFunc == nullptr ||
    2757           4 :                 !EQUAL(poBinFunc->GetType(), "Edsc_BinFunction"))
    2758           2 :                 poBinFunc = HFAEntry::New(hHFA, "#Bin_Function#",
    2759             :                                           "Edsc_BinFunction", poEntry);
    2760             : 
    2761           6 :             poBinFunc->MakeData(30);
    2762           6 :             poBinFunc->SetIntField("numBins", nNumBins);
    2763           6 :             poBinFunc->SetDoubleField("minLimit", dMinLimit);
    2764           6 :             poBinFunc->SetDoubleField("maxLimit", dMaxLimit);
    2765             :             // Direct for thematic layers, linear otherwise.
    2766           6 :             if (STARTS_WITH_CI(poNode->GetStringField("layerType"), "thematic"))
    2767           1 :                 poBinFunc->SetStringField("binFunctionType", "direct");
    2768             :             else
    2769           5 :                 poBinFunc->SetStringField("binFunctionType", "linear");
    2770             : 
    2771             :             // We need a child named histogram.
    2772           6 :             HFAEntry *poHisto = poEntry->GetNamedChild("Histogram");
    2773           6 :             if (poHisto == nullptr || !EQUAL(poHisto->GetType(), "Edsc_Column"))
    2774             :                 poHisto =
    2775           1 :                     HFAEntry::New(hHFA, "Histogram", "Edsc_Column", poEntry);
    2776             : 
    2777           6 :             poHisto->SetIntField("numRows", nNumBins);
    2778             :             // Allocate space for the bin values.
    2779           6 :             const auto nOffset64 = HFAAllocateSpace(hHFA, nNumBins * 8);
    2780           6 :             if (nOffset64 >= static_cast<unsigned>(INT_MAX))
    2781             :             {
    2782           0 :                 return CE_Failure;
    2783             :             }
    2784           6 :             const int nOffset = static_cast<int>(nOffset64);
    2785           6 :             poHisto->SetIntField("columnDataPtr", nOffset);
    2786           6 :             poHisto->SetStringField("dataType", "real");
    2787           6 :             poHisto->SetIntField("maxNumChars", 0);
    2788             :             // Write out histogram data.
    2789           6 :             char *pszWork = pszBinValues;
    2790        1531 :             for (int nBin = 0; nBin < nNumBins; ++nBin)
    2791             :             {
    2792        1525 :                 char *pszEnd = strchr(pszWork, '|');
    2793        1525 :                 if (pszEnd != nullptr)
    2794             :                 {
    2795        1525 :                     *pszEnd = 0;
    2796        1525 :                     bRet &=
    2797        3050 :                         VSIFSeekL(hHFA->fp,
    2798        1525 :                                   nOffset + 8 * static_cast<vsi_l_offset>(nBin),
    2799        1525 :                                   SEEK_SET) >= 0;
    2800        1525 :                     double nValue = CPLAtof(pszWork);
    2801             :                     HFAStandard(8, &nValue);
    2802             : 
    2803        1525 :                     bRet &= VSIFWriteL((void *)&nValue, 8, 1, hHFA->fp) > 0;
    2804        1525 :                     pszWork = pszEnd + 1;
    2805             :                 }
    2806           6 :             }
    2807             :         }
    2808           1 :         else if (poEntry != nullptr)
    2809             :         {
    2810             :             // In this case, there are HistogramParameters present, but we did
    2811             :             // not create them. However, we might be modifying them, in the case
    2812             :             // where the data has changed and the histogram counts need to be
    2813             :             // updated. It could be worse than that, but that is all we are
    2814             :             // going to cope with for now.  We are assuming that we did not
    2815             :             // change any of the other stuff, like skip factors and so
    2816             :             // forth. The main need for this case is for programs (such as
    2817             :             // Imagine itself) which will happily modify the pixel values
    2818             :             // without re-calculating the histogram counts.
    2819           1 :             int nNumBins = poEntry->GetIntField("BinFunction.numBins");
    2820             :             HFAEntry *poEntryDescrTbl =
    2821           1 :                 poNode->GetNamedChild("Descriptor_Table");
    2822           1 :             HFAEntry *poHisto = nullptr;
    2823           1 :             if (poEntryDescrTbl != nullptr)
    2824             :             {
    2825           1 :                 poHisto = poEntryDescrTbl->GetNamedChild("Histogram");
    2826             :             }
    2827           1 :             if (poHisto != nullptr)
    2828             :             {
    2829           1 :                 int nOffset = poHisto->GetIntField("columnDataPtr");
    2830             :                 // Write out histogram data.
    2831           1 :                 char *pszWork = pszBinValues;
    2832             : 
    2833             :                 // Check whether histogram counts were written as int or double
    2834           1 :                 bool bCountIsInt = true;
    2835           1 :                 const char *pszDataType = poHisto->GetStringField("dataType");
    2836           1 :                 if (STARTS_WITH_CI(pszDataType, "real"))
    2837             :                 {
    2838           1 :                     bCountIsInt = false;
    2839             :                 }
    2840         257 :                 for (int nBin = 0; nBin < nNumBins; ++nBin)
    2841             :                 {
    2842         256 :                     char *pszEnd = strchr(pszWork, '|');
    2843         256 :                     if (pszEnd != nullptr)
    2844             :                     {
    2845         256 :                         *pszEnd = 0;
    2846         256 :                         if (bCountIsInt)
    2847             :                         {
    2848             :                             // Histogram counts were written as ints, so
    2849             :                             // re-write them the same way.
    2850           0 :                             bRet &= VSIFSeekL(hHFA->fp, nOffset + 4 * nBin,
    2851           0 :                                               SEEK_SET) >= 0;
    2852           0 :                             int nValue = atoi(pszWork);
    2853             :                             HFAStandard(4, &nValue);
    2854           0 :                             bRet &=
    2855           0 :                                 VSIFWriteL((void *)&nValue, 4, 1, hHFA->fp) > 0;
    2856             :                         }
    2857             :                         else
    2858             :                         {
    2859             :                             // Histogram were written as doubles, as is now the
    2860             :                             // default behavior.
    2861         256 :                             bRet &= VSIFSeekL(hHFA->fp, nOffset + 8 * nBin,
    2862         256 :                                               SEEK_SET) >= 0;
    2863         256 :                             double nValue = CPLAtof(pszWork);
    2864             :                             HFAStandard(8, &nValue);
    2865         256 :                             bRet &=
    2866         256 :                                 VSIFWriteL((void *)&nValue, 8, 1, hHFA->fp) > 0;
    2867             :                         }
    2868         256 :                         pszWork = pszEnd + 1;
    2869             :                     }
    2870             :                 }
    2871             :             }
    2872             :         }
    2873           7 :         CPLFree(pszBinValues);
    2874             :     }
    2875             : 
    2876             :     // If we created a statistics node then try to create a
    2877             :     // StatisticsParameters node too.
    2878          65 :     if (bCreatedStatistics)
    2879             :     {
    2880             :         HFAEntry *poEntry =
    2881           8 :             HFAEntry::New(hHFA, "StatisticsParameters",
    2882             :                           "Eimg_StatisticsParameters830", poNode);
    2883             : 
    2884           8 :         poEntry->MakeData(70);
    2885             :         // poEntry->SetStringField( "BinFunction.binFunctionType", "linear" );
    2886             : 
    2887           8 :         poEntry->SetIntField("SkipFactorX", 1);
    2888           8 :         poEntry->SetIntField("SkipFactorY", 1);
    2889             :     }
    2890             : 
    2891             :     // Write out metadata items without a special place.
    2892          65 :     if (bRet && CSLCount(papszGDALMD) != 0)
    2893             :     {
    2894          42 :         CPLErr eErr = HFASetGDALMetadata(hHFA, nBand, papszGDALMD);
    2895             : 
    2896          42 :         CSLDestroy(papszGDALMD);
    2897          42 :         return eErr;
    2898             :     }
    2899             :     else
    2900             :     {
    2901          23 :         CSLDestroy(papszGDALMD);
    2902          23 :         return CE_Failure;
    2903             :     }
    2904             : }
    2905             : 
    2906             : /************************************************************************/
    2907             : /*                         HFAGetIGEFilename()                          */
    2908             : /*                                                                      */
    2909             : /*      Returns the .ige filename if one is associated with this        */
    2910             : /*      object.  For files not newly created we need to scan the        */
    2911             : /*      bands for spill files.  Presumably there will only be one.      */
    2912             : /*                                                                      */
    2913             : /*      NOTE: Returns full path, not just the filename portion.         */
    2914             : /************************************************************************/
    2915             : 
    2916         115 : std::string HFAGetIGEFilename(HFAHandle hHFA)
    2917             : 
    2918             : {
    2919         115 :     if (hHFA->pszIGEFilename == nullptr)
    2920             :     {
    2921             :         std::vector<HFAEntry *> apoDMSList =
    2922         112 :             hHFA->poRoot->FindChildren(nullptr, "ImgExternalRaster");
    2923             : 
    2924         112 :         HFAEntry *poDMS = apoDMSList.empty() ? nullptr : apoDMSList[0];
    2925             : 
    2926             :         // Get the IGE filename from if we have an ExternalRasterDMS.
    2927         112 :         if (poDMS)
    2928             :         {
    2929             :             const char *pszRawFilename =
    2930          32 :                 poDMS->GetStringField("fileName.string");
    2931             : 
    2932          32 :             if (pszRawFilename != nullptr)
    2933             :             {
    2934          32 :                 if (CPLHasPathTraversal(pszRawFilename))
    2935             :                 {
    2936           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2937             :                              "Path traversal detected in %s", pszRawFilename);
    2938           0 :                     return std::string();
    2939             :                 }
    2940             : 
    2941             :                 VSIStatBufL sStatBuf;
    2942             :                 std::string osFullFilename =
    2943          64 :                     CPLFormFilenameSafe(hHFA->pszPath, pszRawFilename, nullptr);
    2944             : 
    2945          32 :                 if (VSIStatL(osFullFilename.c_str(), &sStatBuf) != 0)
    2946             :                 {
    2947             :                     const CPLString osExtension =
    2948           0 :                         CPLGetExtensionSafe(pszRawFilename);
    2949             :                     const CPLString osBasename =
    2950           0 :                         CPLGetBasenameSafe(hHFA->pszFilename);
    2951           0 :                     osFullFilename = CPLFormFilenameSafe(
    2952           0 :                         hHFA->pszPath, osBasename, osExtension);
    2953             : 
    2954           0 :                     if (VSIStatL(osFullFilename.c_str(), &sStatBuf) == 0)
    2955           0 :                         hHFA->pszIGEFilename =
    2956           0 :                             CPLStrdup(CPLFormFilenameSafe(nullptr, osBasename,
    2957             :                                                           osExtension)
    2958             :                                           .c_str());
    2959             :                     else
    2960           0 :                         hHFA->pszIGEFilename = CPLStrdup(pszRawFilename);
    2961             :                 }
    2962             :                 else
    2963             :                 {
    2964          32 :                     hHFA->pszIGEFilename = CPLStrdup(pszRawFilename);
    2965             :                 }
    2966             :             }
    2967             :         }
    2968             :     }
    2969             : 
    2970             :     // Return the full filename.
    2971         115 :     if (hHFA->pszIGEFilename)
    2972          35 :         return CPLFormFilenameSafe(hHFA->pszPath, hHFA->pszIGEFilename,
    2973          35 :                                    nullptr);
    2974             : 
    2975          80 :     return std::string();
    2976             : }
    2977             : 
    2978             : /************************************************************************/
    2979             : /*                        HFACreateSpillStack()                         */
    2980             : /*                                                                      */
    2981             : /*      Create a new stack of raster layers in the spill (.ige)         */
    2982             : /*      file.  Create the spill file if it didn't exist before.         */
    2983             : /************************************************************************/
    2984             : 
    2985           7 : bool HFACreateSpillStack(HFAInfo_t *psInfo, int nXSize, int nYSize, int nLayers,
    2986             :                          int nBlockSize, EPTType eDataType,
    2987             :                          GIntBig *pnValidFlagsOffset, GIntBig *pnDataOffset)
    2988             : 
    2989             : {
    2990             :     // Form .ige filename.
    2991           7 :     if (nBlockSize <= 0)
    2992             :     {
    2993           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2994             :                  "HFACreateSpillStack: nBlockXSize < 0");
    2995           0 :         return false;
    2996             :     }
    2997             : 
    2998           7 :     if (psInfo->pszIGEFilename == nullptr)
    2999             :     {
    3000          14 :         const auto osExt = CPLGetExtensionSafe(psInfo->pszFilename);
    3001           7 :         if (EQUAL(osExt.c_str(), "rrd"))
    3002           0 :             psInfo->pszIGEFilename = CPLStrdup(
    3003           0 :                 CPLResetExtensionSafe(psInfo->pszFilename, "rde").c_str());
    3004           7 :         else if (EQUAL(osExt.c_str(), "aux"))
    3005           0 :             psInfo->pszIGEFilename = CPLStrdup(
    3006           0 :                 CPLResetExtensionSafe(psInfo->pszFilename, "axe").c_str());
    3007             :         else
    3008           7 :             psInfo->pszIGEFilename = CPLStrdup(
    3009          14 :                 CPLResetExtensionSafe(psInfo->pszFilename, "ige").c_str());
    3010             :     }
    3011             : 
    3012           7 :     char *pszFullFilename = CPLStrdup(
    3013          14 :         CPLFormFilenameSafe(psInfo->pszPath, psInfo->pszIGEFilename, nullptr)
    3014             :             .c_str());
    3015             : 
    3016             :     // Try and open it.  If we fail, create it and write the magic header.
    3017             :     static const char *const pszMagick = "ERDAS_IMG_EXTERNAL_RASTER";
    3018             : 
    3019           7 :     bool bRet = true;
    3020           7 :     VSILFILE *fpVSIL = VSIFOpenL(pszFullFilename, "r+b");
    3021           7 :     if (fpVSIL == nullptr)
    3022             :     {
    3023           7 :         fpVSIL = VSIFOpenL(pszFullFilename, "w+");
    3024           7 :         if (fpVSIL == nullptr)
    3025             :         {
    3026           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    3027             :                      "Failed to create spill file %s.\n%s",
    3028           0 :                      psInfo->pszIGEFilename, VSIStrerror(errno));
    3029           0 :             return false;
    3030             :         }
    3031             : 
    3032           7 :         bRet &=
    3033           7 :             VSIFWriteL((void *)pszMagick, strlen(pszMagick) + 1, 1, fpVSIL) > 0;
    3034             :     }
    3035             : 
    3036           7 :     CPLFree(pszFullFilename);
    3037             : 
    3038             :     // Work out some details about the tiling scheme.
    3039           7 :     const int nBlocksPerRow = DIV_ROUND_UP(nXSize, nBlockSize);
    3040           7 :     const int nBlocksPerColumn = DIV_ROUND_UP(nYSize, nBlockSize);
    3041             :     // const int nBlocks = nBlocksPerRow * nBlocksPerColumn;
    3042             :     const int nBytesPerBlock =
    3043           7 :         (nBlockSize * nBlockSize * HFAGetDataTypeBits(eDataType) + 7) / 8;
    3044             : 
    3045           7 :     const int nBytesPerRow = (nBlocksPerRow + 7) / 8;
    3046           7 :     const int nBlockMapSize = nBytesPerRow * nBlocksPerColumn;
    3047             :     // const int iFlagsSize = nBlockMapSize + 20;
    3048             : 
    3049             :     // Write stack prefix information.
    3050           7 :     bRet &= VSIFSeekL(fpVSIL, 0, SEEK_END) >= 0;
    3051             : 
    3052           7 :     GByte bUnknown = 1;
    3053           7 :     bRet &= VSIFWriteL(&bUnknown, 1, 1, fpVSIL) > 0;
    3054             : 
    3055           7 :     GInt32 nValue32 = nLayers;
    3056             :     HFAStandard(4, &nValue32);
    3057           7 :     bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3058           7 :     nValue32 = nXSize;
    3059             :     HFAStandard(4, &nValue32);
    3060           7 :     bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3061           7 :     nValue32 = nYSize;
    3062             :     HFAStandard(4, &nValue32);
    3063           7 :     bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3064           7 :     nValue32 = nBlockSize;
    3065             :     HFAStandard(4, &nValue32);
    3066           7 :     bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3067           7 :     bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3068           7 :     bUnknown = 3;
    3069           7 :     bRet &= VSIFWriteL(&bUnknown, 1, 1, fpVSIL) > 0;
    3070           7 :     bUnknown = 0;
    3071           7 :     bRet &= VSIFWriteL(&bUnknown, 1, 1, fpVSIL) > 0;
    3072             : 
    3073             :     // Write out ValidFlags section(s).
    3074           7 :     *pnValidFlagsOffset = VSIFTellL(fpVSIL);
    3075             : 
    3076             :     unsigned char *pabyBlockMap =
    3077           7 :         static_cast<unsigned char *>(VSI_MALLOC_VERBOSE(nBlockMapSize));
    3078           7 :     if (pabyBlockMap == nullptr)
    3079             :     {
    3080           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpVSIL));
    3081           0 :         return false;
    3082             :     }
    3083             : 
    3084           7 :     memset(pabyBlockMap, 0xff, nBlockMapSize);
    3085          14 :     for (int iBand = 0; iBand < nLayers; iBand++)
    3086             :     {
    3087           7 :         nValue32 = 1;  // Unknown
    3088             :         HFAStandard(4, &nValue32);
    3089           7 :         bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3090           7 :         nValue32 = 0;  // Unknown
    3091           7 :         bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3092           7 :         nValue32 = nBlocksPerColumn;
    3093             :         HFAStandard(4, &nValue32);
    3094           7 :         bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3095           7 :         nValue32 = nBlocksPerRow;
    3096             :         HFAStandard(4, &nValue32);
    3097           7 :         bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3098           7 :         nValue32 = 0x30000;  // Unknown
    3099             :         HFAStandard(4, &nValue32);
    3100           7 :         bRet &= VSIFWriteL(&nValue32, 4, 1, fpVSIL) > 0;
    3101             : 
    3102           7 :         const int iRemainder = nBlocksPerRow % 8;
    3103           7 :         CPLDebug("HFACreate",
    3104             :                  "Block map size %d, bytes per row %d, remainder %d.",
    3105             :                  nBlockMapSize, nBytesPerRow, iRemainder);
    3106           7 :         if (iRemainder)
    3107             :         {
    3108          14 :             for (int i = nBytesPerRow - 1; i < nBlockMapSize; i += nBytesPerRow)
    3109           7 :                 pabyBlockMap[i] = static_cast<GByte>((1 << iRemainder) - 1);
    3110             :         }
    3111             : 
    3112           7 :         bRet &= VSIFWriteL(pabyBlockMap, nBlockMapSize, 1, fpVSIL) > 0;
    3113             :     }
    3114           7 :     CPLFree(pabyBlockMap);
    3115           7 :     pabyBlockMap = nullptr;
    3116             : 
    3117             :     // Extend the file to account for all the imagery space.
    3118           7 :     const GIntBig nTileDataSize = static_cast<GIntBig>(nBytesPerBlock) *
    3119           7 :                                   nBlocksPerRow * nBlocksPerColumn * nLayers;
    3120             : 
    3121           7 :     *pnDataOffset = VSIFTellL(fpVSIL);
    3122             : 
    3123           7 :     if (!bRet || VSIFTruncateL(fpVSIL, nTileDataSize + *pnDataOffset) != 0)
    3124             :     {
    3125           0 :         CPLError(CE_Failure, CPLE_FileIO,
    3126             :                  "Failed to extend %s to full size (" CPL_FRMT_GIB " bytes), "
    3127             :                  "likely out of disk space.\n%s",
    3128           0 :                  psInfo->pszIGEFilename, nTileDataSize + *pnDataOffset,
    3129           0 :                  VSIStrerror(errno));
    3130             : 
    3131           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpVSIL));
    3132           0 :         return false;
    3133             :     }
    3134             : 
    3135           7 :     if (VSIFCloseL(fpVSIL) != 0)
    3136           0 :         return false;
    3137             : 
    3138           7 :     return true;
    3139             : }
    3140             : 
    3141             : /************************************************************************/
    3142             : /*                       HFAReadAndValidatePoly()                       */
    3143             : /************************************************************************/
    3144             : 
    3145           3 : static bool HFAReadAndValidatePoly(HFAEntry *poTarget, const char *pszName,
    3146             :                                    Efga_Polynomial *psRetPoly)
    3147             : 
    3148             : {
    3149           3 :     memset(psRetPoly, 0, sizeof(Efga_Polynomial));
    3150             : 
    3151           6 :     CPLString osFldName;
    3152           3 :     osFldName.Printf("%sorder", pszName);
    3153           3 :     psRetPoly->order = poTarget->GetIntField(osFldName);
    3154             : 
    3155           3 :     if (psRetPoly->order < 1 || psRetPoly->order > 3)
    3156           0 :         return false;
    3157             : 
    3158             :     // Validate that things are in a "well known" form.
    3159           3 :     osFldName.Printf("%snumdimtransform", pszName);
    3160           3 :     const int numdimtransform = poTarget->GetIntField(osFldName);
    3161             : 
    3162           3 :     osFldName.Printf("%snumdimpolynomial", pszName);
    3163           3 :     const int numdimpolynomial = poTarget->GetIntField(osFldName);
    3164             : 
    3165           3 :     osFldName.Printf("%stermcount", pszName);
    3166           3 :     const int termcount = poTarget->GetIntField(osFldName);
    3167             : 
    3168           3 :     if (numdimtransform != 2 || numdimpolynomial != 2)
    3169           0 :         return false;
    3170             : 
    3171           3 :     if ((psRetPoly->order == 1 && termcount != 3) ||
    3172           3 :         (psRetPoly->order == 2 && termcount != 6) ||
    3173           3 :         (psRetPoly->order == 3 && termcount != 10))
    3174           0 :         return false;
    3175             : 
    3176             :     // We don't check the exponent organization for now.  Hopefully
    3177             :     // it is always standard.
    3178             : 
    3179             :     // Get coefficients.
    3180          43 :     for (int i = 0; i < termcount * 2 - 2; i++)
    3181             :     {
    3182          40 :         osFldName.Printf("%spolycoefmtx[%d]", pszName, i);
    3183          40 :         psRetPoly->polycoefmtx[i] = poTarget->GetDoubleField(osFldName);
    3184             :     }
    3185             : 
    3186           9 :     for (int i = 0; i < 2; i++)
    3187             :     {
    3188           6 :         osFldName.Printf("%spolycoefvector[%d]", pszName, i);
    3189           6 :         psRetPoly->polycoefvector[i] = poTarget->GetDoubleField(osFldName);
    3190             :     }
    3191             : 
    3192           3 :     return true;
    3193             : }
    3194             : 
    3195             : /************************************************************************/
    3196             : /*                         HFAReadXFormStack()                          */
    3197             : /************************************************************************/
    3198             : 
    3199         293 : int HFAReadXFormStack(HFAHandle hHFA, Efga_Polynomial **ppasPolyListForward,
    3200             :                       Efga_Polynomial **ppasPolyListReverse)
    3201             : 
    3202             : {
    3203         293 :     if (hHFA->nBands == 0)
    3204           0 :         return 0;
    3205             : 
    3206             :     // Get the HFA node.
    3207             :     HFAEntry *poXFormHeader =
    3208         293 :         hHFA->papoBand[0]->poNode->GetNamedChild("MapToPixelXForm");
    3209         293 :     if (poXFormHeader == nullptr)
    3210         291 :         return 0;
    3211             : 
    3212             :     // Loop over children, collecting XForms.
    3213           2 :     int nStepCount = 0;
    3214           2 :     *ppasPolyListForward = nullptr;
    3215           2 :     *ppasPolyListReverse = nullptr;
    3216             : 
    3217           5 :     for (HFAEntry *poXForm = poXFormHeader->GetChild(); poXForm != nullptr;
    3218           3 :          poXForm = poXForm->GetNext())
    3219             :     {
    3220           3 :         bool bSuccess = false;
    3221             :         Efga_Polynomial sForward;
    3222             :         Efga_Polynomial sReverse;
    3223           3 :         memset(&sForward, 0, sizeof(sForward));
    3224           3 :         memset(&sReverse, 0, sizeof(sReverse));
    3225             : 
    3226           3 :         if (EQUAL(poXForm->GetType(), "Efga_Polynomial"))
    3227             :         {
    3228           1 :             bSuccess = HFAReadAndValidatePoly(poXForm, "", &sForward);
    3229             : 
    3230           1 :             if (bSuccess)
    3231             :             {
    3232             :                 double adfGT[6] = {
    3233           1 :                     sForward.polycoefvector[0], sForward.polycoefmtx[0],
    3234           1 :                     sForward.polycoefmtx[2],    sForward.polycoefvector[1],
    3235           1 :                     sForward.polycoefmtx[1],    sForward.polycoefmtx[3]};
    3236             : 
    3237           1 :                 double adfInvGT[6] = {};
    3238           1 :                 bSuccess = HFAInvGeoTransform(adfGT, adfInvGT);
    3239           1 :                 if (!bSuccess)
    3240           0 :                     memset(adfInvGT, 0, sizeof(adfInvGT));
    3241             : 
    3242           1 :                 sReverse.order = sForward.order;
    3243           1 :                 sReverse.polycoefvector[0] = adfInvGT[0];
    3244           1 :                 sReverse.polycoefmtx[0] = adfInvGT[1];
    3245           1 :                 sReverse.polycoefmtx[2] = adfInvGT[2];
    3246           1 :                 sReverse.polycoefvector[1] = adfInvGT[3];
    3247           1 :                 sReverse.polycoefmtx[1] = adfInvGT[4];
    3248           1 :                 sReverse.polycoefmtx[3] = adfInvGT[5];
    3249             :             }
    3250             :         }
    3251           2 :         else if (EQUAL(poXForm->GetType(), "GM_PolyPair"))
    3252             :         {
    3253           2 :             bSuccess = HFAReadAndValidatePoly(poXForm, "forward.", &sForward) &&
    3254           1 :                        HFAReadAndValidatePoly(poXForm, "reverse.", &sReverse);
    3255             :         }
    3256             : 
    3257           3 :         if (bSuccess)
    3258             :         {
    3259           2 :             nStepCount++;
    3260           4 :             *ppasPolyListForward = static_cast<Efga_Polynomial *>(CPLRealloc(
    3261           2 :                 *ppasPolyListForward, sizeof(Efga_Polynomial) * nStepCount));
    3262           2 :             memcpy(*ppasPolyListForward + nStepCount - 1, &sForward,
    3263             :                    sizeof(sForward));
    3264             : 
    3265           4 :             *ppasPolyListReverse = static_cast<Efga_Polynomial *>(CPLRealloc(
    3266           2 :                 *ppasPolyListReverse, sizeof(Efga_Polynomial) * nStepCount));
    3267           2 :             memcpy(*ppasPolyListReverse + nStepCount - 1, &sReverse,
    3268             :                    sizeof(sReverse));
    3269             :         }
    3270             :     }
    3271             : 
    3272           2 :     return nStepCount;
    3273             : }
    3274             : 
    3275             : /************************************************************************/
    3276             : /*                       HFAEvaluateXFormStack()                        */
    3277             : /************************************************************************/
    3278             : 
    3279          36 : int HFAEvaluateXFormStack(int nStepCount, int bForward,
    3280             :                           Efga_Polynomial *pasPolyList, double *pdfX,
    3281             :                           double *pdfY)
    3282             : 
    3283             : {
    3284         108 :     for (int iStep = 0; iStep < nStepCount; iStep++)
    3285             :     {
    3286          72 :         const Efga_Polynomial *psStep =
    3287           0 :             bForward ? pasPolyList + iStep
    3288          72 :                      : pasPolyList + nStepCount - iStep - 1;
    3289             : 
    3290          72 :         if (psStep->order == 1)
    3291             :         {
    3292          36 :             const double dfXOut = psStep->polycoefvector[0] +
    3293          36 :                                   psStep->polycoefmtx[0] * *pdfX +
    3294          36 :                                   psStep->polycoefmtx[2] * *pdfY;
    3295             : 
    3296          36 :             const double dfYOut = psStep->polycoefvector[1] +
    3297          36 :                                   psStep->polycoefmtx[1] * *pdfX +
    3298          36 :                                   psStep->polycoefmtx[3] * *pdfY;
    3299             : 
    3300          36 :             *pdfX = dfXOut;
    3301          36 :             *pdfY = dfYOut;
    3302             :         }
    3303          36 :         else if (psStep->order == 2)
    3304             :         {
    3305           0 :             const double dfXOut = psStep->polycoefvector[0] +
    3306           0 :                                   psStep->polycoefmtx[0] * *pdfX +
    3307           0 :                                   psStep->polycoefmtx[2] * *pdfY +
    3308           0 :                                   psStep->polycoefmtx[4] * *pdfX * *pdfX +
    3309           0 :                                   psStep->polycoefmtx[6] * *pdfX * *pdfY +
    3310           0 :                                   psStep->polycoefmtx[8] * *pdfY * *pdfY;
    3311           0 :             const double dfYOut = psStep->polycoefvector[1] +
    3312           0 :                                   psStep->polycoefmtx[1] * *pdfX +
    3313           0 :                                   psStep->polycoefmtx[3] * *pdfY +
    3314           0 :                                   psStep->polycoefmtx[5] * *pdfX * *pdfX +
    3315           0 :                                   psStep->polycoefmtx[7] * *pdfX * *pdfY +
    3316           0 :                                   psStep->polycoefmtx[9] * *pdfY * *pdfY;
    3317             : 
    3318           0 :             *pdfX = dfXOut;
    3319           0 :             *pdfY = dfYOut;
    3320             :         }
    3321          36 :         else if (psStep->order == 3)
    3322             :         {
    3323          36 :             const double dfXOut =
    3324          36 :                 psStep->polycoefvector[0] + psStep->polycoefmtx[0] * *pdfX +
    3325          36 :                 psStep->polycoefmtx[2] * *pdfY +
    3326          36 :                 psStep->polycoefmtx[4] * *pdfX * *pdfX +
    3327          36 :                 psStep->polycoefmtx[6] * *pdfX * *pdfY +
    3328          36 :                 psStep->polycoefmtx[8] * *pdfY * *pdfY +
    3329          36 :                 psStep->polycoefmtx[10] * *pdfX * *pdfX * *pdfX +
    3330          36 :                 psStep->polycoefmtx[12] * *pdfX * *pdfX * *pdfY +
    3331          36 :                 psStep->polycoefmtx[14] * *pdfX * *pdfY * *pdfY +
    3332          36 :                 psStep->polycoefmtx[16] * *pdfY * *pdfY * *pdfY;
    3333          36 :             const double dfYOut =
    3334          36 :                 psStep->polycoefvector[1] + psStep->polycoefmtx[1] * *pdfX +
    3335          36 :                 psStep->polycoefmtx[3] * *pdfY +
    3336          36 :                 psStep->polycoefmtx[5] * *pdfX * *pdfX +
    3337          36 :                 psStep->polycoefmtx[7] * *pdfX * *pdfY +
    3338          36 :                 psStep->polycoefmtx[9] * *pdfY * *pdfY +
    3339          36 :                 psStep->polycoefmtx[11] * *pdfX * *pdfX * *pdfX +
    3340          36 :                 psStep->polycoefmtx[13] * *pdfX * *pdfX * *pdfY +
    3341          36 :                 psStep->polycoefmtx[15] * *pdfX * *pdfY * *pdfY +
    3342          36 :                 psStep->polycoefmtx[17] * *pdfY * *pdfY * *pdfY;
    3343             : 
    3344          36 :             *pdfX = dfXOut;
    3345          36 :             *pdfY = dfYOut;
    3346             :         }
    3347             :         else
    3348           0 :             return FALSE;
    3349             :     }
    3350             : 
    3351          36 :     return TRUE;
    3352             : }
    3353             : 
    3354             : /************************************************************************/
    3355             : /*                         HFAWriteXFormStack()                         */
    3356             : /************************************************************************/
    3357             : 
    3358           2 : CPLErr HFAWriteXFormStack(HFAHandle hHFA, int nBand, int nXFormCount,
    3359             :                           Efga_Polynomial **ppasPolyListForward,
    3360             :                           Efga_Polynomial **ppasPolyListReverse)
    3361             : 
    3362             : {
    3363           2 :     if (nXFormCount == 0)
    3364           0 :         return CE_None;
    3365             : 
    3366           2 :     if (ppasPolyListForward[0]->order != 1)
    3367             :     {
    3368           0 :         CPLError(
    3369             :             CE_Failure, CPLE_AppDefined,
    3370             :             "For now HFAWriteXFormStack() only supports order 1 polynomials");
    3371           0 :         return CE_Failure;
    3372             :     }
    3373             : 
    3374           2 :     if (nBand < 0 || nBand > hHFA->nBands)
    3375           0 :         return CE_Failure;
    3376             : 
    3377             :     // If no band number is provided, operate on all bands.
    3378           2 :     if (nBand == 0)
    3379             :     {
    3380           2 :         for (nBand = 1; nBand <= hHFA->nBands; nBand++)
    3381             :         {
    3382             :             CPLErr eErr =
    3383           1 :                 HFAWriteXFormStack(hHFA, nBand, nXFormCount,
    3384             :                                    ppasPolyListForward, ppasPolyListReverse);
    3385           1 :             if (eErr != CE_None)
    3386           0 :                 return eErr;
    3387             :         }
    3388             : 
    3389           1 :         return CE_None;
    3390             :     }
    3391             : 
    3392             :     // Fetch our band node.
    3393           1 :     HFAEntry *poBandNode = hHFA->papoBand[nBand - 1]->poNode;
    3394           1 :     HFAEntry *poXFormHeader = poBandNode->GetNamedChild("MapToPixelXForm");
    3395           1 :     if (poXFormHeader == nullptr)
    3396             :     {
    3397           1 :         poXFormHeader = HFAEntry::New(hHFA, "MapToPixelXForm",
    3398             :                                       "Exfr_GenericXFormHeader", poBandNode);
    3399           1 :         poXFormHeader->MakeData(23);
    3400           1 :         poXFormHeader->SetPosition();
    3401           1 :         poXFormHeader->SetStringField("titleList.string", "Affine");
    3402             :     }
    3403             : 
    3404             :     // Loop over XForms.
    3405           2 :     for (int iXForm = 0; iXForm < nXFormCount; iXForm++)
    3406             :     {
    3407           1 :         Efga_Polynomial *psForward = *ppasPolyListForward + iXForm;
    3408           2 :         CPLString osXFormName;
    3409           1 :         osXFormName.Printf("XForm%d", iXForm);
    3410             : 
    3411           1 :         HFAEntry *poXForm = poXFormHeader->GetNamedChild(osXFormName);
    3412             : 
    3413           1 :         if (poXForm == nullptr)
    3414             :         {
    3415           1 :             poXForm = HFAEntry::New(hHFA, osXFormName, "Efga_Polynomial",
    3416             :                                     poXFormHeader);
    3417           1 :             poXForm->MakeData(136);
    3418           1 :             poXForm->SetPosition();
    3419             :         }
    3420             : 
    3421           1 :         poXForm->SetIntField("order", 1);
    3422           1 :         poXForm->SetIntField("numdimtransform", 2);
    3423           1 :         poXForm->SetIntField("numdimpolynomial", 2);
    3424           1 :         poXForm->SetIntField("termcount", 3);
    3425           1 :         poXForm->SetIntField("exponentlist[0]", 0);
    3426           1 :         poXForm->SetIntField("exponentlist[1]", 0);
    3427           1 :         poXForm->SetIntField("exponentlist[2]", 1);
    3428           1 :         poXForm->SetIntField("exponentlist[3]", 0);
    3429           1 :         poXForm->SetIntField("exponentlist[4]", 0);
    3430           1 :         poXForm->SetIntField("exponentlist[5]", 1);
    3431             : 
    3432           1 :         poXForm->SetIntField("polycoefmtx[-3]", EPT_f64);
    3433           1 :         poXForm->SetIntField("polycoefmtx[-2]", 2);
    3434           1 :         poXForm->SetIntField("polycoefmtx[-1]", 2);
    3435           1 :         poXForm->SetDoubleField("polycoefmtx[0]", psForward->polycoefmtx[0]);
    3436           1 :         poXForm->SetDoubleField("polycoefmtx[1]", psForward->polycoefmtx[1]);
    3437           1 :         poXForm->SetDoubleField("polycoefmtx[2]", psForward->polycoefmtx[2]);
    3438           1 :         poXForm->SetDoubleField("polycoefmtx[3]", psForward->polycoefmtx[3]);
    3439             : 
    3440           1 :         poXForm->SetIntField("polycoefvector[-3]", EPT_f64);
    3441           1 :         poXForm->SetIntField("polycoefvector[-2]", 1);
    3442           1 :         poXForm->SetIntField("polycoefvector[-1]", 2);
    3443           1 :         poXForm->SetDoubleField("polycoefvector[0]",
    3444             :                                 psForward->polycoefvector[0]);
    3445           1 :         poXForm->SetDoubleField("polycoefvector[1]",
    3446             :                                 psForward->polycoefvector[1]);
    3447             :     }
    3448             : 
    3449           1 :     return CE_None;
    3450             : }
    3451             : 
    3452             : /************************************************************************/
    3453             : /*                         HFAReadCameraModel()                         */
    3454             : /************************************************************************/
    3455             : 
    3456         543 : char **HFAReadCameraModel(HFAHandle hHFA)
    3457             : 
    3458             : {
    3459         543 :     if (hHFA->nBands == 0)
    3460           0 :         return nullptr;
    3461             : 
    3462             :     // Get the camera model node, and confirm its type.
    3463             :     HFAEntry *poXForm =
    3464         543 :         hHFA->papoBand[0]->poNode->GetNamedChild("MapToPixelXForm.XForm0");
    3465         543 :     if (poXForm == nullptr)
    3466         537 :         return nullptr;
    3467             : 
    3468           6 :     if (!EQUAL(poXForm->GetType(), "Camera_ModelX"))
    3469           5 :         return nullptr;
    3470             : 
    3471             :     // Convert the values to metadata.
    3472           1 :     char **papszMD = nullptr;
    3473             :     static const char *const apszFields[] = {"direction",
    3474             :                                              "refType",
    3475             :                                              "demsource",
    3476             :                                              "PhotoDirection",
    3477             :                                              "RotationSystem",
    3478             :                                              "demfilename",
    3479             :                                              "demzunits",
    3480             :                                              "forSrcAffine[0]",
    3481             :                                              "forSrcAffine[1]",
    3482             :                                              "forSrcAffine[2]",
    3483             :                                              "forSrcAffine[3]",
    3484             :                                              "forSrcAffine[4]",
    3485             :                                              "forSrcAffine[5]",
    3486             :                                              "forDstAffine[0]",
    3487             :                                              "forDstAffine[1]",
    3488             :                                              "forDstAffine[2]",
    3489             :                                              "forDstAffine[3]",
    3490             :                                              "forDstAffine[4]",
    3491             :                                              "forDstAffine[5]",
    3492             :                                              "invSrcAffine[0]",
    3493             :                                              "invSrcAffine[1]",
    3494             :                                              "invSrcAffine[2]",
    3495             :                                              "invSrcAffine[3]",
    3496             :                                              "invSrcAffine[4]",
    3497             :                                              "invSrcAffine[5]",
    3498             :                                              "invDstAffine[0]",
    3499             :                                              "invDstAffine[1]",
    3500             :                                              "invDstAffine[2]",
    3501             :                                              "invDstAffine[3]",
    3502             :                                              "invDstAffine[4]",
    3503             :                                              "invDstAffine[5]",
    3504             :                                              "z_mean",
    3505             :                                              "lat0",
    3506             :                                              "lon0",
    3507             :                                              "coeffs[0]",
    3508             :                                              "coeffs[1]",
    3509             :                                              "coeffs[2]",
    3510             :                                              "coeffs[3]",
    3511             :                                              "coeffs[4]",
    3512             :                                              "coeffs[5]",
    3513             :                                              "coeffs[6]",
    3514             :                                              "coeffs[7]",
    3515             :                                              "coeffs[8]",
    3516             :                                              "LensDistortion[0]",
    3517             :                                              "LensDistortion[1]",
    3518             :                                              "LensDistortion[2]",
    3519             :                                              nullptr};
    3520             : 
    3521           1 :     const char *pszValue = nullptr;
    3522          47 :     for (int i = 0; apszFields[i] != nullptr; i++)
    3523             :     {
    3524          46 :         pszValue = poXForm->GetStringField(apszFields[i]);
    3525          46 :         if (pszValue == nullptr)
    3526           1 :             pszValue = "";
    3527             : 
    3528          46 :         papszMD = CSLSetNameValue(papszMD, apszFields[i], pszValue);
    3529             :     }
    3530             : 
    3531             :     // Create a pseudo-entry for the MIFObject with the outputProjection.
    3532             :     HFAEntry *poProjInfo =
    3533           1 :         HFAEntry::BuildEntryFromMIFObject(poXForm, "outputProjection");
    3534           1 :     if (poProjInfo)
    3535             :     {
    3536             :         // Fetch the datum.
    3537             :         Eprj_Datum sDatum;
    3538             : 
    3539           1 :         memset(&sDatum, 0, sizeof(sDatum));
    3540             : 
    3541           1 :         sDatum.datumname =
    3542           1 :             (char *)poProjInfo->GetStringField("earthModel.datum.datumname");
    3543             : 
    3544           1 :         const int nDatumType = poProjInfo->GetIntField("earthModel.datum.type");
    3545           1 :         if (nDatumType < 0 || nDatumType > EPRJ_DATUM_NONE)
    3546             :         {
    3547           0 :             CPLDebug("HFA", "Invalid value for datum type: %d", nDatumType);
    3548           0 :             sDatum.type = EPRJ_DATUM_NONE;
    3549             :         }
    3550             :         else
    3551             :         {
    3552           1 :             sDatum.type = static_cast<Eprj_DatumType>(nDatumType);
    3553             :         }
    3554             : 
    3555           8 :         for (int i = 0; i < 7; i++)
    3556             :         {
    3557           7 :             char szFieldName[60] = {};
    3558             : 
    3559           7 :             snprintf(szFieldName, sizeof(szFieldName),
    3560             :                      "earthModel.datum.params[%d]", i);
    3561           7 :             sDatum.params[i] = poProjInfo->GetDoubleField(szFieldName);
    3562             :         }
    3563             : 
    3564           1 :         sDatum.gridname =
    3565           1 :             (char *)poProjInfo->GetStringField("earthModel.datum.gridname");
    3566             : 
    3567             :         // Fetch the projection parameters.
    3568             :         Eprj_ProParameters sPro;
    3569             : 
    3570           1 :         memset(&sPro, 0, sizeof(sPro));
    3571             : 
    3572           1 :         sPro.proType =
    3573           1 :             (Eprj_ProType)poProjInfo->GetIntField("projectionObject.proType");
    3574           1 :         sPro.proNumber = poProjInfo->GetIntField("projectionObject.proNumber");
    3575           1 :         sPro.proExeName =
    3576           1 :             (char *)poProjInfo->GetStringField("projectionObject.proExeName");
    3577           1 :         sPro.proName =
    3578           1 :             (char *)poProjInfo->GetStringField("projectionObject.proName");
    3579           1 :         sPro.proZone = poProjInfo->GetIntField("projectionObject.proZone");
    3580             : 
    3581          16 :         for (int i = 0; i < 15; i++)
    3582             :         {
    3583          15 :             char szFieldName[40] = {};
    3584             : 
    3585          15 :             snprintf(szFieldName, sizeof(szFieldName),
    3586             :                      "projectionObject.proParams[%d]", i);
    3587          15 :             sPro.proParams[i] = poProjInfo->GetDoubleField(szFieldName);
    3588             :         }
    3589             : 
    3590             :         // Fetch the spheroid.
    3591           1 :         sPro.proSpheroid.sphereName = (char *)poProjInfo->GetStringField(
    3592             :             "earthModel.proSpheroid.sphereName");
    3593           1 :         sPro.proSpheroid.a =
    3594           1 :             poProjInfo->GetDoubleField("earthModel.proSpheroid.a");
    3595           1 :         sPro.proSpheroid.b =
    3596           1 :             poProjInfo->GetDoubleField("earthModel.proSpheroid.b");
    3597           1 :         sPro.proSpheroid.eSquared =
    3598           1 :             poProjInfo->GetDoubleField("earthModel.proSpheroid.eSquared");
    3599           1 :         sPro.proSpheroid.radius =
    3600           1 :             poProjInfo->GetDoubleField("earthModel.proSpheroid.radius");
    3601             : 
    3602             :         // Fetch the projection info.
    3603             :         // poProjInfo->DumpFieldValues( stdout, "" );
    3604             : 
    3605           2 :         auto poSRS = HFAPCSStructToOSR(&sDatum, &sPro, nullptr, nullptr);
    3606             : 
    3607           1 :         if (poSRS)
    3608             :         {
    3609           1 :             char *pszProjection = nullptr;
    3610           1 :             if (poSRS->exportToWkt(&pszProjection) == OGRERR_NONE)
    3611             :             {
    3612             :                 papszMD =
    3613           1 :                     CSLSetNameValue(papszMD, "outputProjection", pszProjection);
    3614             :             }
    3615           1 :             CPLFree(pszProjection);
    3616             :         }
    3617             : 
    3618           1 :         delete poProjInfo;
    3619             :     }
    3620             : 
    3621             :     // Fetch the horizontal units.
    3622           1 :     pszValue = poXForm->GetStringField("outputHorizontalUnits.string");
    3623           1 :     if (pszValue == nullptr)
    3624           0 :         pszValue = "";
    3625             : 
    3626           1 :     papszMD = CSLSetNameValue(papszMD, "outputHorizontalUnits", pszValue);
    3627             : 
    3628             :     // Fetch the elevationinfo.
    3629             :     HFAEntry *poElevInfo =
    3630           1 :         HFAEntry::BuildEntryFromMIFObject(poXForm, "outputElevationInfo");
    3631           1 :     if (poElevInfo)
    3632             :     {
    3633             :         // poElevInfo->DumpFieldValues( stdout, "" );
    3634             : 
    3635           1 :         if (poElevInfo->GetDataSize() != 0)
    3636             :         {
    3637             :             static const char *const apszEFields[] = {
    3638             :                 "verticalDatum.datumname", "verticalDatum.type",
    3639             :                 "elevationUnit", "elevationType", nullptr};
    3640             : 
    3641           5 :             for (int i = 0; apszEFields[i] != nullptr; i++)
    3642             :             {
    3643           4 :                 pszValue = poElevInfo->GetStringField(apszEFields[i]);
    3644           4 :                 if (pszValue == nullptr)
    3645           0 :                     pszValue = "";
    3646             : 
    3647           4 :                 papszMD = CSLSetNameValue(papszMD, apszEFields[i], pszValue);
    3648             :             }
    3649             :         }
    3650             : 
    3651           1 :         delete poElevInfo;
    3652             :     }
    3653             : 
    3654           1 :     return papszMD;
    3655             : }
    3656             : 
    3657             : /************************************************************************/
    3658             : /*                         HFAReadElevationUnit()                       */
    3659             : /************************************************************************/
    3660             : 
    3661         623 : const char *HFAReadElevationUnit(HFAHandle hHFA, int iBand)
    3662             : {
    3663         623 :     if (hHFA->nBands <= iBand)
    3664           0 :         return nullptr;
    3665             : 
    3666         623 :     HFABand *poBand(hHFA->papoBand[iBand]);
    3667         623 :     if (poBand == nullptr || poBand->poNode == nullptr)
    3668             :     {
    3669           0 :         return nullptr;
    3670             :     }
    3671         623 :     HFAEntry *poElevInfo = poBand->poNode->GetNamedChild("Elevation_Info");
    3672         623 :     if (poElevInfo == nullptr)
    3673             :     {
    3674         620 :         return nullptr;
    3675             :     }
    3676           3 :     return poElevInfo->GetStringField("elevationUnit");
    3677             : }
    3678             : 
    3679             : /************************************************************************/
    3680             : /*                         HFASetGeoTransform()                         */
    3681             : /*                                                                      */
    3682             : /*      Set a MapInformation and XForm block.  Allows for rotated       */
    3683             : /*      and shared geotransforms.                                       */
    3684             : /************************************************************************/
    3685             : 
    3686           1 : CPLErr HFASetGeoTransform(HFAHandle hHFA, const char *pszProName,
    3687             :                           const char *pszUnits, double *padfGeoTransform)
    3688             : 
    3689             : {
    3690             :     // Write MapInformation.
    3691           2 :     for (int nBand = 1; nBand <= hHFA->nBands; nBand++)
    3692             :     {
    3693           1 :         HFAEntry *poBandNode = hHFA->papoBand[nBand - 1]->poNode;
    3694             : 
    3695           1 :         HFAEntry *poMI = poBandNode->GetNamedChild("MapInformation");
    3696           1 :         if (poMI == nullptr)
    3697             :         {
    3698           1 :             poMI = HFAEntry::New(hHFA, "MapInformation", "Eimg_MapInformation",
    3699             :                                  poBandNode);
    3700           1 :             poMI->MakeData(
    3701           1 :                 static_cast<int>(18 + strlen(pszProName) + strlen(pszUnits)));
    3702           1 :             poMI->SetPosition();
    3703             :         }
    3704             : 
    3705           1 :         poMI->SetStringField("projection.string", pszProName);
    3706           1 :         poMI->SetStringField("units.string", pszUnits);
    3707             :     }
    3708             : 
    3709             :     // Write XForm.
    3710           1 :     double adfAdjTransform[6] = {};
    3711             : 
    3712             :     // Offset by half pixel.
    3713             : 
    3714           1 :     memcpy(adfAdjTransform, padfGeoTransform, sizeof(double) * 6);
    3715           1 :     adfAdjTransform[0] += adfAdjTransform[1] * 0.5;
    3716           1 :     adfAdjTransform[0] += adfAdjTransform[2] * 0.5;
    3717           1 :     adfAdjTransform[3] += adfAdjTransform[4] * 0.5;
    3718           1 :     adfAdjTransform[3] += adfAdjTransform[5] * 0.5;
    3719             : 
    3720             :     // Invert.
    3721           1 :     double adfRevTransform[6] = {};
    3722           1 :     if (!HFAInvGeoTransform(adfAdjTransform, adfRevTransform))
    3723           0 :         memset(adfRevTransform, 0, sizeof(adfRevTransform));
    3724             : 
    3725             :     // Assign to polynomial object.
    3726             : 
    3727             :     Efga_Polynomial sForward;
    3728           1 :     memset(&sForward, 0, sizeof(sForward));
    3729           1 :     Efga_Polynomial *psForward = &sForward;
    3730           1 :     sForward.order = 1;
    3731           1 :     sForward.polycoefvector[0] = adfRevTransform[0];
    3732           1 :     sForward.polycoefmtx[0] = adfRevTransform[1];
    3733           1 :     sForward.polycoefmtx[1] = adfRevTransform[4];
    3734           1 :     sForward.polycoefvector[1] = adfRevTransform[3];
    3735           1 :     sForward.polycoefmtx[2] = adfRevTransform[2];
    3736           1 :     sForward.polycoefmtx[3] = adfRevTransform[5];
    3737             : 
    3738           1 :     Efga_Polynomial sReverse = sForward;
    3739           1 :     Efga_Polynomial *psReverse = &sReverse;
    3740             : 
    3741           2 :     return HFAWriteXFormStack(hHFA, 0, 1, &psForward, &psReverse);
    3742             : }
    3743             : 
    3744             : /************************************************************************/
    3745             : /*                        HFARenameReferences()                         */
    3746             : /*                                                                      */
    3747             : /*      Rename references in this .img file from the old basename to    */
    3748             : /*      a new basename.  This should be passed on to .aux and .rrd      */
    3749             : /*      files and should include references to .aux, .rrd and .ige.     */
    3750             : /************************************************************************/
    3751             : 
    3752           4 : CPLErr HFARenameReferences(HFAHandle hHFA, const char *pszNewBase,
    3753             :                            const char *pszOldBase)
    3754             : 
    3755             : {
    3756             :     // Handle RRDNamesList updates.
    3757             :     std::vector<HFAEntry *> apoNodeList =
    3758           4 :         hHFA->poRoot->FindChildren("RRDNamesList", nullptr);
    3759             : 
    3760           6 :     for (size_t iNode = 0; iNode < apoNodeList.size(); iNode++)
    3761             :     {
    3762           2 :         HFAEntry *poRRDNL = apoNodeList[iNode];
    3763           4 :         std::vector<CPLString> aosNL;
    3764             : 
    3765             :         // Collect all the existing names.
    3766           2 :         const int nNameCount = poRRDNL->GetFieldCount("nameList");
    3767           2 :         if (nNameCount >= 0)
    3768             :         {
    3769           4 :             CPLString osAlgorithm = poRRDNL->GetStringField("algorithm.string");
    3770           4 :             for (int i = 0; i < nNameCount; i++)
    3771             :             {
    3772           2 :                 CPLString osFN;
    3773           2 :                 osFN.Printf("nameList[%d].string", i);
    3774           2 :                 aosNL.push_back(poRRDNL->GetStringField(osFN));
    3775             :             }
    3776             : 
    3777             :             // Adjust the names to the new form.
    3778           4 :             for (int i = 0; i < nNameCount; i++)
    3779             :             {
    3780           2 :                 if (strncmp(aosNL[i], pszOldBase, strlen(pszOldBase)) == 0)
    3781             :                 {
    3782           2 :                     std::string osNew = pszNewBase;
    3783           2 :                     osNew += aosNL[i].c_str() + strlen(pszOldBase);
    3784           2 :                     aosNL[i] = std::move(osNew);
    3785             :                 }
    3786             :             }
    3787             : 
    3788             :             // Try to make sure the RRDNamesList is big enough to hold the
    3789             :             // adjusted name list.
    3790           2 :             if (strlen(pszNewBase) > strlen(pszOldBase))
    3791             :             {
    3792           1 :                 CPLDebug("HFA", "Growing RRDNamesList to hold new names");
    3793           1 :                 poRRDNL->MakeData(static_cast<int>(
    3794           1 :                     poRRDNL->GetDataSize() +
    3795           1 :                     nNameCount * (strlen(pszNewBase) - strlen(pszOldBase))));
    3796             :             }
    3797             : 
    3798             :             // Initialize the whole thing to zeros for a clean start.
    3799           2 :             memset(poRRDNL->GetData(), 0, poRRDNL->GetDataSize());
    3800             : 
    3801             :             // Write the updates back to the file.
    3802           2 :             poRRDNL->SetStringField("algorithm.string", osAlgorithm);
    3803           4 :             for (int i = 0; i < nNameCount; i++)
    3804             :             {
    3805           4 :                 CPLString osFN;
    3806           2 :                 osFN.Printf("nameList[%d].string", i);
    3807           2 :                 poRRDNL->SetStringField(osFN, aosNL[i]);
    3808             :             }
    3809             :         }
    3810             :     }
    3811             : 
    3812             :     // Spill file references.
    3813             :     apoNodeList =
    3814           4 :         hHFA->poRoot->FindChildren("ExternalRasterDMS", "ImgExternalRaster");
    3815             : 
    3816           8 :     for (size_t iNode = 0; iNode < apoNodeList.size(); iNode++)
    3817             :     {
    3818           4 :         HFAEntry *poERDMS = apoNodeList[iNode];
    3819             : 
    3820           4 :         if (poERDMS == nullptr)
    3821           0 :             continue;
    3822             : 
    3823             :         // Fetch all existing values.
    3824           8 :         CPLString osFileName = poERDMS->GetStringField("fileName.string");
    3825             : 
    3826             :         GInt32 anValidFlagsOffset[2] = {
    3827           4 :             poERDMS->GetIntField("layerStackValidFlagsOffset[0]"),
    3828           4 :             poERDMS->GetIntField("layerStackValidFlagsOffset[1]")};
    3829             : 
    3830             :         GInt32 anStackDataOffset[2] = {
    3831           4 :             poERDMS->GetIntField("layerStackDataOffset[0]"),
    3832           4 :             poERDMS->GetIntField("layerStackDataOffset[1]")};
    3833             : 
    3834           4 :         const GInt32 nStackCount = poERDMS->GetIntField("layerStackCount");
    3835           4 :         const GInt32 nStackIndex = poERDMS->GetIntField("layerStackIndex");
    3836             : 
    3837             :         // Update the filename.
    3838           4 :         if (strncmp(osFileName, pszOldBase, strlen(pszOldBase)) == 0)
    3839             :         {
    3840           4 :             std::string osNew = pszNewBase;
    3841           4 :             osNew += osFileName.c_str() + strlen(pszOldBase);
    3842           4 :             osFileName = std::move(osNew);
    3843             :         }
    3844             : 
    3845             :         // Grow the node if needed.
    3846           4 :         if (strlen(pszNewBase) > strlen(pszOldBase))
    3847             :         {
    3848           2 :             CPLDebug("HFA", "Growing ExternalRasterDMS to hold new names");
    3849           2 :             poERDMS->MakeData(
    3850           2 :                 static_cast<int>(poERDMS->GetDataSize() +
    3851           2 :                                  (strlen(pszNewBase) - strlen(pszOldBase))));
    3852             :         }
    3853             : 
    3854             :         // Initialize the whole thing to zeros for a clean start.
    3855           4 :         memset(poERDMS->GetData(), 0, poERDMS->GetDataSize());
    3856             : 
    3857             :         // Write it all out again, this may change the size of the node.
    3858           4 :         poERDMS->SetStringField("fileName.string", osFileName);
    3859           4 :         poERDMS->SetIntField("layerStackValidFlagsOffset[0]",
    3860             :                              anValidFlagsOffset[0]);
    3861           4 :         poERDMS->SetIntField("layerStackValidFlagsOffset[1]",
    3862             :                              anValidFlagsOffset[1]);
    3863             : 
    3864           4 :         poERDMS->SetIntField("layerStackDataOffset[0]", anStackDataOffset[0]);
    3865           4 :         poERDMS->SetIntField("layerStackDataOffset[1]", anStackDataOffset[1]);
    3866             : 
    3867           4 :         poERDMS->SetIntField("layerStackCount", nStackCount);
    3868           4 :         poERDMS->SetIntField("layerStackIndex", nStackIndex);
    3869             :     }
    3870             : 
    3871             :     // DependentFile.
    3872             :     apoNodeList =
    3873           4 :         hHFA->poRoot->FindChildren("DependentFile", "Eimg_DependentFile");
    3874             : 
    3875           6 :     for (size_t iNode = 0; iNode < apoNodeList.size(); iNode++)
    3876             :     {
    3877             :         CPLString osFileName =
    3878           4 :             apoNodeList[iNode]->GetStringField("dependent.string");
    3879             : 
    3880             :         // Grow the node if needed.
    3881           2 :         if (strlen(pszNewBase) > strlen(pszOldBase))
    3882             :         {
    3883           1 :             CPLDebug("HFA", "Growing DependentFile to hold new names");
    3884           1 :             apoNodeList[iNode]->MakeData(
    3885           1 :                 static_cast<int>(apoNodeList[iNode]->GetDataSize() +
    3886           1 :                                  (strlen(pszNewBase) - strlen(pszOldBase))));
    3887             :         }
    3888             : 
    3889             :         // Update the filename.
    3890           2 :         if (strncmp(osFileName, pszOldBase, strlen(pszOldBase)) == 0)
    3891             :         {
    3892           2 :             std::string osNew = pszNewBase;
    3893           2 :             osNew += (osFileName.c_str() + strlen(pszOldBase));
    3894           2 :             osFileName = std::move(osNew);
    3895             :         }
    3896             : 
    3897           2 :         apoNodeList[iNode]->SetStringField("dependent.string", osFileName);
    3898             :     }
    3899             : 
    3900           8 :     return CE_None;
    3901             : }
    3902             : 
    3903             : /* ==================================================================== */
    3904             : /*      Table relating USGS and ESRI state plane zones.                 */
    3905             : /* ==================================================================== */
    3906             : constexpr int anUsgsEsriZones[] = {
    3907             :     101,  3101, 102,  3126, 201,  3151, 202,  3176, 203,  3201, 301,  3226,
    3908             :     302,  3251, 401,  3276, 402,  3301, 403,  3326, 404,  3351, 405,  3376,
    3909             :     406,  3401, 407,  3426, 501,  3451, 502,  3476, 503,  3501, 600,  3526,
    3910             :     700,  3551, 901,  3601, 902,  3626, 903,  3576, 1001, 3651, 1002, 3676,
    3911             :     1101, 3701, 1102, 3726, 1103, 3751, 1201, 3776, 1202, 3801, 1301, 3826,
    3912             :     1302, 3851, 1401, 3876, 1402, 3901, 1501, 3926, 1502, 3951, 1601, 3976,
    3913             :     1602, 4001, 1701, 4026, 1702, 4051, 1703, 6426, 1801, 4076, 1802, 4101,
    3914             :     1900, 4126, 2001, 4151, 2002, 4176, 2101, 4201, 2102, 4226, 2103, 4251,
    3915             :     2111, 6351, 2112, 6376, 2113, 6401, 2201, 4276, 2202, 4301, 2203, 4326,
    3916             :     2301, 4351, 2302, 4376, 2401, 4401, 2402, 4426, 2403, 4451, 2500, 0,
    3917             :     2501, 4476, 2502, 4501, 2503, 4526, 2600, 0,    2601, 4551, 2602, 4576,
    3918             :     2701, 4601, 2702, 4626, 2703, 4651, 2800, 4676, 2900, 4701, 3001, 4726,
    3919             :     3002, 4751, 3003, 4776, 3101, 4801, 3102, 4826, 3103, 4851, 3104, 4876,
    3920             :     3200, 4901, 3301, 4926, 3302, 4951, 3401, 4976, 3402, 5001, 3501, 5026,
    3921             :     3502, 5051, 3601, 5076, 3602, 5101, 3701, 5126, 3702, 5151, 3800, 5176,
    3922             :     3900, 0,    3901, 5201, 3902, 5226, 4001, 5251, 4002, 5276, 4100, 5301,
    3923             :     4201, 5326, 4202, 5351, 4203, 5376, 4204, 5401, 4205, 5426, 4301, 5451,
    3924             :     4302, 5476, 4303, 5501, 4400, 5526, 4501, 5551, 4502, 5576, 4601, 5601,
    3925             :     4602, 5626, 4701, 5651, 4702, 5676, 4801, 5701, 4802, 5726, 4803, 5751,
    3926             :     4901, 5776, 4902, 5801, 4903, 5826, 4904, 5851, 5001, 6101, 5002, 6126,
    3927             :     5003, 6151, 5004, 6176, 5005, 6201, 5006, 6226, 5007, 6251, 5008, 6276,
    3928             :     5009, 6301, 5010, 6326, 5101, 5876, 5102, 5901, 5103, 5926, 5104, 5951,
    3929             :     5105, 5976, 5201, 6001, 5200, 6026, 5200, 6076, 5201, 6051, 5202, 6051,
    3930             :     5300, 0,    5400, 0};
    3931             : 
    3932             : /************************************************************************/
    3933             : /*                           ESRIToUSGSZone()                           */
    3934             : /*                                                                      */
    3935             : /*      Convert ESRI style state plane zones to USGS style state        */
    3936             : /*      plane zones.                                                    */
    3937             : /************************************************************************/
    3938             : 
    3939           3 : static int ESRIToUSGSZone(int nESRIZone)
    3940             : 
    3941             : {
    3942           3 :     if (nESRIZone == INT_MIN)
    3943           0 :         return 0;
    3944           3 :     if (nESRIZone < 0)
    3945           1 :         return std::abs(nESRIZone);
    3946             : 
    3947           2 :     const int nPairs = sizeof(anUsgsEsriZones) / (2 * sizeof(int));
    3948         212 :     for (int i = 0; i < nPairs; i++)
    3949             :     {
    3950         212 :         if (anUsgsEsriZones[i * 2 + 1] == nESRIZone)
    3951           2 :             return anUsgsEsriZones[i * 2];
    3952             :     }
    3953             : 
    3954           0 :     return 0;
    3955             : }
    3956             : 
    3957             : static const char *const apszDatumMap[] = {
    3958             :     // Imagine name, WKT name.
    3959             :     "NAD27",
    3960             :     "North_American_Datum_1927",
    3961             :     "NAD83",
    3962             :     "North_American_Datum_1983",
    3963             :     "WGS 84",
    3964             :     "WGS_1984",
    3965             :     "WGS 1972",
    3966             :     "WGS_1972",
    3967             :     "GDA94",
    3968             :     "Geocentric_Datum_of_Australia_1994",
    3969             :     "Pulkovo 1942",
    3970             :     "Pulkovo_1942",
    3971             :     "Geodetic Datum 1949",
    3972             :     "New_Zealand_Geodetic_Datum_1949",
    3973             :     nullptr,
    3974             :     nullptr};
    3975             : 
    3976         337 : const char *const *HFAGetDatumMap()
    3977             : {
    3978         337 :     return apszDatumMap;
    3979             : }
    3980             : 
    3981             : static const char *const apszUnitMap[] = {"meters",
    3982             :                                           "1.0",
    3983             :                                           "meter",
    3984             :                                           "1.0",
    3985             :                                           "m",
    3986             :                                           "1.0",
    3987             :                                           "centimeters",
    3988             :                                           "0.01",
    3989             :                                           "centimeter",
    3990             :                                           "0.01",
    3991             :                                           "cm",
    3992             :                                           "0.01",
    3993             :                                           "millimeters",
    3994             :                                           "0.001",
    3995             :                                           "millimeter",
    3996             :                                           "0.001",
    3997             :                                           "mm",
    3998             :                                           "0.001",
    3999             :                                           "kilometers",
    4000             :                                           "1000.0",
    4001             :                                           "kilometer",
    4002             :                                           "1000.0",
    4003             :                                           "km",
    4004             :                                           "1000.0",
    4005             :                                           "us_survey_feet",
    4006             :                                           "0.3048006096012192",
    4007             :                                           "us_survey_foot",
    4008             :                                           "0.3048006096012192",
    4009             :                                           "feet",
    4010             :                                           "0.3048006096012192",
    4011             :                                           "foot",
    4012             :                                           "0.3048006096012192",
    4013             :                                           "ft",
    4014             :                                           "0.3048006096012192",
    4015             :                                           "international_feet",
    4016             :                                           "0.3048",
    4017             :                                           "international_foot",
    4018             :                                           "0.3048",
    4019             :                                           "inches",
    4020             :                                           "0.0254000508001",
    4021             :                                           "inch",
    4022             :                                           "0.0254000508001",
    4023             :                                           "in",
    4024             :                                           "0.0254000508001",
    4025             :                                           "yards",
    4026             :                                           "0.9144",
    4027             :                                           "yard",
    4028             :                                           "0.9144",
    4029             :                                           "yd",
    4030             :                                           "0.9144",
    4031             :                                           "clarke_yard",
    4032             :                                           "0.9143917962",
    4033             :                                           "miles",
    4034             :                                           "1304.544",
    4035             :                                           "mile",
    4036             :                                           "1304.544",
    4037             :                                           "mi",
    4038             :                                           "1304.544",
    4039             :                                           "modified_american_feet",
    4040             :                                           "0.3048122530",
    4041             :                                           "modified_american_foot",
    4042             :                                           "0.3048122530",
    4043             :                                           "clarke_feet",
    4044             :                                           "0.3047972651",
    4045             :                                           "clarke_foot",
    4046             :                                           "0.3047972651",
    4047             :                                           "indian_feet",
    4048             :                                           "0.3047995142",
    4049             :                                           "indian_foot",
    4050             :                                           "0.3047995142",
    4051             :                                           nullptr,
    4052             :                                           nullptr};
    4053             : 
    4054         197 : const char *const *HFAGetUnitMap()
    4055             : {
    4056         197 :     return apszUnitMap;
    4057             : }
    4058             : 
    4059             : /************************************************************************/
    4060             : /*                          HFAPCSStructToOSR()                         */
    4061             : /*                                                                      */
    4062             : /*      Convert the datum, proparameters and mapinfo structures into    */
    4063             : /*      WKT format.                                                     */
    4064             : /************************************************************************/
    4065             : 
    4066             : std::unique_ptr<OGRSpatialReference>
    4067         210 : HFAPCSStructToOSR(const Eprj_Datum *psDatum, const Eprj_ProParameters *psPro,
    4068             :                   const Eprj_MapInfo *psMapInfo, HFAEntry *poMapInformation)
    4069             : 
    4070             : {
    4071             :     // General case for Erdas style projections.
    4072             : 
    4073             :     // We make a particular effort to adapt the mapinfo->proname as
    4074             :     // the PROJCS[] name per #2422.
    4075         420 :     auto poSRS = std::make_unique<OGRSpatialReference>();
    4076         210 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4077             : 
    4078         210 :     if (psPro == nullptr && psMapInfo != nullptr)
    4079             :     {
    4080           6 :         poSRS->SetLocalCS(psMapInfo->proName);
    4081             :     }
    4082         204 :     else if (psPro == nullptr)
    4083             :     {
    4084           0 :         return nullptr;
    4085             :     }
    4086         204 :     else if (psPro->proType == EPRJ_EXTERNAL)
    4087             :     {
    4088           4 :         if (EQUALN(psPro->proExeName, EPRJ_EXTERNAL_NZMG, 4))
    4089             :         {
    4090             :             // Handle New Zealand Map Grid (NZMG) external projection.  See:
    4091             :             // http://www.linz.govt.nz/
    4092             :             //
    4093             :             // Is there a better way that doesn't require hardcoding
    4094             :             // of these numbers?
    4095           4 :             poSRS->SetNZMG(-41.0, 173.0, 2510000, 6023150);
    4096             :         }
    4097             :         else
    4098             :         {
    4099           0 :             poSRS->SetLocalCS(psPro->proName);
    4100             :         }
    4101             :     }
    4102         200 :     else if (psPro->proNumber != EPRJ_LATLONG && psMapInfo != nullptr)
    4103             :     {
    4104         167 :         poSRS->SetProjCS(psMapInfo->proName);
    4105             :     }
    4106          33 :     else if (psPro->proNumber != EPRJ_LATLONG)
    4107             :     {
    4108           7 :         poSRS->SetProjCS(psPro->proName);
    4109             :     }
    4110             : 
    4111             :     // Handle units.  It is important to deal with this first so
    4112             :     // that the projection Set methods will automatically do
    4113             :     // translation of linear values (like false easting) to PROJCS
    4114             :     // units from meters.  Erdas linear projection values are
    4115             :     // always in meters.
    4116         210 :     if (poSRS->IsProjected() || poSRS->IsLocal())
    4117             :     {
    4118         184 :         const char *pszUnits = nullptr;
    4119             : 
    4120         184 :         if (psMapInfo)
    4121         177 :             pszUnits = psMapInfo->units;
    4122           7 :         else if (poMapInformation != nullptr)
    4123           6 :             pszUnits = poMapInformation->GetStringField("units.string");
    4124             : 
    4125         184 :         if (pszUnits != nullptr)
    4126             :         {
    4127         183 :             const char *const *papszUnitMap = HFAGetUnitMap();
    4128         183 :             int iUnitIndex = 0;  // Used after for.
    4129         304 :             for (; papszUnitMap[iUnitIndex] != nullptr; iUnitIndex += 2)
    4130             :             {
    4131         304 :                 if (EQUAL(papszUnitMap[iUnitIndex], pszUnits))
    4132         183 :                     break;
    4133             :             }
    4134             : 
    4135         183 :             if (papszUnitMap[iUnitIndex] == nullptr)
    4136           0 :                 iUnitIndex = 0;
    4137             : 
    4138         366 :             poSRS->SetLinearUnits(pszUnits,
    4139         183 :                                   CPLAtof(papszUnitMap[iUnitIndex + 1]));
    4140             :         }
    4141             :         else
    4142             :         {
    4143           1 :             poSRS->SetLinearUnits(SRS_UL_METER, 1.0);
    4144             :         }
    4145             :     }
    4146             : 
    4147         210 :     if (psPro == nullptr)
    4148             :     {
    4149           6 :         if (poSRS->IsLocal())
    4150             :         {
    4151           6 :             return poSRS;
    4152             :         }
    4153             :         else
    4154           0 :             return nullptr;
    4155             :     }
    4156             : 
    4157             :     // Try to work out ellipsoid and datum information.
    4158         204 :     const char *pszDatumName = psPro->proSpheroid.sphereName;
    4159         204 :     const char *pszEllipsoidName = psPro->proSpheroid.sphereName;
    4160             : 
    4161         204 :     if (psDatum != nullptr)
    4162             :     {
    4163         204 :         pszDatumName = psDatum->datumname;
    4164             : 
    4165             :         // Imagine to WKT translation.
    4166         204 :         const char *const *papszDatumMap = HFAGetDatumMap();
    4167         741 :         for (int i = 0; papszDatumMap[i] != nullptr; i += 2)
    4168             :         {
    4169         689 :             if (EQUAL(pszDatumName, papszDatumMap[i]))
    4170             :             {
    4171         152 :                 pszDatumName = papszDatumMap[i + 1];
    4172         152 :                 break;
    4173             :             }
    4174             :         }
    4175             :     }
    4176             : 
    4177         204 :     if (psPro->proSpheroid.a == 0.0)
    4178           0 :         ((Eprj_ProParameters *)psPro)->proSpheroid.a = 6378137.0;
    4179         204 :     if (psPro->proSpheroid.b == 0.0)
    4180           0 :         ((Eprj_ProParameters *)psPro)->proSpheroid.b = 6356752.3;
    4181             : 
    4182             :     const double dfInvFlattening =
    4183         204 :         OSRCalcInvFlattening(psPro->proSpheroid.a, psPro->proSpheroid.b);
    4184             : 
    4185             :     // Handle different projection methods.
    4186         204 :     switch (psPro->proNumber)
    4187             :     {
    4188          30 :         case EPRJ_LATLONG:
    4189          30 :             break;
    4190             : 
    4191          90 :         case EPRJ_UTM:
    4192             :             // We change this to unnamed so that SetUTM will set the long
    4193             :             // UTM description.
    4194          90 :             poSRS->SetProjCS("unnamed");
    4195          90 :             poSRS->SetUTM(psPro->proZone, psPro->proParams[3] >= 0.0);
    4196             : 
    4197             :             // The PCS name from the above function may be different with the
    4198             :             // input name.  If there is a PCS name in psMapInfo that is
    4199             :             // different with the one in psPro, just use it as the PCS name.
    4200             :             // This case happens if the dataset's SR was written by the new
    4201             :             // GDAL.
    4202          90 :             if (psMapInfo && strlen(psMapInfo->proName) > 0 &&
    4203          88 :                 strlen(psPro->proName) > 0 &&
    4204          88 :                 !EQUAL(psMapInfo->proName, psPro->proName))
    4205          63 :                 poSRS->SetProjCS(psMapInfo->proName);
    4206          90 :             break;
    4207             : 
    4208           3 :         case EPRJ_STATE_PLANE:
    4209             :         {
    4210           3 :             CPLString osUnitsName;
    4211             :             double dfLinearUnits;
    4212             :             {
    4213           3 :                 const char *pszUnitsName = nullptr;
    4214           3 :                 dfLinearUnits = poSRS->GetLinearUnits(&pszUnitsName);
    4215           3 :                 if (pszUnitsName)
    4216           3 :                     osUnitsName = pszUnitsName;
    4217             :             }
    4218             : 
    4219             :             // Historically, hfa used esri state plane zone code. Try esri pe
    4220             :             // string first.
    4221           3 :             const int zoneCode = ESRIToUSGSZone(psPro->proZone);
    4222             :             const char *pszDatum;
    4223           3 :             if (psDatum)
    4224           3 :                 pszDatum = psDatum->datumname;
    4225             :             else
    4226           0 :                 pszDatum = "HARN";
    4227             :             const char *pszUnits;
    4228           3 :             if (psMapInfo)
    4229           0 :                 pszUnits = psMapInfo->units;
    4230           3 :             else if (!osUnitsName.empty())
    4231           3 :                 pszUnits = osUnitsName;
    4232             :             else
    4233           0 :                 pszUnits = "meters";
    4234           3 :             const int proNu = psPro->proNumber;
    4235           3 :             if (poSRS->ImportFromESRIStatePlaneWKT(zoneCode, pszDatum, pszUnits,
    4236           3 :                                                    proNu) == OGRERR_NONE)
    4237             :             {
    4238           3 :                 poSRS->AutoIdentifyEPSG();
    4239             : 
    4240           3 :                 return poSRS;
    4241             :             }
    4242             : 
    4243             :             // Set state plane zone.  Set NAD83/27 on basis of spheroid.
    4244           0 :             poSRS->SetStatePlane(ESRIToUSGSZone(psPro->proZone),
    4245           0 :                                  fabs(psPro->proSpheroid.a - 6378137.0) < 1.0,
    4246           0 :                                  osUnitsName.empty() ? nullptr
    4247           0 :                                                      : osUnitsName.c_str(),
    4248             :                                  dfLinearUnits);
    4249             : 
    4250             :             // Same as the UTM, The following is needed.
    4251           0 :             if (psMapInfo && strlen(psMapInfo->proName) > 0 &&
    4252           0 :                 strlen(psPro->proName) > 0 &&
    4253           0 :                 !EQUAL(psMapInfo->proName, psPro->proName))
    4254           0 :                 poSRS->SetProjCS(psMapInfo->proName);
    4255             :         }
    4256           0 :         break;
    4257             : 
    4258           1 :         case EPRJ_ALBERS_CONIC_EQUAL_AREA:
    4259           1 :             poSRS->SetACEA(psPro->proParams[2] * R2D, psPro->proParams[3] * R2D,
    4260           1 :                            psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4261           1 :                            psPro->proParams[6], psPro->proParams[7]);
    4262           1 :             break;
    4263             : 
    4264           6 :         case EPRJ_LAMBERT_CONFORMAL_CONIC:
    4265             :             // Check the possible Wisconsin first.
    4266           6 :             if (psDatum && psMapInfo && EQUAL(psDatum->datumname, "HARN"))
    4267             :             {
    4268             :                 // ERO: I doubt this works. Wisconsin LCC is LCC_1SP whereas
    4269             :                 // we are here in the LCC_2SP case...
    4270           0 :                 if (poSRS->ImportFromESRIWisconsinWKT(
    4271           0 :                         "Lambert_Conformal_Conic", psPro->proParams[4] * R2D,
    4272           0 :                         psPro->proParams[5] * R2D,
    4273           0 :                         psMapInfo->units) == OGRERR_NONE)
    4274             :                 {
    4275           0 :                     return poSRS;
    4276             :                 }
    4277             :             }
    4278           6 :             poSRS->SetLCC(psPro->proParams[2] * R2D, psPro->proParams[3] * R2D,
    4279           6 :                           psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4280           6 :                           psPro->proParams[6], psPro->proParams[7]);
    4281           6 :             break;
    4282             : 
    4283           1 :         case EPRJ_MERCATOR:
    4284           1 :             poSRS->SetMercator(psPro->proParams[5] * R2D,
    4285           1 :                                psPro->proParams[4] * R2D, 1.0,
    4286           1 :                                psPro->proParams[6], psPro->proParams[7]);
    4287           1 :             break;
    4288             : 
    4289           2 :         case EPRJ_POLAR_STEREOGRAPHIC:
    4290           2 :             poSRS->SetPS(psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4291           2 :                          1.0, psPro->proParams[6], psPro->proParams[7]);
    4292           2 :             break;
    4293             : 
    4294           2 :         case EPRJ_POLYCONIC:
    4295           2 :             poSRS->SetPolyconic(psPro->proParams[5] * R2D,
    4296           2 :                                 psPro->proParams[4] * R2D, psPro->proParams[6],
    4297           2 :                                 psPro->proParams[7]);
    4298           2 :             break;
    4299             : 
    4300           1 :         case EPRJ_EQUIDISTANT_CONIC:
    4301             :         {
    4302           2 :             const double dfStdParallel2 = psPro->proParams[8] != 0.0
    4303           1 :                                               ? psPro->proParams[3] * R2D
    4304           0 :                                               : psPro->proParams[2] * R2D;
    4305           1 :             poSRS->SetEC(psPro->proParams[2] * R2D, dfStdParallel2,
    4306           1 :                          psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4307           1 :                          psPro->proParams[6], psPro->proParams[7]);
    4308           1 :             break;
    4309             :         }
    4310          18 :         case EPRJ_TRANSVERSE_MERCATOR:
    4311             :         case EPRJ_GAUSS_KRUGER:
    4312             :             // Check the possible Wisconsin first.
    4313          18 :             if (psDatum && psMapInfo && EQUAL(psDatum->datumname, "HARN"))
    4314             :             {
    4315           1 :                 if (poSRS->ImportFromESRIWisconsinWKT(
    4316           1 :                         "Transverse_Mercator", psPro->proParams[4] * R2D,
    4317           1 :                         psPro->proParams[5] * R2D,
    4318           1 :                         psMapInfo->units) == OGRERR_NONE)
    4319             :                 {
    4320           1 :                     return poSRS;
    4321             :                 }
    4322             :             }
    4323          17 :             poSRS->SetTM(psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4324          17 :                          psPro->proParams[2], psPro->proParams[6],
    4325          17 :                          psPro->proParams[7]);
    4326          17 :             break;
    4327             : 
    4328           0 :         case EPRJ_STEREOGRAPHIC:
    4329           0 :             poSRS->SetStereographic(psPro->proParams[5] * R2D,
    4330           0 :                                     psPro->proParams[4] * R2D, 1.0,
    4331           0 :                                     psPro->proParams[6], psPro->proParams[7]);
    4332           0 :             break;
    4333             : 
    4334           1 :         case EPRJ_LAMBERT_AZIMUTHAL_EQUAL_AREA:
    4335           1 :             poSRS->SetLAEA(psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4336           1 :                            psPro->proParams[6], psPro->proParams[7]);
    4337           1 :             break;
    4338             : 
    4339           1 :         case EPRJ_AZIMUTHAL_EQUIDISTANT:
    4340           1 :             poSRS->SetAE(psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4341           1 :                          psPro->proParams[6], psPro->proParams[7]);
    4342           1 :             break;
    4343             : 
    4344           1 :         case EPRJ_GNOMONIC:
    4345           1 :             poSRS->SetGnomonic(psPro->proParams[5] * R2D,
    4346           1 :                                psPro->proParams[4] * R2D, psPro->proParams[6],
    4347           1 :                                psPro->proParams[7]);
    4348           1 :             break;
    4349             : 
    4350           2 :         case EPRJ_ORTHOGRAPHIC:
    4351           2 :             poSRS->SetOrthographic(psPro->proParams[5] * R2D,
    4352           2 :                                    psPro->proParams[4] * R2D,
    4353           2 :                                    psPro->proParams[6], psPro->proParams[7]);
    4354           2 :             break;
    4355             : 
    4356           1 :         case EPRJ_SINUSOIDAL:
    4357           1 :             poSRS->SetSinusoidal(psPro->proParams[4] * R2D, psPro->proParams[6],
    4358           1 :                                  psPro->proParams[7]);
    4359           1 :             break;
    4360             : 
    4361           3 :         case EPRJ_PLATE_CARREE:
    4362             :         case EPRJ_EQUIRECTANGULAR:
    4363           3 :             poSRS->SetEquirectangular2(
    4364           3 :                 0.0, psPro->proParams[4] * R2D, psPro->proParams[5] * R2D,
    4365           3 :                 psPro->proParams[6], psPro->proParams[7]);
    4366           3 :             break;
    4367             : 
    4368           0 :         case EPRJ_EQUIDISTANT_CYLINDRICAL:
    4369           0 :             poSRS->SetEquirectangular2(
    4370           0 :                 0.0, psPro->proParams[4] * R2D, psPro->proParams[2] * R2D,
    4371           0 :                 psPro->proParams[6], psPro->proParams[7]);
    4372           0 :             break;
    4373             : 
    4374           1 :         case EPRJ_MILLER_CYLINDRICAL:
    4375           1 :             poSRS->SetMC(0.0, psPro->proParams[4] * R2D, psPro->proParams[6],
    4376           1 :                          psPro->proParams[7]);
    4377           1 :             break;
    4378             : 
    4379           1 :         case EPRJ_VANDERGRINTEN:
    4380           1 :             poSRS->SetVDG(psPro->proParams[4] * R2D, psPro->proParams[6],
    4381           1 :                           psPro->proParams[7]);
    4382           1 :             break;
    4383             : 
    4384           0 :         case EPRJ_HOTINE_OBLIQUE_MERCATOR:
    4385           0 :             if (psPro->proParams[12] > 0.0)
    4386           0 :                 poSRS->SetHOM(
    4387           0 :                     psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4388           0 :                     psPro->proParams[3] * R2D, 0.0, psPro->proParams[2],
    4389           0 :                     psPro->proParams[6], psPro->proParams[7]);
    4390           0 :             break;
    4391             : 
    4392           2 :         case EPRJ_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER:
    4393           2 :             poSRS->SetHOMAC(
    4394           2 :                 psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4395           2 :                 psPro->proParams[3] * R2D,
    4396           2 :                 psPro->proParams[3] *
    4397             :                     R2D,  // We reuse azimuth as rectified_grid_angle
    4398           2 :                 psPro->proParams[2], psPro->proParams[6], psPro->proParams[7]);
    4399           2 :             break;
    4400             : 
    4401           1 :         case EPRJ_ROBINSON:
    4402           1 :             poSRS->SetRobinson(psPro->proParams[4] * R2D, psPro->proParams[6],
    4403           1 :                                psPro->proParams[7]);
    4404           1 :             break;
    4405             : 
    4406           1 :         case EPRJ_MOLLWEIDE:
    4407           1 :             poSRS->SetMollweide(psPro->proParams[4] * R2D, psPro->proParams[6],
    4408           1 :                                 psPro->proParams[7]);
    4409           1 :             break;
    4410             : 
    4411           1 :         case EPRJ_GALL_STEREOGRAPHIC:
    4412           1 :             poSRS->SetGS(psPro->proParams[4] * R2D, psPro->proParams[6],
    4413           1 :                          psPro->proParams[7]);
    4414           1 :             break;
    4415             : 
    4416           1 :         case EPRJ_ECKERT_I:
    4417           1 :             poSRS->SetEckert(1, psPro->proParams[4] * R2D, psPro->proParams[6],
    4418           1 :                              psPro->proParams[7]);
    4419           1 :             break;
    4420             : 
    4421           1 :         case EPRJ_ECKERT_II:
    4422           1 :             poSRS->SetEckert(2, psPro->proParams[4] * R2D, psPro->proParams[6],
    4423           1 :                              psPro->proParams[7]);
    4424           1 :             break;
    4425             : 
    4426           1 :         case EPRJ_ECKERT_III:
    4427           1 :             poSRS->SetEckert(3, psPro->proParams[4] * R2D, psPro->proParams[6],
    4428           1 :                              psPro->proParams[7]);
    4429           1 :             break;
    4430             : 
    4431           1 :         case EPRJ_ECKERT_IV:
    4432           1 :             poSRS->SetEckert(4, psPro->proParams[4] * R2D, psPro->proParams[6],
    4433           1 :                              psPro->proParams[7]);
    4434           1 :             break;
    4435             : 
    4436           1 :         case EPRJ_ECKERT_V:
    4437           1 :             poSRS->SetEckert(5, psPro->proParams[4] * R2D, psPro->proParams[6],
    4438           1 :                              psPro->proParams[7]);
    4439           1 :             break;
    4440             : 
    4441           1 :         case EPRJ_ECKERT_VI:
    4442           1 :             poSRS->SetEckert(6, psPro->proParams[4] * R2D, psPro->proParams[6],
    4443           1 :                              psPro->proParams[7]);
    4444           1 :             break;
    4445             : 
    4446           2 :         case EPRJ_CASSINI:
    4447           2 :             poSRS->SetCS(psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4448           2 :                          psPro->proParams[6], psPro->proParams[7]);
    4449           2 :             break;
    4450             : 
    4451           1 :         case EPRJ_TWO_POINT_EQUIDISTANT:
    4452           1 :             poSRS->SetTPED(psPro->proParams[9] * R2D, psPro->proParams[8] * R2D,
    4453           1 :                            psPro->proParams[11] * R2D,
    4454           1 :                            psPro->proParams[10] * R2D, psPro->proParams[6],
    4455           1 :                            psPro->proParams[7]);
    4456           1 :             break;
    4457             : 
    4458           0 :         case EPRJ_STEREOGRAPHIC_EXTENDED:
    4459           0 :             poSRS->SetStereographic(
    4460           0 :                 psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4461           0 :                 psPro->proParams[2], psPro->proParams[6], psPro->proParams[7]);
    4462           0 :             break;
    4463             : 
    4464           1 :         case EPRJ_BONNE:
    4465           1 :             poSRS->SetBonne(psPro->proParams[2] * R2D,
    4466           1 :                             psPro->proParams[4] * R2D, psPro->proParams[6],
    4467           1 :                             psPro->proParams[7]);
    4468           1 :             break;
    4469             : 
    4470           1 :         case EPRJ_LOXIMUTHAL:
    4471             :         {
    4472           1 :             poSRS->SetProjection("Loximuthal");
    4473           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4474           1 :                                    psPro->proParams[4] * R2D);
    4475           1 :             poSRS->SetNormProjParm("central_parallel",
    4476           1 :                                    psPro->proParams[5] * R2D);
    4477           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4478           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4479             :         }
    4480           1 :         break;
    4481             : 
    4482           1 :         case EPRJ_QUARTIC_AUTHALIC:
    4483             :         {
    4484           1 :             poSRS->SetProjection("Quartic_Authalic");
    4485           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4486           1 :                                    psPro->proParams[4] * R2D);
    4487           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4488           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4489             :         }
    4490           1 :         break;
    4491             : 
    4492           1 :         case EPRJ_WINKEL_I:
    4493             :         {
    4494           1 :             poSRS->SetProjection("Winkel_I");
    4495           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4496           1 :                                    psPro->proParams[4] * R2D);
    4497           1 :             poSRS->SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,
    4498           1 :                                    psPro->proParams[2] * R2D);
    4499           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4500           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4501             :         }
    4502           1 :         break;
    4503             : 
    4504           1 :         case EPRJ_WINKEL_II:
    4505             :         {
    4506           1 :             poSRS->SetProjection("Winkel_II");
    4507           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4508           1 :                                    psPro->proParams[4] * R2D);
    4509           1 :             poSRS->SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,
    4510           1 :                                    psPro->proParams[2] * R2D);
    4511           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4512           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4513             :         }
    4514           1 :         break;
    4515             : 
    4516           0 :         case EPRJ_BEHRMANN:
    4517             :         {
    4518           0 :             poSRS->SetProjection("Behrmann");
    4519           0 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4520           0 :                                    psPro->proParams[4] * R2D);
    4521           0 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4522           0 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4523             :         }
    4524           0 :         break;
    4525             : 
    4526           1 :         case EPRJ_KROVAK:
    4527           1 :             poSRS->SetKrovak(
    4528           1 :                 psPro->proParams[4] * R2D, psPro->proParams[5] * R2D,
    4529           1 :                 psPro->proParams[3] * R2D, psPro->proParams[9] * R2D,
    4530           1 :                 psPro->proParams[2], psPro->proParams[6], psPro->proParams[7]);
    4531           1 :             break;
    4532             : 
    4533           0 :         case EPRJ_DOUBLE_STEREOGRAPHIC:
    4534             :         {
    4535           0 :             poSRS->SetProjection("Double_Stereographic");
    4536           0 :             poSRS->SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,
    4537           0 :                                    psPro->proParams[5] * R2D);
    4538           0 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4539           0 :                                    psPro->proParams[4] * R2D);
    4540           0 :             poSRS->SetNormProjParm(SRS_PP_SCALE_FACTOR, psPro->proParams[2]);
    4541           0 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4542           0 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4543             :         }
    4544           0 :         break;
    4545             : 
    4546           1 :         case EPRJ_AITOFF:
    4547             :         {
    4548           1 :             poSRS->SetProjection("Aitoff");
    4549           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4550           1 :                                    psPro->proParams[4] * R2D);
    4551           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4552           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4553             :         }
    4554           1 :         break;
    4555             : 
    4556           1 :         case EPRJ_CRASTER_PARABOLIC:
    4557             :         {
    4558           1 :             poSRS->SetProjection("Craster_Parabolic");
    4559           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4560           1 :                                    psPro->proParams[4] * R2D);
    4561           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4562           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4563             :         }
    4564           1 :         break;
    4565             : 
    4566           1 :         case EPRJ_CYLINDRICAL_EQUAL_AREA:
    4567           1 :             poSRS->SetCEA(psPro->proParams[2] * R2D, psPro->proParams[4] * R2D,
    4568           1 :                           psPro->proParams[6], psPro->proParams[7]);
    4569           1 :             break;
    4570             : 
    4571           1 :         case EPRJ_FLAT_POLAR_QUARTIC:
    4572             :         {
    4573           1 :             poSRS->SetProjection("Flat_Polar_Quartic");
    4574           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4575           1 :                                    psPro->proParams[4] * R2D);
    4576           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4577           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4578             :         }
    4579           1 :         break;
    4580             : 
    4581           1 :         case EPRJ_TIMES:
    4582             :         {
    4583           1 :             poSRS->SetProjection("Times");
    4584           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4585           1 :                                    psPro->proParams[4] * R2D);
    4586           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4587           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4588             :         }
    4589           1 :         break;
    4590             : 
    4591           1 :         case EPRJ_WINKEL_TRIPEL:
    4592             :         {
    4593           1 :             poSRS->SetProjection("Winkel_Tripel");
    4594           1 :             poSRS->SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,
    4595           1 :                                    psPro->proParams[2] * R2D);
    4596           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4597           1 :                                    psPro->proParams[4] * R2D);
    4598           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4599           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4600             :         }
    4601           1 :         break;
    4602             : 
    4603           1 :         case EPRJ_HAMMER_AITOFF:
    4604             :         {
    4605           1 :             poSRS->SetProjection("Hammer_Aitoff");
    4606           1 :             poSRS->SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,
    4607           1 :                                    psPro->proParams[4] * R2D);
    4608           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4609           1 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4610             :         }
    4611           1 :         break;
    4612             : 
    4613           1 :         case EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE:
    4614             :         {
    4615           1 :             poSRS->SetVerticalPerspective(
    4616           1 :                 psPro->proParams[5] * R2D,  // dfTopoOriginLat
    4617           1 :                 psPro->proParams[4] * R2D,  // dfTopoOriginLon
    4618             :                 0,                          // dfTopoOriginHeight
    4619           1 :                 psPro->proParams[2],        // dfViewPointHeight
    4620           1 :                 psPro->proParams[6],        // dfFalseEasting
    4621           1 :                 psPro->proParams[7]);       // dfFalseNorthing
    4622             :         }
    4623           1 :         break;
    4624             : 
    4625           0 :         case EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_CENTER:
    4626             :         {
    4627           0 :             poSRS->SetProjection("Hotine_Oblique_Mercator_Twp_Point_Center");
    4628           0 :             poSRS->SetNormProjParm(SRS_PP_LATITUDE_OF_CENTER,
    4629           0 :                                    psPro->proParams[5] * R2D);
    4630           0 :             poSRS->SetNormProjParm(SRS_PP_LATITUDE_OF_1ST_POINT,
    4631           0 :                                    psPro->proParams[9] * R2D);
    4632           0 :             poSRS->SetNormProjParm(SRS_PP_LONGITUDE_OF_1ST_POINT,
    4633           0 :                                    psPro->proParams[8] * R2D);
    4634           0 :             poSRS->SetNormProjParm(SRS_PP_LATITUDE_OF_2ND_POINT,
    4635           0 :                                    psPro->proParams[11] * R2D);
    4636           0 :             poSRS->SetNormProjParm(SRS_PP_LONGITUDE_OF_2ND_POINT,
    4637           0 :                                    psPro->proParams[10] * R2D);
    4638           0 :             poSRS->SetNormProjParm(SRS_PP_SCALE_FACTOR, psPro->proParams[2]);
    4639           0 :             poSRS->SetNormProjParm(SRS_PP_FALSE_EASTING, psPro->proParams[6]);
    4640           0 :             poSRS->SetNormProjParm(SRS_PP_FALSE_NORTHING, psPro->proParams[7]);
    4641             :         }
    4642           0 :         break;
    4643             : 
    4644           1 :         case EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN:
    4645           1 :             poSRS->SetHOM2PNO(
    4646           1 :                 psPro->proParams[5] * R2D, psPro->proParams[8] * R2D,
    4647           1 :                 psPro->proParams[9] * R2D, psPro->proParams[10] * R2D,
    4648           1 :                 psPro->proParams[11] * R2D, psPro->proParams[2],
    4649           1 :                 psPro->proParams[6], psPro->proParams[7]);
    4650           1 :             break;
    4651             : 
    4652           0 :         case EPRJ_LAMBERT_CONFORMAL_CONIC_1SP:
    4653           0 :             poSRS->SetLCC1SP(psPro->proParams[3] * R2D,
    4654           0 :                              psPro->proParams[2] * R2D, psPro->proParams[4],
    4655           0 :                              psPro->proParams[5], psPro->proParams[6]);
    4656           0 :             break;
    4657             : 
    4658           2 :         case EPRJ_MERCATOR_VARIANT_A:
    4659           2 :             poSRS->SetMercator(psPro->proParams[5] * R2D,
    4660           2 :                                psPro->proParams[4] * R2D, psPro->proParams[2],
    4661           2 :                                psPro->proParams[6], psPro->proParams[7]);
    4662           2 :             break;
    4663             : 
    4664           0 :         case EPRJ_PSEUDO_MERCATOR:  // Likely this is google mercator?
    4665           0 :             poSRS->SetMercator(psPro->proParams[5] * R2D,
    4666           0 :                                psPro->proParams[4] * R2D, 1.0,
    4667           0 :                                psPro->proParams[6], psPro->proParams[7]);
    4668           0 :             break;
    4669             : 
    4670           5 :         case EPRJ_HOTINE_OBLIQUE_MERCATOR_VARIANT_A:
    4671           5 :             poSRS->SetHOM(psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4672           5 :                           psPro->proParams[3] * R2D, psPro->proParams[8] * R2D,
    4673           5 :                           psPro->proParams[2], psPro->proParams[6],
    4674           5 :                           psPro->proParams[7]);
    4675           5 :             break;
    4676             : 
    4677           3 :         case EPRJ_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED:
    4678           3 :             poSRS->SetTMSO(psPro->proParams[5] * R2D, psPro->proParams[4] * R2D,
    4679           3 :                            psPro->proParams[2], psPro->proParams[6],
    4680           3 :                            psPro->proParams[7]);
    4681           3 :             break;
    4682             : 
    4683           0 :         default:
    4684           0 :             if (poSRS->IsProjected())
    4685           0 :                 poSRS->GetRoot()->SetValue("LOCAL_CS");
    4686             :             else
    4687           0 :                 poSRS->SetLocalCS(psPro->proName);
    4688           0 :             break;
    4689             :     }
    4690             : 
    4691             :     // Try and set the GeogCS information.
    4692         200 :     if (!poSRS->IsLocal())
    4693             :     {
    4694         200 :         bool bWellKnownDatum = false;
    4695         200 :         if (pszDatumName == nullptr)
    4696           0 :             poSRS->SetGeogCS(pszDatumName, pszDatumName, pszEllipsoidName,
    4697           0 :                              psPro->proSpheroid.a, dfInvFlattening);
    4698         200 :         else if (EQUAL(pszDatumName, "WGS 84") ||
    4699         200 :                  EQUAL(pszDatumName, "WGS_1984"))
    4700             :         {
    4701          36 :             bWellKnownDatum = true;
    4702          36 :             poSRS->SetWellKnownGeogCS("WGS84");
    4703             :         }
    4704         164 :         else if (strstr(pszDatumName, "NAD27") != nullptr ||
    4705         162 :                  EQUAL(pszDatumName, "North_American_Datum_1927"))
    4706             :         {
    4707          88 :             bWellKnownDatum = true;
    4708          88 :             poSRS->SetWellKnownGeogCS("NAD27");
    4709             :         }
    4710          76 :         else if (EQUAL(pszDatumName, "NAD83") ||
    4711          76 :                  EQUAL(pszDatumName, "North_American_Datum_1983"))
    4712             :         {
    4713           7 :             bWellKnownDatum = true;
    4714           7 :             poSRS->SetWellKnownGeogCS("NAD83");
    4715             :         }
    4716             :         else
    4717             :         {
    4718         138 :             CPLString osGeogCRSName(pszDatumName);
    4719             : 
    4720          69 :             if (poSRS->IsProjected())
    4721             :             {
    4722          68 :                 PJ_CONTEXT *ctxt = OSRGetProjTLSContext();
    4723          68 :                 const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
    4724             :                 PJ_OBJ_LIST *list =
    4725          68 :                     proj_create_from_name(ctxt, nullptr, poSRS->GetName(),
    4726             :                                           &type, 1, false, 1, nullptr);
    4727          68 :                 if (list)
    4728             :                 {
    4729          68 :                     const auto listSize = proj_list_get_count(list);
    4730          68 :                     if (listSize == 1)
    4731             :                     {
    4732          43 :                         auto crs = proj_list_get(ctxt, list, 0);
    4733          43 :                         if (crs)
    4734             :                         {
    4735          43 :                             auto geogCRS = proj_crs_get_geodetic_crs(ctxt, crs);
    4736          43 :                             if (geogCRS)
    4737             :                             {
    4738          43 :                                 const char *pszName = proj_get_name(geogCRS);
    4739          43 :                                 if (pszName)
    4740          43 :                                     osGeogCRSName = pszName;
    4741          43 :                                 proj_destroy(geogCRS);
    4742             :                             }
    4743          43 :                             proj_destroy(crs);
    4744             :                         }
    4745             :                     }
    4746          68 :                     proj_list_destroy(list);
    4747             :                 }
    4748             :             }
    4749             : 
    4750         138 :             poSRS->SetGeogCS(osGeogCRSName, pszDatumName, pszEllipsoidName,
    4751          69 :                              psPro->proSpheroid.a, dfInvFlattening);
    4752             :         }
    4753             : 
    4754         200 :         if (psDatum != nullptr && psDatum->type == EPRJ_DATUM_PARAMETRIC)
    4755             :         {
    4756         159 :             if (bWellKnownDatum &&
    4757          45 :                 CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
    4758             :             {
    4759          45 :                 CPLDebug("OSR",
    4760             :                          "TOWGS84 information has been removed. "
    4761             :                          "It can be kept by setting the OSR_STRIP_TOWGS84 "
    4762             :                          "configuration option to NO");
    4763             :             }
    4764             :             else
    4765             :             {
    4766          69 :                 poSRS->SetTOWGS84(psDatum->params[0], psDatum->params[1],
    4767          69 :                                   psDatum->params[2],
    4768          69 :                                   -psDatum->params[3] * RAD2ARCSEC,
    4769          69 :                                   -psDatum->params[4] * RAD2ARCSEC,
    4770          69 :                                   -psDatum->params[5] * RAD2ARCSEC,
    4771          69 :                                   psDatum->params[6] * 1e+6);
    4772          69 :                 poSRS->StripTOWGS84IfKnownDatumAndAllowed();
    4773             :             }
    4774             :         }
    4775             :     }
    4776             : 
    4777             :     // Try to insert authority information if possible.
    4778         200 :     poSRS->AutoIdentifyEPSG();
    4779             : 
    4780         200 :     auto poSRSBestMatch = poSRS->FindBestMatch(90, nullptr, nullptr);
    4781         200 :     if (poSRSBestMatch)
    4782             :     {
    4783         137 :         poSRS.reset(poSRSBestMatch);
    4784             :     }
    4785             : 
    4786         200 :     return poSRS;
    4787             : }

Generated by: LCOV version 1.14