LCOV - code coverage report
Current view: top level - frmts/hfa - hfadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2036 2579 78.9 %
Date: 2025-09-10 17:48:50 Functions: 85 87 97.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     hfadataset.cpp
       4             :  * Project:  Erdas Imagine Driver
       5             :  * Purpose:  Main driver for Erdas Imagine format.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Frank Warmerdam
      10             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "hfadataset.h"
      17             : #include "hfa_p.h"
      18             : 
      19             : #include <cassert>
      20             : #include <climits>
      21             : #include <cmath>
      22             : #include <cstddef>
      23             : #include <cstdio>
      24             : #include <cstdlib>
      25             : #include <cstring>
      26             : #include <algorithm>
      27             : #include <limits>
      28             : #include <memory>
      29             : #include <string>
      30             : #include <vector>
      31             : 
      32             : #include "cpl_conv.h"
      33             : #include "cpl_error.h"
      34             : #include "cpl_minixml.h"
      35             : #include "cpl_progress.h"
      36             : #include "cpl_string.h"
      37             : #include "cpl_vsi.h"
      38             : #include "gdal.h"
      39             : #include "gdal_frmts.h"
      40             : #include "gdal_pam.h"
      41             : #include "gdal_priv.h"
      42             : #include "gdal_rat.h"
      43             : #include "hfa.h"
      44             : #include "ogr_core.h"
      45             : #include "ogr_spatialref.h"
      46             : #include "ogr_srs_api.h"
      47             : 
      48             : constexpr double D2R = M_PI / 180.0;
      49             : 
      50             : constexpr double ARCSEC2RAD = M_PI / 648000.0;
      51             : 
      52             : int WritePeStringIfNeeded(const OGRSpatialReference *poSRS, HFAHandle hHFA);
      53             : void ClearSR(HFAHandle hHFA);
      54             : 
      55             : /************************************************************************/
      56             : /*                     HFARasterAttributeTable()                        */
      57             : /************************************************************************/
      58             : 
      59         624 : HFARasterAttributeTable::HFARasterAttributeTable(HFARasterBand *poBand,
      60         624 :                                                  const char *pszName)
      61         624 :     : hHFA(poBand->hHFA),
      62        1248 :       poDT(poBand->hHFA->papoBand[poBand->nBand - 1]->poNode->GetNamedChild(
      63             :           pszName)),
      64        1248 :       osName(pszName), nBand(poBand->nBand), eAccess(poBand->GetAccess()),
      65             :       nRows(0), bLinearBinning(false), dfRow0Min(0.0), dfBinSize(0.0),
      66         624 :       eTableType(GRTT_THEMATIC)
      67             : {
      68         624 :     if (poDT != nullptr)
      69             :     {
      70         105 :         nRows = std::max(0, poDT->GetIntField("numRows"));
      71             : 
      72             :         // Scan under table for columns.
      73         447 :         for (HFAEntry *poDTChild = poDT->GetChild(); poDTChild != nullptr;
      74         342 :              poDTChild = poDTChild->GetNext())
      75             :         {
      76         342 :             if (EQUAL(poDTChild->GetType(), "Edsc_BinFunction"))
      77             :             {
      78          73 :                 const double dfMax = poDTChild->GetDoubleField("maxLimit");
      79          73 :                 const double dfMin = poDTChild->GetDoubleField("minLimit");
      80          73 :                 const int nBinCount = poDTChild->GetIntField("numBins");
      81             : 
      82          73 :                 if (nBinCount == nRows && dfMax != dfMin && nBinCount > 1)
      83             :                 {
      84             :                     // Can't call SetLinearBinning since it will re-write
      85             :                     // which we might not have permission to do.
      86          69 :                     bLinearBinning = true;
      87          69 :                     dfRow0Min = dfMin;
      88          69 :                     dfBinSize = (dfMax - dfMin) / (nBinCount - 1);
      89             :                 }
      90             :             }
      91             : 
      92         342 :             if (EQUAL(poDTChild->GetType(), "Edsc_BinFunction840"))
      93             :             {
      94             :                 const char *pszValue =
      95          13 :                     poDTChild->GetStringField("binFunction.type.string");
      96          13 :                 if (pszValue && EQUAL(pszValue, "BFUnique"))
      97             :                 {
      98          13 :                     AddColumn("BinValues", GFT_Real, GFU_MinMax, 0, 0,
      99             :                               poDTChild, true);
     100             :                 }
     101             :             }
     102             : 
     103         342 :             if (!EQUAL(poDTChild->GetType(), "Edsc_Column"))
     104          86 :                 continue;
     105             : 
     106         256 :             const int nOffset = poDTChild->GetIntField("columnDataPtr");
     107         256 :             const char *pszType = poDTChild->GetStringField("dataType");
     108         256 :             GDALRATFieldUsage eUsage = GFU_Generic;
     109         256 :             bool bConvertColors = false;
     110             : 
     111         256 :             if (pszType == nullptr || nOffset == 0)
     112           0 :                 continue;
     113             : 
     114             :             GDALRATFieldType eType;
     115         256 :             if (EQUAL(pszType, "real"))
     116         170 :                 eType = GFT_Real;
     117          86 :             else if (EQUAL(pszType, "string"))
     118          42 :                 eType = GFT_String;
     119          44 :             else if (STARTS_WITH_CI(pszType, "int"))
     120          44 :                 eType = GFT_Integer;
     121             :             else
     122           0 :                 continue;
     123             : 
     124         256 :             if (EQUAL(poDTChild->GetName(), "Histogram"))
     125          83 :                 eUsage = GFU_PixelCount;
     126         173 :             else if (EQUAL(poDTChild->GetName(), "Red"))
     127             :             {
     128          11 :                 eUsage = GFU_Red;
     129             :                 // Treat color columns as ints regardless
     130             :                 // of how they are stored.
     131          11 :                 bConvertColors = eType == GFT_Real;
     132          11 :                 eType = GFT_Integer;
     133             :             }
     134         162 :             else if (EQUAL(poDTChild->GetName(), "Green"))
     135             :             {
     136          11 :                 eUsage = GFU_Green;
     137          11 :                 bConvertColors = eType == GFT_Real;
     138          11 :                 eType = GFT_Integer;
     139             :             }
     140         151 :             else if (EQUAL(poDTChild->GetName(), "Blue"))
     141             :             {
     142          11 :                 eUsage = GFU_Blue;
     143          11 :                 bConvertColors = eType == GFT_Real;
     144          11 :                 eType = GFT_Integer;
     145             :             }
     146         140 :             else if (EQUAL(poDTChild->GetName(), "Opacity"))
     147             :             {
     148          11 :                 eUsage = GFU_Alpha;
     149          11 :                 bConvertColors = eType == GFT_Real;
     150          11 :                 eType = GFT_Integer;
     151             :             }
     152         129 :             else if (EQUAL(poDTChild->GetName(), "Class_Names"))
     153           0 :                 eUsage = GFU_Name;
     154             : 
     155         256 :             if (eType == GFT_Real)
     156             :             {
     157         126 :                 AddColumn(poDTChild->GetName(), GFT_Real, eUsage, nOffset,
     158             :                           sizeof(double), poDTChild);
     159             :             }
     160         130 :             else if (eType == GFT_String)
     161             :             {
     162          42 :                 int nMaxNumChars = poDTChild->GetIntField("maxNumChars");
     163          42 :                 if (nMaxNumChars <= 0)
     164             :                 {
     165           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     166             :                              "Invalid nMaxNumChars = %d for column %s",
     167             :                              nMaxNumChars, poDTChild->GetName());
     168           0 :                     nMaxNumChars = 1;
     169             :                 }
     170          42 :                 AddColumn(poDTChild->GetName(), GFT_String, eUsage, nOffset,
     171             :                           nMaxNumChars, poDTChild);
     172             :             }
     173          88 :             else if (eType == GFT_Integer)
     174             :             {
     175          88 :                 int nSize = sizeof(GInt32);
     176          88 :                 if (bConvertColors)
     177          44 :                     nSize = sizeof(double);
     178          88 :                 AddColumn(poDTChild->GetName(), GFT_Integer, eUsage, nOffset,
     179             :                           nSize, poDTChild, false, bConvertColors);
     180             :             }
     181             :         }
     182             :     }
     183         624 : }
     184             : 
     185             : /************************************************************************/
     186             : /*                    ~HFARasterAttributeTable()                        */
     187             : /************************************************************************/
     188             : 
     189        1248 : HFARasterAttributeTable::~HFARasterAttributeTable()
     190             : {
     191        1248 : }
     192             : 
     193             : /************************************************************************/
     194             : /*                              Clone()                                 */
     195             : /************************************************************************/
     196             : 
     197          17 : GDALRasterAttributeTable *HFARasterAttributeTable::Clone() const
     198             : {
     199          17 :     if (nRows > 0 && GetColumnCount() > RAT_MAX_ELEM_FOR_CLONE / nRows)
     200           0 :         return nullptr;
     201             : 
     202             :     GDALDefaultRasterAttributeTable *poRAT =
     203          17 :         new GDALDefaultRasterAttributeTable();
     204             : 
     205          44 :     for (int iCol = 0; iCol < static_cast<int>(aoFields.size()); iCol++)
     206             :     {
     207          27 :         poRAT->CreateColumn(aoFields[iCol].sName, aoFields[iCol].eType,
     208          27 :                             aoFields[iCol].eUsage);
     209          27 :         poRAT->SetRowCount(nRows);
     210             : 
     211          27 :         if (aoFields[iCol].eType == GFT_Integer)
     212             :         {
     213             :             int *panColData =
     214           5 :                 static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nRows));
     215           5 :             if (panColData == nullptr)
     216             :             {
     217           0 :                 delete poRAT;
     218           0 :                 return nullptr;
     219             :             }
     220             : 
     221          10 :             if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
     222           5 :                     GF_Read, iCol, 0, nRows, panColData) != CE_None)
     223             :             {
     224           0 :                 CPLFree(panColData);
     225           0 :                 delete poRAT;
     226           0 :                 return nullptr;
     227             :             }
     228             : 
     229         501 :             for (int iRow = 0; iRow < nRows; iRow++)
     230             :             {
     231         496 :                 poRAT->SetValue(iRow, iCol, panColData[iRow]);
     232             :             }
     233           5 :             CPLFree(panColData);
     234             :         }
     235          27 :         if (aoFields[iCol].eType == GFT_Real)
     236             :         {
     237             :             double *padfColData = static_cast<double *>(
     238          17 :                 VSI_MALLOC2_VERBOSE(sizeof(double), nRows));
     239          17 :             if (padfColData == nullptr)
     240             :             {
     241           0 :                 delete poRAT;
     242           0 :                 return nullptr;
     243             :             }
     244             : 
     245          34 :             if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
     246          17 :                     GF_Read, iCol, 0, nRows, padfColData) != CE_None)
     247             :             {
     248           0 :                 CPLFree(padfColData);
     249           0 :                 delete poRAT;
     250           0 :                 return nullptr;
     251             :             }
     252             : 
     253        3509 :             for (int iRow = 0; iRow < nRows; iRow++)
     254             :             {
     255        3492 :                 poRAT->SetValue(iRow, iCol, padfColData[iRow]);
     256             :             }
     257          17 :             CPLFree(padfColData);
     258             :         }
     259          27 :         if (aoFields[iCol].eType == GFT_String)
     260             :         {
     261             :             char **papszColData = static_cast<char **>(
     262           5 :                 VSI_MALLOC2_VERBOSE(sizeof(char *), nRows));
     263           5 :             if (papszColData == nullptr)
     264             :             {
     265           0 :                 delete poRAT;
     266           0 :                 return nullptr;
     267             :             }
     268             : 
     269          10 :             if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
     270           5 :                     GF_Read, iCol, 0, nRows, papszColData) != CE_None)
     271             :             {
     272           0 :                 CPLFree(papszColData);
     273           0 :                 delete poRAT;
     274           0 :                 return nullptr;
     275             :             }
     276             : 
     277         501 :             for (int iRow = 0; iRow < nRows; iRow++)
     278             :             {
     279         496 :                 poRAT->SetValue(iRow, iCol, papszColData[iRow]);
     280         496 :                 CPLFree(papszColData[iRow]);
     281             :             }
     282           5 :             CPLFree(papszColData);
     283             :         }
     284             :     }
     285             : 
     286          17 :     if (bLinearBinning)
     287          11 :         poRAT->SetLinearBinning(dfRow0Min, dfBinSize);
     288             : 
     289          17 :     poRAT->SetTableType(this->GetTableType());
     290             : 
     291          17 :     return poRAT;
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                          GetColumnCount()                            */
     296             : /************************************************************************/
     297             : 
     298          39 : int HFARasterAttributeTable::GetColumnCount() const
     299             : {
     300          39 :     return static_cast<int>(aoFields.size());
     301             : }
     302             : 
     303             : /************************************************************************/
     304             : /*                          GetNameOfCol()                              */
     305             : /************************************************************************/
     306             : 
     307           9 : const char *HFARasterAttributeTable::GetNameOfCol(int nCol) const
     308             : {
     309           9 :     if (nCol < 0 || nCol >= static_cast<int>(aoFields.size()))
     310           0 :         return nullptr;
     311             : 
     312           9 :     return aoFields[nCol].sName;
     313             : }
     314             : 
     315             : /************************************************************************/
     316             : /*                          GetUsageOfCol()                             */
     317             : /************************************************************************/
     318             : 
     319          95 : GDALRATFieldUsage HFARasterAttributeTable::GetUsageOfCol(int nCol) const
     320             : {
     321          95 :     if (nCol < 0 || nCol >= static_cast<int>(aoFields.size()))
     322           0 :         return GFU_Generic;
     323             : 
     324          95 :     return aoFields[nCol].eUsage;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                          GetTypeOfCol()                              */
     329             : /************************************************************************/
     330             : 
     331        3741 : GDALRATFieldType HFARasterAttributeTable::GetTypeOfCol(int nCol) const
     332             : {
     333        3741 :     if (nCol < 0 || nCol >= static_cast<int>(aoFields.size()))
     334           0 :         return GFT_Integer;
     335             : 
     336        3741 :     return aoFields[nCol].eType;
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                          GetColOfUsage()                             */
     341             : /************************************************************************/
     342             : 
     343           1 : int HFARasterAttributeTable::GetColOfUsage(GDALRATFieldUsage eUsage) const
     344             : {
     345           1 :     for (unsigned int i = 0; i < aoFields.size(); i++)
     346             :     {
     347           1 :         if (aoFields[i].eUsage == eUsage)
     348           1 :             return i;
     349             :     }
     350             : 
     351           0 :     return -1;
     352             : }
     353             : 
     354             : /************************************************************************/
     355             : /*                          GetRowCount()                               */
     356             : /************************************************************************/
     357             : 
     358          39 : int HFARasterAttributeTable::GetRowCount() const
     359             : {
     360          39 :     return nRows;
     361             : }
     362             : 
     363             : /************************************************************************/
     364             : /*                      GetValueAsString()                              */
     365             : /************************************************************************/
     366             : 
     367          10 : const char *HFARasterAttributeTable::GetValueAsString(int iRow,
     368             :                                                       int iField) const
     369             : {
     370             :     // Let ValuesIO do the work.
     371          10 :     char *apszStrList[1] = {nullptr};
     372          10 :     if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
     373          10 :             GF_Read, iField, iRow, 1, apszStrList) != CE_None)
     374             :     {
     375           0 :         return "";
     376             :     }
     377             : 
     378             :     const_cast<HFARasterAttributeTable *>(this)->osWorkingResult =
     379          10 :         apszStrList[0];
     380          10 :     CPLFree(apszStrList[0]);
     381             : 
     382          10 :     return osWorkingResult;
     383             : }
     384             : 
     385             : /************************************************************************/
     386             : /*                        GetValueAsInt()                               */
     387             : /************************************************************************/
     388             : 
     389        3125 : int HFARasterAttributeTable::GetValueAsInt(int iRow, int iField) const
     390             : {
     391             :     // Let ValuesIO do the work.
     392        3125 :     int nValue = 0;
     393        3125 :     if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
     394        3125 :             GF_Read, iField, iRow, 1, &nValue) != CE_None)
     395             :     {
     396           0 :         return 0;
     397             :     }
     398             : 
     399        3125 :     return nValue;
     400             : }
     401             : 
     402             : /************************************************************************/
     403             : /*                      GetValueAsDouble()                              */
     404             : /************************************************************************/
     405             : 
     406        1592 : double HFARasterAttributeTable::GetValueAsDouble(int iRow, int iField) const
     407             : {
     408             :     // Let ValuesIO do the work.
     409        1592 :     double dfValue = 0.0;
     410        1592 :     if (const_cast<HFARasterAttributeTable *>(this)->ValuesIO(
     411        1592 :             GF_Read, iField, iRow, 1, &dfValue) != CE_None)
     412             :     {
     413           0 :         return 0.0;
     414             :     }
     415             : 
     416        1592 :     return dfValue;
     417             : }
     418             : 
     419             : /************************************************************************/
     420             : /*                          SetValue()                                  */
     421             : /************************************************************************/
     422             : 
     423          20 : CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField,
     424             :                                          const char *pszValue)
     425             : {
     426             :     // Let ValuesIO do the work.
     427          20 :     char *apszValues[1] = {const_cast<char *>(pszValue)};
     428          40 :     return ValuesIO(GF_Write, iField, iRow, 1, apszValues);
     429             : }
     430             : 
     431             : /************************************************************************/
     432             : /*                          SetValue()                                  */
     433             : /************************************************************************/
     434             : 
     435          20 : CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField, double dfValue)
     436             : {
     437             :     // Let ValuesIO do the work.
     438          20 :     return ValuesIO(GF_Write, iField, iRow, 1, &dfValue);
     439             : }
     440             : 
     441             : /************************************************************************/
     442             : /*                          SetValue()                                  */
     443             : /************************************************************************/
     444             : 
     445          20 : CPLErr HFARasterAttributeTable::SetValue(int iRow, int iField, int nValue)
     446             : {
     447             :     // Let ValuesIO do the work.
     448          20 :     return ValuesIO(GF_Write, iField, iRow, 1, &nValue);
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                          ValuesIO()                                  */
     453             : /************************************************************************/
     454             : 
     455        1651 : CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     456             :                                          int iStartRow, int iLength,
     457             :                                          double *pdfData)
     458             : {
     459        1651 :     if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
     460             :     {
     461           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     462             :                  "Dataset not open in update mode");
     463           0 :         return CE_Failure;
     464             :     }
     465             : 
     466        1651 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
     467             :     {
     468           0 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
     469             :                  iField);
     470             : 
     471           0 :         return CE_Failure;
     472             :     }
     473             : 
     474        1651 :     if (iStartRow < 0 || iLength >= INT_MAX - iStartRow ||
     475        1651 :         (iStartRow + iLength) > nRows)
     476             :     {
     477           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     478             :                  "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
     479             :                  iLength);
     480             : 
     481           0 :         return CE_Failure;
     482             :     }
     483             : 
     484        1651 :     if (aoFields[iField].bConvertColors)
     485             :     {
     486             :         // Convert to/from float color field.
     487             :         int *panColData =
     488           0 :             static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
     489           0 :         if (panColData == nullptr)
     490             :         {
     491           0 :             CPLFree(panColData);
     492           0 :             return CE_Failure;
     493             :         }
     494             : 
     495           0 :         if (eRWFlag == GF_Write)
     496             :         {
     497           0 :             for (int i = 0; i < iLength; i++)
     498           0 :                 panColData[i] = static_cast<int>(pdfData[i]);
     499             :         }
     500             : 
     501             :         const CPLErr ret =
     502           0 :             ColorsIO(eRWFlag, iField, iStartRow, iLength, panColData);
     503             : 
     504           0 :         if (eRWFlag == GF_Read)
     505             :         {
     506             :             // Copy them back to doubles.
     507           0 :             for (int i = 0; i < iLength; i++)
     508           0 :                 pdfData[i] = panColData[i];
     509             :         }
     510             : 
     511           0 :         CPLFree(panColData);
     512           0 :         return ret;
     513             :     }
     514             : 
     515        1651 :     switch (aoFields[iField].eType)
     516             :     {
     517           1 :         case GFT_Integer:
     518             :         {
     519             :             // Allocate space for ints.
     520             :             int *panColData =
     521           1 :                 static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
     522           1 :             if (panColData == nullptr)
     523             :             {
     524           0 :                 CPLFree(panColData);
     525           0 :                 return CE_Failure;
     526             :             }
     527             : 
     528           1 :             if (eRWFlag == GF_Write)
     529             :             {
     530             :                 // Copy the application supplied doubles to ints.
     531          11 :                 for (int i = 0; i < iLength; i++)
     532          10 :                     panColData[i] = static_cast<int>(pdfData[i]);
     533             :             }
     534             : 
     535             :             // Do the ValuesIO as ints.
     536             :             const CPLErr eVal =
     537           1 :                 ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData);
     538           1 :             if (eVal != CE_None)
     539             :             {
     540           0 :                 CPLFree(panColData);
     541           0 :                 return eVal;
     542             :             }
     543             : 
     544           1 :             if (eRWFlag == GF_Read)
     545             :             {
     546             :                 // Copy them back to doubles.
     547           0 :                 for (int i = 0; i < iLength; i++)
     548           0 :                     pdfData[i] = panColData[i];
     549             :             }
     550             : 
     551           1 :             CPLFree(panColData);
     552             :         }
     553           1 :         break;
     554        1649 :         case GFT_Real:
     555             :         {
     556        1649 :             if ((eRWFlag == GF_Read) && aoFields[iField].bIsBinValues)
     557             :             {
     558             :                 // Probably could change HFAReadBFUniqueBins to only read needed
     559             :                 // rows.
     560         193 :                 double *padfBinValues = HFAReadBFUniqueBins(
     561         193 :                     aoFields[iField].poColumn, iStartRow + iLength);
     562         193 :                 if (padfBinValues == nullptr)
     563           0 :                     return CE_Failure;
     564         193 :                 memcpy(pdfData, &padfBinValues[iStartRow],
     565         193 :                        sizeof(double) * iLength);
     566         193 :                 CPLFree(padfBinValues);
     567             :             }
     568             :             else
     569             :             {
     570        2912 :                 if (VSIFSeekL(hHFA->fp,
     571        1456 :                               aoFields[iField].nDataOffset +
     572        2912 :                                   (static_cast<vsi_l_offset>(iStartRow) *
     573        1456 :                                    aoFields[iField].nElementSize),
     574        1456 :                               SEEK_SET) != 0)
     575             :                 {
     576           0 :                     return CE_Failure;
     577             :                 }
     578             : 
     579        1456 :                 if (eRWFlag == GF_Read)
     580             :                 {
     581        2864 :                     if (static_cast<int>(VSIFReadL(pdfData, sizeof(double),
     582        1432 :                                                    iLength, hHFA->fp)) !=
     583             :                         iLength)
     584             :                     {
     585           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     586             :                                  "HFARasterAttributeTable::ValuesIO: "
     587             :                                  "Cannot read values");
     588           0 :                         return CE_Failure;
     589             :                     }
     590             : #ifdef CPL_MSB
     591             :                     GDALSwapWords(pdfData, 8, iLength, 8);
     592             : #endif
     593             :                 }
     594             :                 else
     595             :                 {
     596             : #ifdef CPL_MSB
     597             :                     GDALSwapWords(pdfData, 8, iLength, 8);
     598             : #endif
     599             :                     // Note: HFAAllocateSpace now called by CreateColumn so
     600             :                     // space should exist.
     601          48 :                     if (static_cast<int>(VSIFWriteL(pdfData, sizeof(double),
     602          24 :                                                     iLength, hHFA->fp)) !=
     603             :                         iLength)
     604             :                     {
     605           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     606             :                                  "HFARasterAttributeTable::ValuesIO: "
     607             :                                  "Cannot write values");
     608           0 :                         return CE_Failure;
     609             :                     }
     610             : #ifdef CPL_MSB
     611             :                     // Swap back.
     612             :                     GDALSwapWords(pdfData, 8, iLength, 8);
     613             : #endif
     614             :                 }
     615             :             }
     616             :         }
     617        1649 :         break;
     618           1 :         case GFT_String:
     619             :         {
     620             :             // Allocate space for string pointers.
     621             :             char **papszColData = static_cast<char **>(
     622           1 :                 VSI_MALLOC2_VERBOSE(iLength, sizeof(char *)));
     623           1 :             if (papszColData == nullptr)
     624             :             {
     625           0 :                 return CE_Failure;
     626             :             }
     627             : 
     628           1 :             if (eRWFlag == GF_Write)
     629             :             {
     630             :                 // Copy the application supplied doubles to strings.
     631          11 :                 for (int i = 0; i < iLength; i++)
     632             :                 {
     633          10 :                     osWorkingResult.Printf("%.16g", pdfData[i]);
     634          10 :                     papszColData[i] = CPLStrdup(osWorkingResult);
     635             :                 }
     636             :             }
     637             : 
     638             :             // Do the ValuesIO as strings.
     639             :             const CPLErr eVal =
     640           1 :                 ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData);
     641           1 :             if (eVal != CE_None)
     642             :             {
     643           0 :                 if (eRWFlag == GF_Write)
     644             :                 {
     645           0 :                     for (int i = 0; i < iLength; i++)
     646           0 :                         CPLFree(papszColData[i]);
     647             :                 }
     648           0 :                 CPLFree(papszColData);
     649           0 :                 return eVal;
     650             :             }
     651             : 
     652           1 :             if (eRWFlag == GF_Read)
     653             :             {
     654             :                 // Copy them back to doubles.
     655           0 :                 for (int i = 0; i < iLength; i++)
     656           0 :                     pdfData[i] = CPLAtof(papszColData[i]);
     657             :             }
     658             : 
     659             :             // Either we allocated them for write, or they were allocated
     660             :             // by ValuesIO on read.
     661          11 :             for (int i = 0; i < iLength; i++)
     662          10 :                 CPLFree(papszColData[i]);
     663             : 
     664           1 :             CPLFree(papszColData);
     665             :         }
     666           1 :         break;
     667             :     }
     668             : 
     669        1651 :     return CE_None;
     670             : }
     671             : 
     672             : /************************************************************************/
     673             : /*                          ValuesIO()                                  */
     674             : /************************************************************************/
     675             : 
     676        3169 : CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     677             :                                          int iStartRow, int iLength,
     678             :                                          int *pnData)
     679             : {
     680        3169 :     if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
     681             :     {
     682           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     683             :                  "Dataset not open in update mode");
     684           0 :         return CE_Failure;
     685             :     }
     686             : 
     687        3169 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
     688             :     {
     689           0 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
     690             :                  iField);
     691             : 
     692           0 :         return CE_Failure;
     693             :     }
     694             : 
     695        3169 :     if (iStartRow < 0 || iLength >= INT_MAX - iStartRow ||
     696        3169 :         (iStartRow + iLength) > nRows)
     697             :     {
     698           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     699             :                  "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
     700             :                  iLength);
     701             : 
     702           0 :         return CE_Failure;
     703             :     }
     704             : 
     705        3169 :     if (aoFields[iField].bConvertColors)
     706             :     {
     707             :         // Convert to/from float color field.
     708        3112 :         return ColorsIO(eRWFlag, iField, iStartRow, iLength, pnData);
     709             :     }
     710             : 
     711          57 :     switch (aoFields[iField].eType)
     712             :     {
     713          52 :         case GFT_Integer:
     714             :         {
     715         104 :             if (VSIFSeekL(hHFA->fp,
     716          52 :                           aoFields[iField].nDataOffset +
     717         104 :                               (static_cast<vsi_l_offset>(iStartRow) *
     718          52 :                                aoFields[iField].nElementSize),
     719          52 :                           SEEK_SET) != 0)
     720             :             {
     721           0 :                 return CE_Failure;
     722             :             }
     723             :             GInt32 *panColData = static_cast<GInt32 *>(
     724          52 :                 VSI_MALLOC2_VERBOSE(iLength, sizeof(GInt32)));
     725          52 :             if (panColData == nullptr)
     726             :             {
     727           0 :                 return CE_Failure;
     728             :             }
     729             : 
     730          52 :             if (eRWFlag == GF_Read)
     731             :             {
     732          56 :                 if (static_cast<int>(VSIFReadL(panColData, sizeof(GInt32),
     733          28 :                                                iLength, hHFA->fp)) != iLength)
     734             :                 {
     735           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     736             :                              "HFARasterAttributeTable::ValuesIO: "
     737             :                              "Cannot read values");
     738           0 :                     CPLFree(panColData);
     739           0 :                     return CE_Failure;
     740             :                 }
     741             : #ifdef CPL_MSB
     742             :                 GDALSwapWords(panColData, 4, iLength, 4);
     743             : #endif
     744             :                 // Now copy into application buffer. This extra step
     745             :                 // may not be necessary if sizeof(int) == sizeof(GInt32).
     746         664 :                 for (int i = 0; i < iLength; i++)
     747         636 :                     pnData[i] = panColData[i];
     748             :             }
     749             :             else
     750             :             {
     751             :                 // Copy from application buffer.
     752          84 :                 for (int i = 0; i < iLength; i++)
     753          60 :                     panColData[i] = pnData[i];
     754             : 
     755             : #ifdef CPL_MSB
     756             :                 GDALSwapWords(panColData, 4, iLength, 4);
     757             : #endif
     758             :                 // Note: HFAAllocateSpace now called by CreateColumn so space
     759             :                 // should exist.
     760          48 :                 if (static_cast<int>(VSIFWriteL(panColData, sizeof(GInt32),
     761          24 :                                                 iLength, hHFA->fp)) != iLength)
     762             :                 {
     763           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     764             :                              "HFARasterAttributeTable::ValuesIO: "
     765             :                              "Cannot write values");
     766           0 :                     CPLFree(panColData);
     767           0 :                     return CE_Failure;
     768             :                 }
     769             :             }
     770          52 :             CPLFree(panColData);
     771             :         }
     772          52 :         break;
     773           4 :         case GFT_Real:
     774             :         {
     775             :             // Allocate space for doubles.
     776             :             double *padfColData = static_cast<double *>(
     777           4 :                 VSI_MALLOC2_VERBOSE(iLength, sizeof(double)));
     778           4 :             if (padfColData == nullptr)
     779             :             {
     780           0 :                 return CE_Failure;
     781             :             }
     782             : 
     783           4 :             if (eRWFlag == GF_Write)
     784             :             {
     785             :                 // Copy the application supplied ints to doubles.
     786          11 :                 for (int i = 0; i < iLength; i++)
     787          10 :                     padfColData[i] = pnData[i];
     788             :             }
     789             : 
     790             :             // Do the ValuesIO as doubles.
     791             :             const CPLErr eVal =
     792           4 :                 ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData);
     793           4 :             if (eVal != CE_None)
     794             :             {
     795           0 :                 CPLFree(padfColData);
     796           0 :                 return eVal;
     797             :             }
     798             : 
     799           4 :             if (eRWFlag == GF_Read)
     800             :             {
     801             :                 // Copy them back to ints.
     802           6 :                 for (int i = 0; i < iLength; i++)
     803           3 :                     pnData[i] = static_cast<int>(padfColData[i]);
     804             :             }
     805             : 
     806           4 :             CPLFree(padfColData);
     807             :         }
     808           4 :         break;
     809           1 :         case GFT_String:
     810             :         {
     811             :             // Allocate space for string pointers.
     812             :             char **papszColData = static_cast<char **>(
     813           1 :                 VSI_MALLOC2_VERBOSE(iLength, sizeof(char *)));
     814           1 :             if (papszColData == nullptr)
     815             :             {
     816           0 :                 return CE_Failure;
     817             :             }
     818             : 
     819           1 :             if (eRWFlag == GF_Write)
     820             :             {
     821             :                 // Copy the application supplied ints to strings.
     822          11 :                 for (int i = 0; i < iLength; i++)
     823             :                 {
     824          10 :                     osWorkingResult.Printf("%d", pnData[i]);
     825          10 :                     papszColData[i] = CPLStrdup(osWorkingResult);
     826             :                 }
     827             :             }
     828             : 
     829             :             // Do the ValuesIO as strings.
     830             :             const CPLErr eVal =
     831           1 :                 ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData);
     832           1 :             if (eVal != CE_None)
     833             :             {
     834           0 :                 if (eRWFlag == GF_Write)
     835             :                 {
     836           0 :                     for (int i = 0; i < iLength; i++)
     837           0 :                         CPLFree(papszColData[i]);
     838             :                 }
     839           0 :                 CPLFree(papszColData);
     840           0 :                 return eVal;
     841             :             }
     842             : 
     843           1 :             if (eRWFlag == GF_Read)
     844             :             {
     845             :                 // Copy them back to ints.
     846           0 :                 for (int i = 0; i < iLength; i++)
     847           0 :                     pnData[i] = atoi(papszColData[i]);
     848             :             }
     849             : 
     850             :             // Either we allocated them for write, or they were allocated
     851             :             // by ValuesIO on read.
     852          11 :             for (int i = 0; i < iLength; i++)
     853          10 :                 CPLFree(papszColData[i]);
     854             : 
     855           1 :             CPLFree(papszColData);
     856             :         }
     857           1 :         break;
     858             :     }
     859             : 
     860          57 :     return CE_None;
     861             : }
     862             : 
     863             : /************************************************************************/
     864             : /*                          ValuesIO()                                  */
     865             : /************************************************************************/
     866             : 
     867          56 : CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     868             :                                          int iStartRow, int iLength,
     869             :                                          char **papszStrList)
     870             : {
     871          56 :     if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
     872             :     {
     873           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     874             :                  "Dataset not open in update mode");
     875           0 :         return CE_Failure;
     876             :     }
     877             : 
     878          56 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
     879             :     {
     880           0 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
     881             :                  iField);
     882             : 
     883           0 :         return CE_Failure;
     884             :     }
     885             : 
     886          56 :     if (iStartRow < 0 || iLength >= INT_MAX - iStartRow ||
     887          56 :         (iStartRow + iLength) > nRows)
     888             :     {
     889           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     890             :                  "iStartRow (%d) + iLength(%d) out of range.", iStartRow,
     891             :                  iLength);
     892             : 
     893           0 :         return CE_Failure;
     894             :     }
     895             : 
     896          56 :     if (aoFields[iField].bConvertColors)
     897             :     {
     898             :         // Convert to/from float color field.
     899             :         int *panColData =
     900           0 :             static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
     901           0 :         if (panColData == nullptr)
     902             :         {
     903           0 :             CPLFree(panColData);
     904           0 :             return CE_Failure;
     905             :         }
     906             : 
     907           0 :         if (eRWFlag == GF_Write)
     908             :         {
     909           0 :             for (int i = 0; i < iLength; i++)
     910           0 :                 panColData[i] = atoi(papszStrList[i]);
     911             :         }
     912             : 
     913             :         const CPLErr ret =
     914           0 :             ColorsIO(eRWFlag, iField, iStartRow, iLength, panColData);
     915             : 
     916           0 :         if (eRWFlag == GF_Read)
     917             :         {
     918             :             // Copy them back to strings.
     919           0 :             for (int i = 0; i < iLength; i++)
     920             :             {
     921           0 :                 osWorkingResult.Printf("%d", panColData[i]);
     922           0 :                 papszStrList[i] = CPLStrdup(osWorkingResult);
     923             :             }
     924             :         }
     925             : 
     926           0 :         CPLFree(panColData);
     927           0 :         return ret;
     928             :     }
     929             : 
     930          56 :     switch (aoFields[iField].eType)
     931             :     {
     932           1 :         case GFT_Integer:
     933             :         {
     934             :             // Allocate space for ints.
     935             :             int *panColData =
     936           1 :                 static_cast<int *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(int)));
     937           1 :             if (panColData == nullptr)
     938             :             {
     939           0 :                 return CE_Failure;
     940             :             }
     941             : 
     942           1 :             if (eRWFlag == GF_Write)
     943             :             {
     944             :                 // Convert user supplied strings to ints.
     945          11 :                 for (int i = 0; i < iLength; i++)
     946          10 :                     panColData[i] = atoi(papszStrList[i]);
     947             :             }
     948             : 
     949             :             // Call values IO to read/write ints.
     950             :             const CPLErr eVal =
     951           1 :                 ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData);
     952           1 :             if (eVal != CE_None)
     953             :             {
     954           0 :                 CPLFree(panColData);
     955           0 :                 return eVal;
     956             :             }
     957             : 
     958           1 :             if (eRWFlag == GF_Read)
     959             :             {
     960             :                 // Convert ints back to strings.
     961           0 :                 for (int i = 0; i < iLength; i++)
     962             :                 {
     963           0 :                     osWorkingResult.Printf("%d", panColData[i]);
     964           0 :                     papszStrList[i] = CPLStrdup(osWorkingResult);
     965             :                 }
     966             :             }
     967           1 :             CPLFree(panColData);
     968             :         }
     969           1 :         break;
     970           1 :         case GFT_Real:
     971             :         {
     972             :             // Allocate space for doubles.
     973             :             double *padfColData = static_cast<double *>(
     974           1 :                 VSI_MALLOC2_VERBOSE(iLength, sizeof(double)));
     975           1 :             if (padfColData == nullptr)
     976             :             {
     977           0 :                 return CE_Failure;
     978             :             }
     979             : 
     980           1 :             if (eRWFlag == GF_Write)
     981             :             {
     982             :                 // Convert user supplied strings to doubles.
     983          11 :                 for (int i = 0; i < iLength; i++)
     984          10 :                     padfColData[i] = CPLAtof(papszStrList[i]);
     985             :             }
     986             : 
     987             :             // Call value IO to read/write doubles.
     988             :             const CPLErr eVal =
     989           1 :                 ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData);
     990           1 :             if (eVal != CE_None)
     991             :             {
     992           0 :                 CPLFree(padfColData);
     993           0 :                 return eVal;
     994             :             }
     995             : 
     996           1 :             if (eRWFlag == GF_Read)
     997             :             {
     998             :                 // Convert doubles back to strings.
     999           0 :                 for (int i = 0; i < iLength; i++)
    1000             :                 {
    1001           0 :                     osWorkingResult.Printf("%.16g", padfColData[i]);
    1002           0 :                     papszStrList[i] = CPLStrdup(osWorkingResult);
    1003             :                 }
    1004             :             }
    1005           1 :             CPLFree(padfColData);
    1006             :         }
    1007           1 :         break;
    1008          54 :         case GFT_String:
    1009             :         {
    1010         108 :             if (VSIFSeekL(hHFA->fp,
    1011          54 :                           aoFields[iField].nDataOffset +
    1012         108 :                               (static_cast<vsi_l_offset>(iStartRow) *
    1013          54 :                                aoFields[iField].nElementSize),
    1014          54 :                           SEEK_SET) != 0)
    1015             :             {
    1016           0 :                 return CE_Failure;
    1017             :             }
    1018             :             char *pachColData = static_cast<char *>(
    1019          54 :                 VSI_MALLOC2_VERBOSE(iLength, aoFields[iField].nElementSize));
    1020          54 :             if (pachColData == nullptr)
    1021             :             {
    1022           0 :                 return CE_Failure;
    1023             :             }
    1024             : 
    1025          54 :             if (eRWFlag == GF_Read)
    1026             :             {
    1027          87 :                 if (static_cast<int>(VSIFReadL(pachColData,
    1028          29 :                                                aoFields[iField].nElementSize,
    1029          58 :                                                iLength, hHFA->fp)) != iLength)
    1030             :                 {
    1031           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1032             :                              "HFARasterAttributeTable::ValuesIO: "
    1033             :                              "Cannot read values");
    1034           0 :                     CPLFree(pachColData);
    1035           0 :                     return CE_Failure;
    1036             :                 }
    1037             : 
    1038             :                 // Now copy into application buffer.
    1039         675 :                 for (int i = 0; i < iLength; i++)
    1040             :                 {
    1041             :                     osWorkingResult.assign(
    1042        1292 :                         pachColData + aoFields[iField].nElementSize * i,
    1043         646 :                         aoFields[iField].nElementSize);
    1044         646 :                     papszStrList[i] = CPLStrdup(osWorkingResult);
    1045             :                 }
    1046             :             }
    1047             :             else
    1048             :             {
    1049             :                 // We need to check that these strings will fit in the allocated
    1050             :                 // space.
    1051          25 :                 int nNewMaxChars = aoFields[iField].nElementSize;
    1052          95 :                 for (int i = 0; i < iLength; i++)
    1053             :                 {
    1054          70 :                     const int nStringSize =
    1055          70 :                         static_cast<int>(strlen(papszStrList[i])) + 1;
    1056          70 :                     if (nStringSize > nNewMaxChars)
    1057           3 :                         nNewMaxChars = nStringSize;
    1058             :                 }
    1059             : 
    1060          25 :                 if (nNewMaxChars > aoFields[iField].nElementSize)
    1061             :                 {
    1062             :                     // OK we have a problem: The allocated space is not big
    1063             :                     // enough we need to re-allocate the space and update the
    1064             :                     // pointers and copy across the old data.
    1065             :                     const int nNewOffset =
    1066           4 :                         HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
    1067           2 :                                          nRows * nNewMaxChars);
    1068           4 :                     char *pszBuffer = static_cast<char *>(VSIMalloc2(
    1069           2 :                         aoFields[iField].nElementSize, sizeof(char)));
    1070          32 :                     for (int i = 0; i < nRows; i++)
    1071             :                     {
    1072             :                         // Seek to the old place.
    1073          30 :                         CPL_IGNORE_RET_VAL(
    1074          30 :                             VSIFSeekL(hHFA->fp,
    1075          30 :                                       aoFields[iField].nDataOffset +
    1076          60 :                                           (static_cast<vsi_l_offset>(i) *
    1077          30 :                                            aoFields[iField].nElementSize),
    1078             :                                       SEEK_SET));
    1079             :                         // Read in old data.
    1080          30 :                         CPL_IGNORE_RET_VAL(
    1081          30 :                             VSIFReadL(pszBuffer, aoFields[iField].nElementSize,
    1082          30 :                                       1, hHFA->fp));
    1083             :                         // Seek to new place.
    1084          60 :                         bool bOK = VSIFSeekL(hHFA->fp,
    1085          30 :                                              nNewOffset +
    1086          30 :                                                  (static_cast<vsi_l_offset>(i) *
    1087          30 :                                                   nNewMaxChars),
    1088          30 :                                              SEEK_SET) == 0;
    1089             :                         // Write data to new place.
    1090          30 :                         bOK &=
    1091          30 :                             VSIFWriteL(pszBuffer, aoFields[iField].nElementSize,
    1092          30 :                                        1, hHFA->fp) == 1;
    1093             :                         // Make sure there is a terminating null byte just to be
    1094             :                         // safe.
    1095          30 :                         const char cNullByte = '\0';
    1096          60 :                         bOK &= VSIFWriteL(&cNullByte, sizeof(char), 1,
    1097          30 :                                           hHFA->fp) == 1;
    1098          30 :                         if (!bOK)
    1099             :                         {
    1100           0 :                             CPLFree(pszBuffer);
    1101           0 :                             CPLFree(pachColData);
    1102           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1103             :                                      "HFARasterAttributeTable::ValuesIO: "
    1104             :                                      "Cannot write values");
    1105           0 :                             return CE_Failure;
    1106             :                         }
    1107             :                     }
    1108             :                     // Update our data structures.
    1109           2 :                     aoFields[iField].nElementSize = nNewMaxChars;
    1110           2 :                     aoFields[iField].nDataOffset = nNewOffset;
    1111             :                     // Update file.
    1112           2 :                     aoFields[iField].poColumn->SetIntField("columnDataPtr",
    1113             :                                                            nNewOffset);
    1114           2 :                     aoFields[iField].poColumn->SetIntField("maxNumChars",
    1115             :                                                            nNewMaxChars);
    1116             : 
    1117             :                     // Note: There isn't an HFAFreeSpace so we can't un-allocate
    1118             :                     // the old space in the file.
    1119           2 :                     CPLFree(pszBuffer);
    1120             : 
    1121             :                     // Re-allocate our buffer.
    1122           2 :                     CPLFree(pachColData);
    1123             :                     pachColData = static_cast<char *>(
    1124           2 :                         VSI_MALLOC2_VERBOSE(iLength, nNewMaxChars));
    1125           2 :                     if (pachColData == nullptr)
    1126             :                     {
    1127           0 :                         return CE_Failure;
    1128             :                     }
    1129             : 
    1130             :                     // Lastly seek to the right place in the new space ready to
    1131             :                     // write.
    1132           4 :                     if (VSIFSeekL(hHFA->fp,
    1133           2 :                                   nNewOffset +
    1134           2 :                                       (static_cast<vsi_l_offset>(iStartRow) *
    1135           2 :                                        nNewMaxChars),
    1136           2 :                                   SEEK_SET) != 0)
    1137             :                     {
    1138           0 :                         VSIFree(pachColData);
    1139           0 :                         return CE_Failure;
    1140             :                     }
    1141             :                 }
    1142             : 
    1143             :                 // Copy from application buffer.
    1144          95 :                 for (int i = 0; i < iLength; i++)
    1145          70 :                     strcpy(&pachColData[nNewMaxChars * i], papszStrList[i]);
    1146             : 
    1147             :                 // Note: HFAAllocateSpace now called by CreateColumn so space
    1148             :                 // should exist.
    1149          75 :                 if (static_cast<int>(VSIFWriteL(pachColData,
    1150          25 :                                                 aoFields[iField].nElementSize,
    1151          50 :                                                 iLength, hHFA->fp)) != iLength)
    1152             :                 {
    1153           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1154             :                              "HFARasterAttributeTable::ValuesIO: "
    1155             :                              "Cannot write values");
    1156           0 :                     CPLFree(pachColData);
    1157           0 :                     return CE_Failure;
    1158             :                 }
    1159             :             }
    1160          54 :             CPLFree(pachColData);
    1161             :         }
    1162          54 :         break;
    1163             :     }
    1164             : 
    1165          56 :     return CE_None;
    1166             : }
    1167             : 
    1168             : /************************************************************************/
    1169             : /*                               ColorsIO()                              */
    1170             : /************************************************************************/
    1171             : 
    1172             : // Handle the fact that HFA stores colours as floats, but we need to
    1173             : // read them in as ints 0...255.
    1174        3112 : CPLErr HFARasterAttributeTable::ColorsIO(GDALRWFlag eRWFlag, int iField,
    1175             :                                          int iStartRow, int iLength,
    1176             :                                          int *pnData)
    1177             : {
    1178             :     // Allocate space for doubles.
    1179             :     double *padfData =
    1180        3112 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(iLength, sizeof(double)));
    1181        3112 :     if (padfData == nullptr)
    1182             :     {
    1183           0 :         return CE_Failure;
    1184             :     }
    1185             : 
    1186        3112 :     if (eRWFlag == GF_Write)
    1187             :     {
    1188             :         // Copy the application supplied ints to doubles
    1189             :         // and convert 0..255 to 0..1 in the same manner
    1190             :         // as the color table.
    1191           0 :         for (int i = 0; i < iLength; i++)
    1192           0 :             padfData[i] = pnData[i] / 255.0;
    1193             :     }
    1194             : 
    1195        6224 :     if (VSIFSeekL(hHFA->fp,
    1196        3112 :                   aoFields[iField].nDataOffset +
    1197        6224 :                       (static_cast<vsi_l_offset>(iStartRow) *
    1198        3112 :                        aoFields[iField].nElementSize),
    1199        3112 :                   SEEK_SET) != 0)
    1200             :     {
    1201           0 :         CPLFree(padfData);
    1202           0 :         return CE_Failure;
    1203             :     }
    1204             : 
    1205        3112 :     if (eRWFlag == GF_Read)
    1206             :     {
    1207        6224 :         if (static_cast<int>(VSIFReadL(padfData, sizeof(double), iLength,
    1208        3112 :                                        hHFA->fp)) != iLength)
    1209             :         {
    1210           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1211             :                      "HFARasterAttributeTable::ColorsIO: Cannot read values");
    1212           0 :             CPLFree(padfData);
    1213           0 :             return CE_Failure;
    1214             :         }
    1215             : #ifdef CPL_MSB
    1216             :         GDALSwapWords(padfData, 8, iLength, 8);
    1217             : #endif
    1218             :     }
    1219             :     else
    1220             :     {
    1221             : #ifdef CPL_MSB
    1222             :         GDALSwapWords(padfData, 8, iLength, 8);
    1223             : #endif
    1224             :         // Note: HFAAllocateSpace now called by CreateColumn so space should
    1225             :         // exist.
    1226           0 :         if (static_cast<int>(VSIFWriteL(padfData, sizeof(double), iLength,
    1227           0 :                                         hHFA->fp)) != iLength)
    1228             :         {
    1229           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1230             :                      "HFARasterAttributeTable::ColorsIO: Cannot write values");
    1231           0 :             CPLFree(padfData);
    1232           0 :             return CE_Failure;
    1233             :         }
    1234             :     }
    1235             : 
    1236        3112 :     if (eRWFlag == GF_Read)
    1237             :     {
    1238             :         // Copy them back to ints converting 0..1 to 0..255 in
    1239             :         // the same manner as the color table.
    1240             :         // TODO(schwehr): Symbolic constants for 255 and 256.
    1241        6224 :         for (int i = 0; i < iLength; i++)
    1242        3112 :             pnData[i] = std::min(255, static_cast<int>(padfData[i] * 256));
    1243             :     }
    1244             : 
    1245        3112 :     CPLFree(padfData);
    1246             : 
    1247        3112 :     return CE_None;
    1248             : }
    1249             : 
    1250             : /************************************************************************/
    1251             : /*                       ChangesAreWrittenToFile()                      */
    1252             : /************************************************************************/
    1253             : 
    1254           1 : int HFARasterAttributeTable::ChangesAreWrittenToFile()
    1255             : {
    1256           1 :     return TRUE;
    1257             : }
    1258             : 
    1259             : /************************************************************************/
    1260             : /*                          SetRowCount()                               */
    1261             : /************************************************************************/
    1262             : 
    1263           2 : void HFARasterAttributeTable::SetRowCount(int iCount)
    1264             : {
    1265           2 :     if (eAccess == GA_ReadOnly)
    1266             :     {
    1267           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1268             :                  "Dataset not open in update mode");
    1269           0 :         return;
    1270             :     }
    1271             : 
    1272           2 :     if (iCount > nRows)
    1273             :     {
    1274             :         // Making the RAT larger - a bit hard.
    1275             :         // We need to re-allocate space on disc.
    1276          20 :         for (int iCol = 0; iCol < static_cast<int>(aoFields.size()); iCol++)
    1277             :         {
    1278             :             // New space.
    1279             :             const int nNewOffset =
    1280          36 :                 HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
    1281          18 :                                  iCount * aoFields[iCol].nElementSize);
    1282             : 
    1283             :             // Only need to bother if there are actually rows.
    1284          18 :             if (nRows > 0)
    1285             :             {
    1286             :                 // Temp buffer for this column.
    1287             :                 void *pData =
    1288           9 :                     VSI_MALLOC2_VERBOSE(nRows, aoFields[iCol].nElementSize);
    1289           9 :                 if (pData == nullptr)
    1290             :                 {
    1291           0 :                     return;
    1292             :                 }
    1293             :                 // Read old data.
    1294           9 :                 if (VSIFSeekL(hHFA->fp, aoFields[iCol].nDataOffset, SEEK_SET) !=
    1295          18 :                         0 ||
    1296          27 :                     static_cast<int>(VSIFReadL(pData,
    1297           9 :                                                aoFields[iCol].nElementSize,
    1298          18 :                                                nRows, hHFA->fp)) != nRows)
    1299             :                 {
    1300           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1301             :                              "HFARasterAttributeTable::SetRowCount: "
    1302             :                              "Cannot read values");
    1303           0 :                     CPLFree(pData);
    1304           0 :                     return;
    1305             :                 }
    1306             : 
    1307             :                 // Write data - new space will be uninitialised.
    1308          18 :                 if (VSIFSeekL(hHFA->fp, nNewOffset, SEEK_SET) != 0 ||
    1309          27 :                     static_cast<int>(VSIFWriteL(pData,
    1310           9 :                                                 aoFields[iCol].nElementSize,
    1311          18 :                                                 nRows, hHFA->fp)) != nRows)
    1312             :                 {
    1313           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1314             :                              "HFARasterAttributeTable::SetRowCount: "
    1315             :                              "Cannot write values");
    1316           0 :                     CPLFree(pData);
    1317           0 :                     return;
    1318             :                 }
    1319           9 :                 CPLFree(pData);
    1320             :             }
    1321             : 
    1322             :             // Update our data structures.
    1323          18 :             aoFields[iCol].nDataOffset = nNewOffset;
    1324             :             // Update file.
    1325          18 :             aoFields[iCol].poColumn->SetIntField("columnDataPtr", nNewOffset);
    1326          18 :             aoFields[iCol].poColumn->SetIntField("numRows", iCount);
    1327             :         }
    1328             :     }
    1329           0 :     else if (iCount < nRows)
    1330             :     {
    1331             :         // Update the numRows.
    1332           0 :         for (int iCol = 0; iCol < static_cast<int>(aoFields.size()); iCol++)
    1333             :         {
    1334           0 :             aoFields[iCol].poColumn->SetIntField("numRows", iCount);
    1335             :         }
    1336             :     }
    1337             : 
    1338           2 :     nRows = iCount;
    1339             : 
    1340           2 :     if (poDT != nullptr && EQUAL(poDT->GetType(), "Edsc_Table"))
    1341             :     {
    1342           2 :         poDT->SetIntField("numrows", iCount);
    1343             :     }
    1344             : }
    1345             : 
    1346             : /************************************************************************/
    1347             : /*                          GetRowOfValue()                             */
    1348             : /************************************************************************/
    1349           1 : int HFARasterAttributeTable::GetRowOfValue(double dfValue) const
    1350             : {
    1351             :     // Handle case of regular binning.
    1352           1 :     if (bLinearBinning)
    1353             :     {
    1354           1 :         const int iBin =
    1355           1 :             static_cast<int>(floor((dfValue - dfRow0Min) / dfBinSize));
    1356           1 :         if (iBin < 0 || iBin >= nRows)
    1357           0 :             return -1;
    1358           1 :         return iBin;
    1359             :     }
    1360             :     // Do we have any information?
    1361           0 :     int nMinCol = GetColOfUsage(GFU_Min);
    1362           0 :     if (nMinCol == -1)
    1363           0 :         nMinCol = GetColOfUsage(GFU_MinMax);
    1364           0 :     int nMaxCol = GetColOfUsage(GFU_Max);
    1365           0 :     if (nMaxCol == -1)
    1366           0 :         nMaxCol = GetColOfUsage(GFU_MinMax);
    1367           0 :     if (nMinCol == -1 && nMaxCol == -1)
    1368           0 :         return -1;
    1369             :     // Search through rows for match.
    1370           0 :     for (int iRow = 0; iRow < nRows; iRow++)
    1371             :     {
    1372           0 :         if (nMinCol != -1)
    1373             :         {
    1374           0 :             while (iRow < nRows && dfValue < GetValueAsDouble(iRow, nMinCol))
    1375           0 :                 iRow++;
    1376           0 :             if (iRow == nRows)
    1377           0 :                 break;
    1378             :         }
    1379           0 :         if (nMaxCol != -1)
    1380             :         {
    1381           0 :             if (dfValue > GetValueAsDouble(iRow, nMaxCol))
    1382           0 :                 continue;
    1383             :         }
    1384           0 :         return iRow;
    1385             :     }
    1386           0 :     return -1;
    1387             : }
    1388             : 
    1389             : /************************************************************************/
    1390             : /*                          GetRowOfValue()                             */
    1391             : /*                                                                      */
    1392             : /*      Int arg for now just converted to double.  Perhaps we will      */
    1393             : /*      handle this in a special way some day?                          */
    1394             : /************************************************************************/
    1395           0 : int HFARasterAttributeTable::GetRowOfValue(int nValue) const
    1396             : {
    1397           0 :     return GetRowOfValue(static_cast<double>(nValue));
    1398             : }
    1399             : 
    1400             : /************************************************************************/
    1401             : /*                          CreateColumn()                              */
    1402             : /************************************************************************/
    1403             : 
    1404           9 : CPLErr HFARasterAttributeTable::CreateColumn(const char *pszFieldName,
    1405             :                                              GDALRATFieldType eFieldType,
    1406             :                                              GDALRATFieldUsage eFieldUsage)
    1407             : {
    1408           9 :     if (eAccess == GA_ReadOnly)
    1409             :     {
    1410           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1411             :                  "Dataset not open in update mode");
    1412           0 :         return CE_Failure;
    1413             :     }
    1414             : 
    1415             :     // Do we have a descriptor table already?
    1416           9 :     if (poDT == nullptr || !EQUAL(poDT->GetType(), "Edsc_Table"))
    1417           1 :         CreateDT();
    1418             : 
    1419           9 :     bool bConvertColors = false;
    1420             : 
    1421             :     // Imagine doesn't have a concept of usage - works of the names instead.
    1422             :     // Must make sure name matches use.
    1423           9 :     if (eFieldUsage == GFU_Red)
    1424             :     {
    1425           0 :         pszFieldName = "Red";
    1426             :         // Create a real column in the file, but make it
    1427             :         // available as int to GDAL.
    1428           0 :         bConvertColors = true;
    1429           0 :         eFieldType = GFT_Real;
    1430             :     }
    1431           9 :     else if (eFieldUsage == GFU_Green)
    1432             :     {
    1433           0 :         pszFieldName = "Green";
    1434           0 :         bConvertColors = true;
    1435           0 :         eFieldType = GFT_Real;
    1436             :     }
    1437           9 :     else if (eFieldUsage == GFU_Blue)
    1438             :     {
    1439           0 :         pszFieldName = "Blue";
    1440           0 :         bConvertColors = true;
    1441           0 :         eFieldType = GFT_Real;
    1442             :     }
    1443           9 :     else if (eFieldUsage == GFU_Alpha)
    1444             :     {
    1445           0 :         pszFieldName = "Opacity";
    1446           0 :         bConvertColors = true;
    1447           0 :         eFieldType = GFT_Real;
    1448             :     }
    1449           9 :     else if (eFieldUsage == GFU_PixelCount)
    1450             :     {
    1451           0 :         pszFieldName = "Histogram";
    1452             :         // Histogram is always float in HFA.
    1453           0 :         eFieldType = GFT_Real;
    1454             :     }
    1455           9 :     else if (eFieldUsage == GFU_Name)
    1456             :     {
    1457           0 :         pszFieldName = "Class_Names";
    1458             :     }
    1459             : 
    1460             :     // Check to see if a column with pszFieldName exists and create it
    1461             :     // if necessary.
    1462           9 :     HFAEntry *poColumn = poDT->GetNamedChild(pszFieldName);
    1463             : 
    1464           9 :     if (poColumn == nullptr || !EQUAL(poColumn->GetType(), "Edsc_Column"))
    1465           9 :         poColumn = HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo,
    1466             :                                  pszFieldName, "Edsc_Column", poDT);
    1467             : 
    1468           9 :     poColumn->SetIntField("numRows", nRows);
    1469           9 :     int nElementSize = 0;
    1470             : 
    1471           9 :     if (eFieldType == GFT_Integer)
    1472             :     {
    1473           3 :         nElementSize = sizeof(GInt32);
    1474           3 :         poColumn->SetStringField("dataType", "integer");
    1475             :     }
    1476           6 :     else if (eFieldType == GFT_Real)
    1477             :     {
    1478           3 :         nElementSize = sizeof(double);
    1479           3 :         poColumn->SetStringField("dataType", "real");
    1480             :     }
    1481           3 :     else if (eFieldType == GFT_String)
    1482             :     {
    1483             :         // Just have to guess here since we don't have any strings to check.
    1484           3 :         nElementSize = 10;
    1485           3 :         poColumn->SetStringField("dataType", "string");
    1486           3 :         poColumn->SetIntField("maxNumChars", nElementSize);
    1487             :     }
    1488             :     else
    1489             :     {
    1490             :         // Cannot deal with any of the others yet.
    1491           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1492             :                  "Writing this data type in a column is not supported "
    1493             :                  "for this Raster Attribute Table.");
    1494           0 :         return CE_Failure;
    1495             :     }
    1496             : 
    1497          18 :     const int nOffset = HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
    1498           9 :                                          nRows * nElementSize);
    1499           9 :     poColumn->SetIntField("columnDataPtr", nOffset);
    1500             : 
    1501           9 :     if (bConvertColors)
    1502             :     {
    1503             :         // GDAL Int column
    1504           0 :         eFieldType = GFT_Integer;
    1505             :     }
    1506             : 
    1507           9 :     AddColumn(pszFieldName, eFieldType, eFieldUsage, nOffset, nElementSize,
    1508             :               poColumn, false, bConvertColors);
    1509             : 
    1510           9 :     return CE_None;
    1511             : }
    1512             : 
    1513             : /************************************************************************/
    1514             : /*                          SetLinearBinning()                          */
    1515             : /************************************************************************/
    1516             : 
    1517           1 : CPLErr HFARasterAttributeTable::SetLinearBinning(double dfRow0MinIn,
    1518             :                                                  double dfBinSizeIn)
    1519             : {
    1520           1 :     if (eAccess == GA_ReadOnly)
    1521             :     {
    1522           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1523             :                  "Dataset not open in update mode");
    1524           0 :         return CE_Failure;
    1525             :     }
    1526             : 
    1527           1 :     bLinearBinning = true;
    1528           1 :     dfRow0Min = dfRow0MinIn;
    1529           1 :     dfBinSize = dfBinSizeIn;
    1530             : 
    1531             :     // Do we have a descriptor table already?
    1532           1 :     if (poDT == nullptr || !EQUAL(poDT->GetType(), "Edsc_Table"))
    1533           0 :         CreateDT();
    1534             : 
    1535             :     // We should have an Edsc_BinFunction.
    1536           1 :     HFAEntry *poBinFunction = poDT->GetNamedChild("#Bin_Function#");
    1537           1 :     if (poBinFunction == nullptr ||
    1538           0 :         !EQUAL(poBinFunction->GetType(), "Edsc_BinFunction"))
    1539             :     {
    1540             :         poBinFunction =
    1541           1 :             HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo, "#Bin_Function#",
    1542             :                           "Edsc_BinFunction", poDT);
    1543             :     }
    1544             : 
    1545             :     // Because of the BaseData we have to hardcode the size.
    1546           1 :     poBinFunction->MakeData(30);
    1547             : 
    1548           1 :     poBinFunction->SetStringField("binFunction", "direct");
    1549           1 :     poBinFunction->SetDoubleField("minLimit", dfRow0Min);
    1550           1 :     poBinFunction->SetDoubleField("maxLimit",
    1551           1 :                                   (nRows - 1) * dfBinSize + dfRow0Min);
    1552           1 :     poBinFunction->SetIntField("numBins", nRows);
    1553             : 
    1554           1 :     return CE_None;
    1555             : }
    1556             : 
    1557             : /************************************************************************/
    1558             : /*                          GetLinearBinning()                          */
    1559             : /************************************************************************/
    1560             : 
    1561           9 : int HFARasterAttributeTable::GetLinearBinning(double *pdfRow0Min,
    1562             :                                               double *pdfBinSize) const
    1563             : {
    1564           9 :     if (!bLinearBinning)
    1565           2 :         return FALSE;
    1566             : 
    1567           7 :     *pdfRow0Min = dfRow0Min;
    1568           7 :     *pdfBinSize = dfBinSize;
    1569             : 
    1570           7 :     return TRUE;
    1571             : }
    1572             : 
    1573             : /************************************************************************/
    1574             : /*                              Serialize()                             */
    1575             : /************************************************************************/
    1576             : 
    1577           1 : CPLXMLNode *HFARasterAttributeTable::Serialize() const
    1578             : {
    1579           2 :     if (GetRowCount() != 0 &&
    1580           1 :         GetColumnCount() > RAT_MAX_ELEM_FOR_CLONE / GetRowCount())
    1581           0 :         return nullptr;
    1582             : 
    1583           1 :     return GDALRasterAttributeTable::Serialize();
    1584             : }
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                              SetTableType()                             */
    1588             : /************************************************************************/
    1589             : 
    1590             : CPLErr
    1591         619 : HFARasterAttributeTable::SetTableType(const GDALRATTableType eInTableType)
    1592             : {
    1593         619 :     eTableType = eInTableType;
    1594         619 :     return CE_None;
    1595             : }
    1596             : 
    1597             : /************************************************************************/
    1598             : /*                              GetTableType()                             */
    1599             : /************************************************************************/
    1600             : 
    1601          25 : GDALRATTableType HFARasterAttributeTable::GetTableType() const
    1602             : {
    1603          25 :     return eTableType;
    1604             : }
    1605             : 
    1606           0 : void HFARasterAttributeTable::RemoveStatistics()
    1607             : {
    1608             :     // since we are storing the fields in a vector it will generally
    1609             :     // be faster to create a new vector and replace the old one
    1610             :     // rather than actually erasing columns.
    1611           0 :     std::vector<HFAAttributeField> aoNewFields;
    1612           0 :     for (const auto &field : aoFields)
    1613             :     {
    1614           0 :         switch (field.eUsage)
    1615             :         {
    1616           0 :             case GFU_PixelCount:
    1617             :             case GFU_Min:
    1618             :             case GFU_Max:
    1619             :             case GFU_RedMin:
    1620             :             case GFU_GreenMin:
    1621             :             case GFU_BlueMin:
    1622             :             case GFU_AlphaMin:
    1623             :             case GFU_RedMax:
    1624             :             case GFU_GreenMax:
    1625             :             case GFU_BlueMax:
    1626             :             case GFU_AlphaMax:
    1627             :             {
    1628           0 :                 break;
    1629             :             }
    1630             : 
    1631           0 :             default:
    1632           0 :                 if (field.sName != "Histogram")
    1633             :                 {
    1634           0 :                     aoNewFields.push_back(field);
    1635             :                 }
    1636             :         }
    1637             :     }
    1638           0 :     aoFields = std::move(aoNewFields);
    1639           0 : }
    1640             : 
    1641             : /************************************************************************/
    1642             : /*                           HFARasterBand()                            */
    1643             : /************************************************************************/
    1644             : 
    1645             : namespace
    1646             : {
    1647             : 
    1648             : // Convert 0..1 input color range to 0..255.
    1649             : // Clamp overflow and underflow.
    1650       11260 : short ColorToShort(double val)
    1651             : {
    1652       11260 :     const double dfScaled = val * 256.0;
    1653             :     // Clamp to [0..255].
    1654       11260 :     const double dfClamped = std::max(0.0, std::min(255.0, dfScaled));
    1655       11260 :     return static_cast<short>(dfClamped);
    1656             : }
    1657             : 
    1658             : }  // namespace
    1659             : 
    1660         674 : HFARasterBand::HFARasterBand(HFADataset *poDSIn, int nBandIn, int iOverview)
    1661             :     : poCT(nullptr),
    1662             :       // eHFADataType
    1663             :       nOverviews(-1), nThisOverview(iOverview), papoOverviewBands(nullptr),
    1664         674 :       hHFA(poDSIn->hHFA), bMetadataDirty(false), poDefaultRAT(nullptr)
    1665             : {
    1666         674 :     if (iOverview == -1)
    1667         618 :         poDS = poDSIn;
    1668             :     else
    1669          56 :         poDS = nullptr;
    1670             : 
    1671         674 :     nBand = nBandIn;
    1672         674 :     eAccess = poDSIn->GetAccess();
    1673             : 
    1674         674 :     int nCompression = 0;
    1675         674 :     HFAGetBandInfo(hHFA, nBand, &eHFADataType, &nBlockXSize, &nBlockYSize,
    1676             :                    &nCompression);
    1677             : 
    1678             :     // If this is an overview, we need to fetch the actual size,
    1679             :     // and block size.
    1680         674 :     if (iOverview > -1)
    1681             :     {
    1682             :         EPTType eHFADataTypeO;
    1683             : 
    1684          56 :         nOverviews = 0;
    1685          56 :         if (HFAGetOverviewInfo(hHFA, nBand, iOverview, &nRasterXSize,
    1686             :                                &nRasterYSize, &nBlockXSize, &nBlockYSize,
    1687          56 :                                &eHFADataTypeO) != CE_None)
    1688             :         {
    1689           0 :             nRasterXSize = 0;
    1690           0 :             nRasterYSize = 0;
    1691           0 :             return;
    1692             :         }
    1693             : 
    1694             :         // If we are an 8bit overview of a 1bit layer, we need to mark
    1695             :         // ourselves as being "resample: average_bit2grayscale".
    1696          56 :         if (eHFADataType == EPT_u1 && eHFADataTypeO == EPT_u8)
    1697             :         {
    1698           5 :             GDALMajorObject::SetMetadataItem("RESAMPLING",
    1699             :                                              "AVERAGE_BIT2GRAYSCALE");
    1700           5 :             GDALMajorObject::SetMetadataItem("NBITS", "8");
    1701             :         }
    1702          56 :         eHFADataType = eHFADataTypeO;
    1703             :     }
    1704             : 
    1705             :     // Set some other information.
    1706         674 :     if (nCompression != 0)
    1707          73 :         GDALMajorObject::SetMetadataItem("COMPRESSION", "RLE",
    1708             :                                          "IMAGE_STRUCTURE");
    1709             : 
    1710         674 :     switch (eHFADataType)
    1711             :     {
    1712         432 :         case EPT_u1:
    1713             :         case EPT_u2:
    1714             :         case EPT_u4:
    1715             :         case EPT_u8:
    1716         432 :             eDataType = GDT_Byte;
    1717         432 :             break;
    1718             : 
    1719          11 :         case EPT_s8:
    1720          11 :             eDataType = GDT_Int8;
    1721          11 :             break;
    1722             : 
    1723          36 :         case EPT_u16:
    1724          36 :             eDataType = GDT_UInt16;
    1725          36 :             break;
    1726             : 
    1727          23 :         case EPT_s16:
    1728          23 :             eDataType = GDT_Int16;
    1729          23 :             break;
    1730             : 
    1731          25 :         case EPT_u32:
    1732          25 :             eDataType = GDT_UInt32;
    1733          25 :             break;
    1734             : 
    1735          40 :         case EPT_s32:
    1736          40 :             eDataType = GDT_Int32;
    1737          40 :             break;
    1738             : 
    1739          33 :         case EPT_f32:
    1740          33 :             eDataType = GDT_Float32;
    1741          33 :             break;
    1742             : 
    1743          32 :         case EPT_f64:
    1744          32 :             eDataType = GDT_Float64;
    1745          32 :             break;
    1746             : 
    1747          21 :         case EPT_c64:
    1748          21 :             eDataType = GDT_CFloat32;
    1749          21 :             break;
    1750             : 
    1751          21 :         case EPT_c128:
    1752          21 :             eDataType = GDT_CFloat64;
    1753          21 :             break;
    1754             : 
    1755           0 :         default:
    1756           0 :             eDataType = GDT_Byte;
    1757             :             // This should really report an error, but this isn't
    1758             :             // so easy from within constructors.
    1759           0 :             CPLDebug("GDAL", "Unsupported pixel type in HFARasterBand: %d.",
    1760           0 :                      eHFADataType);
    1761           0 :             break;
    1762             :     }
    1763             : 
    1764         674 :     if (HFAGetDataTypeBits(eHFADataType) < 8)
    1765             :     {
    1766          18 :         GDALMajorObject::SetMetadataItem(
    1767          36 :             "NBITS", CPLString().Printf("%d", HFAGetDataTypeBits(eHFADataType)),
    1768             :             "IMAGE_STRUCTURE");
    1769             :     }
    1770             : 
    1771             :     // Collect color table if present.
    1772         674 :     double *padfRed = nullptr;
    1773         674 :     double *padfGreen = nullptr;
    1774         674 :     double *padfBlue = nullptr;
    1775         674 :     double *padfAlpha = nullptr;
    1776         674 :     double *padfBins = nullptr;
    1777         674 :     int nColors = 0;
    1778             : 
    1779        1292 :     if (iOverview == -1 &&
    1780         618 :         HFAGetPCT(hHFA, nBand, &nColors, &padfRed, &padfGreen, &padfBlue,
    1781        1292 :                   &padfAlpha, &padfBins) == CE_None &&
    1782          10 :         nColors > 0)
    1783             :     {
    1784          10 :         poCT = new GDALColorTable();
    1785        2825 :         for (int iColor = 0; iColor < nColors; iColor++)
    1786             :         {
    1787             :             // The following mapping assigns "equal sized" section of
    1788             :             // the [0...1] range to each possible output value and avoid
    1789             :             // rounding issues for the "normal" values generated using n/255.
    1790             :             // See bug #1732 for some discussion.
    1791        2815 :             GDALColorEntry sEntry = {ColorToShort(padfRed[iColor]),
    1792        5630 :                                      ColorToShort(padfGreen[iColor]),
    1793        5630 :                                      ColorToShort(padfBlue[iColor]),
    1794        2815 :                                      ColorToShort(padfAlpha[iColor])};
    1795             : 
    1796        2815 :             if (padfBins != nullptr)
    1797             :             {
    1798         300 :                 const double dfIdx = padfBins[iColor];
    1799         300 :                 if (!(dfIdx >= 0.0 && dfIdx <= 65535.0))
    1800             :                 {
    1801           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1802             :                              "Invalid index padfBins[%d] = %g", iColor, dfIdx);
    1803           0 :                     break;
    1804             :                 }
    1805             :                 else
    1806             :                 {
    1807         300 :                     poCT->SetColorEntry(static_cast<int>(dfIdx), &sEntry);
    1808             :                 }
    1809             :             }
    1810             :             else
    1811             :             {
    1812        2515 :                 poCT->SetColorEntry(iColor, &sEntry);
    1813             :             }
    1814             :         }
    1815             :     }
    1816             : }
    1817             : 
    1818             : /************************************************************************/
    1819             : /*                           ~HFARasterBand()                           */
    1820             : /************************************************************************/
    1821             : 
    1822        1348 : HFARasterBand::~HFARasterBand()
    1823             : 
    1824             : {
    1825         674 :     FlushCache(true);
    1826             : 
    1827         729 :     for (int iOvIndex = 0; iOvIndex < nOverviews; iOvIndex++)
    1828             :     {
    1829          55 :         delete papoOverviewBands[iOvIndex];
    1830             :     }
    1831         674 :     CPLFree(papoOverviewBands);
    1832             : 
    1833         674 :     if (poCT != nullptr)
    1834           9 :         delete poCT;
    1835             : 
    1836         674 :     if (poDefaultRAT)
    1837         618 :         delete poDefaultRAT;
    1838        1348 : }
    1839             : 
    1840             : /************************************************************************/
    1841             : /*                          ReadAuxMetadata()                           */
    1842             : /************************************************************************/
    1843             : 
    1844         618 : void HFARasterBand::ReadAuxMetadata()
    1845             : 
    1846             : {
    1847             :     // Only load metadata for full resolution layer.
    1848         618 :     if (nThisOverview != -1)
    1849           0 :         return;
    1850             : 
    1851         618 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
    1852             : 
    1853         618 :     const char *const *pszAuxMetaData = GetHFAAuxMetaDataList();
    1854        9270 :     for (int i = 0; pszAuxMetaData[i] != nullptr; i += 4)
    1855             :     {
    1856             :         HFAEntry *poEntry;
    1857        8652 :         if (strlen(pszAuxMetaData[i]) > 0)
    1858             :         {
    1859        8034 :             poEntry = poBand->poNode->GetNamedChild(pszAuxMetaData[i]);
    1860        8034 :             if (poEntry == nullptr)
    1861        7104 :                 continue;
    1862             :         }
    1863             :         else
    1864             :         {
    1865         618 :             poEntry = poBand->poNode;
    1866         618 :             assert(poEntry);
    1867             :         }
    1868             : 
    1869        1548 :         const char *pszFieldName = pszAuxMetaData[i + 1] + 1;
    1870             : 
    1871        1548 :         switch (pszAuxMetaData[i + 1][0])
    1872             :         {
    1873         692 :             case 'd':
    1874             :             {
    1875        1384 :                 CPLString osValueList;
    1876             : 
    1877         692 :                 CPLErr eErr = CE_None;
    1878         692 :                 int nCount = poEntry->GetFieldCount(pszFieldName, &eErr);
    1879         692 :                 if (nCount > 65536)
    1880             :                 {
    1881           0 :                     nCount = 65536;
    1882           0 :                     CPLDebug("HFA", "Limiting %s to %d entries",
    1883           0 :                              pszAuxMetaData[i + 2], nCount);
    1884             :                 }
    1885        1326 :                 for (int iValue = 0; eErr == CE_None && iValue < nCount;
    1886             :                      iValue++)
    1887             :                 {
    1888         660 :                     CPLString osSubFieldName;
    1889         660 :                     osSubFieldName.Printf("%s[%d]", pszFieldName, iValue);
    1890             :                     const double dfValue =
    1891         660 :                         poEntry->GetDoubleField(osSubFieldName, &eErr);
    1892         660 :                     if (eErr != CE_None)
    1893          26 :                         break;
    1894             : 
    1895         634 :                     char szValueAsString[100] = {};
    1896         634 :                     CPLsnprintf(szValueAsString, sizeof(szValueAsString),
    1897             :                                 "%.14g", dfValue);
    1898             : 
    1899         634 :                     if (iValue > 0)
    1900           2 :                         osValueList += ",";
    1901         634 :                     osValueList += szValueAsString;
    1902             :                 }
    1903         692 :                 if (eErr == CE_None)
    1904         666 :                     SetMetadataItem(pszAuxMetaData[i + 2], osValueList);
    1905             :             }
    1906         692 :             break;
    1907         229 :             case 'i':
    1908             :             case 'l':
    1909             :             {
    1910         458 :                 CPLString osValueList;
    1911             : 
    1912         229 :                 CPLErr eErr = CE_None;
    1913         229 :                 int nCount = poEntry->GetFieldCount(pszFieldName, &eErr);
    1914         229 :                 if (nCount > 65536)
    1915             :                 {
    1916           0 :                     nCount = 65536;
    1917           0 :                     CPLDebug("HFA", "Limiting %s to %d entries",
    1918           0 :                              pszAuxMetaData[i + 2], nCount);
    1919             :                 }
    1920         445 :                 for (int iValue = 0; eErr == CE_None && iValue < nCount;
    1921             :                      iValue++)
    1922             :                 {
    1923         229 :                     CPLString osSubFieldName;
    1924         229 :                     osSubFieldName.Printf("%s[%d]", pszFieldName, iValue);
    1925         229 :                     int nValue = poEntry->GetIntField(osSubFieldName, &eErr);
    1926         229 :                     if (eErr != CE_None)
    1927          13 :                         break;
    1928             : 
    1929         216 :                     char szValueAsString[100] = {};
    1930         216 :                     snprintf(szValueAsString, sizeof(szValueAsString), "%d",
    1931             :                              nValue);
    1932             : 
    1933         216 :                     if (iValue > 0)
    1934           0 :                         osValueList += ",";
    1935         216 :                     osValueList += szValueAsString;
    1936             :                 }
    1937         229 :                 if (eErr == CE_None)
    1938         216 :                     SetMetadataItem(pszAuxMetaData[i + 2], osValueList);
    1939             :             }
    1940         229 :             break;
    1941         627 :             case 's':
    1942             :             case 'e':
    1943             :             {
    1944         627 :                 CPLErr eErr = CE_None;
    1945             :                 const char *pszValue =
    1946         627 :                     poEntry->GetStringField(pszFieldName, &eErr);
    1947         627 :                 if (eErr == CE_None)
    1948         627 :                     SetMetadataItem(pszAuxMetaData[i + 2], pszValue);
    1949             :             }
    1950         627 :             break;
    1951           0 :             default:
    1952           0 :                 CPLAssert(false);
    1953             :         }
    1954             :     }
    1955             : 
    1956             :     /* if we have a default RAT we can now set its thematic/athematic state
    1957             :        from the metadata we just read in */
    1958         618 :     if (GetDefaultRAT())
    1959             :     {
    1960         618 :         const char *psLayerType = GetMetadataItem("LAYER_TYPE", "");
    1961         618 :         if (psLayerType)
    1962             :         {
    1963        1236 :             GetDefaultRAT()->SetTableType(EQUALN(psLayerType, "athematic", 9)
    1964             :                                               ? GRTT_ATHEMATIC
    1965         618 :                                               : GRTT_THEMATIC);
    1966             :         }
    1967             :     }
    1968             : }
    1969             : 
    1970             : /************************************************************************/
    1971             : /*                       ReadHistogramMetadata()                        */
    1972             : /************************************************************************/
    1973             : 
    1974         618 : void HFARasterBand::ReadHistogramMetadata()
    1975             : 
    1976             : {
    1977             :     // Only load metadata for full resolution layer.
    1978         618 :     if (nThisOverview != -1)
    1979           0 :         return;
    1980             : 
    1981         618 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
    1982             : 
    1983             :     HFAEntry *poEntry =
    1984         618 :         poBand->poNode->GetNamedChild("Descriptor_Table.Histogram");
    1985         618 :     if (poEntry == nullptr)
    1986         541 :         return;
    1987             : 
    1988          77 :     int nNumBins = poEntry->GetIntField("numRows");
    1989          77 :     if (nNumBins < 0)
    1990           0 :         return;
    1991             :     // TODO(schwehr): Can we do a better/tighter check?
    1992          77 :     if (nNumBins > 1000000)
    1993             :     {
    1994           0 :         CPLError(CE_Failure, CPLE_FileIO, "Unreasonably large histogram: %d",
    1995             :                  nNumBins);
    1996           0 :         return;
    1997             :     }
    1998             : 
    1999             :     // Fetch the histogram values.
    2000          77 :     const int nOffset = poEntry->GetIntField("columnDataPtr");
    2001          77 :     const char *pszType = poEntry->GetStringField("dataType");
    2002          77 :     int nBinSize = 4;
    2003             : 
    2004          77 :     if (pszType != nullptr && STARTS_WITH_CI(pszType, "real"))
    2005          75 :         nBinSize = 8;
    2006             : 
    2007             :     GUIntBig *panHistValues = static_cast<GUIntBig *>(
    2008          77 :         VSI_MALLOC2_VERBOSE(sizeof(GUIntBig), nNumBins));
    2009             :     GByte *pabyWorkBuf =
    2010          77 :         static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nBinSize, nNumBins));
    2011             : 
    2012          77 :     if (panHistValues == nullptr || pabyWorkBuf == nullptr)
    2013             :     {
    2014           0 :         VSIFree(panHistValues);
    2015           0 :         VSIFree(pabyWorkBuf);
    2016           0 :         return;
    2017             :     }
    2018             : 
    2019         154 :     if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
    2020             :         static_cast<int>(
    2021          77 :             VSIFReadL(pabyWorkBuf, nBinSize, nNumBins, hHFA->fp)) != nNumBins)
    2022             :     {
    2023           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot read histogram values.");
    2024           0 :         CPLFree(panHistValues);
    2025           0 :         CPLFree(pabyWorkBuf);
    2026           0 :         return;
    2027             :     }
    2028             : 
    2029             :     // Swap into local order.
    2030       16620 :     for (int i = 0; i < nNumBins; i++)
    2031             :         HFAStandard(nBinSize, pabyWorkBuf + i * nBinSize);
    2032             : 
    2033          77 :     if (nBinSize == 8)  // Source is doubles.
    2034             :     {
    2035          75 :         const double *padfWorkBuf = reinterpret_cast<double *>(pabyWorkBuf);
    2036       16106 :         for (int i = 0; i < nNumBins; i++)
    2037             :         {
    2038       16031 :             const double dfNumber = padfWorkBuf[i];
    2039       16031 :             if (dfNumber >=
    2040       16031 :                     static_cast<double>(std::numeric_limits<GUIntBig>::max()) ||
    2041             :                 dfNumber <
    2042       32062 :                     static_cast<double>(std::numeric_limits<GUIntBig>::min()) ||
    2043       16031 :                 std::isnan(dfNumber))
    2044             :             {
    2045           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Out of range hist vals.");
    2046           0 :                 CPLFree(panHistValues);
    2047           0 :                 CPLFree(pabyWorkBuf);
    2048           0 :                 return;
    2049             :             }
    2050       16031 :             panHistValues[i] = static_cast<GUIntBig>(dfNumber);
    2051             :         }
    2052             :     }
    2053             :     else  // Source is 32bit integers.
    2054             :     {
    2055           2 :         const int *panWorkBuf = reinterpret_cast<int *>(pabyWorkBuf);
    2056         514 :         for (int i = 0; i < nNumBins; i++)
    2057             :         {
    2058         512 :             const int nNumber = panWorkBuf[i];
    2059             :             // Positive int should always fit.
    2060         512 :             if (nNumber < 0)
    2061             :             {
    2062           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Out of range hist vals.");
    2063           0 :                 CPLFree(panHistValues);
    2064           0 :                 CPLFree(pabyWorkBuf);
    2065           0 :                 return;
    2066             :             }
    2067         512 :             panHistValues[i] = static_cast<GUIntBig>(nNumber);
    2068             :         }
    2069             :     }
    2070             : 
    2071          77 :     CPLFree(pabyWorkBuf);
    2072          77 :     pabyWorkBuf = nullptr;
    2073             : 
    2074             :     // Do we have unique values for the bins?
    2075          77 :     double *padfBinValues = nullptr;
    2076             :     HFAEntry *poBinEntry =
    2077          77 :         poBand->poNode->GetNamedChild("Descriptor_Table.#Bin_Function840#");
    2078             : 
    2079          90 :     if (poBinEntry != nullptr &&
    2080          13 :         EQUAL(poBinEntry->GetType(), "Edsc_BinFunction840"))
    2081             :     {
    2082             :         const char *pszValue =
    2083          13 :             poBinEntry->GetStringField("binFunction.type.string");
    2084          13 :         if (pszValue && EQUAL(pszValue, "BFUnique"))
    2085          13 :             padfBinValues = HFAReadBFUniqueBins(poBinEntry, nNumBins);
    2086             :     }
    2087             : 
    2088          77 :     if (padfBinValues)
    2089             :     {
    2090          13 :         int nMaxValue = 0;
    2091          13 :         int nMinValue = 1000000;
    2092             : 
    2093        1469 :         for (int i = 0; i < nNumBins; i++)
    2094             :         {
    2095        1456 :             const double dfCurrent = padfBinValues[i];
    2096             : 
    2097        1456 :             if (dfCurrent != floor(dfCurrent) || /* not an integer value */
    2098        1456 :                 dfCurrent < 0.0 || dfCurrent > 1000.0)
    2099             :             {
    2100           0 :                 CPLFree(padfBinValues);
    2101           0 :                 CPLFree(panHistValues);
    2102           0 :                 CPLDebug("HFA",
    2103             :                          "Unable to offer histogram because unique values "
    2104             :                          "list is not convenient to reform as HISTOBINVALUES.");
    2105           0 :                 return;
    2106             :             }
    2107             : 
    2108        1456 :             nMaxValue = std::max(nMaxValue, static_cast<int>(dfCurrent));
    2109        1456 :             nMinValue = std::min(nMinValue, static_cast<int>(dfCurrent));
    2110             :         }
    2111             : 
    2112          13 :         const int nNewBins = nMaxValue + 1;
    2113             :         GUIntBig *panNewHistValues =
    2114          13 :             static_cast<GUIntBig *>(CPLCalloc(sizeof(GUIntBig), nNewBins));
    2115             : 
    2116        1469 :         for (int i = 0; i < nNumBins; i++)
    2117        1456 :             panNewHistValues[static_cast<int>(padfBinValues[i])] =
    2118        1456 :                 panHistValues[i];
    2119             : 
    2120          13 :         CPLFree(panHistValues);
    2121          13 :         panHistValues = panNewHistValues;
    2122          13 :         nNumBins = nNewBins;
    2123             : 
    2124          13 :         SetMetadataItem("STATISTICS_HISTOMIN", "0");
    2125          13 :         SetMetadataItem("STATISTICS_HISTOMAX",
    2126          26 :                         CPLString().Printf("%d", nMaxValue));
    2127          13 :         SetMetadataItem("STATISTICS_HISTONUMBINS",
    2128          26 :                         CPLString().Printf("%d", nMaxValue + 1));
    2129             : 
    2130          13 :         CPLFree(padfBinValues);
    2131          13 :         padfBinValues = nullptr;
    2132             :     }
    2133             : 
    2134             :     // Format into HISTOBINVALUES text format.
    2135          77 :     unsigned int nBufSize = 1024;
    2136          77 :     char *pszBinValues = static_cast<char *>(CPLMalloc(nBufSize));
    2137          77 :     pszBinValues[0] = 0;
    2138          77 :     int nBinValuesLen = 0;
    2139             : 
    2140       18103 :     for (int nBin = 0; nBin < nNumBins; ++nBin)
    2141             :     {
    2142       18026 :         char szBuf[32] = {};
    2143       18026 :         snprintf(szBuf, 31, CPL_FRMT_GUIB, panHistValues[nBin]);
    2144       18026 :         if ((nBinValuesLen + strlen(szBuf) + 2) > nBufSize)
    2145             :         {
    2146           6 :             nBufSize *= 2;
    2147             :             char *pszNewBinValues = static_cast<char *>(
    2148           6 :                 VSI_REALLOC_VERBOSE(pszBinValues, nBufSize));
    2149           6 :             if (pszNewBinValues == nullptr)
    2150             :             {
    2151           0 :                 break;
    2152             :             }
    2153           6 :             pszBinValues = pszNewBinValues;
    2154             :         }
    2155       18026 :         strcat(pszBinValues + nBinValuesLen, szBuf);
    2156       18026 :         strcat(pszBinValues + nBinValuesLen, "|");
    2157       18026 :         nBinValuesLen += static_cast<int>(strlen(pszBinValues + nBinValuesLen));
    2158             :     }
    2159             : 
    2160          77 :     SetMetadataItem("STATISTICS_HISTOBINVALUES", pszBinValues);
    2161          77 :     CPLFree(panHistValues);
    2162          77 :     CPLFree(pszBinValues);
    2163             : }
    2164             : 
    2165             : /************************************************************************/
    2166             : /*                             GetNoDataValue()                         */
    2167             : /************************************************************************/
    2168             : 
    2169         193 : double HFARasterBand::GetNoDataValue(int *pbSuccess)
    2170             : 
    2171             : {
    2172         193 :     double dfNoData = 0.0;
    2173             : 
    2174         193 :     if (HFAGetBandNoData(hHFA, nBand, &dfNoData))
    2175             :     {
    2176          21 :         if (pbSuccess)
    2177          17 :             *pbSuccess = TRUE;
    2178          21 :         return dfNoData;
    2179             :     }
    2180             : 
    2181         172 :     return GDALPamRasterBand::GetNoDataValue(pbSuccess);
    2182             : }
    2183             : 
    2184             : /************************************************************************/
    2185             : /*                             SetNoDataValue()                         */
    2186             : /************************************************************************/
    2187             : 
    2188           9 : CPLErr HFARasterBand::SetNoDataValue(double dfValue)
    2189             : {
    2190           9 :     return HFASetBandNoData(hHFA, nBand, dfValue);
    2191             : }
    2192             : 
    2193             : /************************************************************************/
    2194             : /*                             GetMinimum()                             */
    2195             : /************************************************************************/
    2196             : 
    2197           4 : double HFARasterBand::GetMinimum(int *pbSuccess)
    2198             : 
    2199             : {
    2200           4 :     const char *pszValue = GetMetadataItem("STATISTICS_MINIMUM");
    2201             : 
    2202           4 :     if (pszValue != nullptr)
    2203             :     {
    2204           3 :         if (pbSuccess)
    2205           3 :             *pbSuccess = TRUE;
    2206           3 :         return CPLAtofM(pszValue);
    2207             :     }
    2208             : 
    2209           1 :     return GDALRasterBand::GetMinimum(pbSuccess);
    2210             : }
    2211             : 
    2212             : /************************************************************************/
    2213             : /*                             GetMaximum()                             */
    2214             : /************************************************************************/
    2215             : 
    2216           4 : double HFARasterBand::GetMaximum(int *pbSuccess)
    2217             : 
    2218             : {
    2219           4 :     const char *pszValue = GetMetadataItem("STATISTICS_MAXIMUM");
    2220             : 
    2221           4 :     if (pszValue != nullptr)
    2222             :     {
    2223           3 :         if (pbSuccess)
    2224           3 :             *pbSuccess = TRUE;
    2225           3 :         return CPLAtofM(pszValue);
    2226             :     }
    2227             : 
    2228           1 :     return GDALRasterBand::GetMaximum(pbSuccess);
    2229             : }
    2230             : 
    2231             : /************************************************************************/
    2232             : /*                         EstablishOverviews()                         */
    2233             : /*                                                                      */
    2234             : /*      Delayed population of overview information.                     */
    2235             : /************************************************************************/
    2236             : 
    2237         288 : void HFARasterBand::EstablishOverviews()
    2238             : 
    2239             : {
    2240         288 :     if (nOverviews != -1)
    2241         164 :         return;
    2242             : 
    2243         124 :     nOverviews = HFAGetOverviewCount(hHFA, nBand);
    2244         124 :     if (nOverviews > 0)
    2245             :     {
    2246          30 :         papoOverviewBands = static_cast<HFARasterBand **>(
    2247          30 :             CPLMalloc(sizeof(void *) * nOverviews));
    2248             : 
    2249          74 :         for (int iOvIndex = 0; iOvIndex < nOverviews; iOvIndex++)
    2250             :         {
    2251          44 :             papoOverviewBands[iOvIndex] = new HFARasterBand(
    2252          44 :                 cpl::down_cast<HFADataset *>(poDS), nBand, iOvIndex);
    2253          44 :             if (papoOverviewBands[iOvIndex]->GetXSize() == 0)
    2254             :             {
    2255           0 :                 delete papoOverviewBands[iOvIndex];
    2256           0 :                 papoOverviewBands[iOvIndex] = nullptr;
    2257             :             }
    2258             :         }
    2259             :     }
    2260             : }
    2261             : 
    2262             : /************************************************************************/
    2263             : /*                          GetOverviewCount()                          */
    2264             : /************************************************************************/
    2265             : 
    2266         192 : int HFARasterBand::GetOverviewCount()
    2267             : 
    2268             : {
    2269         192 :     EstablishOverviews();
    2270             : 
    2271         192 :     if (nOverviews == 0)
    2272          91 :         return GDALRasterBand::GetOverviewCount();
    2273             : 
    2274         101 :     return nOverviews;
    2275             : }
    2276             : 
    2277             : /************************************************************************/
    2278             : /*                            GetOverview()                             */
    2279             : /************************************************************************/
    2280             : 
    2281          84 : GDALRasterBand *HFARasterBand::GetOverview(int i)
    2282             : 
    2283             : {
    2284          84 :     EstablishOverviews();
    2285             : 
    2286          84 :     if (nOverviews == 0)
    2287           0 :         return GDALRasterBand::GetOverview(i);
    2288          84 :     else if (i < 0 || i >= nOverviews)
    2289           0 :         return nullptr;
    2290             :     else
    2291          84 :         return papoOverviewBands[i];
    2292             : }
    2293             : 
    2294             : /************************************************************************/
    2295             : /*                             IReadBlock()                             */
    2296             : /************************************************************************/
    2297             : 
    2298        1354 : CPLErr HFARasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
    2299             : 
    2300             : {
    2301        1354 :     CPLErr eErr = CE_None;
    2302             : 
    2303        1354 :     if (nThisOverview == -1)
    2304        1330 :         eErr = HFAGetRasterBlockEx(hHFA, nBand, nBlockXOff, nBlockYOff, pImage,
    2305        1330 :                                    nBlockXSize * nBlockYSize *
    2306        1330 :                                        GDALGetDataTypeSizeBytes(eDataType));
    2307             :     else
    2308          24 :         eErr = HFAGetOverviewRasterBlockEx(
    2309             :             hHFA, nBand, nThisOverview, nBlockXOff, nBlockYOff, pImage,
    2310          24 :             nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType));
    2311             : 
    2312        1354 :     if (eErr == CE_None && eHFADataType == EPT_u4)
    2313             :     {
    2314           2 :         GByte *pabyData = static_cast<GByte *>(pImage);
    2315             : 
    2316        4098 :         for (int ii = nBlockXSize * nBlockYSize - 2; ii >= 0; ii -= 2)
    2317             :         {
    2318        4096 :             int k = ii >> 1;
    2319        4096 :             pabyData[ii + 1] = (pabyData[k] >> 4) & 0xf;
    2320        4096 :             pabyData[ii] = (pabyData[k]) & 0xf;
    2321             :         }
    2322             :     }
    2323        1354 :     if (eErr == CE_None && eHFADataType == EPT_u2)
    2324             :     {
    2325           6 :         GByte *pabyData = static_cast<GByte *>(pImage);
    2326             : 
    2327        6150 :         for (int ii = nBlockXSize * nBlockYSize - 4; ii >= 0; ii -= 4)
    2328             :         {
    2329        6144 :             int k = ii >> 2;
    2330        6144 :             pabyData[ii + 3] = (pabyData[k] >> 6) & 0x3;
    2331        6144 :             pabyData[ii + 2] = (pabyData[k] >> 4) & 0x3;
    2332        6144 :             pabyData[ii + 1] = (pabyData[k] >> 2) & 0x3;
    2333        6144 :             pabyData[ii] = (pabyData[k]) & 0x3;
    2334             :         }
    2335             :     }
    2336        1354 :     if (eErr == CE_None && eHFADataType == EPT_u1)
    2337             :     {
    2338          52 :         GByte *pabyData = static_cast<GByte *>(pImage);
    2339             : 
    2340      213044 :         for (int ii = nBlockXSize * nBlockYSize - 1; ii >= 0; ii--)
    2341             :         {
    2342      212992 :             if ((pabyData[ii >> 3] & (1 << (ii & 0x7))))
    2343      160690 :                 pabyData[ii] = 1;
    2344             :             else
    2345       52302 :                 pabyData[ii] = 0;
    2346             :         }
    2347             :     }
    2348             : 
    2349        1354 :     return eErr;
    2350             : }
    2351             : 
    2352             : /************************************************************************/
    2353             : /*                            IWriteBlock()                             */
    2354             : /************************************************************************/
    2355             : 
    2356         134 : CPLErr HFARasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
    2357             : 
    2358             : {
    2359         134 :     GByte *pabyOutBuf = static_cast<GByte *>(pImage);
    2360             : 
    2361             :     // Do we need to pack 1/2/4 bit data?
    2362             :     // TODO(schwehr): Make symbolic constants with explanations.
    2363         134 :     if (eHFADataType == EPT_u1 || eHFADataType == EPT_u2 ||
    2364         130 :         eHFADataType == EPT_u4)
    2365             :     {
    2366           6 :         const int nPixCount = nBlockXSize * nBlockYSize;
    2367           6 :         pabyOutBuf = static_cast<GByte *>(VSIMalloc2(nBlockXSize, nBlockYSize));
    2368           6 :         if (pabyOutBuf == nullptr)
    2369           0 :             return CE_Failure;
    2370             : 
    2371           6 :         if (eHFADataType == EPT_u1)
    2372             :         {
    2373        1026 :             for (int ii = 0; ii < nPixCount - 7; ii += 8)
    2374             :             {
    2375        1024 :                 const int k = ii >> 3;
    2376             :                 // TODO(schwehr): Create a temp for (GByte *)pImage.
    2377        1024 :                 pabyOutBuf[k] = (((GByte *)pImage)[ii] & 0x1) |
    2378        1024 :                                 ((((GByte *)pImage)[ii + 1] & 0x1) << 1) |
    2379        1024 :                                 ((((GByte *)pImage)[ii + 2] & 0x1) << 2) |
    2380        1024 :                                 ((((GByte *)pImage)[ii + 3] & 0x1) << 3) |
    2381        1024 :                                 ((((GByte *)pImage)[ii + 4] & 0x1) << 4) |
    2382        1024 :                                 ((((GByte *)pImage)[ii + 5] & 0x1) << 5) |
    2383        1024 :                                 ((((GByte *)pImage)[ii + 6] & 0x1) << 6) |
    2384        1024 :                                 ((((GByte *)pImage)[ii + 7] & 0x1) << 7);
    2385             :             }
    2386             :         }
    2387           4 :         else if (eHFADataType == EPT_u2)
    2388             :         {
    2389        2050 :             for (int ii = 0; ii < nPixCount - 3; ii += 4)
    2390             :             {
    2391        2048 :                 const int k = ii >> 2;
    2392        2048 :                 pabyOutBuf[k] = (((GByte *)pImage)[ii] & 0x3) |
    2393        2048 :                                 ((((GByte *)pImage)[ii + 1] & 0x3) << 2) |
    2394        2048 :                                 ((((GByte *)pImage)[ii + 2] & 0x3) << 4) |
    2395        2048 :                                 ((((GByte *)pImage)[ii + 3] & 0x3) << 6);
    2396             :             }
    2397             :         }
    2398           2 :         else if (eHFADataType == EPT_u4)
    2399             :         {
    2400        4098 :             for (int ii = 0; ii < nPixCount - 1; ii += 2)
    2401             :             {
    2402        4096 :                 const int k = ii >> 1;
    2403        4096 :                 pabyOutBuf[k] = (((GByte *)pImage)[ii] & 0xf) |
    2404        4096 :                                 ((((GByte *)pImage)[ii + 1] & 0xf) << 4);
    2405             :             }
    2406             :         }
    2407             :     }
    2408             : 
    2409             :     // Actually write out.
    2410             :     const CPLErr nRetCode =
    2411         134 :         nThisOverview == -1
    2412         134 :             ? HFASetRasterBlock(hHFA, nBand, nBlockXOff, nBlockYOff, pabyOutBuf)
    2413          29 :             : HFASetOverviewRasterBlock(hHFA, nBand, nThisOverview, nBlockXOff,
    2414         134 :                                         nBlockYOff, pabyOutBuf);
    2415             : 
    2416         134 :     if (pabyOutBuf != pImage)
    2417           6 :         CPLFree(pabyOutBuf);
    2418             : 
    2419         134 :     return nRetCode;
    2420             : }
    2421             : 
    2422             : /************************************************************************/
    2423             : /*                         GetDescription()                             */
    2424             : /************************************************************************/
    2425             : 
    2426         213 : const char *HFARasterBand::GetDescription() const
    2427             : {
    2428         213 :     const char *pszName = HFAGetBandName(hHFA, nBand);
    2429             : 
    2430         213 :     if (pszName == nullptr)
    2431           0 :         return GDALPamRasterBand::GetDescription();
    2432             : 
    2433         213 :     return pszName;
    2434             : }
    2435             : 
    2436             : /************************************************************************/
    2437             : /*                         SetDescription()                             */
    2438             : /************************************************************************/
    2439           7 : void HFARasterBand::SetDescription(const char *pszName)
    2440             : {
    2441           7 :     if (strlen(pszName) > 0)
    2442           7 :         HFASetBandName(hHFA, nBand, pszName);
    2443           7 : }
    2444             : 
    2445             : /************************************************************************/
    2446             : /*                       GetColorInterpretation()                       */
    2447             : /************************************************************************/
    2448             : 
    2449          55 : GDALColorInterp HFARasterBand::GetColorInterpretation()
    2450             : 
    2451             : {
    2452          55 :     if (poCT != nullptr)
    2453           4 :         return GCI_PaletteIndex;
    2454             : 
    2455          51 :     return GCI_Undefined;
    2456             : }
    2457             : 
    2458             : /************************************************************************/
    2459             : /*                           GetColorTable()                            */
    2460             : /************************************************************************/
    2461             : 
    2462          40 : GDALColorTable *HFARasterBand::GetColorTable()
    2463             : {
    2464          40 :     return poCT;
    2465             : }
    2466             : 
    2467             : /************************************************************************/
    2468             : /*                           SetColorTable()                            */
    2469             : /************************************************************************/
    2470             : 
    2471           3 : CPLErr HFARasterBand::SetColorTable(GDALColorTable *poCTable)
    2472             : 
    2473             : {
    2474           3 :     if (GetAccess() == GA_ReadOnly)
    2475             :     {
    2476           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    2477             :                  "Unable to set color table on read-only file.");
    2478           0 :         return CE_Failure;
    2479             :     }
    2480             : 
    2481             :     // Special case if we are clearing the color table.
    2482           3 :     if (poCTable == nullptr)
    2483             :     {
    2484           2 :         delete poCT;
    2485           2 :         poCT = nullptr;
    2486             : 
    2487           2 :         HFASetPCT(hHFA, nBand, 0, nullptr, nullptr, nullptr, nullptr);
    2488             : 
    2489           2 :         return CE_None;
    2490             :     }
    2491             : 
    2492             :     // Write out the colortable, and update the configuration.
    2493           1 :     int nColors = poCTable->GetColorEntryCount();
    2494             : 
    2495             :     /* -------------------------------------------------------------------- */
    2496             :     /*      If we already have a non-empty RAT set and it's smaller than    */
    2497             :     /*      the colour table, and all the trailing CT entries are the same, */
    2498             :     /*      truncate the colour table. Helps when RATs travel via GTiff.    */
    2499             :     /* -------------------------------------------------------------------- */
    2500           1 :     const GDALRasterAttributeTable *poRAT = GetDefaultRAT();
    2501           1 :     if (poRAT != nullptr && poRAT->GetRowCount() > 0 &&
    2502           0 :         poRAT->GetRowCount() < nColors)
    2503             :     {
    2504           0 :         bool match = true;
    2505             :         const GDALColorEntry *color1 =
    2506           0 :             poCTable->GetColorEntry(poRAT->GetRowCount());
    2507           0 :         for (int i = poRAT->GetRowCount() + 1; match && i < nColors; i++)
    2508             :         {
    2509           0 :             const GDALColorEntry *color2 = poCTable->GetColorEntry(i);
    2510           0 :             match = (color1->c1 == color2->c1 && color1->c2 == color2->c2 &&
    2511           0 :                      color1->c3 == color2->c3 && color1->c4 == color2->c4);
    2512             :         }
    2513           0 :         if (match)
    2514             :         {
    2515           0 :             CPLDebug("HFA",
    2516             :                      "SetColorTable: Truncating PCT size (%d) to RAT size (%d)",
    2517           0 :                      nColors, poRAT->GetRowCount());
    2518           0 :             nColors = poRAT->GetRowCount();
    2519             :         }
    2520             :     }
    2521             : 
    2522             :     double *padfRed =
    2523           1 :         static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
    2524             :     double *padfGreen =
    2525           1 :         static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
    2526             :     double *padfBlue =
    2527           1 :         static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
    2528             :     double *padfAlpha =
    2529           1 :         static_cast<double *>(CPLMalloc(sizeof(double) * nColors));
    2530             : 
    2531         257 :     for (int iColor = 0; iColor < nColors; iColor++)
    2532             :     {
    2533             :         GDALColorEntry sRGB;
    2534             : 
    2535         256 :         poCTable->GetColorEntryAsRGB(iColor, &sRGB);
    2536             : 
    2537         256 :         padfRed[iColor] = sRGB.c1 / 255.0;
    2538         256 :         padfGreen[iColor] = sRGB.c2 / 255.0;
    2539         256 :         padfBlue[iColor] = sRGB.c3 / 255.0;
    2540         256 :         padfAlpha[iColor] = sRGB.c4 / 255.0;
    2541             :     }
    2542             : 
    2543           1 :     HFASetPCT(hHFA, nBand, nColors, padfRed, padfGreen, padfBlue, padfAlpha);
    2544             : 
    2545           1 :     CPLFree(padfRed);
    2546           1 :     CPLFree(padfGreen);
    2547           1 :     CPLFree(padfBlue);
    2548           1 :     CPLFree(padfAlpha);
    2549             : 
    2550           1 :     if (poCT)
    2551           0 :         delete poCT;
    2552             : 
    2553           1 :     poCT = poCTable->Clone();
    2554             : 
    2555           1 :     return CE_None;
    2556             : }
    2557             : 
    2558             : /************************************************************************/
    2559             : /*                            SetMetadata()                             */
    2560             : /************************************************************************/
    2561             : 
    2562          21 : CPLErr HFARasterBand::SetMetadata(char **papszMDIn, const char *pszDomain)
    2563             : 
    2564             : {
    2565          21 :     bMetadataDirty = true;
    2566             : 
    2567          21 :     return GDALPamRasterBand::SetMetadata(papszMDIn, pszDomain);
    2568             : }
    2569             : 
    2570             : /************************************************************************/
    2571             : /*                            SetMetadata()                             */
    2572             : /************************************************************************/
    2573             : 
    2574        1642 : CPLErr HFARasterBand::SetMetadataItem(const char *pszTag, const char *pszValue,
    2575             :                                       const char *pszDomain)
    2576             : 
    2577             : {
    2578        1642 :     bMetadataDirty = true;
    2579             : 
    2580        1642 :     return GDALPamRasterBand::SetMetadataItem(pszTag, pszValue, pszDomain);
    2581             : }
    2582             : 
    2583             : /************************************************************************/
    2584             : /*                           CleanOverviews()                           */
    2585             : /************************************************************************/
    2586             : 
    2587           1 : CPLErr HFARasterBand::CleanOverviews()
    2588             : 
    2589             : {
    2590           1 :     if (nOverviews == 0)
    2591           0 :         return CE_None;
    2592             : 
    2593             :     // Clear our reference to overviews as bands.
    2594           2 :     for (int iOverview = 0; iOverview < nOverviews; iOverview++)
    2595           1 :         delete papoOverviewBands[iOverview];
    2596             : 
    2597           1 :     CPLFree(papoOverviewBands);
    2598           1 :     papoOverviewBands = nullptr;
    2599           1 :     nOverviews = 0;
    2600             : 
    2601             :     // Search for any RRDNamesList and destroy it.
    2602           1 :     HFABand *poBand = hHFA->papoBand[nBand - 1];
    2603           1 :     HFAEntry *poEntry = poBand->poNode->GetNamedChild("RRDNamesList");
    2604           1 :     if (poEntry != nullptr)
    2605             :     {
    2606           1 :         poEntry->RemoveAndDestroy();
    2607             :     }
    2608             : 
    2609             :     // Destroy and subsample layers under our band.
    2610           5 :     for (HFAEntry *poChild = poBand->poNode->GetChild(); poChild != nullptr;)
    2611             :     {
    2612           4 :         HFAEntry *poNext = poChild->GetNext();
    2613             : 
    2614           4 :         if (EQUAL(poChild->GetType(), "Eimg_Layer_SubSample"))
    2615           0 :             poChild->RemoveAndDestroy();
    2616             : 
    2617           4 :         poChild = poNext;
    2618             :     }
    2619             : 
    2620             :     // Clean up dependent file if we are the last band under the
    2621             :     // assumption there will be nothing else referencing it after
    2622             :     // this.
    2623           1 :     if (hHFA->psDependent != hHFA && hHFA->psDependent != nullptr)
    2624             :     {
    2625             :         const CPLString osFilename =
    2626           0 :             CPLFormFilenameSafe(hHFA->psDependent->pszPath,
    2627           2 :                                 hHFA->psDependent->pszFilename, nullptr);
    2628             : 
    2629           1 :         CPL_IGNORE_RET_VAL(HFAClose(hHFA->psDependent));
    2630           1 :         hHFA->psDependent = nullptr;
    2631             : 
    2632           1 :         CPLDebug("HFA", "Unlink(%s)", osFilename.c_str());
    2633           1 :         VSIUnlink(osFilename);
    2634             :     }
    2635             : 
    2636           1 :     return CE_None;
    2637             : }
    2638             : 
    2639             : /************************************************************************/
    2640             : /*                           BuildOverviews()                           */
    2641             : /************************************************************************/
    2642             : 
    2643          12 : CPLErr HFARasterBand::BuildOverviews(const char *pszResampling,
    2644             :                                      int nReqOverviews,
    2645             :                                      const int *panOverviewList,
    2646             :                                      GDALProgressFunc pfnProgress,
    2647             :                                      void *pProgressData,
    2648             :                                      CSLConstList papszOptions)
    2649             : 
    2650             : {
    2651          12 :     EstablishOverviews();
    2652             : 
    2653          12 :     if (nThisOverview != -1)
    2654             :     {
    2655           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2656             :                  "Attempt to build overviews on an overview layer.");
    2657             : 
    2658           0 :         return CE_Failure;
    2659             :     }
    2660             : 
    2661          12 :     if (nReqOverviews == 0)
    2662           1 :         return CleanOverviews();
    2663             : 
    2664             :     GDALRasterBand **papoOvBands = static_cast<GDALRasterBand **>(
    2665          11 :         CPLCalloc(sizeof(void *), nReqOverviews));
    2666             : 
    2667             :     const bool bRegenerate =
    2668          11 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "@REGENERATE", "YES"));
    2669             : 
    2670             :     // Loop over overview levels requested.
    2671          24 :     for (int iOverview = 0; iOverview < nReqOverviews; iOverview++)
    2672             :     {
    2673             :         // Find this overview level.
    2674          13 :         const int nReqOvLevel = GDALOvLevelAdjust2(panOverviewList[iOverview],
    2675             :                                                    nRasterXSize, nRasterYSize);
    2676             : 
    2677          21 :         for (int i = 0; i < nOverviews && papoOvBands[iOverview] == nullptr;
    2678             :              i++)
    2679             :         {
    2680           8 :             if (papoOverviewBands[i] == nullptr)
    2681             :             {
    2682           0 :                 CPLDebug("HFA", "Shouldn't happen happened at line %d",
    2683             :                          __LINE__);
    2684           0 :                 continue;
    2685             :             }
    2686             : 
    2687          24 :             const int nThisOvLevel = GDALComputeOvFactor(
    2688           8 :                 papoOverviewBands[i]->GetXSize(), GetXSize(),
    2689           8 :                 papoOverviewBands[i]->GetYSize(), GetYSize());
    2690             : 
    2691           8 :             if (nReqOvLevel == nThisOvLevel)
    2692           1 :                 papoOvBands[iOverview] = papoOverviewBands[i];
    2693             :         }
    2694             : 
    2695             :         // If this overview level does not yet exist, create it now.
    2696          13 :         if (papoOvBands[iOverview] == nullptr)
    2697             :         {
    2698          24 :             const int iResult = HFACreateOverview(
    2699          12 :                 hHFA, nBand, panOverviewList[iOverview], pszResampling);
    2700          12 :             if (iResult < 0)
    2701             :             {
    2702           0 :                 CPLFree(papoOvBands);
    2703           0 :                 return CE_Failure;
    2704             :             }
    2705             : 
    2706          12 :             if (papoOverviewBands == nullptr && nOverviews == 0 && iResult > 0)
    2707             :             {
    2708           0 :                 CPLDebug("HFA", "Shouldn't happen happened at line %d",
    2709             :                          __LINE__);
    2710           0 :                 papoOverviewBands = static_cast<HFARasterBand **>(
    2711           0 :                     CPLCalloc(sizeof(void *), iResult));
    2712             :             }
    2713             : 
    2714          12 :             nOverviews = iResult + 1;
    2715          12 :             papoOverviewBands = static_cast<HFARasterBand **>(
    2716          12 :                 CPLRealloc(papoOverviewBands, sizeof(void *) * nOverviews));
    2717          12 :             papoOverviewBands[iResult] = new HFARasterBand(
    2718          12 :                 cpl::down_cast<HFADataset *>(poDS), nBand, iResult);
    2719             : 
    2720          12 :             papoOvBands[iOverview] = papoOverviewBands[iResult];
    2721             :         }
    2722             :     }
    2723             : 
    2724          11 :     CPLErr eErr = CE_None;
    2725             : 
    2726          11 :     if (bRegenerate)
    2727           5 :         eErr = GDALRegenerateOverviewsEx((GDALRasterBandH)this, nReqOverviews,
    2728             :                                          (GDALRasterBandH *)papoOvBands,
    2729             :                                          pszResampling, pfnProgress,
    2730             :                                          pProgressData, papszOptions);
    2731             : 
    2732          11 :     CPLFree(papoOvBands);
    2733             : 
    2734          11 :     return eErr;
    2735             : }
    2736             : 
    2737             : /************************************************************************/
    2738             : /*                        GetDefaultHistogram()                         */
    2739             : /************************************************************************/
    2740             : 
    2741          12 : CPLErr HFARasterBand::GetDefaultHistogram(double *pdfMin, double *pdfMax,
    2742             :                                           int *pnBuckets,
    2743             :                                           GUIntBig **ppanHistogram, int bForce,
    2744             :                                           GDALProgressFunc pfnProgress,
    2745             :                                           void *pProgressData)
    2746             : 
    2747             : {
    2748          12 :     if (GetMetadataItem("STATISTICS_HISTOBINVALUES") != nullptr &&
    2749          20 :         GetMetadataItem("STATISTICS_HISTOMIN") != nullptr &&
    2750           8 :         GetMetadataItem("STATISTICS_HISTOMAX") != nullptr)
    2751             :     {
    2752           8 :         const char *pszBinValues = GetMetadataItem("STATISTICS_HISTOBINVALUES");
    2753             : 
    2754           8 :         *pdfMin = CPLAtof(GetMetadataItem("STATISTICS_HISTOMIN"));
    2755           8 :         *pdfMax = CPLAtof(GetMetadataItem("STATISTICS_HISTOMAX"));
    2756             : 
    2757           8 :         *pnBuckets = 0;
    2758        5402 :         for (int i = 0; pszBinValues[i] != '\0'; i++)
    2759             :         {
    2760        5394 :             if (pszBinValues[i] == '|')
    2761        1976 :                 (*pnBuckets)++;
    2762             :         }
    2763             : 
    2764           8 :         *ppanHistogram =
    2765           8 :             static_cast<GUIntBig *>(CPLCalloc(sizeof(GUIntBig), *pnBuckets));
    2766             : 
    2767           8 :         const char *pszNextBin = pszBinValues;
    2768        1984 :         for (int i = 0; i < *pnBuckets; i++)
    2769             :         {
    2770        1976 :             (*ppanHistogram)[i] =
    2771        1976 :                 static_cast<GUIntBig>(CPLAtoGIntBig(pszNextBin));
    2772             : 
    2773        5394 :             while (*pszNextBin != '|' && *pszNextBin != '\0')
    2774        3418 :                 pszNextBin++;
    2775        1976 :             if (*pszNextBin == '|')
    2776        1976 :                 pszNextBin++;
    2777             :         }
    2778             : 
    2779             :         // Adjust min/max to reflect outer edges of buckets.
    2780           8 :         double dfBucketWidth = (*pdfMax - *pdfMin) / (*pnBuckets - 1);
    2781           8 :         *pdfMax += 0.5 * dfBucketWidth;
    2782           8 :         *pdfMin -= 0.5 * dfBucketWidth;
    2783             : 
    2784           8 :         return CE_None;
    2785             :     }
    2786             : 
    2787           4 :     return GDALPamRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets,
    2788             :                                                   ppanHistogram, bForce,
    2789           4 :                                                   pfnProgress, pProgressData);
    2790             : }
    2791             : 
    2792             : /************************************************************************/
    2793             : /*                           SetDefaultRAT()                            */
    2794             : /************************************************************************/
    2795             : 
    2796           6 : CPLErr HFARasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
    2797             : 
    2798             : {
    2799           6 :     if (poRAT == nullptr)
    2800           0 :         return CE_Failure;
    2801             : 
    2802           6 :     delete poDefaultRAT;
    2803           6 :     poDefaultRAT = nullptr;
    2804             : 
    2805           6 :     CPLErr r = WriteNamedRAT("Descriptor_Table", poRAT);
    2806           6 :     if (!r)
    2807           6 :         GetDefaultRAT();
    2808             : 
    2809           6 :     return r;
    2810             : }
    2811             : 
    2812             : /************************************************************************/
    2813             : /*                           GetDefaultRAT()                            */
    2814             : /************************************************************************/
    2815             : 
    2816        1313 : GDALRasterAttributeTable *HFARasterBand::GetDefaultRAT()
    2817             : 
    2818             : {
    2819        1313 :     if (poDefaultRAT == nullptr)
    2820         624 :         poDefaultRAT = new HFARasterAttributeTable(this, "Descriptor_Table");
    2821             : 
    2822        1313 :     return poDefaultRAT;
    2823             : }
    2824             : 
    2825             : /************************************************************************/
    2826             : /*                            WriteNamedRAT()                            */
    2827             : /************************************************************************/
    2828             : 
    2829           6 : CPLErr HFARasterBand::WriteNamedRAT(const char * /*pszName*/,
    2830             :                                     const GDALRasterAttributeTable *poRAT)
    2831             : {
    2832             :     // Find the requested table.
    2833             :     HFAEntry *poDT =
    2834           6 :         hHFA->papoBand[nBand - 1]->poNode->GetNamedChild("Descriptor_Table");
    2835           6 :     if (poDT == nullptr || !EQUAL(poDT->GetType(), "Edsc_Table"))
    2836             :         poDT =
    2837           6 :             HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo, "Descriptor_Table",
    2838           6 :                           "Edsc_Table", hHFA->papoBand[nBand - 1]->poNode);
    2839             : 
    2840           6 :     const int nRowCount = poRAT->GetRowCount();
    2841             : 
    2842           6 :     poDT->SetIntField("numrows", nRowCount);
    2843             :     // Check if binning is set on this RAT.
    2844           6 :     double dfBinSize = 0.0;
    2845           6 :     double dfRow0Min = 0.0;
    2846           6 :     if (poRAT->GetLinearBinning(&dfRow0Min, &dfBinSize))
    2847             :     {
    2848             :         // Then it should have an Edsc_BinFunction.
    2849           4 :         HFAEntry *poBinFunction = poDT->GetNamedChild("#Bin_Function#");
    2850           4 :         if (poBinFunction == nullptr ||
    2851           0 :             !EQUAL(poBinFunction->GetType(), "Edsc_BinFunction"))
    2852             :         {
    2853             :             poBinFunction =
    2854           4 :                 HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo,
    2855             :                               "#Bin_Function#", "Edsc_BinFunction", poDT);
    2856             :         }
    2857             : 
    2858             :         // direct for thematic layers, linear otherwise
    2859             :         const char *pszLayerType =
    2860           4 :             hHFA->papoBand[nBand - 1]->poNode->GetStringField("layerType");
    2861           4 :         if (pszLayerType == nullptr || STARTS_WITH_CI(pszLayerType, "thematic"))
    2862           0 :             poBinFunction->SetStringField("binFunctionType", "direct");
    2863             :         else
    2864           4 :             poBinFunction->SetStringField("binFunctionType", "linear");
    2865             : 
    2866           4 :         poBinFunction->SetDoubleField("minLimit", dfRow0Min);
    2867           4 :         poBinFunction->SetDoubleField("maxLimit",
    2868           4 :                                       (nRowCount - 1) * dfBinSize + dfRow0Min);
    2869           4 :         poBinFunction->SetIntField("numBins", nRowCount);
    2870             :     }
    2871             : 
    2872             :     // Loop through each column in the RAT.
    2873          18 :     for (int col = 0; col < poRAT->GetColumnCount(); col++)
    2874             :     {
    2875          12 :         const char *pszName = nullptr;
    2876             : 
    2877          12 :         if (poRAT->GetUsageOfCol(col) == GFU_Red)
    2878             :         {
    2879           1 :             pszName = "Red";
    2880             :         }
    2881          11 :         else if (poRAT->GetUsageOfCol(col) == GFU_Green)
    2882             :         {
    2883           1 :             pszName = "Green";
    2884             :         }
    2885          10 :         else if (poRAT->GetUsageOfCol(col) == GFU_Blue)
    2886             :         {
    2887           1 :             pszName = "Blue";
    2888             :         }
    2889           9 :         else if (poRAT->GetUsageOfCol(col) == GFU_Alpha)
    2890             :         {
    2891           1 :             pszName = "Opacity";
    2892             :         }
    2893           8 :         else if (poRAT->GetUsageOfCol(col) == GFU_PixelCount)
    2894             :         {
    2895           6 :             pszName = "Histogram";
    2896             :         }
    2897           2 :         else if (poRAT->GetUsageOfCol(col) == GFU_Name)
    2898             :         {
    2899           0 :             pszName = "Class_Names";
    2900             :         }
    2901             :         else
    2902             :         {
    2903           2 :             pszName = poRAT->GetNameOfCol(col);
    2904             :         }
    2905             : 
    2906             :         // Check to see if a column with pszName exists and create if
    2907             :         // if necessary.
    2908          12 :         HFAEntry *poColumn = poDT->GetNamedChild(pszName);
    2909             : 
    2910          12 :         if (poColumn == nullptr || !EQUAL(poColumn->GetType(), "Edsc_Column"))
    2911          12 :             poColumn = HFAEntry::New(hHFA->papoBand[nBand - 1]->psInfo, pszName,
    2912             :                                      "Edsc_Column", poDT);
    2913             : 
    2914          12 :         poColumn->SetIntField("numRows", nRowCount);
    2915             :         // Color cols which are integer in GDAL are written as floats in HFA.
    2916          12 :         bool bIsColorCol = false;
    2917          12 :         if (poRAT->GetUsageOfCol(col) == GFU_Red ||
    2918          11 :             poRAT->GetUsageOfCol(col) == GFU_Green ||
    2919          32 :             poRAT->GetUsageOfCol(col) == GFU_Blue ||
    2920           9 :             poRAT->GetUsageOfCol(col) == GFU_Alpha)
    2921             :         {
    2922           4 :             bIsColorCol = true;
    2923             :         }
    2924             : 
    2925             :         // Write float also if a color column or histogram.
    2926          12 :         if (poRAT->GetTypeOfCol(col) == GFT_Real || bIsColorCol ||
    2927           0 :             poRAT->GetUsageOfCol(col) == GFU_PixelCount)
    2928             :         {
    2929             :             const int nOffset =
    2930          24 :                 HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
    2931          12 :                                  static_cast<GUInt32>(nRowCount) *
    2932          12 :                                      static_cast<GUInt32>(sizeof(double)));
    2933          12 :             poColumn->SetIntField("columnDataPtr", nOffset);
    2934          12 :             poColumn->SetStringField("dataType", "real");
    2935             : 
    2936             :             double *padfColData =
    2937          12 :                 static_cast<double *>(CPLCalloc(nRowCount, sizeof(double)));
    2938        1716 :             for (int i = 0; i < nRowCount; i++)
    2939             :             {
    2940        1704 :                 if (bIsColorCol)
    2941             :                     // Stored 0..1
    2942         300 :                     padfColData[i] = poRAT->GetValueAsInt(i, col) / 255.0;
    2943             :                 else
    2944        1404 :                     padfColData[i] = poRAT->GetValueAsDouble(i, col);
    2945             :             }
    2946             : #ifdef CPL_MSB
    2947             :             GDALSwapWords(padfColData, 8, nRowCount, 8);
    2948             : #endif
    2949          24 :             if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
    2950          12 :                 VSIFWriteL(padfColData, nRowCount, sizeof(double), hHFA->fp) !=
    2951             :                     sizeof(double))
    2952             :             {
    2953           0 :                 CPLError(CE_Failure, CPLE_FileIO, "WriteNamedRAT() failed");
    2954           0 :                 CPLFree(padfColData);
    2955           0 :                 return CE_Failure;
    2956             :             }
    2957          12 :             CPLFree(padfColData);
    2958             :         }
    2959           0 :         else if (poRAT->GetTypeOfCol(col) == GFT_String)
    2960             :         {
    2961           0 :             unsigned int nMaxNumChars = 0;
    2962             :             // Find the length of the longest string.
    2963           0 :             for (int i = 0; i < nRowCount; i++)
    2964             :             {
    2965             :                 // Include terminating byte.
    2966             :                 const unsigned int nNumChars = static_cast<unsigned int>(
    2967           0 :                     strlen(poRAT->GetValueAsString(i, col)) + 1);
    2968           0 :                 if (nMaxNumChars < nNumChars)
    2969             :                 {
    2970           0 :                     nMaxNumChars = nNumChars;
    2971             :                 }
    2972             :             }
    2973             : 
    2974             :             const int nOffset =
    2975           0 :                 HFAAllocateSpace(hHFA->papoBand[nBand - 1]->psInfo,
    2976           0 :                                  (nRowCount + 1) * nMaxNumChars);
    2977           0 :             poColumn->SetIntField("columnDataPtr", nOffset);
    2978           0 :             poColumn->SetStringField("dataType", "string");
    2979           0 :             poColumn->SetIntField("maxNumChars", nMaxNumChars);
    2980             : 
    2981             :             char *pachColData =
    2982           0 :                 static_cast<char *>(CPLCalloc(nRowCount + 1, nMaxNumChars));
    2983           0 :             for (int i = 0; i < nRowCount; i++)
    2984             :             {
    2985           0 :                 strcpy(&pachColData[nMaxNumChars * i],
    2986           0 :                        poRAT->GetValueAsString(i, col));
    2987             :             }
    2988           0 :             if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
    2989           0 :                 VSIFWriteL(pachColData, nRowCount, nMaxNumChars, hHFA->fp) !=
    2990           0 :                     nMaxNumChars)
    2991             :             {
    2992           0 :                 CPLError(CE_Failure, CPLE_FileIO, "WriteNamedRAT() failed");
    2993           0 :                 CPLFree(pachColData);
    2994           0 :                 return CE_Failure;
    2995             :             }
    2996           0 :             CPLFree(pachColData);
    2997             :         }
    2998           0 :         else if (poRAT->GetTypeOfCol(col) == GFT_Integer)
    2999             :         {
    3000           0 :             const int nOffset = HFAAllocateSpace(
    3001           0 :                 hHFA->papoBand[nBand - 1]->psInfo,
    3002           0 :                 static_cast<GUInt32>(nRowCount) * (GUInt32)sizeof(GInt32));
    3003           0 :             poColumn->SetIntField("columnDataPtr", nOffset);
    3004           0 :             poColumn->SetStringField("dataType", "integer");
    3005             : 
    3006             :             GInt32 *panColData =
    3007           0 :                 static_cast<GInt32 *>(CPLCalloc(nRowCount, sizeof(GInt32)));
    3008           0 :             for (int i = 0; i < nRowCount; i++)
    3009             :             {
    3010           0 :                 panColData[i] = poRAT->GetValueAsInt(i, col);
    3011             :             }
    3012             : #ifdef CPL_MSB
    3013             :             GDALSwapWords(panColData, 4, nRowCount, 4);
    3014             : #endif
    3015           0 :             if (VSIFSeekL(hHFA->fp, nOffset, SEEK_SET) != 0 ||
    3016           0 :                 VSIFWriteL(panColData, nRowCount, sizeof(GInt32), hHFA->fp) !=
    3017             :                     sizeof(GInt32))
    3018             :             {
    3019           0 :                 CPLError(CE_Failure, CPLE_FileIO, "WriteNamedRAT() failed");
    3020           0 :                 CPLFree(panColData);
    3021           0 :                 return CE_Failure;
    3022             :             }
    3023           0 :             CPLFree(panColData);
    3024             :         }
    3025             :         else
    3026             :         {
    3027             :             // Can't deal with any of the others yet.
    3028           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    3029             :                      "Writing this data type in a column is not supported "
    3030             :                      "for this Raster Attribute Table.");
    3031             :         }
    3032             :     }
    3033             : 
    3034           6 :     return CE_None;
    3035             : }
    3036             : 
    3037             : /************************************************************************/
    3038             : /* ==================================================================== */
    3039             : /*                            HFADataset                               */
    3040             : /* ==================================================================== */
    3041             : /************************************************************************/
    3042             : 
    3043             : /************************************************************************/
    3044             : /*                            HFADataset()                            */
    3045             : /************************************************************************/
    3046             : 
    3047         542 : HFADataset::HFADataset()
    3048             : {
    3049         542 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3050         542 : }
    3051             : 
    3052             : /************************************************************************/
    3053             : /*                           ~HFADataset()                            */
    3054             : /************************************************************************/
    3055             : 
    3056        1084 : HFADataset::~HFADataset()
    3057             : 
    3058             : {
    3059         542 :     HFADataset::FlushCache(true);
    3060             : 
    3061             :     // Destroy the raster bands if they exist.  We forcibly clean
    3062             :     // them up now to avoid any effort to write to them after the
    3063             :     // file is closed.
    3064        1160 :     for (int i = 0; i < nBands && papoBands != nullptr; i++)
    3065             :     {
    3066         618 :         if (papoBands[i] != nullptr)
    3067         618 :             delete papoBands[i];
    3068             :     }
    3069             : 
    3070         542 :     CPLFree(papoBands);
    3071         542 :     papoBands = nullptr;
    3072             : 
    3073             :     // Close the file.
    3074         542 :     if (hHFA != nullptr)
    3075             :     {
    3076         542 :         if (HFAClose(hHFA) != 0)
    3077             :         {
    3078           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
    3079             :         }
    3080         542 :         hHFA = nullptr;
    3081             :     }
    3082        1084 : }
    3083             : 
    3084             : /************************************************************************/
    3085             : /*                             FlushCache()                             */
    3086             : /************************************************************************/
    3087             : 
    3088         576 : CPLErr HFADataset::FlushCache(bool bAtClosing)
    3089             : 
    3090             : {
    3091         576 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    3092             : 
    3093         576 :     if (eAccess != GA_Update)
    3094         356 :         return eErr;
    3095             : 
    3096         220 :     if (bGeoDirty)
    3097         146 :         WriteProjection();
    3098             : 
    3099         220 :     if (bMetadataDirty && GetMetadata() != nullptr)
    3100             :     {
    3101          50 :         HFASetMetadata(hHFA, 0, GetMetadata());
    3102          50 :         bMetadataDirty = false;
    3103             :     }
    3104             : 
    3105         476 :     for (int iBand = 0; iBand < nBands; iBand++)
    3106             :     {
    3107             :         HFARasterBand *poBand =
    3108         256 :             static_cast<HFARasterBand *>(GetRasterBand(iBand + 1));
    3109         256 :         if (poBand->bMetadataDirty && poBand->GetMetadata() != nullptr)
    3110             :         {
    3111          14 :             HFASetMetadata(hHFA, iBand + 1, poBand->GetMetadata());
    3112          14 :             poBand->bMetadataDirty = false;
    3113             :         }
    3114             :     }
    3115             : 
    3116         220 :     return eErr;
    3117             : }
    3118             : 
    3119             : /************************************************************************/
    3120             : /*                          WriteProjection()                           */
    3121             : /************************************************************************/
    3122             : 
    3123         146 : CPLErr HFADataset::WriteProjection()
    3124             : 
    3125             : {
    3126         146 :     bool bPEStringStored = false;
    3127             : 
    3128         146 :     bGeoDirty = false;
    3129             : 
    3130         146 :     const OGRSpatialReference &oSRS = m_oSRS;
    3131         146 :     const bool bHaveSRS = !oSRS.IsEmpty();
    3132             : 
    3133             :     // Initialize projection and datum.
    3134             :     Eprj_Datum sDatum;
    3135             :     Eprj_ProParameters sPro;
    3136             :     Eprj_MapInfo sMapInfo;
    3137         146 :     memset(&sPro, 0, sizeof(sPro));
    3138         146 :     memset(&sDatum, 0, sizeof(sDatum));
    3139         146 :     memset(&sMapInfo, 0, sizeof(sMapInfo));
    3140             : 
    3141             :     // Collect datum information.
    3142         146 :     OGRSpatialReference *poGeogSRS = bHaveSRS ? oSRS.CloneGeogCS() : nullptr;
    3143             : 
    3144         146 :     if (poGeogSRS)
    3145             :     {
    3146         133 :         sDatum.datumname =
    3147         133 :             const_cast<char *>(poGeogSRS->GetAttrValue("GEOGCS|DATUM"));
    3148         133 :         if (sDatum.datumname == nullptr)
    3149           0 :             sDatum.datumname = const_cast<char *>("");
    3150             : 
    3151             :         // WKT to Imagine translation.
    3152         133 :         const char *const *papszDatumMap = HFAGetDatumMap();
    3153         563 :         for (int i = 0; papszDatumMap[i] != nullptr; i += 2)
    3154             :         {
    3155         518 :             if (EQUAL(sDatum.datumname, papszDatumMap[i + 1]))
    3156             :             {
    3157          88 :                 sDatum.datumname = (char *)papszDatumMap[i];
    3158          88 :                 break;
    3159             :             }
    3160             :         }
    3161             : 
    3162             :         // Map some EPSG datum codes directly to Imagine names.
    3163         133 :         const int nGCS = poGeogSRS->GetEPSGGeogCS();
    3164             : 
    3165         133 :         if (nGCS == 4326)
    3166           7 :             sDatum.datumname = const_cast<char *>("WGS 84");
    3167         126 :         else if (nGCS == 4322)
    3168           0 :             sDatum.datumname = const_cast<char *>("WGS 1972");
    3169         126 :         else if (nGCS == 4267)
    3170          30 :             sDatum.datumname = const_cast<char *>("NAD27");
    3171          96 :         else if (nGCS == 4269)
    3172           2 :             sDatum.datumname = const_cast<char *>("NAD83");
    3173          94 :         else if (nGCS == 4283)
    3174           0 :             sDatum.datumname = const_cast<char *>("GDA94");
    3175          94 :         else if (nGCS == 4284)
    3176           0 :             sDatum.datumname = const_cast<char *>("Pulkovo 1942");
    3177          94 :         else if (nGCS == 4272)
    3178           1 :             sDatum.datumname = const_cast<char *>("Geodetic Datum 1949");
    3179             : 
    3180         133 :         if (poGeogSRS->GetTOWGS84(sDatum.params) == OGRERR_NONE)
    3181             :         {
    3182           2 :             sDatum.type = EPRJ_DATUM_PARAMETRIC;
    3183           2 :             sDatum.params[3] *= -ARCSEC2RAD;
    3184           2 :             sDatum.params[4] *= -ARCSEC2RAD;
    3185           2 :             sDatum.params[5] *= -ARCSEC2RAD;
    3186           2 :             sDatum.params[6] *= 1e-6;
    3187             :         }
    3188         131 :         else if (EQUAL(sDatum.datumname, "NAD27"))
    3189             :         {
    3190          30 :             sDatum.type = EPRJ_DATUM_GRID;
    3191          30 :             sDatum.gridname = const_cast<char *>("nadcon.dat");
    3192             :         }
    3193             :         else
    3194             :         {
    3195             :             // We will default to this (effectively WGS84) for now.
    3196         101 :             sDatum.type = EPRJ_DATUM_PARAMETRIC;
    3197             :         }
    3198             : 
    3199             :         // Verify if we need to write a ESRI PE string.
    3200         133 :         if (!bDisablePEString)
    3201         132 :             bPEStringStored = CPL_TO_BOOL(WritePeStringIfNeeded(&oSRS, hHFA));
    3202             : 
    3203         133 :         sPro.proSpheroid.sphereName =
    3204         133 :             (char *)poGeogSRS->GetAttrValue("GEOGCS|DATUM|SPHEROID");
    3205         133 :         sPro.proSpheroid.a = poGeogSRS->GetSemiMajor();
    3206         133 :         sPro.proSpheroid.b = poGeogSRS->GetSemiMinor();
    3207         133 :         sPro.proSpheroid.radius = sPro.proSpheroid.a;
    3208             : 
    3209         133 :         const double a2 = sPro.proSpheroid.a * sPro.proSpheroid.a;
    3210         133 :         const double b2 = sPro.proSpheroid.b * sPro.proSpheroid.b;
    3211             : 
    3212             :         // a2 == 0 is non sensical of course. Just to please fuzzers
    3213         133 :         sPro.proSpheroid.eSquared = (a2 == 0.0) ? 0.0 : (a2 - b2) / a2;
    3214             :     }
    3215             : 
    3216         146 :     if (sDatum.datumname == nullptr)
    3217          13 :         sDatum.datumname = const_cast<char *>("");
    3218         146 :     if (sPro.proSpheroid.sphereName == nullptr)
    3219          13 :         sPro.proSpheroid.sphereName = const_cast<char *>("");
    3220             : 
    3221             :     // Recognise various projections.
    3222         146 :     const char *pszProjName = nullptr;
    3223             : 
    3224         146 :     if (bHaveSRS)
    3225         133 :         pszProjName = oSRS.GetAttrValue("PROJCS|PROJECTION");
    3226             : 
    3227         146 :     if (bForceToPEString && !bPEStringStored)
    3228             :     {
    3229           0 :         char *pszPEString = nullptr;
    3230           0 :         const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
    3231           0 :         oSRS.exportToWkt(&pszPEString, apszOptions);
    3232             :         // Need to transform this into ESRI format.
    3233           0 :         HFASetPEString(hHFA, pszPEString);
    3234           0 :         CPLFree(pszPEString);
    3235             : 
    3236           0 :         bPEStringStored = true;
    3237             :     }
    3238         146 :     else if (pszProjName == nullptr)
    3239             :     {
    3240          54 :         if (bHaveSRS && oSRS.IsGeographic())
    3241             :         {
    3242          41 :             sPro.proNumber = EPRJ_LATLONG;
    3243          41 :             sPro.proName = const_cast<char *>("Geographic (Lat/Lon)");
    3244             :         }
    3245             :     }
    3246             :     // TODO: Add State Plane.
    3247          92 :     else if (!bIgnoreUTM && oSRS.GetUTMZone(nullptr) != 0)
    3248             :     {
    3249          32 :         int bNorth = FALSE;
    3250          32 :         const int nZone = oSRS.GetUTMZone(&bNorth);
    3251          32 :         sPro.proNumber = EPRJ_UTM;
    3252          32 :         sPro.proName = const_cast<char *>("UTM");
    3253          32 :         sPro.proZone = nZone;
    3254          32 :         if (bNorth)
    3255          32 :             sPro.proParams[3] = 1.0;
    3256             :         else
    3257           0 :             sPro.proParams[3] = -1.0;
    3258             :     }
    3259          60 :     else if (EQUAL(pszProjName, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
    3260             :     {
    3261           1 :         sPro.proNumber = EPRJ_ALBERS_CONIC_EQUAL_AREA;
    3262           1 :         sPro.proName = const_cast<char *>("Albers Conical Equal Area");
    3263           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3264           1 :         sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
    3265           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3266           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3267           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3268           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3269             :     }
    3270          59 :     else if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
    3271             :     {
    3272             :         // Not sure if Imagine has a mapping of LCC_1SP. In the mean time
    3273             :         // convert it to LCC_2SP
    3274             :         auto poTmpSRS = std::unique_ptr<OGRSpatialReference>(
    3275           2 :             oSRS.convertToOtherProjection(SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP));
    3276           1 :         if (poTmpSRS)
    3277             :         {
    3278           1 :             sPro.proNumber = EPRJ_LAMBERT_CONFORMAL_CONIC;
    3279           1 :             sPro.proName = const_cast<char *>("Lambert Conformal Conic");
    3280           1 :             sPro.proParams[2] =
    3281           1 :                 poTmpSRS->GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3282           1 :             sPro.proParams[3] =
    3283           1 :                 poTmpSRS->GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
    3284           1 :             sPro.proParams[4] =
    3285           1 :                 poTmpSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3286           1 :             sPro.proParams[5] =
    3287           1 :                 poTmpSRS->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3288           1 :             sPro.proParams[6] = poTmpSRS->GetProjParm(SRS_PP_FALSE_EASTING);
    3289           1 :             sPro.proParams[7] = poTmpSRS->GetProjParm(SRS_PP_FALSE_NORTHING);
    3290             :         }
    3291             :     }
    3292          58 :     else if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    3293             :     {
    3294           3 :         sPro.proNumber = EPRJ_LAMBERT_CONFORMAL_CONIC;
    3295           3 :         sPro.proName = const_cast<char *>("Lambert Conformal Conic");
    3296           3 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3297           3 :         sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
    3298           3 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3299           3 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3300           3 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3301           3 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3302             :     }
    3303          57 :     else if (EQUAL(pszProjName, SRS_PT_MERCATOR_1SP) &&
    3304           2 :              oSRS.GetProjParm(SRS_PP_SCALE_FACTOR) == 1.0)
    3305             :     {
    3306           1 :         sPro.proNumber = EPRJ_MERCATOR;
    3307           1 :         sPro.proName = const_cast<char *>("Mercator");
    3308           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3309           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3310           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3311           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3312             :     }
    3313          54 :     else if (EQUAL(pszProjName, SRS_PT_MERCATOR_1SP))
    3314             :     {
    3315           1 :         sPro.proNumber = EPRJ_MERCATOR_VARIANT_A;
    3316           1 :         sPro.proName = const_cast<char *>("Mercator (Variant A)");
    3317           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3318           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3319           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR);
    3320           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3321           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3322             :     }
    3323          53 :     else if (EQUAL(pszProjName, SRS_PT_MERCATOR_2SP))
    3324             :     {
    3325             :         // Not sure if Imagine has a mapping of Mercator_2SP. In the mean time
    3326             :         // convert it to Mercator_1SP
    3327             :         auto poTmpSRS = std::unique_ptr<OGRSpatialReference>(
    3328           2 :             oSRS.convertToOtherProjection(SRS_PT_MERCATOR_1SP));
    3329           1 :         if (poTmpSRS)
    3330             :         {
    3331           1 :             sPro.proNumber = EPRJ_MERCATOR_VARIANT_A;
    3332           1 :             sPro.proName = const_cast<char *>("Mercator (Variant A)");
    3333           1 :             sPro.proParams[4] =
    3334           1 :                 poTmpSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3335           1 :             sPro.proParams[5] =
    3336           1 :                 poTmpSRS->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3337           1 :             sPro.proParams[2] = poTmpSRS->GetProjParm(SRS_PP_SCALE_FACTOR);
    3338           1 :             sPro.proParams[6] = poTmpSRS->GetProjParm(SRS_PP_FALSE_EASTING);
    3339           1 :             sPro.proParams[7] = poTmpSRS->GetProjParm(SRS_PP_FALSE_NORTHING);
    3340             :         }
    3341             :     }
    3342          52 :     else if (EQUAL(pszProjName, SRS_PT_KROVAK))
    3343             :     {
    3344           1 :         sPro.proNumber = EPRJ_KROVAK;
    3345           1 :         sPro.proName = const_cast<char *>("Krovak");
    3346           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR);
    3347           1 :         sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
    3348           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3349           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3350           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3351           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3352           1 :         sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_PSEUDO_STD_PARALLEL_1);
    3353             : 
    3354           1 :         sPro.proParams[8] = 0.0;   // XY plane rotation
    3355           1 :         sPro.proParams[10] = 1.0;  // X scale
    3356           1 :         sPro.proParams[11] = 1.0;  // Y scale
    3357             :     }
    3358          51 :     else if (EQUAL(pszProjName, SRS_PT_POLAR_STEREOGRAPHIC))
    3359             :     {
    3360           2 :         sPro.proNumber = EPRJ_POLAR_STEREOGRAPHIC;
    3361           2 :         sPro.proName = const_cast<char *>("Polar Stereographic");
    3362           2 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3363           2 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3364             :         // Hopefully the scale factor is 1.0!
    3365           2 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3366           2 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3367             :     }
    3368          49 :     else if (EQUAL(pszProjName, SRS_PT_POLYCONIC))
    3369             :     {
    3370           2 :         sPro.proNumber = EPRJ_POLYCONIC;
    3371           2 :         sPro.proName = const_cast<char *>("Polyconic");
    3372           2 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3373           2 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3374           2 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3375           2 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3376             :     }
    3377          47 :     else if (EQUAL(pszProjName, SRS_PT_EQUIDISTANT_CONIC))
    3378             :     {
    3379           1 :         sPro.proNumber = EPRJ_EQUIDISTANT_CONIC;
    3380           1 :         sPro.proName = const_cast<char *>("Equidistant Conic");
    3381           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3382           1 :         sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2) * D2R;
    3383           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3384           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3385           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3386           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3387           1 :         sPro.proParams[8] = 1.0;
    3388             :     }
    3389          46 :     else if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
    3390             :     {
    3391           1 :         sPro.proNumber = EPRJ_TRANSVERSE_MERCATOR;
    3392           1 :         sPro.proName = const_cast<char *>("Transverse Mercator");
    3393           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3394           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3395           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3396           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3397           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3398             :     }
    3399          45 :     else if (EQUAL(pszProjName, SRS_PT_STEREOGRAPHIC))
    3400             :     {
    3401           0 :         sPro.proNumber = EPRJ_STEREOGRAPHIC_EXTENDED;
    3402           0 :         sPro.proName = const_cast<char *>("Stereographic (Extended)");
    3403           0 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3404           0 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3405           0 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3406           0 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3407           0 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3408             :     }
    3409          45 :     else if (EQUAL(pszProjName, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
    3410             :     {
    3411           1 :         sPro.proNumber = EPRJ_LAMBERT_AZIMUTHAL_EQUAL_AREA;
    3412           1 :         sPro.proName = const_cast<char *>("Lambert Azimuthal Equal-area");
    3413           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3414           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3415           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3416           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3417             :     }
    3418          44 :     else if (EQUAL(pszProjName, SRS_PT_AZIMUTHAL_EQUIDISTANT))
    3419             :     {
    3420           1 :         sPro.proNumber = EPRJ_AZIMUTHAL_EQUIDISTANT;
    3421           1 :         sPro.proName = const_cast<char *>("Azimuthal Equidistant");
    3422           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3423           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3424           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3425           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3426             :     }
    3427          43 :     else if (EQUAL(pszProjName, SRS_PT_GNOMONIC))
    3428             :     {
    3429           1 :         sPro.proNumber = EPRJ_GNOMONIC;
    3430           1 :         sPro.proName = const_cast<char *>("Gnomonic");
    3431           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3432           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3433           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3434           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3435             :     }
    3436          42 :     else if (EQUAL(pszProjName, SRS_PT_ORTHOGRAPHIC))
    3437             :     {
    3438           2 :         sPro.proNumber = EPRJ_ORTHOGRAPHIC;
    3439           2 :         sPro.proName = const_cast<char *>("Orthographic");
    3440           2 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3441           2 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3442           2 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3443           2 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3444             :     }
    3445          40 :     else if (EQUAL(pszProjName, SRS_PT_SINUSOIDAL))
    3446             :     {
    3447           1 :         sPro.proNumber = EPRJ_SINUSOIDAL;
    3448           1 :         sPro.proName = const_cast<char *>("Sinusoidal");
    3449           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3450           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3451           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3452             :     }
    3453          39 :     else if (EQUAL(pszProjName, SRS_PT_EQUIRECTANGULAR))
    3454             :     {
    3455           3 :         sPro.proNumber = EPRJ_EQUIRECTANGULAR;
    3456           3 :         sPro.proName = const_cast<char *>("Equirectangular");
    3457           3 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3458           3 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3459           3 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3460           3 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3461             :     }
    3462          36 :     else if (EQUAL(pszProjName, SRS_PT_MILLER_CYLINDRICAL))
    3463             :     {
    3464           1 :         sPro.proNumber = EPRJ_MILLER_CYLINDRICAL;
    3465           1 :         sPro.proName = const_cast<char *>("Miller Cylindrical");
    3466           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3467             :         // Hopefully the latitude is zero!
    3468           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3469           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3470             :     }
    3471          35 :     else if (EQUAL(pszProjName, SRS_PT_VANDERGRINTEN))
    3472             :     {
    3473           1 :         sPro.proNumber = EPRJ_VANDERGRINTEN;
    3474           1 :         sPro.proName = const_cast<char *>("Van der Grinten");
    3475           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3476           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3477           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3478             :     }
    3479          34 :     else if (EQUAL(pszProjName, SRS_PT_HOTINE_OBLIQUE_MERCATOR))
    3480             :     {
    3481           2 :         if (oSRS.GetProjParm(SRS_PP_RECTIFIED_GRID_ANGLE) == 0.0)
    3482             :         {
    3483           0 :             sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR;
    3484           0 :             sPro.proName = const_cast<char *>("Oblique Mercator (Hotine)");
    3485           0 :             sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3486           0 :             sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
    3487           0 :             sPro.proParams[4] =
    3488           0 :                 oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3489           0 :             sPro.proParams[5] =
    3490           0 :                 oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3491           0 :             sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3492           0 :             sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3493           0 :             sPro.proParams[12] = 1.0;
    3494             :         }
    3495             :         else
    3496             :         {
    3497           2 :             sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_VARIANT_A;
    3498           2 :             sPro.proName =
    3499             :                 const_cast<char *>("Hotine Oblique Mercator (Variant A)");
    3500           2 :             sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3501           2 :             sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
    3502           2 :             sPro.proParams[4] =
    3503           2 :                 oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3504           2 :             sPro.proParams[5] =
    3505           2 :                 oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3506           2 :             sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3507           2 :             sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3508           2 :             sPro.proParams[8] =
    3509           2 :                 oSRS.GetProjParm(SRS_PP_RECTIFIED_GRID_ANGLE) * D2R;
    3510             :         }
    3511             :     }
    3512          32 :     else if (EQUAL(pszProjName, SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER))
    3513             :     {
    3514           2 :         sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER;
    3515           2 :         sPro.proName =
    3516             :             const_cast<char *>("Hotine Oblique Mercator Azimuth Center");
    3517           2 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3518           2 :         sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
    3519           2 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3520           2 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3521           2 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3522           2 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3523           2 :         sPro.proParams[12] = 1.0;
    3524             :     }
    3525          30 :     else if (EQUAL(pszProjName, SRS_PT_ROBINSON))
    3526             :     {
    3527           1 :         sPro.proNumber = EPRJ_ROBINSON;
    3528           1 :         sPro.proName = const_cast<char *>("Robinson");
    3529           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3530           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3531           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3532             :     }
    3533          29 :     else if (EQUAL(pszProjName, SRS_PT_MOLLWEIDE))
    3534             :     {
    3535           1 :         sPro.proNumber = EPRJ_MOLLWEIDE;
    3536           1 :         sPro.proName = const_cast<char *>("Mollweide");
    3537           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3538           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3539           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3540             :     }
    3541          28 :     else if (EQUAL(pszProjName, SRS_PT_ECKERT_I))
    3542             :     {
    3543           1 :         sPro.proNumber = EPRJ_ECKERT_I;
    3544           1 :         sPro.proName = const_cast<char *>("Eckert I");
    3545           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3546           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3547           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3548             :     }
    3549          27 :     else if (EQUAL(pszProjName, SRS_PT_ECKERT_II))
    3550             :     {
    3551           1 :         sPro.proNumber = EPRJ_ECKERT_II;
    3552           1 :         sPro.proName = const_cast<char *>("Eckert II");
    3553           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3554           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3555           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3556             :     }
    3557          26 :     else if (EQUAL(pszProjName, SRS_PT_ECKERT_III))
    3558             :     {
    3559           1 :         sPro.proNumber = EPRJ_ECKERT_III;
    3560           1 :         sPro.proName = const_cast<char *>("Eckert III");
    3561           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3562           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3563           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3564             :     }
    3565          25 :     else if (EQUAL(pszProjName, SRS_PT_ECKERT_IV))
    3566             :     {
    3567           1 :         sPro.proNumber = EPRJ_ECKERT_IV;
    3568           1 :         sPro.proName = const_cast<char *>("Eckert IV");
    3569           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3570           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3571           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3572             :     }
    3573          24 :     else if (EQUAL(pszProjName, SRS_PT_ECKERT_V))
    3574             :     {
    3575           1 :         sPro.proNumber = EPRJ_ECKERT_V;
    3576           1 :         sPro.proName = const_cast<char *>("Eckert V");
    3577           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3578           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3579           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3580             :     }
    3581          23 :     else if (EQUAL(pszProjName, SRS_PT_ECKERT_VI))
    3582             :     {
    3583           1 :         sPro.proNumber = EPRJ_ECKERT_VI;
    3584           1 :         sPro.proName = const_cast<char *>("Eckert VI");
    3585           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3586           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3587           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3588             :     }
    3589          22 :     else if (EQUAL(pszProjName, SRS_PT_GALL_STEREOGRAPHIC))
    3590             :     {
    3591           1 :         sPro.proNumber = EPRJ_GALL_STEREOGRAPHIC;
    3592           1 :         sPro.proName = const_cast<char *>("Gall Stereographic");
    3593           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3594           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3595           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3596             :     }
    3597          21 :     else if (EQUAL(pszProjName, SRS_PT_CASSINI_SOLDNER))
    3598             :     {
    3599           2 :         sPro.proNumber = EPRJ_CASSINI;
    3600           2 :         sPro.proName = const_cast<char *>("Cassini");
    3601           2 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3602           2 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3603           2 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3604           2 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3605             :     }
    3606          19 :     else if (EQUAL(pszProjName, SRS_PT_TWO_POINT_EQUIDISTANT))
    3607             :     {
    3608           1 :         sPro.proNumber = EPRJ_TWO_POINT_EQUIDISTANT;
    3609           1 :         sPro.proName = const_cast<char *>("Two_Point_Equidistant");
    3610           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3611           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3612           1 :         sPro.proParams[8] =
    3613           1 :             oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0) * D2R;
    3614           1 :         sPro.proParams[9] =
    3615           1 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0) * D2R;
    3616           1 :         sPro.proParams[10] =
    3617           1 :             oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0) * D2R;
    3618           1 :         sPro.proParams[11] =
    3619           1 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0) * D2R;
    3620             :     }
    3621          18 :     else if (EQUAL(pszProjName, SRS_PT_BONNE))
    3622             :     {
    3623           1 :         sPro.proNumber = EPRJ_BONNE;
    3624           1 :         sPro.proName = const_cast<char *>("Bonne");
    3625           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3626           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3627           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3628           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3629             :     }
    3630          17 :     else if (EQUAL(pszProjName, "Loximuthal"))
    3631             :     {
    3632           1 :         sPro.proNumber = EPRJ_LOXIMUTHAL;
    3633           1 :         sPro.proName = const_cast<char *>("Loximuthal");
    3634           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3635           1 :         sPro.proParams[5] = oSRS.GetProjParm("latitude_of_origin") * D2R;
    3636           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3637           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3638             :     }
    3639          16 :     else if (EQUAL(pszProjName, "Quartic_Authalic"))
    3640             :     {
    3641           1 :         sPro.proNumber = EPRJ_QUARTIC_AUTHALIC;
    3642           1 :         sPro.proName = const_cast<char *>("Quartic Authalic");
    3643           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3644           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3645           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3646             :     }
    3647          15 :     else if (EQUAL(pszProjName, "Winkel_I"))
    3648             :     {
    3649           1 :         sPro.proNumber = EPRJ_WINKEL_I;
    3650           1 :         sPro.proName = const_cast<char *>("Winkel I");
    3651           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3652           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3653           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3654           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3655             :     }
    3656          14 :     else if (EQUAL(pszProjName, "Winkel_II"))
    3657             :     {
    3658           1 :         sPro.proNumber = EPRJ_WINKEL_II;
    3659           1 :         sPro.proName = const_cast<char *>("Winkel II");
    3660           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3661           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3662           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3663           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3664             :     }
    3665          13 :     else if (EQUAL(pszProjName, "Behrmann"))
    3666             :     {
    3667             :         // Mapped to Lambert Cylindrical Equal Area in recent PROJ versions
    3668           0 :         sPro.proNumber = EPRJ_BEHRMANN;
    3669           0 :         sPro.proName = const_cast<char *>("Behrmann");
    3670           0 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3671           0 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3672           0 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3673             :     }
    3674          13 :     else if (EQUAL(pszProjName, "Equidistant_Cylindrical"))
    3675             :     {
    3676             :         // Dead code path. Mapped to Equirectangular
    3677           0 :         sPro.proNumber = EPRJ_EQUIDISTANT_CYLINDRICAL;
    3678           0 :         sPro.proName = const_cast<char *>("Equidistant_Cylindrical");
    3679           0 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3680           0 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3681           0 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3682           0 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3683             :     }
    3684          13 :     else if (EQUAL(pszProjName, SRS_PT_KROVAK))
    3685             :     {
    3686           0 :         sPro.proNumber = EPRJ_KROVAK;
    3687           0 :         sPro.proName = const_cast<char *>("Krovak");
    3688           0 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3689           0 :         sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH) * D2R;
    3690           0 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) * D2R;
    3691           0 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER) * D2R;
    3692           0 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3693           0 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3694           0 :         sPro.proParams[8] = oSRS.GetProjParm("XY_Plane_Rotation", 0.0) * D2R;
    3695           0 :         sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3696           0 :         sPro.proParams[10] = oSRS.GetProjParm("X_Scale", 1.0);
    3697           0 :         sPro.proParams[11] = oSRS.GetProjParm("Y_Scale", 1.0);
    3698             :     }
    3699          13 :     else if (EQUAL(pszProjName, "Double_Stereographic"))
    3700             :     {
    3701           0 :         sPro.proNumber = EPRJ_DOUBLE_STEREOGRAPHIC;
    3702           0 :         sPro.proName = const_cast<char *>("Double_Stereographic");
    3703           0 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3704           0 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3705           0 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3706           0 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3707           0 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3708             :     }
    3709          13 :     else if (EQUAL(pszProjName, "Aitoff"))
    3710             :     {
    3711           1 :         sPro.proNumber = EPRJ_AITOFF;
    3712           1 :         sPro.proName = const_cast<char *>("Aitoff");
    3713           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3714           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3715           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3716             :     }
    3717          12 :     else if (EQUAL(pszProjName, "Craster_Parabolic"))
    3718             :     {
    3719           1 :         sPro.proNumber = EPRJ_CRASTER_PARABOLIC;
    3720           1 :         sPro.proName = const_cast<char *>("Craster_Parabolic");
    3721           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3722           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3723           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3724             :     }
    3725          11 :     else if (EQUAL(pszProjName, SRS_PT_CYLINDRICAL_EQUAL_AREA))
    3726             :     {
    3727           1 :         sPro.proNumber = EPRJ_CYLINDRICAL_EQUAL_AREA;
    3728           1 :         sPro.proName = const_cast<char *>("Cylindrical_Equal_Area");
    3729           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3730           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3731           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3732           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3733             :     }
    3734          10 :     else if (EQUAL(pszProjName, "Flat_Polar_Quartic"))
    3735             :     {
    3736           1 :         sPro.proNumber = EPRJ_FLAT_POLAR_QUARTIC;
    3737           1 :         sPro.proName = const_cast<char *>("Flat_Polar_Quartic");
    3738           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3739           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3740           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3741             :     }
    3742           9 :     else if (EQUAL(pszProjName, "Times"))
    3743             :     {
    3744           1 :         sPro.proNumber = EPRJ_TIMES;
    3745           1 :         sPro.proName = const_cast<char *>("Times");
    3746           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3747           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3748           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3749             :     }
    3750           8 :     else if (EQUAL(pszProjName, "Winkel_Tripel"))
    3751             :     {
    3752           1 :         sPro.proNumber = EPRJ_WINKEL_TRIPEL;
    3753           1 :         sPro.proName = const_cast<char *>("Winkel_Tripel");
    3754           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1) * D2R;
    3755           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3756           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3757           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3758             :     }
    3759           7 :     else if (EQUAL(pszProjName, "Hammer_Aitoff"))
    3760             :     {
    3761           1 :         sPro.proNumber = EPRJ_HAMMER_AITOFF;
    3762           1 :         sPro.proName = const_cast<char *>("Hammer_Aitoff");
    3763           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3764           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3765           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3766             :     }
    3767           6 :     else if (
    3768           6 :         EQUAL(pszProjName,
    3769             :               "Vertical_Near_Side_Perspective"))  // ESRI WKT, before PROJ 6.3.0
    3770             :     {
    3771           1 :         sPro.proNumber = EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE;
    3772           1 :         sPro.proName = const_cast<char *>("Vertical_Near_Side_Perspective");
    3773           1 :         sPro.proParams[2] = oSRS.GetProjParm("Height");
    3774           1 :         sPro.proParams[4] =
    3775           1 :             oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER, 75.0) * D2R;
    3776           1 :         sPro.proParams[5] =
    3777           1 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0) * D2R;
    3778           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3779           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3780             :     }
    3781           5 :     else if (EQUAL(pszProjName,
    3782             :                    "Vertical Perspective"))  // WKT2, starting with PROJ 6.3.0
    3783             :     {
    3784           0 :         sPro.proNumber = EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE;
    3785           0 :         sPro.proName = const_cast<char *>("Vertical_Near_Side_Perspective");
    3786           0 :         sPro.proParams[2] = oSRS.GetProjParm("Viewpoint height");
    3787           0 :         sPro.proParams[4] =
    3788           0 :             oSRS.GetProjParm("Longitude of topocentric origin", 75.0) * D2R;
    3789           0 :         sPro.proParams[5] =
    3790           0 :             oSRS.GetProjParm("Latitude of topocentric origin", 40.0) * D2R;
    3791           0 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3792           0 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3793             :     }
    3794           5 :     else if (EQUAL(pszProjName, "Hotine_Oblique_Mercator_Two_Point_Center"))
    3795             :     {
    3796           0 :         sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_CENTER;
    3797           0 :         sPro.proName =
    3798             :             const_cast<char *>("Hotine_Oblique_Mercator_Two_Point_Center");
    3799           0 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3800           0 :         sPro.proParams[5] =
    3801           0 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0) * D2R;
    3802           0 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3803           0 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3804           0 :         sPro.proParams[8] =
    3805           0 :             oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0) * D2R;
    3806           0 :         sPro.proParams[9] =
    3807           0 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0) * D2R;
    3808           0 :         sPro.proParams[10] =
    3809           0 :             oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0) * D2R;
    3810           0 :         sPro.proParams[11] =
    3811           0 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0) * D2R;
    3812             :     }
    3813           5 :     else if (EQUAL(pszProjName,
    3814             :                    SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN))
    3815             :     {
    3816           1 :         sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN;
    3817           1 :         sPro.proName = const_cast<char *>(
    3818             :             "Hotine_Oblique_Mercator_Two_Point_Natural_Origin");
    3819           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3820           1 :         sPro.proParams[5] =
    3821           1 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0) * D2R;
    3822           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3823           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3824           1 :         sPro.proParams[8] =
    3825           1 :             oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0) * D2R;
    3826           1 :         sPro.proParams[9] =
    3827           1 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0) * D2R;
    3828           1 :         sPro.proParams[10] =
    3829           1 :             oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0) * D2R;
    3830           1 :         sPro.proParams[11] =
    3831           1 :             oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0) * D2R;
    3832             :     }
    3833           4 :     else if (EQUAL(pszProjName, "New_Zealand_Map_Grid"))
    3834             :     {
    3835           1 :         sPro.proType = EPRJ_EXTERNAL;
    3836           1 :         sPro.proNumber = 0;
    3837           1 :         sPro.proExeName = const_cast<char *>(EPRJ_EXTERNAL_NZMG);
    3838           1 :         sPro.proName = const_cast<char *>("New Zealand Map Grid");
    3839           1 :         sPro.proZone = 0;
    3840           1 :         sPro.proParams[0] = 0;  // False easting etc not stored in .img it seems
    3841           1 :         sPro.proParams[1] = 0;  // always fixed by definition.
    3842           1 :         sPro.proParams[2] = 0;
    3843           1 :         sPro.proParams[3] = 0;
    3844           1 :         sPro.proParams[4] = 0;
    3845           1 :         sPro.proParams[5] = 0;
    3846           1 :         sPro.proParams[6] = 0;
    3847           1 :         sPro.proParams[7] = 0;
    3848             :     }
    3849           3 :     else if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED))
    3850             :     {
    3851           1 :         sPro.proNumber = EPRJ_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED;
    3852           1 :         sPro.proName =
    3853             :             const_cast<char *>("Transverse Mercator (South Orientated)");
    3854           1 :         sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN) * D2R;
    3855           1 :         sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN) * D2R;
    3856           1 :         sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    3857           1 :         sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING);
    3858           1 :         sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    3859             :     }
    3860             : 
    3861             :     // Anything we can't map, we store as an ESRI PE_STRING.
    3862           2 :     else if (oSRS.IsProjected() || oSRS.IsGeographic())
    3863             :     {
    3864           2 :         if (!bPEStringStored)
    3865             :         {
    3866           0 :             char *pszPEString = nullptr;
    3867           0 :             const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
    3868           0 :             oSRS.exportToWkt(&pszPEString, apszOptions);
    3869             :             // Need to transform this into ESRI format.
    3870           0 :             HFASetPEString(hHFA, pszPEString);
    3871           0 :             CPLFree(pszPEString);
    3872           0 :             bPEStringStored = true;
    3873             :         }
    3874             :     }
    3875             :     else
    3876             :     {
    3877           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    3878             :                  "Projection %s not supported for translation to Imagine.",
    3879             :                  pszProjName);
    3880             :     }
    3881             : 
    3882             :     // MapInfo
    3883         146 :     const char *pszPROJCS = oSRS.GetAttrValue("PROJCS");
    3884             : 
    3885         146 :     if (pszPROJCS)
    3886          92 :         sMapInfo.proName = (char *)pszPROJCS;
    3887          54 :     else if (bHaveSRS && sPro.proName != nullptr)
    3888          41 :         sMapInfo.proName = sPro.proName;
    3889             :     else
    3890          13 :         sMapInfo.proName = const_cast<char *>("Unknown");
    3891             : 
    3892         146 :     sMapInfo.upperLeftCenter.x = m_gt[0] + m_gt[1] * 0.5;
    3893         146 :     sMapInfo.upperLeftCenter.y = m_gt[3] + m_gt[5] * 0.5;
    3894             : 
    3895         146 :     sMapInfo.lowerRightCenter.x = m_gt[0] + m_gt[1] * (GetRasterXSize() - 0.5);
    3896         146 :     sMapInfo.lowerRightCenter.y = m_gt[3] + m_gt[5] * (GetRasterYSize() - 0.5);
    3897             : 
    3898         146 :     sMapInfo.pixelSize.width = std::abs(m_gt[1]);
    3899         146 :     sMapInfo.pixelSize.height = std::abs(m_gt[5]);
    3900             : 
    3901             :     // Handle units.  Try to match up with a known name.
    3902         146 :     sMapInfo.units = const_cast<char *>("meters");
    3903             : 
    3904         146 :     if (bHaveSRS && oSRS.IsGeographic())
    3905          41 :         sMapInfo.units = const_cast<char *>("dd");
    3906         105 :     else if (bHaveSRS && oSRS.GetLinearUnits() != 1.0)
    3907             :     {
    3908           5 :         double dfClosestDiff = 100.0;
    3909           5 :         int iClosest = -1;
    3910           5 :         const char *pszUnitName = nullptr;
    3911           5 :         const double dfActualSize = oSRS.GetLinearUnits(&pszUnitName);
    3912             : 
    3913           5 :         const char *const *papszUnitMap = HFAGetUnitMap();
    3914         180 :         for (int iUnit = 0; papszUnitMap[iUnit] != nullptr; iUnit += 2)
    3915             :         {
    3916         175 :             if (fabs(CPLAtof(papszUnitMap[iUnit + 1]) - dfActualSize) <
    3917             :                 dfClosestDiff)
    3918             :             {
    3919          17 :                 iClosest = iUnit;
    3920          17 :                 dfClosestDiff =
    3921          17 :                     fabs(CPLAtof(papszUnitMap[iUnit + 1]) - dfActualSize);
    3922             :             }
    3923             :         }
    3924             : 
    3925           5 :         if (iClosest == -1 || fabs(dfClosestDiff / dfActualSize) > 0.0001)
    3926             :         {
    3927           1 :             CPLError(CE_Warning, CPLE_NotSupported,
    3928             :                      "Unable to identify Erdas units matching %s/%gm, "
    3929             :                      "output units will be wrong.",
    3930             :                      pszUnitName, dfActualSize);
    3931             :         }
    3932             :         else
    3933             :         {
    3934           4 :             sMapInfo.units = (char *)papszUnitMap[iClosest];
    3935             :         }
    3936             : 
    3937             :         // We need to convert false easting and northing to meters.
    3938           5 :         sPro.proParams[6] *= dfActualSize;
    3939           5 :         sPro.proParams[7] *= dfActualSize;
    3940             :     }
    3941             : 
    3942             :     // Write out definitions.
    3943         146 :     if (m_gt[2] == 0.0 && m_gt[4] == 0.0)
    3944             :     {
    3945         145 :         HFASetMapInfo(hHFA, &sMapInfo);
    3946             :     }
    3947             :     else
    3948             :     {
    3949           1 :         HFASetGeoTransform(hHFA, sMapInfo.proName, sMapInfo.units, m_gt.data());
    3950             :     }
    3951             : 
    3952         146 :     if (bHaveSRS && sPro.proName != nullptr)
    3953             :     {
    3954         131 :         HFASetProParameters(hHFA, &sPro);
    3955         131 :         HFASetDatum(hHFA, &sDatum);
    3956             : 
    3957         131 :         if (!bPEStringStored)
    3958           1 :             HFASetPEString(hHFA, "");
    3959             :     }
    3960          15 :     else if (!bPEStringStored)
    3961             :     {
    3962          13 :         ClearSR(hHFA);
    3963             :     }
    3964             : 
    3965         146 :     if (poGeogSRS != nullptr)
    3966         133 :         delete poGeogSRS;
    3967             : 
    3968         146 :     return CE_None;
    3969             : }
    3970             : 
    3971             : /************************************************************************/
    3972             : /*                       WritePeStringIfNeeded()                        */
    3973             : /************************************************************************/
    3974         132 : int WritePeStringIfNeeded(const OGRSpatialReference *poSRS, HFAHandle hHFA)
    3975             : {
    3976         132 :     if (!poSRS || !hHFA)
    3977           0 :         return FALSE;
    3978             : 
    3979         132 :     const char *pszGEOGCS = poSRS->GetAttrValue("GEOGCS");
    3980         132 :     if (pszGEOGCS == nullptr)
    3981           0 :         pszGEOGCS = "";
    3982             : 
    3983         132 :     const char *pszDatum = poSRS->GetAttrValue("DATUM");
    3984         132 :     if (pszDatum == nullptr)
    3985           0 :         pszDatum = "";
    3986             : 
    3987             :     // The strlen() checks are just there to make Coverity happy because it
    3988             :     // doesn't seem to realize that STARTS_WITH() success implies them.
    3989         132 :     const size_t gcsNameOffset =
    3990         132 :         (strlen(pszGEOGCS) > strlen("GCS_") && STARTS_WITH(pszGEOGCS, "GCS_"))
    3991         264 :             ? strlen("GCS_")
    3992             :             : 0;
    3993             : 
    3994         132 :     const size_t datumNameOffset =
    3995         132 :         (strlen(pszDatum) > strlen("D_") && STARTS_WITH(pszDatum, "D_"))
    3996         264 :             ? strlen("D_")
    3997             :             : 0;
    3998             : 
    3999         132 :     bool ret = false;
    4000         132 :     if (CPLString(pszGEOGCS + gcsNameOffset).replaceAll(' ', '_').tolower() !=
    4001         264 :         CPLString(pszDatum + datumNameOffset).replaceAll(' ', '_').tolower())
    4002             :     {
    4003         123 :         ret = true;
    4004             :     }
    4005             :     else
    4006             :     {
    4007           9 :         const char *name = poSRS->GetAttrValue("PRIMEM");
    4008           9 :         if (name && !EQUAL(name, "Greenwich"))
    4009           0 :             ret = true;
    4010             : 
    4011           9 :         if (!ret)
    4012             :         {
    4013           9 :             const OGR_SRSNode *poAUnits = poSRS->GetAttrNode("GEOGCS|UNIT");
    4014             :             const OGR_SRSNode *poChild =
    4015           9 :                 poAUnits == nullptr ? nullptr : poAUnits->GetChild(0);
    4016           9 :             name = poChild == nullptr ? nullptr : poChild->GetValue();
    4017           9 :             if (name && !EQUAL(name, "Degree"))
    4018           0 :                 ret = true;
    4019             :         }
    4020           9 :         if (!ret)
    4021             :         {
    4022           9 :             name = poSRS->GetAttrValue("UNIT");
    4023           9 :             if (name)
    4024             :             {
    4025           9 :                 ret = true;
    4026           9 :                 const char *const *papszUnitMap = HFAGetUnitMap();
    4027         324 :                 for (int i = 0; papszUnitMap[i] != nullptr; i += 2)
    4028         315 :                     if (EQUAL(name, papszUnitMap[i]))
    4029           0 :                         ret = false;
    4030             :             }
    4031             :         }
    4032           9 :         if (!ret)
    4033             :         {
    4034           0 :             const int nGCS = poSRS->GetEPSGGeogCS();
    4035           0 :             switch (nGCS)
    4036             :             {
    4037           0 :                 case 4326:
    4038           0 :                     if (!EQUAL(pszDatum + datumNameOffset, "WGS_84"))
    4039           0 :                         ret = true;
    4040           0 :                     break;
    4041           0 :                 case 4322:
    4042           0 :                     if (!EQUAL(pszDatum + datumNameOffset, "WGS_72"))
    4043           0 :                         ret = true;
    4044           0 :                     break;
    4045           0 :                 case 4267:
    4046           0 :                     if (!EQUAL(pszDatum + datumNameOffset,
    4047             :                                "North_America_1927"))
    4048           0 :                         ret = true;
    4049           0 :                     break;
    4050           0 :                 case 4269:
    4051           0 :                     if (!EQUAL(pszDatum + datumNameOffset,
    4052             :                                "North_America_1983"))
    4053           0 :                         ret = true;
    4054           0 :                     break;
    4055             :             }
    4056             :         }
    4057             :     }
    4058         132 :     if (ret)
    4059             :     {
    4060         132 :         char *pszPEString = nullptr;
    4061         264 :         OGRSpatialReference oSRSForESRI(*poSRS);
    4062         132 :         oSRSForESRI.morphToESRI();
    4063         132 :         oSRSForESRI.exportToWkt(&pszPEString);
    4064         132 :         HFASetPEString(hHFA, pszPEString);
    4065         132 :         CPLFree(pszPEString);
    4066             :     }
    4067             : 
    4068         132 :     return ret;
    4069             : }
    4070             : 
    4071             : /************************************************************************/
    4072             : /*                              ClearSR()                               */
    4073             : /************************************************************************/
    4074          13 : void ClearSR(HFAHandle hHFA)
    4075             : {
    4076          26 :     for (int iBand = 0; iBand < hHFA->nBands; iBand++)
    4077             :     {
    4078          13 :         HFAEntry *poMIEntry = nullptr;
    4079          26 :         if (hHFA->papoBand[iBand]->poNode &&
    4080          13 :             (poMIEntry = hHFA->papoBand[iBand]->poNode->GetNamedChild(
    4081             :                  "Projection")) != nullptr)
    4082             :         {
    4083           0 :             poMIEntry->MarkDirty();
    4084           0 :             poMIEntry->SetIntField("proType", 0);
    4085           0 :             poMIEntry->SetIntField("proNumber", 0);
    4086           0 :             poMIEntry->SetStringField("proExeName", "");
    4087           0 :             poMIEntry->SetStringField("proName", "");
    4088           0 :             poMIEntry->SetIntField("proZone", 0);
    4089           0 :             poMIEntry->SetDoubleField("proParams[0]", 0.0);
    4090           0 :             poMIEntry->SetDoubleField("proParams[1]", 0.0);
    4091           0 :             poMIEntry->SetDoubleField("proParams[2]", 0.0);
    4092           0 :             poMIEntry->SetDoubleField("proParams[3]", 0.0);
    4093           0 :             poMIEntry->SetDoubleField("proParams[4]", 0.0);
    4094           0 :             poMIEntry->SetDoubleField("proParams[5]", 0.0);
    4095           0 :             poMIEntry->SetDoubleField("proParams[6]", 0.0);
    4096           0 :             poMIEntry->SetDoubleField("proParams[7]", 0.0);
    4097           0 :             poMIEntry->SetDoubleField("proParams[8]", 0.0);
    4098           0 :             poMIEntry->SetDoubleField("proParams[9]", 0.0);
    4099           0 :             poMIEntry->SetDoubleField("proParams[10]", 0.0);
    4100           0 :             poMIEntry->SetDoubleField("proParams[11]", 0.0);
    4101           0 :             poMIEntry->SetDoubleField("proParams[12]", 0.0);
    4102           0 :             poMIEntry->SetDoubleField("proParams[13]", 0.0);
    4103           0 :             poMIEntry->SetDoubleField("proParams[14]", 0.0);
    4104           0 :             poMIEntry->SetStringField("proSpheroid.sphereName", "");
    4105           0 :             poMIEntry->SetDoubleField("proSpheroid.a", 0.0);
    4106           0 :             poMIEntry->SetDoubleField("proSpheroid.b", 0.0);
    4107           0 :             poMIEntry->SetDoubleField("proSpheroid.eSquared", 0.0);
    4108           0 :             poMIEntry->SetDoubleField("proSpheroid.radius", 0.0);
    4109           0 :             HFAEntry *poDatumEntry = poMIEntry->GetNamedChild("Datum");
    4110           0 :             if (poDatumEntry != nullptr)
    4111             :             {
    4112           0 :                 poDatumEntry->MarkDirty();
    4113           0 :                 poDatumEntry->SetStringField("datumname", "");
    4114           0 :                 poDatumEntry->SetIntField("type", 0);
    4115           0 :                 poDatumEntry->SetDoubleField("params[0]", 0.0);
    4116           0 :                 poDatumEntry->SetDoubleField("params[1]", 0.0);
    4117           0 :                 poDatumEntry->SetDoubleField("params[2]", 0.0);
    4118           0 :                 poDatumEntry->SetDoubleField("params[3]", 0.0);
    4119           0 :                 poDatumEntry->SetDoubleField("params[4]", 0.0);
    4120           0 :                 poDatumEntry->SetDoubleField("params[5]", 0.0);
    4121           0 :                 poDatumEntry->SetDoubleField("params[6]", 0.0);
    4122           0 :                 poDatumEntry->SetStringField("gridname", "");
    4123             :             }
    4124           0 :             poMIEntry->FlushToDisk();
    4125           0 :             char *peStr = HFAGetPEString(hHFA);
    4126           0 :             if (peStr != nullptr && strlen(peStr) > 0)
    4127           0 :                 HFASetPEString(hHFA, "");
    4128             :         }
    4129             :     }
    4130          13 : }
    4131             : 
    4132             : /************************************************************************/
    4133             : /*                           ReadProjection()                           */
    4134             : /************************************************************************/
    4135             : 
    4136         538 : CPLErr HFADataset::ReadProjection()
    4137             : 
    4138             : {
    4139             :     // General case for Erdas style projections.
    4140             :     //
    4141             :     // We make a particular effort to adapt the mapinfo->proname as
    4142             :     // the PROJCS[] name per #2422.
    4143         538 :     const Eprj_Datum *psDatum = HFAGetDatum(hHFA);
    4144         538 :     const Eprj_ProParameters *psPro = HFAGetProParameters(hHFA);
    4145         538 :     const Eprj_MapInfo *psMapInfo = HFAGetMapInfo(hHFA);
    4146             : 
    4147         538 :     HFAEntry *poMapInformation = nullptr;
    4148         538 :     if (psMapInfo == nullptr)
    4149             :     {
    4150         292 :         HFABand *poBand = hHFA->papoBand[0];
    4151         292 :         poMapInformation = poBand->poNode->GetNamedChild("MapInformation");
    4152             :     }
    4153             : 
    4154         538 :     m_oSRS.Clear();
    4155             : 
    4156         538 :     if (psMapInfo == nullptr && poMapInformation == nullptr)
    4157             :     {
    4158         286 :         return CE_None;
    4159             :     }
    4160         252 :     else if (((!psDatum || strlen(psDatum->datumname) == 0 ||
    4161         252 :                EQUAL(psDatum->datumname, "Unknown")) &&
    4162           0 :               (!psPro || strlen(psPro->proName) == 0 ||
    4163          49 :                EQUAL(psPro->proName, "Unknown")) &&
    4164          49 :               (psMapInfo && (strlen(psMapInfo->proName) == 0 ||
    4165          49 :                              EQUAL(psMapInfo->proName, "Unknown"))) &&
    4166           0 :               (!psPro || psPro->proZone == 0)))
    4167             :     {
    4168             :         // It is not clear if Erdas Imagine would recognize a ESRI_PE string
    4169             :         // alone, but versions of GDAL between 3.0 and 3.6.3 have written CRS
    4170             :         // using for example the Vertical Projection with a ESRI_PE string only.
    4171          43 :         char *pszPE_COORDSYS = HFAGetPEString(hHFA);
    4172          43 :         OGRSpatialReference oSRSFromPE;
    4173          43 :         oSRSFromPE.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4174           1 :         if (pszPE_COORDSYS != nullptr && strlen(pszPE_COORDSYS) > 0 &&
    4175             :             // Config option for testing purposes only/mostly
    4176          45 :             CPLTestBool(CPLGetConfigOption("HFA_USE_ESRI_PE_STRING", "YES")) &&
    4177           1 :             oSRSFromPE.importFromWkt(pszPE_COORDSYS) == OGRERR_NONE)
    4178             :         {
    4179             :             const char *pszProjName =
    4180           1 :                 oSRSFromPE.GetAttrValue("PROJCS|PROJECTION");
    4181           2 :             if (pszProjName &&
    4182           1 :                 (EQUAL(pszProjName, "Vertical Perspective") ||
    4183           3 :                  EQUAL(pszProjName, "Vertical_Near_Side_Perspective")) &&
    4184           1 :                 CPLTestBool(CPLGetConfigOption(
    4185             :                     "HFA_SHOW_ESRI_PE_STRING_ONLY_WARNING", "YES")))
    4186             :             {
    4187           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4188             :                          "A ESRI_PE string encoding a CRS has been found for "
    4189             :                          "projection method %s, but no corresponding "
    4190             :                          "Eprj_ProParameters are present. This file has likely "
    4191             :                          "been generated by GDAL >= 3.0 and <= 3.6.2. It is "
    4192             :                          "recommended to recreate it, e.g with gdal_translate, "
    4193             :                          "with GDAL >= 3.6.3. This warning can be suppressed "
    4194             :                          "by setting the HFA_SHOW_ESRI_PE_STRING_ONLY_WARNING "
    4195             :                          "configuration option to NO.",
    4196             :                          pszProjName);
    4197             :             }
    4198           1 :             m_oSRS = std::move(oSRSFromPE);
    4199             :         }
    4200          43 :         CPLFree(pszPE_COORDSYS);
    4201          43 :         return m_oSRS.IsEmpty() ? CE_Failure : CE_None;
    4202             :     }
    4203             : 
    4204         418 :     auto poSRS = HFAPCSStructToOSR(psDatum, psPro, psMapInfo, poMapInformation);
    4205         209 :     if (poSRS)
    4206         209 :         m_oSRS = *poSRS;
    4207             : 
    4208             :     // If we got a valid projection and managed to identify a EPSG code,
    4209             :     // then do not use the ESRI PE String.
    4210             :     const bool bTryReadingPEString =
    4211         209 :         poSRS == nullptr || poSRS->GetAuthorityCode(nullptr) == nullptr;
    4212             : 
    4213             :     // Special logic for PE string in ProjectionX node.
    4214         209 :     char *pszPE_COORDSYS = nullptr;
    4215         209 :     if (bTryReadingPEString)
    4216          38 :         pszPE_COORDSYS = HFAGetPEString(hHFA);
    4217             : 
    4218         209 :     OGRSpatialReference oSRSFromPE;
    4219         209 :     oSRSFromPE.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4220          18 :     if (pszPE_COORDSYS != nullptr && strlen(pszPE_COORDSYS) > 0 &&
    4221             :         // Config option for testing purposes only/mostly
    4222         240 :         CPLTestBool(CPLGetConfigOption("HFA_USE_ESRI_PE_STRING", "YES")) &&
    4223          13 :         oSRSFromPE.importFromWkt(pszPE_COORDSYS) == OGRERR_NONE)
    4224             :     {
    4225          13 :         m_oSRS = std::move(oSRSFromPE);
    4226             : 
    4227             :         // Copy TOWGS84 clause from HFA SRS to PE SRS.
    4228          13 :         if (poSRS != nullptr)
    4229             :         {
    4230             :             double adfCoeffs[7];
    4231             :             double adfCoeffsUnused[7];
    4232          13 :             if (poSRS->GetTOWGS84(adfCoeffs, 7) == OGRERR_NONE &&
    4233           0 :                 m_oSRS.GetTOWGS84(adfCoeffsUnused, 7) == OGRERR_FAILURE)
    4234             :             {
    4235           0 :                 m_oSRS.SetTOWGS84(adfCoeffs[0], adfCoeffs[1], adfCoeffs[2],
    4236             :                                   adfCoeffs[3], adfCoeffs[4], adfCoeffs[5],
    4237             :                                   adfCoeffs[6]);
    4238             :             }
    4239             :         }
    4240             :     }
    4241             : 
    4242         209 :     CPLFree(pszPE_COORDSYS);
    4243             : 
    4244         209 :     return m_oSRS.IsEmpty() ? CE_Failure : CE_None;
    4245             : }
    4246             : 
    4247             : /************************************************************************/
    4248             : /*                          IBuildOverviews()                           */
    4249             : /************************************************************************/
    4250             : 
    4251          12 : CPLErr HFADataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    4252             :                                    const int *panOverviewList, int nListBands,
    4253             :                                    const int *panBandList,
    4254             :                                    GDALProgressFunc pfnProgress,
    4255             :                                    void *pProgressData,
    4256             :                                    CSLConstList papszOptions)
    4257             : 
    4258             : {
    4259          12 :     if (GetAccess() == GA_ReadOnly)
    4260             :     {
    4261           0 :         for (int i = 0; i < nListBands; i++)
    4262             :         {
    4263           0 :             if (HFAGetOverviewCount(hHFA, panBandList[i]) > 0)
    4264             :             {
    4265           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    4266             :                          "Cannot add external overviews when there are already "
    4267             :                          "internal overviews");
    4268           0 :                 return CE_Failure;
    4269             :             }
    4270             :         }
    4271             : 
    4272           0 :         return GDALDataset::IBuildOverviews(
    4273             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    4274           0 :             pfnProgress, pProgressData, papszOptions);
    4275             :     }
    4276             : 
    4277          24 :     for (int i = 0; i < nListBands; i++)
    4278             :     {
    4279          24 :         void *pScaledProgressData = GDALCreateScaledProgress(
    4280          12 :             i * 1.0 / nListBands, (i + 1) * 1.0 / nListBands, pfnProgress,
    4281             :             pProgressData);
    4282             : 
    4283          12 :         GDALRasterBand *poBand = GetRasterBand(panBandList[i]);
    4284             : 
    4285             :         // GetRasterBand can return NULL.
    4286          12 :         if (poBand == nullptr)
    4287             :         {
    4288           0 :             CPLError(CE_Failure, CPLE_ObjectNull, "GetRasterBand failed");
    4289           0 :             GDALDestroyScaledProgress(pScaledProgressData);
    4290           0 :             return CE_Failure;
    4291             :         }
    4292             : 
    4293          24 :         const CPLErr eErr = poBand->BuildOverviews(
    4294             :             pszResampling, nOverviews, panOverviewList, GDALScaledProgress,
    4295          12 :             pScaledProgressData, papszOptions);
    4296             : 
    4297          12 :         GDALDestroyScaledProgress(pScaledProgressData);
    4298             : 
    4299          12 :         if (eErr != CE_None)
    4300           0 :             return eErr;
    4301             :     }
    4302             : 
    4303          12 :     return CE_None;
    4304             : }
    4305             : 
    4306             : /************************************************************************/
    4307             : /*                              Identify()                              */
    4308             : /************************************************************************/
    4309             : 
    4310       64539 : int HFADataset::Identify(GDALOpenInfo *poOpenInfo)
    4311             : 
    4312             : {
    4313             :     // Verify that this is a HFA file.
    4314       64539 :     if (poOpenInfo->nHeaderBytes < 15 ||
    4315        8861 :         !STARTS_WITH_CI((char *)poOpenInfo->pabyHeader, "EHFA_HEADER_TAG"))
    4316       63446 :         return FALSE;
    4317             : 
    4318        1093 :     return TRUE;
    4319             : }
    4320             : 
    4321             : /************************************************************************/
    4322             : /*                                Open()                                */
    4323             : /************************************************************************/
    4324             : 
    4325         542 : GDALDataset *HFADataset::Open(GDALOpenInfo *poOpenInfo)
    4326             : 
    4327             : {
    4328             :     // Verify that this is a HFA file.
    4329             : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    4330             :     // During fuzzing, do not use Identify to reject crazy content.
    4331         542 :     if (!Identify(poOpenInfo))
    4332           0 :         return nullptr;
    4333             : #endif
    4334             : 
    4335             :     // Open the file.
    4336         542 :     HFAHandle hHFA = HFAOpen(poOpenInfo->pszFilename,
    4337         542 :                              (poOpenInfo->eAccess == GA_Update ? "r+" : "r"));
    4338             : 
    4339         542 :     if (hHFA == nullptr)
    4340           0 :         return nullptr;
    4341             : 
    4342             :     // Create a corresponding GDALDataset.
    4343         542 :     HFADataset *poDS = new HFADataset();
    4344             : 
    4345         542 :     poDS->hHFA = hHFA;
    4346         542 :     poDS->eAccess = poOpenInfo->eAccess;
    4347             : 
    4348             :     // Establish raster info.
    4349         542 :     HFAGetRasterInfo(hHFA, &poDS->nRasterXSize, &poDS->nRasterYSize,
    4350             :                      &poDS->nBands);
    4351             : 
    4352         542 :     if (poDS->nBands == 0)
    4353             :     {
    4354           4 :         delete poDS;
    4355           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    4356             :                  "Unable to open %s, it has zero usable bands.",
    4357             :                  poOpenInfo->pszFilename);
    4358           4 :         return nullptr;
    4359             :     }
    4360             : 
    4361         538 :     if (poDS->nRasterXSize == 0 || poDS->nRasterYSize == 0)
    4362             :     {
    4363           0 :         delete poDS;
    4364           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4365             :                  "Unable to open %s, it has no pixels.",
    4366             :                  poOpenInfo->pszFilename);
    4367           0 :         return nullptr;
    4368             :     }
    4369             : 
    4370             :     // Get geotransform, or if that fails, try to find XForms to
    4371             :     // build gcps, and metadata.
    4372         538 :     if (!HFAGetGeoTransform(hHFA, poDS->m_gt.data()))
    4373             :     {
    4374         288 :         Efga_Polynomial *pasPolyListForward = nullptr;
    4375         288 :         Efga_Polynomial *pasPolyListReverse = nullptr;
    4376             :         const int nStepCount =
    4377         288 :             HFAReadXFormStack(hHFA, &pasPolyListForward, &pasPolyListReverse);
    4378             : 
    4379         288 :         if (nStepCount > 0)
    4380             :         {
    4381           1 :             poDS->UseXFormStack(nStepCount, pasPolyListForward,
    4382             :                                 pasPolyListReverse);
    4383           1 :             CPLFree(pasPolyListForward);
    4384           1 :             CPLFree(pasPolyListReverse);
    4385             :         }
    4386             :     }
    4387             : 
    4388         538 :     poDS->ReadProjection();
    4389             : 
    4390         538 :     char **papszCM = HFAReadCameraModel(hHFA);
    4391             : 
    4392         538 :     if (papszCM != nullptr)
    4393             :     {
    4394           1 :         poDS->SetMetadata(papszCM, "CAMERA_MODEL");
    4395           1 :         CSLDestroy(papszCM);
    4396             :     }
    4397             : 
    4398        1156 :     for (int i = 0; i < poDS->nBands; i++)
    4399             :     {
    4400         618 :         poDS->SetBand(i + 1, new HFARasterBand(poDS, i + 1, -1));
    4401             :     }
    4402             : 
    4403             :     // Collect GDAL custom Metadata, and "auxiliary" metadata from
    4404             :     // well known HFA structures for the bands.  We defer this till
    4405             :     // now to ensure that the bands are properly setup before
    4406             :     // interacting with PAM.
    4407        1156 :     for (int i = 0; i < poDS->nBands; i++)
    4408             :     {
    4409             :         HFARasterBand *poBand =
    4410         618 :             static_cast<HFARasterBand *>(poDS->GetRasterBand(i + 1));
    4411             : 
    4412         618 :         char **papszMD = HFAGetMetadata(hHFA, i + 1);
    4413         618 :         if (papszMD != nullptr)
    4414             :         {
    4415          10 :             poBand->SetMetadata(papszMD);
    4416          10 :             CSLDestroy(papszMD);
    4417             :         }
    4418             : 
    4419         618 :         poBand->ReadAuxMetadata();
    4420         618 :         poBand->ReadHistogramMetadata();
    4421             :     }
    4422             : 
    4423             :     // Check for GDAL style metadata.
    4424         538 :     char **papszMD = HFAGetMetadata(hHFA, 0);
    4425         538 :     if (papszMD != nullptr)
    4426             :     {
    4427          85 :         poDS->SetMetadata(papszMD);
    4428          85 :         CSLDestroy(papszMD);
    4429             :     }
    4430             : 
    4431             :     // Read the elevation metadata, if present.
    4432        1156 :     for (int iBand = 0; iBand < poDS->nBands; iBand++)
    4433             :     {
    4434             :         HFARasterBand *poBand =
    4435         618 :             static_cast<HFARasterBand *>(poDS->GetRasterBand(iBand + 1));
    4436         618 :         const char *pszEU = HFAReadElevationUnit(hHFA, iBand);
    4437             : 
    4438         618 :         if (pszEU != nullptr)
    4439             :         {
    4440           3 :             poBand->SetUnitType(pszEU);
    4441           3 :             if (poDS->nBands == 1)
    4442             :             {
    4443           3 :                 poDS->SetMetadataItem("ELEVATION_UNITS", pszEU);
    4444             :             }
    4445             :         }
    4446             :     }
    4447             : 
    4448             :     // Check for dependent dataset value.
    4449         538 :     HFAInfo_t *psInfo = hHFA;
    4450         538 :     HFAEntry *poEntry = psInfo->poRoot->GetNamedChild("DependentFile");
    4451         538 :     if (poEntry != nullptr)
    4452             :     {
    4453          32 :         poDS->SetMetadataItem("HFA_DEPENDENT_FILE",
    4454             :                               poEntry->GetStringField("dependent.string"),
    4455             :                               "HFA");
    4456             :     }
    4457             : 
    4458             :     // Initialize any PAM information.
    4459         538 :     poDS->SetDescription(poOpenInfo->pszFilename);
    4460         538 :     poDS->TryLoadXML();
    4461             : 
    4462             :     // Check for external overviews.
    4463         538 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    4464             : 
    4465             :     // Clear dirty metadata flags.
    4466        1156 :     for (int i = 0; i < poDS->nBands; i++)
    4467             :     {
    4468             :         HFARasterBand *poBand =
    4469         618 :             static_cast<HFARasterBand *>(poDS->GetRasterBand(i + 1));
    4470         618 :         poBand->bMetadataDirty = false;
    4471             :     }
    4472         538 :     poDS->bMetadataDirty = false;
    4473             : 
    4474         538 :     return poDS;
    4475             : }
    4476             : 
    4477             : /************************************************************************/
    4478             : /*                          GetSpatialRef()                             */
    4479             : /************************************************************************/
    4480             : 
    4481         154 : const OGRSpatialReference *HFADataset::GetSpatialRef() const
    4482             : {
    4483         154 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
    4484             : }
    4485             : 
    4486             : /************************************************************************/
    4487             : /*                           SetSpatialRef()                            */
    4488             : /************************************************************************/
    4489             : 
    4490         133 : CPLErr HFADataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    4491             : 
    4492             : {
    4493         133 :     m_oSRS.Clear();
    4494         133 :     if (poSRS)
    4495         133 :         m_oSRS = *poSRS;
    4496         133 :     bGeoDirty = true;
    4497             : 
    4498         133 :     return CE_None;
    4499             : }
    4500             : 
    4501             : /************************************************************************/
    4502             : /*                            SetMetadata()                             */
    4503             : /************************************************************************/
    4504             : 
    4505         136 : CPLErr HFADataset::SetMetadata(char **papszMDIn, const char *pszDomain)
    4506             : 
    4507             : {
    4508         136 :     bMetadataDirty = true;
    4509             : 
    4510         136 :     return GDALPamDataset::SetMetadata(papszMDIn, pszDomain);
    4511             : }
    4512             : 
    4513             : /************************************************************************/
    4514             : /*                            SetMetadata()                             */
    4515             : /************************************************************************/
    4516             : 
    4517          35 : CPLErr HFADataset::SetMetadataItem(const char *pszTag, const char *pszValue,
    4518             :                                    const char *pszDomain)
    4519             : 
    4520             : {
    4521          35 :     bMetadataDirty = true;
    4522             : 
    4523          35 :     return GDALPamDataset::SetMetadataItem(pszTag, pszValue, pszDomain);
    4524             : }
    4525             : 
    4526             : /************************************************************************/
    4527             : /*                          GetGeoTransform()                           */
    4528             : /************************************************************************/
    4529             : 
    4530         142 : CPLErr HFADataset::GetGeoTransform(GDALGeoTransform &gt) const
    4531             : 
    4532             : {
    4533         154 :     if (m_gt[0] != 0.0 || m_gt[1] != 1.0 || m_gt[2] != 0.0 || m_gt[3] != 0.0 ||
    4534         154 :         m_gt[4] != 0.0 || m_gt[5] != 1.0)
    4535             :     {
    4536         136 :         gt = m_gt;
    4537         136 :         return CE_None;
    4538             :     }
    4539             : 
    4540           6 :     return GDALPamDataset::GetGeoTransform(gt);
    4541             : }
    4542             : 
    4543             : /************************************************************************/
    4544             : /*                          SetGeoTransform()                           */
    4545             : /************************************************************************/
    4546             : 
    4547          86 : CPLErr HFADataset::SetGeoTransform(const GDALGeoTransform &gt)
    4548             : 
    4549             : {
    4550          86 :     m_gt = gt;
    4551          86 :     bGeoDirty = true;
    4552             : 
    4553          86 :     return CE_None;
    4554             : }
    4555             : 
    4556             : /************************************************************************/
    4557             : /*                             IRasterIO()                              */
    4558             : /*                                                                      */
    4559             : /*      Multi-band raster io handler.  Here we ensure that the block    */
    4560             : /*      based loading is used for spill file rasters.  That is          */
    4561             : /*      because they are effectively pixel interleaved, so              */
    4562             : /*      processing all bands for a given block together avoid extra     */
    4563             : /*      seeks.                                                          */
    4564             : /************************************************************************/
    4565             : 
    4566          80 : CPLErr HFADataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    4567             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
    4568             :                              int nBufYSize, GDALDataType eBufType,
    4569             :                              int nBandCount, BANDMAP_TYPE panBandMap,
    4570             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    4571             :                              GSpacing nBandSpace,
    4572             :                              GDALRasterIOExtraArg *psExtraArg)
    4573             : 
    4574             : {
    4575          80 :     if (hHFA->papoBand[panBandMap[0] - 1]->fpExternal != nullptr &&
    4576             :         nBandCount > 1)
    4577           0 :         return GDALDataset::BlockBasedRasterIO(
    4578             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    4579             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    4580           0 :             nBandSpace, psExtraArg);
    4581             : 
    4582          80 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    4583             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
    4584             :                                   panBandMap, nPixelSpace, nLineSpace,
    4585          80 :                                   nBandSpace, psExtraArg);
    4586             : }
    4587             : 
    4588             : /************************************************************************/
    4589             : /*                           UseXFormStack()                            */
    4590             : /************************************************************************/
    4591             : 
    4592           1 : void HFADataset::UseXFormStack(int nStepCount, Efga_Polynomial *pasPLForward,
    4593             :                                Efga_Polynomial *pasPLReverse)
    4594             : 
    4595             : {
    4596             :     // Generate GCPs using the transform.
    4597           7 :     for (double dfYRatio = 0.0; dfYRatio < 1.001; dfYRatio += 0.2)
    4598             :     {
    4599          42 :         for (double dfXRatio = 0.0; dfXRatio < 1.001; dfXRatio += 0.2)
    4600             :         {
    4601          36 :             const double dfLine = 0.5 + (GetRasterYSize() - 1) * dfYRatio;
    4602          36 :             const double dfPixel = 0.5 + (GetRasterXSize() - 1) * dfXRatio;
    4603             : 
    4604             :             gdal::GCP gcp("", "", dfPixel, dfLine,
    4605             :                           /* X = */ dfPixel,
    4606          72 :                           /* Y = */ dfLine);
    4607          36 :             if (HFAEvaluateXFormStack(nStepCount, FALSE, pasPLReverse,
    4608          72 :                                       &(gcp.X()), &(gcp.Y())))
    4609             :             {
    4610          36 :                 m_aoGCPs.emplace_back(std::move(gcp));
    4611             :             }
    4612             :         }
    4613             :     }
    4614             : 
    4615             :     // Store the transform as metadata.
    4616           1 :     GDALMajorObject::SetMetadataItem(
    4617           2 :         "XFORM_STEPS", CPLString().Printf("%d", nStepCount), "XFORMS");
    4618             : 
    4619           3 :     for (int iStep = 0; iStep < nStepCount; iStep++)
    4620             :     {
    4621           2 :         GDALMajorObject::SetMetadataItem(
    4622           4 :             CPLString().Printf("XFORM%d_ORDER", iStep),
    4623           4 :             CPLString().Printf("%d", pasPLForward[iStep].order), "XFORMS");
    4624             : 
    4625           2 :         if (pasPLForward[iStep].order == 1)
    4626             :         {
    4627           5 :             for (int i = 0; i < 4; i++)
    4628           4 :                 GDALMajorObject::SetMetadataItem(
    4629           8 :                     CPLString().Printf("XFORM%d_POLYCOEFMTX[%d]", iStep, i),
    4630           8 :                     CPLString().Printf("%.15g",
    4631           4 :                                        pasPLForward[iStep].polycoefmtx[i]),
    4632             :                     "XFORMS");
    4633             : 
    4634           3 :             for (int i = 0; i < 2; i++)
    4635           2 :                 GDALMajorObject::SetMetadataItem(
    4636           4 :                     CPLString().Printf("XFORM%d_POLYCOEFVECTOR[%d]", iStep, i),
    4637           4 :                     CPLString().Printf("%.15g",
    4638           2 :                                        pasPLForward[iStep].polycoefvector[i]),
    4639             :                     "XFORMS");
    4640             : 
    4641           1 :             continue;
    4642             :         }
    4643             : 
    4644           1 :         int nCoefCount = 10;
    4645             : 
    4646           1 :         if (pasPLForward[iStep].order != 2)
    4647             :         {
    4648           1 :             CPLAssert(pasPLForward[iStep].order == 3);
    4649           1 :             nCoefCount = 18;
    4650             :         }
    4651             : 
    4652          19 :         for (int i = 0; i < nCoefCount; i++)
    4653          18 :             GDALMajorObject::SetMetadataItem(
    4654          36 :                 CPLString().Printf("XFORM%d_FWD_POLYCOEFMTX[%d]", iStep, i),
    4655          36 :                 CPLString().Printf("%.15g", pasPLForward[iStep].polycoefmtx[i]),
    4656             :                 "XFORMS");
    4657             : 
    4658           3 :         for (int i = 0; i < 2; i++)
    4659           2 :             GDALMajorObject::SetMetadataItem(
    4660           4 :                 CPLString().Printf("XFORM%d_FWD_POLYCOEFVECTOR[%d]", iStep, i),
    4661           4 :                 CPLString().Printf("%.15g",
    4662           2 :                                    pasPLForward[iStep].polycoefvector[i]),
    4663             :                 "XFORMS");
    4664             : 
    4665          19 :         for (int i = 0; i < nCoefCount; i++)
    4666          18 :             GDALMajorObject::SetMetadataItem(
    4667          36 :                 CPLString().Printf("XFORM%d_REV_POLYCOEFMTX[%d]", iStep, i),
    4668          36 :                 CPLString().Printf("%.15g", pasPLReverse[iStep].polycoefmtx[i]),
    4669             :                 "XFORMS");
    4670             : 
    4671           3 :         for (int i = 0; i < 2; i++)
    4672           2 :             GDALMajorObject::SetMetadataItem(
    4673           4 :                 CPLString().Printf("XFORM%d_REV_POLYCOEFVECTOR[%d]", iStep, i),
    4674           4 :                 CPLString().Printf("%.15g",
    4675           2 :                                    pasPLReverse[iStep].polycoefvector[i]),
    4676             :                 "XFORMS");
    4677             :     }
    4678           1 : }
    4679             : 
    4680             : /************************************************************************/
    4681             : /*                            GetGCPCount()                             */
    4682             : /************************************************************************/
    4683             : 
    4684          25 : int HFADataset::GetGCPCount()
    4685             : {
    4686          25 :     const int nPAMCount = GDALPamDataset::GetGCPCount();
    4687          25 :     return nPAMCount > 0 ? nPAMCount : static_cast<int>(m_aoGCPs.size());
    4688             : }
    4689             : 
    4690             : /************************************************************************/
    4691             : /*                          GetGCPSpatialRef()                          */
    4692             : /************************************************************************/
    4693             : 
    4694           1 : const OGRSpatialReference *HFADataset::GetGCPSpatialRef() const
    4695             : 
    4696             : {
    4697           1 :     const OGRSpatialReference *poSRS = GDALPamDataset::GetGCPSpatialRef();
    4698           1 :     if (poSRS)
    4699           1 :         return poSRS;
    4700           0 :     return !m_aoGCPs.empty() && !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
    4701             : }
    4702             : 
    4703             : /************************************************************************/
    4704             : /*                               GetGCPs()                              */
    4705             : /************************************************************************/
    4706             : 
    4707           2 : const GDAL_GCP *HFADataset::GetGCPs()
    4708             : {
    4709           2 :     const GDAL_GCP *psPAMGCPs = GDALPamDataset::GetGCPs();
    4710           2 :     if (psPAMGCPs)
    4711           1 :         return psPAMGCPs;
    4712           1 :     return gdal::GCP::c_ptr(m_aoGCPs);
    4713             : }
    4714             : 
    4715             : /************************************************************************/
    4716             : /*                            GetFileList()                             */
    4717             : /************************************************************************/
    4718             : 
    4719          92 : char **HFADataset::GetFileList()
    4720             : 
    4721             : {
    4722         184 :     CPLStringList oFileList(GDALPamDataset::GetFileList());
    4723             : 
    4724         184 :     const std::string osIGEFilename = HFAGetIGEFilename(hHFA);
    4725          92 :     if (!osIGEFilename.empty())
    4726             :     {
    4727          14 :         oFileList.push_back(osIGEFilename);
    4728             :     }
    4729             : 
    4730             :     // Request an overview to force opening of dependent overview files.
    4731          92 :     if (nBands > 0 && GetRasterBand(1)->GetOverviewCount() > 0)
    4732          12 :         GetRasterBand(1)->GetOverview(0);
    4733             : 
    4734          92 :     if (hHFA->psDependent != nullptr)
    4735             :     {
    4736           6 :         HFAInfo_t *psDep = hHFA->psDependent;
    4737             : 
    4738           6 :         oFileList.push_back(
    4739          12 :             CPLFormFilenameSafe(psDep->pszPath, psDep->pszFilename, nullptr));
    4740             : 
    4741          12 :         const std::string osIGEFilenameDep = HFAGetIGEFilename(psDep);
    4742           6 :         if (!osIGEFilenameDep.empty())
    4743           5 :             oFileList.push_back(osIGEFilenameDep);
    4744             :     }
    4745             : 
    4746         184 :     return oFileList.StealList();
    4747             : }
    4748             : 
    4749             : /************************************************************************/
    4750             : /*                               Create()                               */
    4751             : /************************************************************************/
    4752             : 
    4753         213 : GDALDataset *HFADataset::Create(const char *pszFilenameIn, int nXSize,
    4754             :                                 int nYSize, int nBandsIn, GDALDataType eType,
    4755             :                                 char **papszParamList)
    4756             : 
    4757             : {
    4758         213 :     const int nBits = CSLFetchNameValue(papszParamList, "NBITS") != nullptr
    4759         213 :                           ? atoi(CSLFetchNameValue(papszParamList, "NBITS"))
    4760         213 :                           : 0;
    4761             : 
    4762         213 :     const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE");
    4763         213 :     if (pszPixelType == nullptr)
    4764         213 :         pszPixelType = "";
    4765             : 
    4766             :     // Translate the data type.
    4767             :     EPTType eHfaDataType;
    4768         213 :     switch (eType)
    4769             :     {
    4770         132 :         case GDT_Byte:
    4771         132 :             if (nBits == 1)
    4772           2 :                 eHfaDataType = EPT_u1;
    4773         130 :             else if (nBits == 2)
    4774           2 :                 eHfaDataType = EPT_u2;
    4775         128 :             else if (nBits == 4)
    4776           2 :                 eHfaDataType = EPT_u4;
    4777         126 :             else if (EQUAL(pszPixelType, "SIGNEDBYTE"))
    4778           0 :                 eHfaDataType = EPT_s8;
    4779             :             else
    4780         126 :                 eHfaDataType = EPT_u8;
    4781         132 :             break;
    4782             : 
    4783           2 :         case GDT_Int8:
    4784           2 :             eHfaDataType = EPT_s8;
    4785           2 :             break;
    4786             : 
    4787          12 :         case GDT_UInt16:
    4788          12 :             eHfaDataType = EPT_u16;
    4789          12 :             break;
    4790             : 
    4791           7 :         case GDT_Int16:
    4792           7 :             eHfaDataType = EPT_s16;
    4793           7 :             break;
    4794             : 
    4795           8 :         case GDT_Int32:
    4796           8 :             eHfaDataType = EPT_s32;
    4797           8 :             break;
    4798             : 
    4799           8 :         case GDT_UInt32:
    4800           8 :             eHfaDataType = EPT_u32;
    4801           8 :             break;
    4802             : 
    4803           9 :         case GDT_Float32:
    4804           9 :             eHfaDataType = EPT_f32;
    4805           9 :             break;
    4806             : 
    4807          10 :         case GDT_Float64:
    4808          10 :             eHfaDataType = EPT_f64;
    4809          10 :             break;
    4810             : 
    4811           7 :         case GDT_CFloat32:
    4812           7 :             eHfaDataType = EPT_c64;
    4813           7 :             break;
    4814             : 
    4815           7 :         case GDT_CFloat64:
    4816           7 :             eHfaDataType = EPT_c128;
    4817           7 :             break;
    4818             : 
    4819          11 :         default:
    4820          11 :             CPLError(
    4821             :                 CE_Failure, CPLE_NotSupported,
    4822             :                 "Data type %s not supported by Erdas Imagine (HFA) format.",
    4823             :                 GDALGetDataTypeName(eType));
    4824          11 :             return nullptr;
    4825             :     }
    4826             : 
    4827             :     const bool bForceToPEString =
    4828         202 :         CPLFetchBool(papszParamList, "FORCETOPESTRING", false);
    4829             :     const bool bDisablePEString =
    4830         202 :         CPLFetchBool(papszParamList, "DISABLEPESTRING", false);
    4831         202 :     if (bForceToPEString && bDisablePEString)
    4832             :     {
    4833           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4834             :                  "FORCETOPESTRING and DISABLEPESTRING are mutually exclusive");
    4835           0 :         return nullptr;
    4836             :     }
    4837             : 
    4838             :     // Create the new file.
    4839         202 :     HFAHandle hHFA = HFACreate(pszFilenameIn, nXSize, nYSize, nBandsIn,
    4840             :                                eHfaDataType, papszParamList);
    4841         202 :     if (hHFA == nullptr)
    4842          12 :         return nullptr;
    4843             : 
    4844         190 :     if (HFAClose(hHFA) != 0)
    4845             :     {
    4846           0 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
    4847           0 :         return nullptr;
    4848             :     }
    4849             : 
    4850             :     // Open the dataset normally.
    4851         190 :     HFADataset *poDS = (HFADataset *)GDALOpen(pszFilenameIn, GA_Update);
    4852             : 
    4853             :     // Special creation option to disable checking for UTM
    4854             :     // parameters when writing the projection.  This is a special
    4855             :     // hack for sam.gillingham@nrm.qld.gov.au.
    4856         190 :     if (poDS != nullptr)
    4857             :     {
    4858         188 :         poDS->bIgnoreUTM = CPLFetchBool(papszParamList, "IGNOREUTM", false);
    4859             :     }
    4860             : 
    4861             :     // Sometimes we can improve ArcGIS compatibility by forcing
    4862             :     // generation of a PEString instead of traditional Imagine
    4863             :     // coordinate system descriptions.
    4864         190 :     if (poDS != nullptr)
    4865             :     {
    4866         188 :         poDS->bForceToPEString = bForceToPEString;
    4867         188 :         poDS->bDisablePEString = bDisablePEString;
    4868             :     }
    4869             : 
    4870         190 :     return poDS;
    4871             : }
    4872             : 
    4873             : /************************************************************************/
    4874             : /*                               Rename()                               */
    4875             : /*                                                                      */
    4876             : /*      Custom Rename() implementation that knows how to update         */
    4877             : /*      filename references in .img and .aux files.                     */
    4878             : /************************************************************************/
    4879             : 
    4880           1 : CPLErr HFADataset::Rename(const char *pszNewName, const char *pszOldName)
    4881             : 
    4882             : {
    4883             :     // Rename all the files at the filesystem level.
    4884           1 :     CPLErr eErr = GDALDriver::DefaultRename(pszNewName, pszOldName);
    4885           1 :     if (eErr != CE_None)
    4886           0 :         return eErr;
    4887             : 
    4888             :     // Now try to go into the .img file and update RRDNames[] lists.
    4889           2 :     CPLString osOldBasename = CPLGetBasenameSafe(pszOldName);
    4890           1 :     CPLString osNewBasename = CPLGetBasenameSafe(pszNewName);
    4891             : 
    4892           1 :     if (osOldBasename != osNewBasename)
    4893             :     {
    4894           1 :         HFAHandle hHFA = HFAOpen(pszNewName, "r+");
    4895             : 
    4896           1 :         if (hHFA != nullptr)
    4897             :         {
    4898           1 :             eErr = HFARenameReferences(hHFA, osNewBasename, osOldBasename);
    4899             : 
    4900           1 :             HFAGetOverviewCount(hHFA, 1);
    4901             : 
    4902           1 :             if (hHFA->psDependent != nullptr)
    4903           1 :                 HFARenameReferences(hHFA->psDependent, osNewBasename,
    4904             :                                     osOldBasename);
    4905             : 
    4906           1 :             if (HFAClose(hHFA) != 0)
    4907           0 :                 eErr = CE_Failure;
    4908             :         }
    4909             :     }
    4910             : 
    4911           1 :     return eErr;
    4912             : }
    4913             : 
    4914             : /************************************************************************/
    4915             : /*                             CopyFiles()                              */
    4916             : /*                                                                      */
    4917             : /*      Custom CopyFiles() implementation that knows how to update      */
    4918             : /*      filename references in .img and .aux files.                     */
    4919             : /************************************************************************/
    4920             : 
    4921           1 : CPLErr HFADataset::CopyFiles(const char *pszNewName, const char *pszOldName)
    4922             : 
    4923             : {
    4924             :     // Rename all the files at the filesystem level.
    4925           1 :     CPLErr eErr = GDALDriver::DefaultCopyFiles(pszNewName, pszOldName);
    4926             : 
    4927           1 :     if (eErr != CE_None)
    4928           0 :         return eErr;
    4929             : 
    4930             :     // Now try to go into the .img file and update RRDNames[] lists.
    4931           2 :     CPLString osOldBasename = CPLGetBasenameSafe(pszOldName);
    4932           1 :     CPLString osNewBasename = CPLGetBasenameSafe(pszNewName);
    4933             : 
    4934           1 :     if (osOldBasename != osNewBasename)
    4935             :     {
    4936           1 :         HFAHandle hHFA = HFAOpen(pszNewName, "r+");
    4937             : 
    4938           1 :         if (hHFA != nullptr)
    4939             :         {
    4940           1 :             eErr = HFARenameReferences(hHFA, osNewBasename, osOldBasename);
    4941             : 
    4942           1 :             HFAGetOverviewCount(hHFA, 1);
    4943             : 
    4944           1 :             if (hHFA->psDependent != nullptr)
    4945           1 :                 HFARenameReferences(hHFA->psDependent, osNewBasename,
    4946             :                                     osOldBasename);
    4947             : 
    4948           1 :             if (HFAClose(hHFA) != 0)
    4949           0 :                 eErr = CE_Failure;
    4950             :         }
    4951             :     }
    4952             : 
    4953           1 :     return eErr;
    4954             : }
    4955             : 
    4956             : /************************************************************************/
    4957             : /*                             CreateCopy()                             */
    4958             : /************************************************************************/
    4959             : 
    4960          66 : GDALDataset *HFADataset::CreateCopy(const char *pszFilename,
    4961             :                                     GDALDataset *poSrcDS, int /* bStrict */,
    4962             :                                     char **papszOptions,
    4963             :                                     GDALProgressFunc pfnProgress,
    4964             :                                     void *pProgressData)
    4965             : {
    4966             :     // Do we really just want to create an .aux file?
    4967          66 :     const bool bCreateAux = CPLFetchBool(papszOptions, "AUX", false);
    4968             : 
    4969             :     // Establish a representative data type to use.
    4970          66 :     char **papszModOptions = CSLDuplicate(papszOptions);
    4971          66 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    4972             :     {
    4973           0 :         CSLDestroy(papszModOptions);
    4974           0 :         return nullptr;
    4975             :     }
    4976             : 
    4977          66 :     const int nBandCount = poSrcDS->GetRasterCount();
    4978          66 :     GDALDataType eType = GDT_Unknown;
    4979             : 
    4980         141 :     for (int iBand = 0; iBand < nBandCount; iBand++)
    4981             :     {
    4982          75 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
    4983          75 :         if (iBand == 0)
    4984          65 :             eType = poBand->GetRasterDataType();
    4985             :         else
    4986          10 :             eType = GDALDataTypeUnion(eType, poBand->GetRasterDataType());
    4987             :     }
    4988             : 
    4989             :     // If we have PIXELTYPE metadata in the source, pass it
    4990             :     // through as a creation option.
    4991         132 :     if (CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr &&
    4992         132 :         nBandCount > 0 && eType == GDT_Byte)
    4993             :     {
    4994          42 :         auto poSrcBand = poSrcDS->GetRasterBand(1);
    4995          42 :         poSrcBand->EnablePixelTypeSignedByteWarning(false);
    4996             :         const char *pszPixelType =
    4997          42 :             poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
    4998          42 :         poSrcBand->EnablePixelTypeSignedByteWarning(true);
    4999          42 :         if (pszPixelType)
    5000             :         {
    5001             :             papszModOptions =
    5002           0 :                 CSLSetNameValue(papszModOptions, "PIXELTYPE", pszPixelType);
    5003             :         }
    5004             :     }
    5005             : 
    5006          66 :     HFADataset *poDS = cpl::down_cast<HFADataset *>(
    5007             :         Create(pszFilename, poSrcDS->GetRasterXSize(),
    5008             :                poSrcDS->GetRasterYSize(), nBandCount, eType, papszModOptions));
    5009             : 
    5010          66 :     CSLDestroy(papszModOptions);
    5011             : 
    5012          66 :     if (poDS == nullptr)
    5013          16 :         return nullptr;
    5014             : 
    5015             :     // Does the source have a PCT or RAT for any of the bands?  If so, copy it
    5016             :     // over.
    5017         110 :     for (int iBand = 0; iBand < nBandCount; iBand++)
    5018             :     {
    5019          60 :         GDALRasterBand *poBand = poSrcDS->GetRasterBand(iBand + 1);
    5020             : 
    5021          60 :         GDALColorTable *poCT = poBand->GetColorTable();
    5022          60 :         if (poCT != nullptr)
    5023             :         {
    5024           1 :             poDS->GetRasterBand(iBand + 1)->SetColorTable(poCT);
    5025             :         }
    5026             : 
    5027          60 :         if (poBand->GetDefaultRAT() != nullptr)
    5028           5 :             poDS->GetRasterBand(iBand + 1)->SetDefaultRAT(
    5029           5 :                 poBand->GetDefaultRAT());
    5030             :     }
    5031             : 
    5032             :     // Do we have metadata for any of the bands or the dataset as a whole?
    5033          50 :     if (poSrcDS->GetMetadata() != nullptr)
    5034          39 :         poDS->SetMetadata(poSrcDS->GetMetadata());
    5035             : 
    5036         110 :     for (int iBand = 0; iBand < nBandCount; iBand++)
    5037             :     {
    5038          60 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
    5039          60 :         GDALRasterBand *poDstBand = poDS->GetRasterBand(iBand + 1);
    5040             : 
    5041          60 :         if (poSrcBand->GetMetadata() != nullptr)
    5042           7 :             poDstBand->SetMetadata(poSrcBand->GetMetadata());
    5043             : 
    5044          60 :         if (strlen(poSrcBand->GetDescription()) > 0)
    5045           6 :             poDstBand->SetDescription(poSrcBand->GetDescription());
    5046             : 
    5047          60 :         int bSuccess = FALSE;
    5048          60 :         const double dfNoDataValue = poSrcBand->GetNoDataValue(&bSuccess);
    5049          60 :         if (bSuccess)
    5050           2 :             poDstBand->SetNoDataValue(dfNoDataValue);
    5051             :     }
    5052             : 
    5053             :     // Copy projection information.
    5054          50 :     GDALGeoTransform gt;
    5055          50 :     if (poSrcDS->GetGeoTransform(gt) == CE_None)
    5056          48 :         poDS->SetGeoTransform(gt);
    5057             : 
    5058          50 :     const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
    5059          50 :     if (poSrcSRS)
    5060          46 :         poDS->SetSpatialRef(poSrcSRS);
    5061             : 
    5062             :     // Copy the imagery.
    5063          50 :     if (!bCreateAux)
    5064             :     {
    5065          50 :         const CPLErr eErr = GDALDatasetCopyWholeRaster(
    5066             :             (GDALDatasetH)poSrcDS, (GDALDatasetH)poDS, nullptr, pfnProgress,
    5067             :             pProgressData);
    5068             : 
    5069          50 :         if (eErr != CE_None)
    5070             :         {
    5071           0 :             delete poDS;
    5072           0 :             return nullptr;
    5073             :         }
    5074             :     }
    5075             : 
    5076             :     // Do we want to generate statistics and a histogram?
    5077          50 :     if (CPLFetchBool(papszOptions, "STATISTICS", false))
    5078             :     {
    5079           2 :         for (int iBand = 0; iBand < nBandCount; iBand++)
    5080             :         {
    5081           1 :             GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
    5082           1 :             double dfMin = 0.0;
    5083           1 :             double dfMax = 0.0;
    5084           1 :             double dfMean = 0.0;
    5085           1 :             double dfStdDev = 0.0;
    5086           1 :             char **papszStatsMD = nullptr;
    5087             : 
    5088             :             // Statistics
    5089           3 :             if (poSrcBand->GetStatistics(TRUE, FALSE, &dfMin, &dfMax, &dfMean,
    5090           2 :                                          &dfStdDev) == CE_None ||
    5091           1 :                 poSrcBand->ComputeStatistics(TRUE, &dfMin, &dfMax, &dfMean,
    5092             :                                              &dfStdDev, pfnProgress,
    5093           1 :                                              pProgressData) == CE_None)
    5094             :             {
    5095           1 :                 CPLString osValue;
    5096             : 
    5097             :                 papszStatsMD =
    5098           1 :                     CSLSetNameValue(papszStatsMD, "STATISTICS_MINIMUM",
    5099           1 :                                     osValue.Printf("%.15g", dfMin));
    5100             :                 papszStatsMD =
    5101           1 :                     CSLSetNameValue(papszStatsMD, "STATISTICS_MAXIMUM",
    5102           1 :                                     osValue.Printf("%.15g", dfMax));
    5103           1 :                 papszStatsMD = CSLSetNameValue(papszStatsMD, "STATISTICS_MEAN",
    5104           1 :                                                osValue.Printf("%.15g", dfMean));
    5105             :                 papszStatsMD =
    5106           1 :                     CSLSetNameValue(papszStatsMD, "STATISTICS_STDDEV",
    5107           1 :                                     osValue.Printf("%.15g", dfStdDev));
    5108             :             }
    5109             : 
    5110             :             // Histogram
    5111           1 :             int nBuckets = 0;
    5112           1 :             GUIntBig *panHistogram = nullptr;
    5113             : 
    5114           2 :             if (poSrcBand->GetDefaultHistogram(&dfMin, &dfMax, &nBuckets,
    5115             :                                                &panHistogram, TRUE, pfnProgress,
    5116           1 :                                                pProgressData) == CE_None)
    5117             :             {
    5118           2 :                 CPLString osValue;
    5119           1 :                 const double dfBinWidth = (dfMax - dfMin) / nBuckets;
    5120             : 
    5121           1 :                 papszStatsMD = CSLSetNameValue(
    5122             :                     papszStatsMD, "STATISTICS_HISTOMIN",
    5123           1 :                     osValue.Printf("%.15g", dfMin + dfBinWidth * 0.5));
    5124           1 :                 papszStatsMD = CSLSetNameValue(
    5125             :                     papszStatsMD, "STATISTICS_HISTOMAX",
    5126           1 :                     osValue.Printf("%.15g", dfMax - dfBinWidth * 0.5));
    5127             :                 papszStatsMD =
    5128           1 :                     CSLSetNameValue(papszStatsMD, "STATISTICS_HISTONUMBINS",
    5129           1 :                                     osValue.Printf("%d", nBuckets));
    5130             : 
    5131           1 :                 int nBinValuesLen = 0;
    5132             :                 char *pszBinValues =
    5133           1 :                     static_cast<char *>(CPLCalloc(20, nBuckets + 1));
    5134         257 :                 for (int iBin = 0; iBin < nBuckets; iBin++)
    5135             :                 {
    5136             : 
    5137         512 :                     strcat(pszBinValues + nBinValuesLen,
    5138         256 :                            osValue.Printf(CPL_FRMT_GUIB, panHistogram[iBin]));
    5139         256 :                     strcat(pszBinValues + nBinValuesLen, "|");
    5140         256 :                     nBinValuesLen +=
    5141         256 :                         static_cast<int>(strlen(pszBinValues + nBinValuesLen));
    5142             :                 }
    5143           1 :                 papszStatsMD = CSLSetNameValue(
    5144             :                     papszStatsMD, "STATISTICS_HISTOBINVALUES", pszBinValues);
    5145           1 :                 CPLFree(pszBinValues);
    5146             :             }
    5147             : 
    5148           1 :             CPLFree(panHistogram);
    5149             : 
    5150           1 :             if (CSLCount(papszStatsMD) > 0)
    5151           1 :                 HFASetMetadata(poDS->hHFA, iBand + 1, papszStatsMD);
    5152             : 
    5153           1 :             CSLDestroy(papszStatsMD);
    5154             :         }
    5155             :     }
    5156             : 
    5157             :     // All report completion.
    5158          50 :     if (!pfnProgress(1.0, nullptr, pProgressData))
    5159             :     {
    5160           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    5161           0 :         delete poDS;
    5162             : 
    5163           0 :         GDALDriver *poHFADriver = (GDALDriver *)GDALGetDriverByName("HFA");
    5164           0 :         poHFADriver->Delete(pszFilename);
    5165           0 :         return nullptr;
    5166             :     }
    5167             : 
    5168          50 :     poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    5169             : 
    5170          50 :     return poDS;
    5171             : }
    5172             : 
    5173             : /************************************************************************/
    5174             : /*                          GDALRegister_HFA()                          */
    5175             : /************************************************************************/
    5176             : 
    5177        2024 : void GDALRegister_HFA()
    5178             : 
    5179             : {
    5180        2024 :     if (GDALGetDriverByName("HFA") != nullptr)
    5181         283 :         return;
    5182             : 
    5183        1741 :     GDALDriver *poDriver = new GDALDriver();
    5184             : 
    5185        1741 :     poDriver->SetDescription("HFA");
    5186        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    5187        1741 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Erdas Imagine Images (.img)");
    5188        1741 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/hfa.html");
    5189        1741 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "img");
    5190        1741 :     poDriver->SetMetadataItem(
    5191             :         GDAL_DMD_CREATIONDATATYPES,
    5192             :         "Byte Int8 Int16 UInt16 Int32 UInt32 Float32 Float64 "
    5193        1741 :         "CFloat32 CFloat64");
    5194             : 
    5195        1741 :     poDriver->SetMetadataItem(
    5196             :         GDAL_DMD_CREATIONOPTIONLIST,
    5197             :         "<CreationOptionList>"
    5198             :         "   <Option name='BLOCKSIZE' type='integer' description='tile "
    5199             :         "width/height (32-2048)' default='64'/>"
    5200             :         "   <Option name='USE_SPILL' type='boolean' description='Force use of "
    5201             :         "spill file'/>"
    5202             :         "   <Option name='COMPRESSED' alias='COMPRESS' type='boolean' "
    5203             :         "description='compress blocks'/>"
    5204             :         "   <Option name='PIXELTYPE' type='string' description='(deprecated, "
    5205             :         "use Int8) By setting this to SIGNEDBYTE, a new Byte file can be "
    5206             :         "forced to be written as signed byte'/>"
    5207             :         "   <Option name='AUX' type='boolean' description='Create an .aux "
    5208             :         "file'/>"
    5209             :         "   <Option name='IGNOREUTM' type='boolean' description='Ignore UTM "
    5210             :         "when selecting coordinate system - will use Transverse Mercator. Only "
    5211             :         "used for Create() method'/>"
    5212             :         "   <Option name='NBITS' type='integer' description='Create file with "
    5213             :         "special sub-byte data type (1/2/4)'/>"
    5214             :         "   <Option name='STATISTICS' type='boolean' description='Generate "
    5215             :         "statistics and a histogram'/>"
    5216             :         "   <Option name='DEPENDENT_FILE' type='string' description='Name of "
    5217             :         "dependent file (must not have absolute path)'/>"
    5218             :         "   <Option name='FORCETOPESTRING' type='boolean' description='Force "
    5219             :         "use of ArcGIS PE String in file instead of Imagine coordinate system "
    5220             :         "format' default='NO'/>"
    5221             :         "   <Option name='DISABLEPESTRING' type='boolean' description='Disable "
    5222             :         "use of ArcGIS PE String' default='NO'/>"
    5223        1741 :         "</CreationOptionList>");
    5224             : 
    5225        1741 :     poDriver->SetMetadataItem(
    5226             :         GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST,
    5227             :         "<OverviewCreationOptionList>"
    5228             :         "   <Option name='COMPRESSED' alias='COMPRESS' type='boolean' "
    5229             :         "description='compress blocks'/>"
    5230        1741 :         "</OverviewCreationOptionList>");
    5231             : 
    5232        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    5233             : 
    5234        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
    5235        1741 :     poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
    5236             :                               "GeoTransform SRS NoData "
    5237             :                               "RasterValues "
    5238        1741 :                               "DatasetMetadata BandMetadata");
    5239             : 
    5240        1741 :     poDriver->pfnOpen = HFADataset::Open;
    5241        1741 :     poDriver->pfnCreate = HFADataset::Create;
    5242        1741 :     poDriver->pfnCreateCopy = HFADataset::CreateCopy;
    5243        1741 :     poDriver->pfnIdentify = HFADataset::Identify;
    5244        1741 :     poDriver->pfnRename = HFADataset::Rename;
    5245        1741 :     poDriver->pfnCopyFiles = HFADataset::CopyFiles;
    5246             : 
    5247        1741 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    5248             : }

Generated by: LCOV version 1.14