LCOV - code coverage report
Current view: top level - frmts/hfa - hfadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2153 2707 79.5 %
Date: 2026-07-09 08:35:43 Functions: 95 97 97.9 %

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

Generated by: LCOV version 1.14