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

Generated by: LCOV version 1.14