LCOV - code coverage report
Current view: top level - frmts/hfa - hfadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2016 2577 78.2 %
Date: 2024-05-06 18:28:20 Functions: 85 87 97.7 %

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

Generated by: LCOV version 1.14