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

Generated by: LCOV version 1.14