LCOV - code coverage report
Current view: top level - gcore - gdal_rat.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 904 1056 85.6 %
Date: 2025-11-08 22:13:33 Functions: 83 97 85.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Implementation of GDALRasterAttributeTable and related classes.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam
       9             :  * Copyright (c) 2009, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdal.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdal_rat.h"
      18             : 
      19             : #include <cmath>
      20             : #include <cstddef>
      21             : #include <cstdlib>
      22             : 
      23             : #include <algorithm>
      24             : #include <vector>
      25             : 
      26             : #include "cpl_conv.h"
      27             : #include "cpl_error.h"
      28             : #include "cpl_string.h"
      29             : #include "cpl_vsi.h"
      30             : 
      31             : #ifdef __clang__
      32             : #pragma clang diagnostic push
      33             : #pragma clang diagnostic ignored "-Wunknown-pragmas"
      34             : #pragma clang diagnostic ignored "-Wdocumentation"
      35             : #pragma clang diagnostic ignored "-Wold-style-cast"
      36             : #endif
      37             : #include "json.h"
      38             : #ifdef __clang__
      39             : #pragma clang diagnostic pop
      40             : #endif
      41             : #include "ogrlibjsonutils.h"
      42             : 
      43             : // NOTE: keep the below description in sync with doc/source/user/raster_data_model.rst::raster_data_model_rat
      44             : 
      45             : /**
      46             :  * \class GDALRasterAttributeTable
      47             :  *
      48             :  * The GDALRasterAttributeTable (or RAT) class is used to encapsulate a table
      49             :  * used to provide attribute information about pixel values.  Each row
      50             :  * in the table applies to a range of pixel values (or a single value in
      51             :  * some cases), and might have attributes such as the histogram count for
      52             :  * that range, the color pixels of that range should be drawn names of classes
      53             :  * or any other generic information.
      54             :  *
      55             :  * Raster attribute tables can be used to represent histograms, color tables,
      56             :  * and classification information.
      57             :  *
      58             :  * Each column in a raster attribute table has a name, a type (integer,
      59             :  * floating point, string, boolean, date time, geometries encoded as WKB),
      60             :  * and a GDALRATFieldUsage.
      61             :  * The usage distinguishes columns with particular understood purposes
      62             :  * (such as color, histogram count, name) and columns that have specific
      63             :  * purposes not understood by the library (long label,
      64             :  * suitability_for_growing_wheat, etc).
      65             :  *
      66             :  * In the general case each row has a column indicating the minimum pixel
      67             :  * values falling into that category, and a column indicating the maximum
      68             :  * pixel value.  These are indicated with usage values of GFU_Min, and
      69             :  * GFU_Max.  In other cases where each row is a discrete pixel value, one
      70             :  * column of usage GFU_MinMax can be used.
      71             :  *
      72             :  * In other cases all the categories are of equal size and regularly spaced
      73             :  * and the categorization information can be determined just by knowing the
      74             :  * value at which the categories start, and the size of a category.  This
      75             :  * is called "Linear Binning" and the information is kept specially on
      76             :  * the raster attribute table as a whole.
      77             :  *
      78             :  * RATs are normally associated with GDALRasterBands and can be queried
      79             :  * using the GDALRasterBand::GetDefaultRAT() method.
      80             :  */
      81             : 
      82             : /************************************************************************/
      83             : /*                        GDALGetRATFieldTypeName()                     */
      84             : /************************************************************************/
      85             : 
      86             : /** Return the string representation of a GDALRATFieldType.
      87             :  *
      88             :  * @since 3.12
      89             :  */
      90          44 : const char *GDALGetRATFieldTypeName(GDALRATFieldType eType)
      91             : {
      92             : #define CASE_GFT(x)                                                            \
      93             :     case GFT_##x:                                                              \
      94             :         return #x
      95             : 
      96          44 :     switch (eType)
      97             :     {
      98          13 :         CASE_GFT(Integer);
      99           7 :         CASE_GFT(String);
     100           9 :         CASE_GFT(Real);
     101           5 :         CASE_GFT(Boolean);
     102           5 :         CASE_GFT(DateTime);
     103           5 :         case GFT_WKBGeometry:
     104           5 :             break;
     105             :     }
     106           5 :     return "WKBGeometry";
     107             : 
     108             : #undef CASE_GFT
     109             : }
     110             : 
     111             : /************************************************************************/
     112             : /*                        GDALGetRATFieldUsageName()                    */
     113             : /************************************************************************/
     114             : 
     115             : /** Return the string representation of a GDALRATFieldUsage.
     116             :  *
     117             :  * @since 3.12
     118             :  */
     119          44 : const char *GDALGetRATFieldUsageName(GDALRATFieldUsage eUsage)
     120             : {
     121             : #define CASE_GFU(x)                                                            \
     122             :     case GFU_##x:                                                              \
     123             :         return #x
     124             : 
     125          44 :     switch (eUsage)
     126             :     {
     127          34 :         CASE_GFU(Generic);
     128           5 :         CASE_GFU(PixelCount);
     129           2 :         CASE_GFU(Name);
     130           0 :         CASE_GFU(Min);
     131           0 :         CASE_GFU(Max);
     132           3 :         CASE_GFU(MinMax);
     133           0 :         CASE_GFU(Red);
     134           0 :         CASE_GFU(Green);
     135           0 :         CASE_GFU(Blue);
     136           0 :         CASE_GFU(Alpha);
     137           0 :         CASE_GFU(RedMin);
     138           0 :         CASE_GFU(GreenMin);
     139           0 :         CASE_GFU(BlueMin);
     140           0 :         CASE_GFU(AlphaMin);
     141           0 :         CASE_GFU(RedMax);
     142           0 :         CASE_GFU(GreenMax);
     143           0 :         CASE_GFU(BlueMax);
     144           0 :         CASE_GFU(AlphaMax);
     145           0 :         case GFU_MaxCount:
     146           0 :             break;
     147             :     }
     148           0 :     return "MaxCount";
     149             : 
     150             : #undef CASE_GFU
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                  ~GDALRasterAttributeTable()                         */
     155             : /*                                                                      */
     156             : /*                      Virtual Destructor                              */
     157             : /************************************************************************/
     158             : 
     159             : GDALRasterAttributeTable::~GDALRasterAttributeTable() = default;
     160             : 
     161             : /************************************************************************/
     162             : /*                              ValuesIO()                              */
     163             : /*                                                                      */
     164             : /*                      Default Implementations                         */
     165             : /************************************************************************/
     166             : 
     167             : /**
     168             :  * \brief Read or Write a block of doubles to/from the Attribute Table.
     169             :  *
     170             :  * This method is the same as the C function GDALRATValuesIOAsDouble().
     171             :  *
     172             :  * @param eRWFlag either GF_Read or GF_Write
     173             :  * @param iField column of the Attribute Table
     174             :  * @param iStartRow start row to start reading/writing (zero based)
     175             :  * @param iLength number of rows to read or write
     176             :  * @param pdfData pointer to array of doubles to read/write. Should be at least
     177             :  *   iLength long.
     178             :  *
     179             :  * @return CE_None or CE_Failure if iStartRow + iLength greater than number of
     180             :  *   rows in table.
     181             :  */
     182             : 
     183           8 : CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     184             :                                           int iStartRow, int iLength,
     185             :                                           double *pdfData)
     186             : {
     187           8 :     if ((iStartRow + iLength) > GetRowCount())
     188             :     {
     189           0 :         return CE_Failure;
     190             :     }
     191             : 
     192           8 :     CPLErr eErr = CE_None;
     193           8 :     if (eRWFlag == GF_Read)
     194             :     {
     195          28 :         for (int iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++)
     196             :         {
     197          22 :             pdfData[iIndex - iStartRow] = GetValueAsDouble(iIndex, iField);
     198             :         }
     199             :     }
     200             :     else
     201             :     {
     202           2 :         for (int iIndex = iStartRow;
     203           4 :              eErr == CE_None && iIndex < (iStartRow + iLength); iIndex++)
     204             :         {
     205           2 :             eErr = SetValue(iIndex, iField, pdfData[iIndex - iStartRow]);
     206             :         }
     207             :     }
     208           8 :     return eErr;
     209             : }
     210             : 
     211             : /************************************************************************/
     212             : /*                       GDALRATValuesIOAsDouble()                      */
     213             : /************************************************************************/
     214             : 
     215             : /**
     216             :  * \brief Read or Write a block of doubles to/from the Attribute Table.
     217             :  *
     218             :  * This function is the same as the C++ method
     219             :  * GDALRasterAttributeTable::ValuesIO()
     220             :  */
     221          30 : CPLErr CPL_STDCALL GDALRATValuesIOAsDouble(GDALRasterAttributeTableH hRAT,
     222             :                                            GDALRWFlag eRWFlag, int iField,
     223             :                                            int iStartRow, int iLength,
     224             :                                            double *pdfData)
     225             : 
     226             : {
     227          30 :     VALIDATE_POINTER1(hRAT, "GDALRATValuesIOAsDouble", CE_Failure);
     228             : 
     229          60 :     return GDALRasterAttributeTable::FromHandle(hRAT)->ValuesIO(
     230          30 :         eRWFlag, iField, iStartRow, iLength, pdfData);
     231             : }
     232             : 
     233             : /**
     234             :  * \brief Read or Write a block of integers to/from the Attribute Table.
     235             :  *
     236             :  * This method is the same as the C function GDALRATValuesIOAsInteger().
     237             :  *
     238             :  * @param eRWFlag either GF_Read or GF_Write
     239             :  * @param iField column of the Attribute Table
     240             :  * @param iStartRow start row to start reading/writing (zero based)
     241             :  * @param iLength number of rows to read or write
     242             :  * @param pnData pointer to array of ints to read/write. Should be at least
     243             :  *     iLength long.
     244             :  *
     245             :  * @return CE_None or CE_Failure if iStartRow + iLength greater than number of
     246             :  *     rows in table.
     247             :  */
     248             : 
     249          11 : CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     250             :                                           int iStartRow, int iLength,
     251             :                                           int *pnData)
     252             : {
     253          11 :     if ((iStartRow + iLength) > GetRowCount())
     254             :     {
     255           1 :         return CE_Failure;
     256             :     }
     257             : 
     258          10 :     CPLErr eErr = CE_None;
     259          10 :     if (eRWFlag == GF_Read)
     260             :     {
     261          52 :         for (int iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++)
     262             :         {
     263          42 :             pnData[iIndex - iStartRow] = GetValueAsInt(iIndex, iField);
     264             :         }
     265             :     }
     266             :     else
     267             :     {
     268           0 :         for (int iIndex = iStartRow;
     269           0 :              eErr == CE_None && iIndex < (iStartRow + iLength); iIndex++)
     270             :         {
     271           0 :             eErr = SetValue(iIndex, iField, pnData[iIndex - iStartRow]);
     272             :         }
     273             :     }
     274          10 :     return eErr;
     275             : }
     276             : 
     277             : /************************************************************************/
     278             : /*                       GDALRATValuesIOAsInteger()                     */
     279             : /************************************************************************/
     280             : 
     281             : /**
     282             :  * \brief Read or Write a block of ints to/from the Attribute Table.
     283             :  *
     284             :  * This function is the same as the C++ method
     285             :  * GDALRasterAttributeTable::ValuesIO()
     286             :  */
     287          33 : CPLErr CPL_STDCALL GDALRATValuesIOAsInteger(GDALRasterAttributeTableH hRAT,
     288             :                                             GDALRWFlag eRWFlag, int iField,
     289             :                                             int iStartRow, int iLength,
     290             :                                             int *pnData)
     291             : 
     292             : {
     293          33 :     VALIDATE_POINTER1(hRAT, "GDALRATValuesIOAsInteger", CE_Failure);
     294             : 
     295          66 :     return GDALRasterAttributeTable::FromHandle(hRAT)->ValuesIO(
     296          33 :         eRWFlag, iField, iStartRow, iLength, pnData);
     297             : }
     298             : 
     299             : /**
     300             :  * \brief Read or Write a block of strings to/from the Attribute Table.
     301             :  *
     302             :  * This method is the same as the C function GDALRATValuesIOAsString().
     303             :  * When reading, papszStrList must be already allocated to the correct size.
     304             :  * The caller is expected to call CPLFree on each read string.
     305             :  *
     306             :  * @param eRWFlag either GF_Read or GF_Write
     307             :  * @param iField column of the Attribute Table
     308             :  * @param iStartRow start row to start reading/writing (zero based)
     309             :  * @param iLength number of rows to read or write
     310             :  * @param papszStrList pointer to array of strings to read/write. Should be at
     311             :  *   least iLength long.
     312             :  *
     313             :  * @return CE_None or CE_Failure if iStartRow + iLength greater than number of
     314             :  *   rows in table.
     315             :  */
     316             : 
     317           6 : CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     318             :                                           int iStartRow, int iLength,
     319             :                                           char **papszStrList)
     320             : {
     321           6 :     if ((iStartRow + iLength) > GetRowCount())
     322             :     {
     323           0 :         return CE_Failure;
     324             :     }
     325             : 
     326           6 :     CPLErr eErr = CE_None;
     327           6 :     if (eRWFlag == GF_Read)
     328             :     {
     329          28 :         for (int iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++)
     330             :         {
     331          44 :             papszStrList[iIndex - iStartRow] =
     332          22 :                 VSIStrdup(GetValueAsString(iIndex, iField));
     333             :         }
     334             :     }
     335             :     else
     336             :     {
     337           0 :         for (int iIndex = iStartRow;
     338           0 :              eErr == CE_None && iIndex < (iStartRow + iLength); iIndex++)
     339             :         {
     340           0 :             eErr = SetValue(iIndex, iField, papszStrList[iIndex - iStartRow]);
     341             :         }
     342             :     }
     343           6 :     return eErr;
     344             : }
     345             : 
     346             : /************************************************************************/
     347             : /*                       GDALRATValuesIOAsString()                      */
     348             : /************************************************************************/
     349             : 
     350             : /**
     351             :  * \brief Read or Write a block of strings to/from the Attribute Table.
     352             :  *
     353             :  * This function is the same as the C++ method
     354             :  * GDALRasterAttributeTable::ValuesIO()
     355             :  */
     356          30 : CPLErr CPL_STDCALL GDALRATValuesIOAsString(GDALRasterAttributeTableH hRAT,
     357             :                                            GDALRWFlag eRWFlag, int iField,
     358             :                                            int iStartRow, int iLength,
     359             :                                            char **papszStrList)
     360             : 
     361             : {
     362          30 :     VALIDATE_POINTER1(hRAT, "GDALRATValuesIOAsString", CE_Failure);
     363             : 
     364          60 :     return GDALRasterAttributeTable::FromHandle(hRAT)->ValuesIO(
     365          30 :         eRWFlag, iField, iStartRow, iLength, papszStrList);
     366             : }
     367             : 
     368             : /************************************************************************/
     369             : /*                                ValuesIO()                            */
     370             : /************************************************************************/
     371             : 
     372             : /**
     373             :  * \brief Read or Write a block of booleans to/from the Attribute Table.
     374             :  *
     375             :  * This method is the same as the C function GDALRATValuesIOAsBoolean().
     376             :  *
     377             :  * @param eRWFlag either GF_Read or GF_Write
     378             :  * @param iField column of the Attribute Table
     379             :  * @param iStartRow start row to start reading/writing (zero based)
     380             :  * @param iLength number of rows to read or write
     381             :  * @param pbData pointer to array of booleans to read/write. Should be at least
     382             :  *     iLength long.
     383             :  *
     384             :  * @return CE_None or CE_Failure if iStartRow + iLength greater than number of
     385             :  *     rows in table.
     386             :  * @since 3.12
     387             :  */
     388             : 
     389           5 : CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     390             :                                           int iStartRow, int iLength,
     391             :                                           bool *pbData)
     392             : {
     393           5 :     if ((iStartRow + iLength) > GetRowCount())
     394             :     {
     395           0 :         return CE_Failure;
     396             :     }
     397             : 
     398           5 :     CPLErr eErr = CE_None;
     399           5 :     if (eRWFlag == GF_Read)
     400             :     {
     401          24 :         for (int iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++)
     402             :         {
     403          20 :             pbData[iIndex - iStartRow] = GetValueAsBoolean(iIndex, iField);
     404             :         }
     405             :     }
     406             :     else
     407             :     {
     408           1 :         for (int iIndex = iStartRow;
     409           6 :              eErr == CE_None && iIndex < (iStartRow + iLength); iIndex++)
     410             :         {
     411           5 :             eErr = SetValue(iIndex, iField, pbData[iIndex - iStartRow]);
     412             :         }
     413             :     }
     414           5 :     return eErr;
     415             : }
     416             : 
     417             : /************************************************************************/
     418             : /*                       GDALRATValuesIOAsBoolean()                     */
     419             : /************************************************************************/
     420             : 
     421             : /**
     422             :  * \brief Read or Write a block of booleans to/from the Attribute Table.
     423             :  *
     424             :  * This function is the same as the C++ method
     425             :  * GDALRasterAttributeTable::ValuesIO()
     426             :  *
     427             :  * @since 3.12
     428             :  */
     429           9 : CPLErr GDALRATValuesIOAsBoolean(GDALRasterAttributeTableH hRAT,
     430             :                                 GDALRWFlag eRWFlag, int iField, int iStartRow,
     431             :                                 int iLength, bool *pbData)
     432             : 
     433             : {
     434           9 :     VALIDATE_POINTER1(hRAT, "GDALRATValuesIOAsBoolean", CE_Failure);
     435             : 
     436          18 :     return GDALRasterAttributeTable::FromHandle(hRAT)->ValuesIO(
     437           9 :         eRWFlag, iField, iStartRow, iLength, pbData);
     438             : }
     439             : 
     440             : /************************************************************************/
     441             : /*                                ValuesIO()                            */
     442             : /************************************************************************/
     443             : 
     444             : /**
     445             :  * \brief Read or Write a block of DateTime to/from the Attribute Table.
     446             :  *
     447             :  * This method is the same as the C function GDALRATValuesIOAsDateTime().
     448             :  *
     449             :  * @param eRWFlag either GF_Read or GF_Write
     450             :  * @param iField column of the Attribute Table
     451             :  * @param iStartRow start row to start reading/writing (zero based)
     452             :  * @param iLength number of rows to read or write
     453             :  * @param psDateTime pointer to array of DateTime to read/write. Should be at
     454             :  *                   least iLength long.
     455             :  *
     456             :  * @return CE_None or CE_Failure if iStartRow + iLength greater than number of
     457             :  *     rows in table.
     458             :  * @since 3.12
     459             :  */
     460             : 
     461           2 : CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     462             :                                           int iStartRow, int iLength,
     463             :                                           GDALRATDateTime *psDateTime)
     464             : {
     465           2 :     if ((iStartRow + iLength) > GetRowCount())
     466             :     {
     467           0 :         return CE_Failure;
     468             :     }
     469             : 
     470           2 :     CPLErr eErr = CE_None;
     471           2 :     if (eRWFlag == GF_Read)
     472             :     {
     473           3 :         for (int iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++)
     474             :         {
     475           2 :             psDateTime[iIndex - iStartRow] = GetValueAsDateTime(iIndex, iField);
     476             :         }
     477             :     }
     478             :     else
     479             :     {
     480           1 :         for (int iIndex = iStartRow;
     481           3 :              eErr == CE_None && iIndex < (iStartRow + iLength); iIndex++)
     482             :         {
     483           2 :             eErr = SetValue(iIndex, iField, psDateTime[iIndex - iStartRow]);
     484             :         }
     485             :     }
     486           2 :     return eErr;
     487             : }
     488             : 
     489             : /************************************************************************/
     490             : /*                       GDALRATValuesIOAsDateTime()                    */
     491             : /************************************************************************/
     492             : 
     493             : /**
     494             :  * \brief Read or Write a block of date-times to/from the Attribute Table.
     495             :  *
     496             :  * This function is the same as the C++ method
     497             :  * GDALRasterAttributeTable::ValuesIO()
     498             :  *
     499             :  * @since 3.12
     500             :  */
     501           2 : CPLErr GDALRATValuesIOAsDateTime(GDALRasterAttributeTableH hRAT,
     502             :                                  GDALRWFlag eRWFlag, int iField, int iStartRow,
     503             :                                  int iLength, GDALRATDateTime *psDateTime)
     504             : 
     505             : {
     506           2 :     VALIDATE_POINTER1(hRAT, "GDALRATValuesIOAsDateTime", CE_Failure);
     507             : 
     508           4 :     return GDALRasterAttributeTable::FromHandle(hRAT)->ValuesIO(
     509           2 :         eRWFlag, iField, iStartRow, iLength, psDateTime);
     510             : }
     511             : 
     512             : /************************************************************************/
     513             : /*                                ValuesIO()                            */
     514             : /************************************************************************/
     515             : 
     516             : /**
     517             :  * \brief Read or Write a block of WKB-encoded geometries to/from the Attribute Table.
     518             :  *
     519             :  * When reading, each ppabyWKB[] should be CPLFree'd() after use.
     520             :  *
     521             :  * This method is the same as the C function GDALRATValuesIOAsWKBGeometry().
     522             :  *
     523             :  * @param eRWFlag either GF_Read or GF_Write
     524             :  * @param iField column of the Attribute Table
     525             :  * @param iStartRow start row to start reading/writing (zero based)
     526             :  * @param iLength number of rows to read or write
     527             :  * @param ppabyWKB pointer to array of pointer of WKB-encoded geometries to
     528             :  *                 read/write. Should be at least iLength long.
     529             :  * @param pnWKBSize pointer to array of WKB size.
     530             :  *                  Should be at least iLength long.
     531             :  *
     532             :  * @return CE_None or CE_Failure if iStartRow + iLength greater than number of
     533             :  *     rows in table.
     534             :  * @since 3.12
     535             :  */
     536             : 
     537           2 : CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField,
     538             :                                           int iStartRow, int iLength,
     539             :                                           GByte **ppabyWKB, size_t *pnWKBSize)
     540             : {
     541           2 :     if ((iStartRow + iLength) > GetRowCount())
     542             :     {
     543           0 :         return CE_Failure;
     544             :     }
     545             : 
     546           2 :     CPLErr eErr = CE_None;
     547           2 :     if (eRWFlag == GF_Read)
     548             :     {
     549           2 :         for (int iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++)
     550             :         {
     551           1 :             size_t nSize = 0;
     552           1 :             const GByte *pabyWKB = GetValueAsWKBGeometry(iIndex, iField, nSize);
     553           1 :             pnWKBSize[iIndex - iStartRow] = nSize;
     554           1 :             if (nSize)
     555             :             {
     556           2 :                 ppabyWKB[iIndex - iStartRow] =
     557           1 :                     static_cast<GByte *>(CPLMalloc(nSize));
     558           1 :                 memcpy(ppabyWKB[iIndex - iStartRow], pabyWKB, nSize);
     559             :             }
     560             :             else
     561             :             {
     562           0 :                 ppabyWKB[iIndex - iStartRow] = nullptr;
     563             :             }
     564             :         }
     565             :     }
     566             :     else
     567             :     {
     568           1 :         for (int iIndex = iStartRow;
     569           2 :              eErr == CE_None && iIndex < (iStartRow + iLength); iIndex++)
     570             :         {
     571           1 :             eErr = SetValue(iIndex, iField, ppabyWKB[iIndex - iStartRow],
     572           1 :                             pnWKBSize[iIndex - iStartRow]);
     573             :         }
     574             :     }
     575           2 :     return eErr;
     576             : }
     577             : 
     578             : /************************************************************************/
     579             : /*                     GDALRATValuesIOAsWKBGeometry()                   */
     580             : /************************************************************************/
     581             : 
     582             : /**
     583             :  * \brief Read or Write a block of WKB-encoded geometries to/from the Attribute Table.
     584             :  *
     585             :  * When reading, each ppabyWKB[] should be CPLFree'd() after use.
     586             :  *
     587             :  * This function is the same as the C++ method
     588             :  * GDALRasterAttributeTable::ValuesIO()
     589             :  *
     590             :  * @since 3.12
     591             :  */
     592           2 : CPLErr GDALRATValuesIOAsWKBGeometry(GDALRasterAttributeTableH hRAT,
     593             :                                     GDALRWFlag eRWFlag, int iField,
     594             :                                     int iStartRow, int iLength,
     595             :                                     GByte **ppabyWKB, size_t *pnWKBSize)
     596             : 
     597             : {
     598           2 :     VALIDATE_POINTER1(hRAT, "GDALRATValuesIOAsWKBGeometry", CE_Failure);
     599             : 
     600           4 :     return GDALRasterAttributeTable::FromHandle(hRAT)->ValuesIO(
     601           2 :         eRWFlag, iField, iStartRow, iLength, ppabyWKB, pnWKBSize);
     602             : }
     603             : 
     604             : //! @cond Doxygen_Suppress
     605             : 
     606             : /************************************************************************/
     607             : /*                   ValuesIOBooleanFromIntoInt()                       */
     608             : /************************************************************************/
     609             : 
     610           7 : CPLErr GDALRasterAttributeTable::ValuesIOBooleanFromIntoInt(
     611             :     GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, bool *pbData)
     612             : {
     613           7 :     if (eRWFlag == GF_Read)
     614             :     {
     615           6 :         std::vector<int> anData(iLength);
     616             :         CPLErr eErr =
     617           6 :             ValuesIO(eRWFlag, iField, iStartRow, iLength, anData.data());
     618           6 :         if (eErr == CE_None)
     619             :         {
     620           4 :             for (int i = 0; i < iLength; ++i)
     621             :             {
     622           2 :                 pbData[i] = anData[i] != 0;
     623             :             }
     624             :         }
     625           6 :         return eErr;
     626             :     }
     627             :     else
     628             :     {
     629           2 :         std::vector<int> anData;
     630           1 :         anData.reserve(iLength);
     631           2 :         for (int i = 0; i < iLength; ++i)
     632           1 :             anData.push_back(pbData[i]);
     633           1 :         return ValuesIO(eRWFlag, iField, iStartRow, iLength, anData.data());
     634             :     }
     635             : }
     636             : 
     637             : /************************************************************************/
     638             : /*                          DateTimeToString()                          */
     639             : /************************************************************************/
     640             : 
     641             : /* static */
     642             : std::string
     643          21 : GDALRasterAttributeTable::DateTimeToString(const GDALRATDateTime &sDateTime)
     644             : {
     645          21 :     if (!sDateTime.bIsValid)
     646           3 :         return std::string();
     647          36 :     return CPLString().Printf(
     648          18 :         "%04d-%02d-%02dT%02d:%02d:%06.3f%c%02d:%02d", sDateTime.nYear,
     649          18 :         sDateTime.nMonth, sDateTime.nDay, sDateTime.nHour, sDateTime.nMinute,
     650          18 :         static_cast<double>(sDateTime.fSecond),
     651          18 :         sDateTime.bPositiveTimeZone ? '+' : '-', sDateTime.nTimeZoneHour,
     652          18 :         sDateTime.nTimeZoneMinute);
     653             : }
     654             : 
     655             : /************************************************************************/
     656             : /*                           StringToDateTime()                         */
     657             : /************************************************************************/
     658             : 
     659             : /* static */
     660          17 : bool GDALRasterAttributeTable::StringToDateTime(const char *pszStr,
     661             :                                                 GDALRATDateTime &sDateTime)
     662             : {
     663             :     OGRField sField;
     664          17 :     if (OGRParseDate(pszStr, &sField, 0))
     665             :     {
     666          14 :         sDateTime.nYear = sField.Date.Year;
     667          14 :         sDateTime.nMonth = sField.Date.Month;
     668          14 :         sDateTime.nDay = sField.Date.Day;
     669          14 :         sDateTime.nHour = sField.Date.Hour;
     670          14 :         sDateTime.nMinute = sField.Date.Minute;
     671          14 :         sDateTime.fSecond = sField.Date.Second;
     672          14 :         sDateTime.bPositiveTimeZone =
     673          14 :             sField.Date.TZFlag <= 2 ? false : sField.Date.TZFlag >= 100;
     674          28 :         sDateTime.nTimeZoneHour = sField.Date.TZFlag <= 2
     675          14 :                                       ? 0
     676          14 :                                       : std::abs(sField.Date.TZFlag - 100) / 4;
     677          14 :         sDateTime.nTimeZoneMinute =
     678          14 :             sField.Date.TZFlag <= 2
     679          14 :                 ? 0
     680          14 :                 : (std::abs(sField.Date.TZFlag - 100) % 4) * 15;
     681          14 :         sDateTime.bIsValid = true;
     682          14 :         return true;
     683             :     }
     684             :     else
     685             :     {
     686           3 :         sDateTime = GDALRATDateTime();
     687           3 :         return false;
     688             :     }
     689             : }
     690             : 
     691             : /************************************************************************/
     692             : /*                  ValuesIODateTimeFromIntoString()                    */
     693             : /************************************************************************/
     694             : 
     695          12 : CPLErr GDALRasterAttributeTable::ValuesIODateTimeFromIntoString(
     696             :     GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength,
     697             :     GDALRATDateTime *psDateTime)
     698             : {
     699          12 :     if (eRWFlag == GF_Read)
     700             :     {
     701           7 :         std::vector<char *> apszStrList(iLength);
     702             :         CPLErr eErr =
     703           7 :             ValuesIO(eRWFlag, iField, iStartRow, iLength, apszStrList.data());
     704           7 :         if (eErr == CE_None)
     705             :         {
     706           8 :             for (int i = 0; i < iLength; ++i)
     707             :             {
     708           4 :                 StringToDateTime(apszStrList[i], psDateTime[i]);
     709             :             }
     710             :         }
     711          14 :         for (int i = 0; i < iLength; ++i)
     712           7 :             VSIFree(apszStrList[i]);
     713           7 :         return eErr;
     714             :     }
     715             :     else
     716             :     {
     717          10 :         std::vector<std::string> asStr;
     718          10 :         std::vector<char *> apszStr;
     719           5 :         asStr.reserve(iLength);
     720           5 :         apszStr.reserve(iLength);
     721          10 :         for (int i = 0; i < iLength; ++i)
     722             :         {
     723           5 :             asStr.push_back(DateTimeToString(psDateTime[i]));
     724           5 :             apszStr.push_back(asStr.back().data());
     725             :         }
     726           5 :         return ValuesIO(eRWFlag, iField, iStartRow, iLength, apszStr.data());
     727             :     }
     728             : }
     729             : 
     730             : /************************************************************************/
     731             : /*                          WKBGeometryToWKT()                          */
     732             : /************************************************************************/
     733             : 
     734             : /* static */
     735          12 : std::string GDALRasterAttributeTable::WKBGeometryToWKT(const void *pabyWKB,
     736             :                                                        size_t nWKBSize)
     737             : {
     738          12 :     std::string osWKT;
     739          12 :     if (nWKBSize)
     740             :     {
     741           8 :         OGRGeometry *poGeometry = nullptr;
     742           8 :         if (OGRGeometryFactory::createFromWkb(pabyWKB, nullptr, &poGeometry,
     743             :                                               nWKBSize,
     744           8 :                                               wkbVariantIso) == OGRERR_NONE)
     745             :         {
     746           8 :             osWKT = poGeometry->exportToWkt();
     747             :         }
     748           8 :         delete poGeometry;
     749             :     }
     750          12 :     return osWKT;
     751             : }
     752             : 
     753             : /************************************************************************/
     754             : /*                          WKTGeometryToWKB()                          */
     755             : /************************************************************************/
     756             : 
     757             : /* static */
     758             : std::vector<GByte>
     759          17 : GDALRasterAttributeTable::WKTGeometryToWKB(const char *pszWKT)
     760             : {
     761          17 :     std::vector<GByte> abyWKB;
     762          17 :     OGRGeometry *poGeom = nullptr;
     763          17 :     if (pszWKT[0] && OGRGeometryFactory::createFromWkt(pszWKT, nullptr,
     764             :                                                        &poGeom) == OGRERR_NONE)
     765             :     {
     766          14 :         const size_t nWKBSize = poGeom->WkbSize();
     767          14 :         abyWKB.resize(nWKBSize);
     768          14 :         poGeom->exportToWkb(wkbNDR, abyWKB.data(), wkbVariantIso);
     769             :     }
     770          17 :     delete poGeom;
     771          34 :     return abyWKB;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                     ValuesIOWKBGeometryFromIntoString()              */
     776             : /************************************************************************/
     777             : 
     778          12 : CPLErr GDALRasterAttributeTable::ValuesIOWKBGeometryFromIntoString(
     779             :     GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength,
     780             :     GByte **ppabyWKB, size_t *pnWKBSize)
     781             : {
     782          12 :     if (eRWFlag == GF_Read)
     783             :     {
     784           7 :         std::vector<char *> apszStrList(iLength);
     785             :         CPLErr eErr =
     786           7 :             ValuesIO(eRWFlag, iField, iStartRow, iLength, apszStrList.data());
     787           7 :         if (eErr == CE_None)
     788             :         {
     789           8 :             for (int i = 0; i < iLength; ++i)
     790             :             {
     791           8 :                 auto abyWKB = WKTGeometryToWKB(apszStrList[i]);
     792           4 :                 if (abyWKB.empty())
     793             :                 {
     794           2 :                     ppabyWKB[i] = nullptr;
     795           2 :                     pnWKBSize[i] = 0;
     796             :                 }
     797             :                 else
     798             :                 {
     799           4 :                     ppabyWKB[i] =
     800           2 :                         static_cast<GByte *>(CPLMalloc(abyWKB.size()));
     801           2 :                     memcpy(ppabyWKB[i], abyWKB.data(), abyWKB.size());
     802           2 :                     pnWKBSize[i] = abyWKB.size();
     803             :                 }
     804             :             }
     805             :         }
     806          14 :         for (int i = 0; i < iLength; ++i)
     807           7 :             VSIFree(apszStrList[i]);
     808           7 :         return eErr;
     809             :     }
     810             :     else
     811             :     {
     812          10 :         std::vector<std::string> asStr;
     813          10 :         std::vector<char *> apszStr;
     814           5 :         asStr.reserve(iLength);
     815           5 :         apszStr.reserve(iLength);
     816          10 :         for (int i = 0; i < iLength; ++i)
     817             :         {
     818           5 :             asStr.push_back(WKBGeometryToWKT(ppabyWKB[i], pnWKBSize[i]));
     819           5 :             apszStr.push_back(asStr.back().data());
     820             :         }
     821           5 :         return ValuesIO(eRWFlag, iField, iStartRow, iLength, apszStr.data());
     822             :     }
     823             : }
     824             : 
     825             : //! @endcond
     826             : 
     827             : /************************************************************************/
     828             : /*                            SetRowCount()                             */
     829             : /************************************************************************/
     830             : 
     831             : /**
     832             :  * \brief Set row count.
     833             :  *
     834             :  * Resizes the table to include the indicated number of rows.  Newly created
     835             :  * rows will be initialized to their default values - "" for strings,
     836             :  * and zero for numeric fields.
     837             :  *
     838             :  * This method is the same as the C function GDALRATSetRowCount().
     839             :  *
     840             :  * @param nNewCount the new number of rows.
     841             :  */
     842             : 
     843           0 : void GDALRasterAttributeTable::SetRowCount(CPL_UNUSED int nNewCount)
     844             : {
     845           0 : }
     846             : 
     847             : /************************************************************************/
     848             : /*                         GDALRATSetRowCount()                         */
     849             : /************************************************************************/
     850             : 
     851             : /**
     852             :  * \brief Set row count.
     853             :  *
     854             :  * This function is the same as the C++ method
     855             :  * GDALRasterAttributeTable::SetRowCount()
     856             :  *
     857             :  * @param hRAT RAT handle.
     858             :  * @param nNewCount the new number of rows.
     859             :  */
     860          15 : void CPL_STDCALL GDALRATSetRowCount(GDALRasterAttributeTableH hRAT,
     861             :                                     int nNewCount)
     862             : 
     863             : {
     864          15 :     VALIDATE_POINTER0(hRAT, "GDALRATSetRowCount");
     865             : 
     866          15 :     GDALRasterAttributeTable::FromHandle(hRAT)->SetRowCount(nNewCount);
     867             : }
     868             : 
     869             : /************************************************************************/
     870             : /*                           GetRowOfValue()                            */
     871             : /************************************************************************/
     872             : 
     873             : /**
     874             :  * \fn GDALRasterAttributeTable::GetRowOfValue(double) const
     875             :  * \brief Get row for pixel value.
     876             :  *
     877             :  * Given a raw pixel value, the raster attribute table is scanned to
     878             :  * determine which row in the table applies to the pixel value.  The
     879             :  * row index is returned.
     880             :  *
     881             :  * This method is the same as the C function GDALRATGetRowOfValue().
     882             :  *
     883             :  * @param dfValue the pixel value.
     884             :  *
     885             :  * @return the row index or -1 if no row is appropriate.
     886             :  */
     887             : 
     888             : /**/
     889             : /**/
     890             : 
     891           0 : int GDALRasterAttributeTable::GetRowOfValue(double /* dfValue */) const
     892             : {
     893           0 :     return -1;
     894             : }
     895             : 
     896             : /************************************************************************/
     897             : /*                        GDALRATGetRowOfValue()                        */
     898             : /************************************************************************/
     899             : 
     900             : /**
     901             :  * \brief Get row for pixel value.
     902             :  *
     903             :  * This function is the same as the C++ method
     904             :  * GDALRasterAttributeTable::GetRowOfValue()
     905             :  */
     906           3 : int CPL_STDCALL GDALRATGetRowOfValue(GDALRasterAttributeTableH hRAT,
     907             :                                      double dfValue)
     908             : 
     909             : {
     910           3 :     VALIDATE_POINTER1(hRAT, "GDALRATGetRowOfValue", 0);
     911             : 
     912           3 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetRowOfValue(dfValue);
     913             : }
     914             : 
     915             : /************************************************************************/
     916             : /*                           GetRowOfValue()                            */
     917             : /************************************************************************/
     918             : 
     919             : /**
     920             :  * \brief Get row for pixel value.
     921             :  *
     922             :  * Given a raw pixel value, the raster attribute table is scanned to
     923             :  * determine which row in the table applies to the pixel value.  The
     924             :  * row index is returned.
     925             :  *
     926             :  * Int arg for now just converted to double.  Perhaps we will
     927             :  * handle this in a special way some day?
     928             :  *
     929             :  * This method is the same as the C function GDALRATGetRowOfValue().
     930             :  *
     931             :  * @param nValue the pixel value.
     932             :  *
     933             :  * @return the row index or -1 if no row is appropriate.
     934             :  */
     935             : 
     936           0 : int GDALRasterAttributeTable::GetRowOfValue(int nValue) const
     937             : 
     938             : {
     939           0 :     return GetRowOfValue(static_cast<double>(nValue));
     940             : }
     941             : 
     942             : /************************************************************************/
     943             : /*                            CreateColumn()                            */
     944             : /************************************************************************/
     945             : 
     946             : /**
     947             :  * \fn GDALRasterAttributeTable::CreateColumn(const char*, GDALRATFieldType,
     948             :  * GDALRATFieldUsage) \brief Create new column.
     949             :  *
     950             :  * If the table already has rows, all row values for the new column will
     951             :  * be initialized to the default value ("", or zero).  The new column is
     952             :  * always created as the last column, and will be column (field)
     953             :  * "GetColumnCount()-1" after CreateColumn() has completed successfully.
     954             :  *
     955             :  * This method is the same as the C function GDALRATCreateColumn().
     956             :  *
     957             :  * @param pszFieldName the name of the field to create.
     958             :  * @param eFieldType the field type (integer, double or string).
     959             :  * @param eFieldUsage the field usage, GFU_Generic if not known.
     960             :  *
     961             :  * @return CE_None on success or CE_Failure if something goes wrong.
     962             :  */
     963             : 
     964             : /**/
     965             : /**/
     966             : 
     967             : CPLErr
     968           0 : GDALRasterAttributeTable::CreateColumn(const char * /* pszFieldName */,
     969             :                                        GDALRATFieldType /* eFieldType */,
     970             :                                        GDALRATFieldUsage /* eFieldUsage */)
     971             : {
     972           0 :     return CE_Failure;
     973             : }
     974             : 
     975             : /************************************************************************/
     976             : /*                        GDALRATCreateColumn()                         */
     977             : /************************************************************************/
     978             : 
     979             : /**
     980             :  * \brief Create new column.
     981             :  *
     982             :  * This function is the same as the C++ method
     983             :  * GDALRasterAttributeTable::CreateColumn()
     984             :  */
     985          74 : CPLErr CPL_STDCALL GDALRATCreateColumn(GDALRasterAttributeTableH hRAT,
     986             :                                        const char *pszFieldName,
     987             :                                        GDALRATFieldType eFieldType,
     988             :                                        GDALRATFieldUsage eFieldUsage)
     989             : 
     990             : {
     991          74 :     VALIDATE_POINTER1(hRAT, "GDALRATCreateColumn", CE_Failure);
     992             : 
     993         148 :     return GDALRasterAttributeTable::FromHandle(hRAT)->CreateColumn(
     994          74 :         pszFieldName, eFieldType, eFieldUsage);
     995             : }
     996             : 
     997             : /************************************************************************/
     998             : /*                          SetLinearBinning()                          */
     999             : /************************************************************************/
    1000             : 
    1001             : /**
    1002             :  * \brief Set linear binning information.
    1003             :  *
    1004             :  * For RATs with equal sized categories (in pixel value space) that are
    1005             :  * evenly spaced, this method may be used to associate the linear binning
    1006             :  * information with the table.
    1007             :  *
    1008             :  * This method is the same as the C function GDALRATSetLinearBinning().
    1009             :  *
    1010             :  * @param dfRow0MinIn the lower bound (pixel value) of the first category.
    1011             :  * @param dfBinSizeIn the width of each category (in pixel value units).
    1012             :  *
    1013             :  * @return CE_None on success or CE_Failure on failure.
    1014             :  */
    1015             : 
    1016           0 : CPLErr GDALRasterAttributeTable::SetLinearBinning(CPL_UNUSED double dfRow0MinIn,
    1017             :                                                   CPL_UNUSED double dfBinSizeIn)
    1018             : {
    1019           0 :     return CE_Failure;
    1020             : }
    1021             : 
    1022             : /************************************************************************/
    1023             : /*                      GDALRATSetLinearBinning()                       */
    1024             : /************************************************************************/
    1025             : 
    1026             : /**
    1027             :  * \brief Set linear binning information.
    1028             :  *
    1029             :  * This function is the same as the C++ method
    1030             :  * GDALRasterAttributeTable::SetLinearBinning()
    1031             :  */
    1032           1 : CPLErr CPL_STDCALL GDALRATSetLinearBinning(GDALRasterAttributeTableH hRAT,
    1033             :                                            double dfRow0Min, double dfBinSize)
    1034             : 
    1035             : {
    1036           1 :     VALIDATE_POINTER1(hRAT, "GDALRATSetLinearBinning", CE_Failure);
    1037             : 
    1038           2 :     return GDALRasterAttributeTable::FromHandle(hRAT)->SetLinearBinning(
    1039           1 :         dfRow0Min, dfBinSize);
    1040             : }
    1041             : 
    1042             : /************************************************************************/
    1043             : /*                          GetLinearBinning()                          */
    1044             : /************************************************************************/
    1045             : 
    1046             : /**
    1047             :  * \brief Get linear binning information.
    1048             :  *
    1049             :  * Returns linear binning information if any is associated with the RAT.
    1050             :  *
    1051             :  * This method is the same as the C function GDALRATGetLinearBinning().
    1052             :  *
    1053             :  * @param pdfRow0Min (out) the lower bound (pixel value) of the first category.
    1054             :  * @param pdfBinSize (out) the width of each category (in pixel value units).
    1055             :  *
    1056             :  * @return TRUE if linear binning information exists or FALSE if there is none.
    1057             :  */
    1058             : 
    1059           0 : int GDALRasterAttributeTable::GetLinearBinning(
    1060             :     CPL_UNUSED double *pdfRow0Min, CPL_UNUSED double *pdfBinSize) const
    1061             : {
    1062           0 :     return false;
    1063             : }
    1064             : 
    1065             : /************************************************************************/
    1066             : /*                      GDALRATGetLinearBinning()                       */
    1067             : /************************************************************************/
    1068             : 
    1069             : /**
    1070             :  * \brief Get linear binning information.
    1071             :  *
    1072             :  * This function is the same as the C++ method
    1073             :  * GDALRasterAttributeTable::GetLinearBinning()
    1074             :  */
    1075           1 : int CPL_STDCALL GDALRATGetLinearBinning(GDALRasterAttributeTableH hRAT,
    1076             :                                         double *pdfRow0Min, double *pdfBinSize)
    1077             : 
    1078             : {
    1079           1 :     VALIDATE_POINTER1(hRAT, "GDALRATGetLinearBinning", 0);
    1080             : 
    1081           2 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetLinearBinning(
    1082           1 :         pdfRow0Min, pdfBinSize);
    1083             : }
    1084             : 
    1085             : /************************************************************************/
    1086             : /*                        GDALRATGetTableType()                         */
    1087             : /************************************************************************/
    1088             : 
    1089             : /**
    1090             :  * \brief Get Rat Table Type
    1091             :  *
    1092             :  *
    1093             :  * This function is the same as the C++ method
    1094             :  * GDALRasterAttributeTable::GetTableType()
    1095             :  */
    1096          10 : GDALRATTableType CPL_STDCALL GDALRATGetTableType(GDALRasterAttributeTableH hRAT)
    1097             : {
    1098          10 :     VALIDATE_POINTER1(hRAT, "GDALRATGetTableType", GRTT_THEMATIC);
    1099             : 
    1100          10 :     return GDALDefaultRasterAttributeTable::FromHandle(hRAT)->GetTableType();
    1101             : }
    1102             : 
    1103             : /************************************************************************/
    1104             : /*                        GDALRATSetTableType()                         */
    1105             : /************************************************************************/
    1106             : 
    1107             : /**
    1108             :  * \brief Set RAT Table Type
    1109             :  *
    1110             :  *
    1111             :  * This function is the same as the C++ method
    1112             :  * GDALRasterAttributeTable::SetTableType()
    1113             :  */
    1114           3 : CPLErr CPL_STDCALL GDALRATSetTableType(GDALRasterAttributeTableH hRAT,
    1115             :                                        const GDALRATTableType eInTableType)
    1116             : 
    1117             : {
    1118           3 :     VALIDATE_POINTER1(hRAT, "GDALRATSetTableType", CE_Failure);
    1119             : 
    1120           6 :     return GDALDefaultRasterAttributeTable::FromHandle(hRAT)->SetTableType(
    1121           3 :         eInTableType);
    1122             : }
    1123             : 
    1124             : /************************************************************************/
    1125             : /*                             Serialize()                              */
    1126             : /************************************************************************/
    1127             : 
    1128             : /** Serialize as a XML tree.
    1129             :  * @return XML tree.
    1130             :  */
    1131          17 : CPLXMLNode *GDALRasterAttributeTable::Serialize() const
    1132             : 
    1133             : {
    1134          17 :     if ((GetColumnCount() == 0) && (GetRowCount() == 0))
    1135           1 :         return nullptr;
    1136             : 
    1137             :     CPLXMLNode *psTree =
    1138          16 :         CPLCreateXMLNode(nullptr, CXT_Element, "GDALRasterAttributeTable");
    1139             : 
    1140             :     /* -------------------------------------------------------------------- */
    1141             :     /*      Add attributes with regular binning info if appropriate.        */
    1142             :     /* -------------------------------------------------------------------- */
    1143          16 :     char szValue[128] = {'\0'};
    1144          16 :     double dfRow0Min = 0.0;
    1145          16 :     double dfBinSize = 0.0;
    1146             : 
    1147          16 :     if (GetLinearBinning(&dfRow0Min, &dfBinSize))
    1148             :     {
    1149           4 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", dfRow0Min);
    1150           4 :         CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "Row0Min"),
    1151             :                          CXT_Text, szValue);
    1152             : 
    1153           4 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", dfBinSize);
    1154           4 :         CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "BinSize"),
    1155             :                          CXT_Text, szValue);
    1156             :     }
    1157             : 
    1158             :     /* -------------------------------------------------------------------- */
    1159             :     /*      Store table type                                                */
    1160             :     /* -------------------------------------------------------------------- */
    1161          16 :     const GDALRATTableType tableType = GetTableType();
    1162          16 :     if (tableType == GRTT_ATHEMATIC)
    1163             :     {
    1164           3 :         CPLsnprintf(szValue, sizeof(szValue), "athematic");
    1165             :     }
    1166             :     else
    1167             :     {
    1168          13 :         CPLsnprintf(szValue, sizeof(szValue), "thematic");
    1169             :     }
    1170          16 :     CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "tableType"),
    1171             :                      CXT_Text, szValue);
    1172             : 
    1173             :     /* -------------------------------------------------------------------- */
    1174             :     /*      Define each column.                                             */
    1175             :     /* -------------------------------------------------------------------- */
    1176          16 :     const int iColCount = GetColumnCount();
    1177             : 
    1178          60 :     for (int iCol = 0; iCol < iColCount; iCol++)
    1179             :     {
    1180          44 :         CPLXMLNode *psCol = CPLCreateXMLNode(psTree, CXT_Element, "FieldDefn");
    1181             : 
    1182          44 :         snprintf(szValue, sizeof(szValue), "%d", iCol);
    1183          44 :         CPLCreateXMLNode(CPLCreateXMLNode(psCol, CXT_Attribute, "index"),
    1184             :                          CXT_Text, szValue);
    1185             : 
    1186          44 :         CPLCreateXMLElementAndValue(psCol, "Name", GetNameOfCol(iCol));
    1187             : 
    1188          44 :         snprintf(szValue, sizeof(szValue), "%d",
    1189          44 :                  static_cast<int>(GetTypeOfCol(iCol)));
    1190             :         CPLXMLNode *psType =
    1191          44 :             CPLCreateXMLElementAndValue(psCol, "Type", szValue);
    1192          44 :         CPLAddXMLAttributeAndValue(psType, "typeAsString",
    1193          44 :                                    GDALGetRATFieldTypeName(GetTypeOfCol(iCol)));
    1194             : 
    1195          44 :         snprintf(szValue, sizeof(szValue), "%d",
    1196          44 :                  static_cast<int>(GetUsageOfCol(iCol)));
    1197             :         CPLXMLNode *psUsage =
    1198          44 :             CPLCreateXMLElementAndValue(psCol, "Usage", szValue);
    1199          44 :         CPLAddXMLAttributeAndValue(
    1200             :             psUsage, "usageAsString",
    1201          44 :             GDALGetRATFieldUsageName(GetUsageOfCol(iCol)));
    1202             :     }
    1203             : 
    1204             :     /* -------------------------------------------------------------------- */
    1205             :     /*      Write out each row.                                             */
    1206             :     /* -------------------------------------------------------------------- */
    1207          16 :     const int iRowCount = GetRowCount();
    1208          16 :     CPLXMLNode *psTail = nullptr;
    1209          16 :     CPLXMLNode *psRow = nullptr;
    1210             : 
    1211        1012 :     for (int iRow = 0; iRow < iRowCount; iRow++)
    1212             :     {
    1213         996 :         psRow = CPLCreateXMLNode(nullptr, CXT_Element, "Row");
    1214         996 :         if (psTail == nullptr)
    1215          12 :             CPLAddXMLChild(psTree, psRow);
    1216             :         else
    1217         984 :             psTail->psNext = psRow;
    1218         996 :         psTail = psRow;
    1219             : 
    1220         996 :         snprintf(szValue, sizeof(szValue), "%d", iRow);
    1221         996 :         CPLCreateXMLNode(CPLCreateXMLNode(psRow, CXT_Attribute, "index"),
    1222             :                          CXT_Text, szValue);
    1223             : 
    1224        1992 :         std::string osStr;
    1225        2027 :         for (int iCol = 0; iCol < iColCount; iCol++)
    1226             :         {
    1227        1031 :             const char *pszValue = szValue;
    1228             : 
    1229        1031 :             switch (GetTypeOfCol(iCol))
    1230             :             {
    1231          14 :                 case GFT_Integer:
    1232          28 :                     snprintf(szValue, sizeof(szValue), "%d",
    1233          14 :                              GetValueAsInt(iRow, iCol));
    1234          14 :                     break;
    1235             : 
    1236         991 :                 case GFT_Real:
    1237         991 :                     CPLsnprintf(szValue, sizeof(szValue), "%.16g",
    1238         991 :                                 GetValueAsDouble(iRow, iCol));
    1239         991 :                     break;
    1240             : 
    1241           8 :                 case GFT_String:
    1242           8 :                     pszValue = GetValueAsString(iRow, iCol);
    1243           8 :                     break;
    1244             : 
    1245           6 :                 case GFT_Boolean:
    1246           6 :                     pszValue = GetValueAsBoolean(iRow, iCol) ? "true" : "false";
    1247           6 :                     break;
    1248             : 
    1249           6 :                 case GFT_DateTime:
    1250           6 :                     osStr = DateTimeToString(GetValueAsDateTime(iRow, iCol));
    1251           6 :                     pszValue = osStr.c_str();
    1252           6 :                     break;
    1253             : 
    1254           6 :                 case GFT_WKBGeometry:
    1255             :                 {
    1256           6 :                     size_t nWKBSize = 0;
    1257             :                     const GByte *pabyWKB =
    1258           6 :                         GetValueAsWKBGeometry(iRow, iCol, nWKBSize);
    1259           6 :                     osStr = WKBGeometryToWKT(pabyWKB, nWKBSize);
    1260           6 :                     pszValue = osStr.c_str();
    1261           6 :                     break;
    1262             :                 }
    1263             :             }
    1264             : 
    1265        1031 :             CPLCreateXMLElementAndValue(psRow, "F", pszValue);
    1266             :         }
    1267             :     }
    1268             : 
    1269          16 :     return psTree;
    1270             : }
    1271             : 
    1272             : /************************************************************************/
    1273             : /*                             SerializeJSON()                           */
    1274             : /************************************************************************/
    1275             : 
    1276             : /** Serialize as a JSON object.
    1277             :  * @return JSON object (of type json_object*)
    1278             :  */
    1279           7 : void *GDALRasterAttributeTable::SerializeJSON() const
    1280             : 
    1281             : {
    1282           7 :     json_object *poRAT = json_object_new_object();
    1283             : 
    1284           7 :     if ((GetColumnCount() == 0) && (GetRowCount() == 0))
    1285           0 :         return poRAT;
    1286             : 
    1287             :     /* -------------------------------------------------------------------- */
    1288             :     /*      Add attributes with regular binning info if appropriate.        */
    1289             :     /* -------------------------------------------------------------------- */
    1290           7 :     double dfRow0Min = 0.0;
    1291           7 :     double dfBinSize = 0.0;
    1292           7 :     json_object *poRow0Min = nullptr;
    1293           7 :     json_object *poBinSize = nullptr;
    1294           7 :     json_object *poTableType = nullptr;
    1295             : 
    1296           7 :     if (GetLinearBinning(&dfRow0Min, &dfBinSize))
    1297             :     {
    1298           2 :         poRow0Min = json_object_new_double_with_precision(dfRow0Min, 16);
    1299           2 :         json_object_object_add(poRAT, "row0Min", poRow0Min);
    1300             : 
    1301           2 :         poBinSize = json_object_new_double_with_precision(dfBinSize, 16);
    1302           2 :         json_object_object_add(poRAT, "binSize", poBinSize);
    1303             :     }
    1304             : 
    1305             :     /* -------------------------------------------------------------------- */
    1306             :     /*      Table Type                                                      */
    1307             :     /* -------------------------------------------------------------------- */
    1308           7 :     const GDALRATTableType tableType = GetTableType();
    1309           7 :     if (tableType == GRTT_ATHEMATIC)
    1310             :     {
    1311           2 :         poTableType = json_object_new_string("athematic");
    1312             :     }
    1313             :     else
    1314             :     {
    1315           5 :         poTableType = json_object_new_string("thematic");
    1316             :     }
    1317           7 :     json_object_object_add(poRAT, "tableType", poTableType);
    1318             : 
    1319             :     /* -------------------------------------------------------------------- */
    1320             :     /*      Define each column.                                             */
    1321             :     /* -------------------------------------------------------------------- */
    1322           7 :     const int iColCount = GetColumnCount();
    1323           7 :     json_object *poFieldDefnArray = json_object_new_array();
    1324             : 
    1325          47 :     for (int iCol = 0; iCol < iColCount; iCol++)
    1326             :     {
    1327          40 :         json_object *const poFieldDefn = json_object_new_object();
    1328             : 
    1329          40 :         json_object *const poColumnIndex = json_object_new_int(iCol);
    1330          40 :         json_object_object_add(poFieldDefn, "index", poColumnIndex);
    1331             : 
    1332          40 :         json_object *const poName = json_object_new_string(GetNameOfCol(iCol));
    1333          40 :         json_object_object_add(poFieldDefn, "name", poName);
    1334             : 
    1335             :         json_object *const poType =
    1336          40 :             json_object_new_int(static_cast<int>(GetTypeOfCol(iCol)));
    1337          40 :         json_object_object_add(poFieldDefn, "type", poType);
    1338             : 
    1339             :         json_object *const poUsage =
    1340          40 :             json_object_new_int(static_cast<int>(GetUsageOfCol(iCol)));
    1341          40 :         json_object_object_add(poFieldDefn, "usage", poUsage);
    1342             : 
    1343          40 :         json_object_array_add(poFieldDefnArray, poFieldDefn);
    1344             :     }
    1345             : 
    1346           7 :     json_object_object_add(poRAT, "fieldDefn", poFieldDefnArray);
    1347             : 
    1348             :     /* -------------------------------------------------------------------- */
    1349             :     /*      Write out each row.                                             */
    1350             :     /* -------------------------------------------------------------------- */
    1351           7 :     const int iRowCount = GetRowCount();
    1352           7 :     json_object *poRowArray = json_object_new_array();
    1353             : 
    1354         936 :     for (int iRow = 0; iRow < iRowCount; iRow++)
    1355             :     {
    1356         929 :         json_object *const poRow = json_object_new_object();
    1357             : 
    1358         929 :         json_object *const poRowIndex = json_object_new_int(iRow);
    1359         929 :         json_object_object_add(poRow, "index", poRowIndex);
    1360             : 
    1361         929 :         json_object *const poFArray = json_object_new_array();
    1362             : 
    1363        4025 :         for (int iCol = 0; iCol < iColCount; iCol++)
    1364             :         {
    1365        3096 :             json_object *poF = nullptr;
    1366        3096 :             switch (GetTypeOfCol(iCol))
    1367             :             {
    1368        2845 :                 case GFT_Integer:
    1369        2845 :                     poF = json_object_new_int(GetValueAsInt(iRow, iCol));
    1370        2845 :                     break;
    1371             : 
    1372         225 :                 case GFT_Real:
    1373         225 :                     poF = json_object_new_double_with_precision(
    1374         225 :                         GetValueAsDouble(iRow, iCol), 16);
    1375         225 :                     break;
    1376             : 
    1377          14 :                 case GFT_String:
    1378          14 :                     poF = json_object_new_string(GetValueAsString(iRow, iCol));
    1379          14 :                     break;
    1380             : 
    1381           4 :                 case GFT_Boolean:
    1382             :                     poF =
    1383           4 :                         json_object_new_boolean(GetValueAsBoolean(iRow, iCol));
    1384           4 :                     break;
    1385             : 
    1386           8 :                 case GFT_DateTime:
    1387             :                 case GFT_WKBGeometry:
    1388             :                 {
    1389           8 :                     const char *pszV = GetValueAsString(iRow, iCol);
    1390           8 :                     if (pszV[0])
    1391           4 :                         poF = json_object_new_string(pszV);
    1392           8 :                     break;
    1393             :                 }
    1394             :             }
    1395             : 
    1396        3096 :             json_object_array_add(poFArray, poF);
    1397             :         }
    1398         929 :         json_object_object_add(poRow, "f", poFArray);
    1399         929 :         json_object_array_add(poRowArray, poRow);
    1400             :     }
    1401           7 :     json_object_object_add(poRAT, "row", poRowArray);
    1402             : 
    1403           7 :     return poRAT;
    1404             : }
    1405             : 
    1406             : /************************************************************************/
    1407             : /*                              XMLInit()                               */
    1408             : /************************************************************************/
    1409             : 
    1410             : /** Deserialize from XML.
    1411             :  * @param psTree XML tree
    1412             :  * @return error code.
    1413             :  */
    1414          21 : CPLErr GDALRasterAttributeTable::XMLInit(const CPLXMLNode *psTree,
    1415             :                                          const char * /*pszVRTPath*/)
    1416             : 
    1417             : {
    1418          21 :     CPLAssert(GetRowCount() == 0 && GetColumnCount() == 0);
    1419             : 
    1420             :     /* -------------------------------------------------------------------- */
    1421             :     /*      Linear binning.                                                 */
    1422             :     /* -------------------------------------------------------------------- */
    1423          28 :     if (CPLGetXMLValue(psTree, "Row0Min", nullptr) &&
    1424           7 :         CPLGetXMLValue(psTree, "BinSize", nullptr))
    1425             :     {
    1426           7 :         SetLinearBinning(CPLAtof(CPLGetXMLValue(psTree, "Row0Min", "")),
    1427           7 :                          CPLAtof(CPLGetXMLValue(psTree, "BinSize", "")));
    1428             :     }
    1429             : 
    1430             :     /* -------------------------------------------------------------------- */
    1431             :     /*      Table Type                                                      */
    1432             :     /* -------------------------------------------------------------------- */
    1433          21 :     if (CPLGetXMLValue(psTree, "tableType", nullptr))
    1434             :     {
    1435          19 :         const char *pszValue = CPLGetXMLValue(psTree, "tableType", "thematic");
    1436          19 :         if (EQUAL(pszValue, "athematic"))
    1437             :         {
    1438           4 :             SetTableType(GRTT_ATHEMATIC);
    1439             :         }
    1440             :         else
    1441             :         {
    1442          15 :             SetTableType(GRTT_THEMATIC);
    1443             :         }
    1444             :     }
    1445             : 
    1446             :     /* -------------------------------------------------------------------- */
    1447             :     /*      Column definitions                                              */
    1448             :     /* -------------------------------------------------------------------- */
    1449             : 
    1450        1912 :     for (CPLXMLNode *psChild = psTree->psChild; psChild != nullptr;
    1451        1891 :          psChild = psChild->psNext)
    1452             :     {
    1453        1891 :         if (psChild->eType == CXT_Element &&
    1454        1858 :             EQUAL(psChild->pszValue, "FieldDefn"))
    1455             :         {
    1456          53 :             int nType = atoi(CPLGetXMLValue(psChild, "Type", "1"));
    1457          53 :             if (nType < 0 || nType >= GFT_MaxCount)
    1458             :             {
    1459           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1460             :                          "Invalid RAT field type: %d(%s). Dealing as if it was "
    1461             :                          "String",
    1462             :                          nType,
    1463             :                          CPLGetXMLValue(psChild, "typeAsString", "(unknown)"));
    1464           0 :                 nType = GFT_String;
    1465             :             }
    1466          53 :             int nUsage = atoi(CPLGetXMLValue(psChild, "Usage", "0"));
    1467          53 :             if (nUsage < 0 || nUsage >= GFU_MaxCount)
    1468             :             {
    1469           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1470             :                          "Invalid RAT field usage: %d(%s). Dealing as if it "
    1471             :                          "was Generic",
    1472             :                          nUsage,
    1473             :                          CPLGetXMLValue(psChild, "usageAsString", "(unknown)"));
    1474           0 :                 nUsage = GFU_Generic;
    1475             :             }
    1476          53 :             CreateColumn(CPLGetXMLValue(psChild, "Name", ""),
    1477             :                          static_cast<GDALRATFieldType>(nType),
    1478          53 :                          static_cast<GDALRATFieldUsage>(nUsage));
    1479             :         }
    1480             :     }
    1481             : 
    1482             :     /* -------------------------------------------------------------------- */
    1483             :     /*      Row data.                                                       */
    1484             :     /* -------------------------------------------------------------------- */
    1485        1912 :     for (const CPLXMLNode *psChild = psTree->psChild; psChild != nullptr;
    1486        1891 :          psChild = psChild->psNext)
    1487             :     {
    1488        1891 :         if (psChild->eType == CXT_Element && EQUAL(psChild->pszValue, "Row"))
    1489             :         {
    1490        1805 :             const int iRow = atoi(CPLGetXMLValue(psChild, "index", "0"));
    1491        1805 :             int iField = 0;
    1492             : 
    1493        5456 :             for (CPLXMLNode *psF = psChild->psChild; psF != nullptr;
    1494        3651 :                  psF = psF->psNext)
    1495             :             {
    1496        3651 :                 if (psF->eType != CXT_Element || !EQUAL(psF->pszValue, "F"))
    1497        1805 :                     continue;
    1498             : 
    1499        1846 :                 if (psF->psChild != nullptr && psF->psChild->eType == CXT_Text)
    1500        1843 :                     SetValue(iRow, iField++, psF->psChild->pszValue);
    1501             :                 else
    1502           3 :                     SetValue(iRow, iField++, "");
    1503             :             }
    1504             :         }
    1505             :     }
    1506             : 
    1507          21 :     return CE_None;
    1508             : }
    1509             : 
    1510             : /************************************************************************/
    1511             : /*                      InitializeFromColorTable()                      */
    1512             : /************************************************************************/
    1513             : 
    1514             : /**
    1515             :  * \brief Initialize from color table.
    1516             :  *
    1517             :  * This method will setup a whole raster attribute table based on the
    1518             :  * contents of the passed color table.  The Value (GFU_MinMax),
    1519             :  * Red (GFU_Red), Green (GFU_Green), Blue (GFU_Blue), and Alpha (GFU_Alpha)
    1520             :  * fields are created, and a row is set for each entry in the color table.
    1521             :  *
    1522             :  * The raster attribute table must be empty before calling
    1523             :  * InitializeFromColorTable().
    1524             :  *
    1525             :  * The Value fields are set based on the implicit assumption with color
    1526             :  * tables that entry 0 applies to pixel value 0, 1 to 1, etc.
    1527             :  *
    1528             :  * This method is the same as the C function GDALRATInitializeFromColorTable().
    1529             :  *
    1530             :  * @param poTable the color table to copy from.
    1531             :  *
    1532             :  * @return CE_None on success or CE_Failure if something goes wrong.
    1533             :  */
    1534             : 
    1535           0 : CPLErr GDALRasterAttributeTable::InitializeFromColorTable(
    1536             :     const GDALColorTable *poTable)
    1537             : 
    1538             : {
    1539           0 :     if (GetRowCount() > 0 || GetColumnCount() > 0)
    1540             :     {
    1541           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1542             :                  "Raster Attribute Table not empty in "
    1543             :                  "InitializeFromColorTable()");
    1544           0 :         return CE_Failure;
    1545             :     }
    1546             : 
    1547           0 :     SetLinearBinning(0.0, 1.0);
    1548           0 :     CreateColumn("Value", GFT_Integer, GFU_MinMax);
    1549           0 :     CreateColumn("Red", GFT_Integer, GFU_Red);
    1550           0 :     CreateColumn("Green", GFT_Integer, GFU_Green);
    1551           0 :     CreateColumn("Blue", GFT_Integer, GFU_Blue);
    1552           0 :     CreateColumn("Alpha", GFT_Integer, GFU_Alpha);
    1553             : 
    1554           0 :     SetRowCount(poTable->GetColorEntryCount());
    1555             : 
    1556           0 :     for (int iRow = 0; iRow < poTable->GetColorEntryCount(); iRow++)
    1557             :     {
    1558             :         GDALColorEntry sEntry;
    1559             : 
    1560           0 :         poTable->GetColorEntryAsRGB(iRow, &sEntry);
    1561             : 
    1562           0 :         SetValue(iRow, 0, iRow);
    1563           0 :         SetValue(iRow, 1, sEntry.c1);
    1564           0 :         SetValue(iRow, 2, sEntry.c2);
    1565           0 :         SetValue(iRow, 3, sEntry.c3);
    1566           0 :         SetValue(iRow, 4, sEntry.c4);
    1567             :     }
    1568             : 
    1569           0 :     return CE_None;
    1570             : }
    1571             : 
    1572             : /************************************************************************/
    1573             : /*                  GDALRATInitializeFromColorTable()                   */
    1574             : /************************************************************************/
    1575             : 
    1576             : /**
    1577             :  * \brief Initialize from color table.
    1578             :  *
    1579             :  * This function is the same as the C++ method
    1580             :  * GDALRasterAttributeTable::InitializeFromColorTable()
    1581             :  */
    1582           0 : CPLErr CPL_STDCALL GDALRATInitializeFromColorTable(
    1583             :     GDALRasterAttributeTableH hRAT, GDALColorTableH hCT)
    1584             : 
    1585             : {
    1586           0 :     VALIDATE_POINTER1(hRAT, "GDALRATInitializeFromColorTable", CE_Failure);
    1587             : 
    1588           0 :     return GDALRasterAttributeTable::FromHandle(hRAT)->InitializeFromColorTable(
    1589           0 :         GDALColorTable::FromHandle(hCT));
    1590             : }
    1591             : 
    1592             : /************************************************************************/
    1593             : /*                       TranslateToColorTable()                        */
    1594             : /************************************************************************/
    1595             : 
    1596             : /**
    1597             :  * \brief Translate to a color table.
    1598             :  *
    1599             :  * This method will attempt to create a corresponding GDALColorTable from
    1600             :  * this raster attribute table.
    1601             :  *
    1602             :  * This method is the same as the C function GDALRATTranslateToColorTable().
    1603             :  *
    1604             :  * @param nEntryCount The number of entries to produce (0 to nEntryCount-1),
    1605             :  * or -1 to auto-determine the number of entries.
    1606             :  *
    1607             :  * @return the generated color table or NULL on failure.
    1608             :  */
    1609             : 
    1610           0 : GDALColorTable *GDALRasterAttributeTable::TranslateToColorTable(int nEntryCount)
    1611             : 
    1612             : {
    1613             :     /* -------------------------------------------------------------------- */
    1614             :     /*      Establish which fields are red, green, blue and alpha.          */
    1615             :     /* -------------------------------------------------------------------- */
    1616           0 :     const int iRed = GetColOfUsage(GFU_Red);
    1617           0 :     const int iGreen = GetColOfUsage(GFU_Green);
    1618           0 :     const int iBlue = GetColOfUsage(GFU_Blue);
    1619             : 
    1620           0 :     if (iRed == -1 || iGreen == -1 || iBlue == -1)
    1621           0 :         return nullptr;
    1622             : 
    1623           0 :     const int iAlpha = GetColOfUsage(GFU_Alpha);
    1624             : 
    1625             :     /* -------------------------------------------------------------------- */
    1626             :     /*      If we aren't given an explicit number of values to scan for,    */
    1627             :     /*      search for the maximum "max" value.                             */
    1628             :     /* -------------------------------------------------------------------- */
    1629           0 :     if (nEntryCount == -1)
    1630             :     {
    1631           0 :         int iMaxCol = GetColOfUsage(GFU_Max);
    1632           0 :         if (iMaxCol == -1)
    1633           0 :             iMaxCol = GetColOfUsage(GFU_MinMax);
    1634             : 
    1635           0 :         if (iMaxCol == -1 || GetRowCount() == 0)
    1636           0 :             return nullptr;
    1637             : 
    1638           0 :         for (int iRow = 0; iRow < GetRowCount(); iRow++)
    1639             :         {
    1640           0 :             nEntryCount = std::max(
    1641           0 :                 nEntryCount, std::min(65535, GetValueAsInt(iRow, iMaxCol)) + 1);
    1642             :         }
    1643             : 
    1644           0 :         if (nEntryCount < 0)
    1645           0 :             return nullptr;
    1646             : 
    1647             :         // Restrict our number of entries to something vaguely sensible.
    1648           0 :         nEntryCount = std::min(65535, nEntryCount);
    1649             :     }
    1650             : 
    1651             :     /* -------------------------------------------------------------------- */
    1652             :     /*      Assign values to color table.                                   */
    1653             :     /* -------------------------------------------------------------------- */
    1654           0 :     GDALColorTable *poCT = new GDALColorTable();
    1655             : 
    1656           0 :     for (int iEntry = 0; iEntry < nEntryCount; iEntry++)
    1657             :     {
    1658           0 :         GDALColorEntry sColor = {0, 0, 0, 0};
    1659           0 :         const int iRow = GetRowOfValue(iEntry);
    1660             : 
    1661           0 :         if (iRow != -1)
    1662             :         {
    1663           0 :             sColor.c1 = static_cast<short>(GetValueAsInt(iRow, iRed));
    1664           0 :             sColor.c2 = static_cast<short>(GetValueAsInt(iRow, iGreen));
    1665           0 :             sColor.c3 = static_cast<short>(GetValueAsInt(iRow, iBlue));
    1666           0 :             if (iAlpha == -1)
    1667           0 :                 sColor.c4 = 255;
    1668             :             else
    1669           0 :                 sColor.c4 = static_cast<short>(GetValueAsInt(iRow, iAlpha));
    1670             :         }
    1671             : 
    1672           0 :         poCT->SetColorEntry(iEntry, &sColor);
    1673             :     }
    1674             : 
    1675           0 :     return poCT;
    1676             : }
    1677             : 
    1678             : /************************************************************************/
    1679             : /*                  GDALRATInitializeFromColorTable()                   */
    1680             : /************************************************************************/
    1681             : 
    1682             : /**
    1683             :  * \brief Translate to a color table.
    1684             :  *
    1685             :  * This function is the same as the C++ method
    1686             :  * GDALRasterAttributeTable::TranslateToColorTable()
    1687             :  */
    1688             : GDALColorTableH CPL_STDCALL
    1689           0 : GDALRATTranslateToColorTable(GDALRasterAttributeTableH hRAT, int nEntryCount)
    1690             : 
    1691             : {
    1692           0 :     VALIDATE_POINTER1(hRAT, "GDALRATTranslateToColorTable", nullptr);
    1693             : 
    1694           0 :     return GDALRasterAttributeTable::FromHandle(hRAT)->TranslateToColorTable(
    1695           0 :         nEntryCount);
    1696             : }
    1697             : 
    1698             : /************************************************************************/
    1699             : /*                            DumpReadable()                            */
    1700             : /************************************************************************/
    1701             : 
    1702             : /**
    1703             :  * \brief Dump RAT in readable form.
    1704             :  *
    1705             :  * Currently the readable form is the XML encoding ... only barely
    1706             :  * readable.
    1707             :  *
    1708             :  * This method is the same as the C function GDALRATDumpReadable().
    1709             :  *
    1710             :  * @param fp file to dump to or NULL for stdout.
    1711             :  */
    1712             : 
    1713           0 : void GDALRasterAttributeTable::DumpReadable(FILE *fp)
    1714             : 
    1715             : {
    1716           0 :     CPLXMLNode *psTree = Serialize();
    1717           0 :     char *const pszXMLText = CPLSerializeXMLTree(psTree);
    1718             : 
    1719           0 :     CPLDestroyXMLNode(psTree);
    1720             : 
    1721           0 :     if (fp == nullptr)
    1722           0 :         fp = stdout;
    1723             : 
    1724           0 :     fprintf(fp, "%s\n", pszXMLText);
    1725             : 
    1726           0 :     CPLFree(pszXMLText);
    1727           0 : }
    1728             : 
    1729             : /************************************************************************/
    1730             : /*                        GDALRATDumpReadable()                         */
    1731             : /************************************************************************/
    1732             : 
    1733             : /**
    1734             :  * \brief Dump RAT in readable form.
    1735             :  *
    1736             :  * This function is the same as the C++ method
    1737             :  * GDALRasterAttributeTable::DumpReadable()
    1738             :  */
    1739           0 : void CPL_STDCALL GDALRATDumpReadable(GDALRasterAttributeTableH hRAT, FILE *fp)
    1740             : 
    1741             : {
    1742           0 :     VALIDATE_POINTER0(hRAT, "GDALRATDumpReadable");
    1743             : 
    1744           0 :     GDALRasterAttributeTable::FromHandle(hRAT)->DumpReadable(fp);
    1745             : }
    1746             : 
    1747             : /* \class GDALDefaultRasterAttributeTable
    1748             :  *
    1749             :  * An implementation of GDALRasterAttributeTable that keeps
    1750             :  * all data in memory. This is the same as the implementation
    1751             :  * of GDALRasterAttributeTable in GDAL <= 1.10.
    1752             :  */
    1753             : 
    1754             : /************************************************************************/
    1755             : /*                  GDALDefaultRasterAttributeTable()                   */
    1756             : /*                                                                      */
    1757             : /*      Simple initialization constructor.                              */
    1758             : /************************************************************************/
    1759             : 
    1760             : //! Construct empty table.
    1761             : 
    1762             : GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable() = default;
    1763             : 
    1764             : /************************************************************************/
    1765             : /*                   GDALCreateRasterAttributeTable()                   */
    1766             : /************************************************************************/
    1767             : 
    1768             : /**
    1769             :  * \brief Construct empty table.
    1770             :  *
    1771             :  * This function is the same as the C++ method
    1772             :  * GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable()
    1773             :  */
    1774          24 : GDALRasterAttributeTableH CPL_STDCALL GDALCreateRasterAttributeTable()
    1775             : 
    1776             : {
    1777          24 :     return new GDALDefaultRasterAttributeTable();
    1778             : }
    1779             : 
    1780             : /************************************************************************/
    1781             : /*                 ~GDALDefaultRasterAttributeTable()                   */
    1782             : /*                                                                      */
    1783             : /*      All magic done by magic by the container destructors.           */
    1784             : /************************************************************************/
    1785             : 
    1786             : GDALDefaultRasterAttributeTable::~GDALDefaultRasterAttributeTable() = default;
    1787             : 
    1788             : /************************************************************************/
    1789             : /*                  GDALDestroyRasterAttributeTable()                   */
    1790             : /************************************************************************/
    1791             : 
    1792             : /**
    1793             :  * \brief Destroys a RAT.
    1794             :  *
    1795             :  * This function is the same as the C++ method
    1796             :  * GDALRasterAttributeTable::~GDALRasterAttributeTable()
    1797             :  */
    1798          32 : void CPL_STDCALL GDALDestroyRasterAttributeTable(GDALRasterAttributeTableH hRAT)
    1799             : 
    1800             : {
    1801          32 :     if (hRAT != nullptr)
    1802          32 :         delete GDALRasterAttributeTable::FromHandle(hRAT);
    1803          32 : }
    1804             : 
    1805             : /************************************************************************/
    1806             : /*                           AnalyseColumns()                           */
    1807             : /*                                                                      */
    1808             : /*      Internal method to work out which column to use for various     */
    1809             : /*      tasks.                                                          */
    1810             : /************************************************************************/
    1811             : 
    1812           2 : void GDALDefaultRasterAttributeTable::AnalyseColumns()
    1813             : 
    1814             : {
    1815           2 :     bColumnsAnalysed = true;
    1816             : 
    1817           2 :     nMinCol = GetColOfUsage(GFU_Min);
    1818           2 :     if (nMinCol == -1)
    1819           2 :         nMinCol = GetColOfUsage(GFU_MinMax);
    1820             : 
    1821           2 :     nMaxCol = GetColOfUsage(GFU_Max);
    1822           2 :     if (nMaxCol == -1)
    1823           2 :         nMaxCol = GetColOfUsage(GFU_MinMax);
    1824           2 : }
    1825             : 
    1826             : /************************************************************************/
    1827             : /*                           GetColumnCount()                           */
    1828             : /************************************************************************/
    1829             : 
    1830         135 : int GDALDefaultRasterAttributeTable::GetColumnCount() const
    1831             : 
    1832             : {
    1833         135 :     return static_cast<int>(aoFields.size());
    1834             : }
    1835             : 
    1836             : /************************************************************************/
    1837             : /*                       GDALRATGetColumnCount()                        */
    1838             : /************************************************************************/
    1839             : 
    1840             : /**
    1841             :  * \brief Fetch table column count.
    1842             :  *
    1843             :  * This function is the same as the C++ method
    1844             :  * GDALRasterAttributeTable::GetColumnCount()
    1845             :  */
    1846          60 : int CPL_STDCALL GDALRATGetColumnCount(GDALRasterAttributeTableH hRAT)
    1847             : 
    1848             : {
    1849          60 :     VALIDATE_POINTER1(hRAT, "GDALRATGetColumnCount", 0);
    1850             : 
    1851          60 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetColumnCount();
    1852             : }
    1853             : 
    1854             : /************************************************************************/
    1855             : /*                            GetNameOfCol()                            */
    1856             : /************************************************************************/
    1857             : 
    1858             : /** \brief Fetch name of indicated column.
    1859             :  * @param iCol column index.
    1860             :  * @return name.
    1861             :  */
    1862         196 : const char *GDALDefaultRasterAttributeTable::GetNameOfCol(int iCol) const
    1863             : 
    1864             : {
    1865         196 :     if (iCol < 0 || iCol >= static_cast<int>(aoFields.size()))
    1866           0 :         return "";
    1867             : 
    1868         196 :     return aoFields[iCol].sName;
    1869             : }
    1870             : 
    1871             : /************************************************************************/
    1872             : /*                        GDALRATGetNameOfCol()                         */
    1873             : /************************************************************************/
    1874             : 
    1875             : /**
    1876             :  * \brief Fetch name of indicated column.
    1877             :  *
    1878             :  * This function is the same as the C++ method
    1879             :  * GDALRasterAttributeTable::GetNameOfCol()
    1880             :  * @param hRAT RAT handle.
    1881             :  * @param iCol column index.
    1882             :  * @return name.
    1883             :  */
    1884         124 : const char *CPL_STDCALL GDALRATGetNameOfCol(GDALRasterAttributeTableH hRAT,
    1885             :                                             int iCol)
    1886             : 
    1887             : {
    1888         124 :     VALIDATE_POINTER1(hRAT, "GDALRATGetNameOfCol", nullptr);
    1889             : 
    1890         124 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetNameOfCol(iCol);
    1891             : }
    1892             : 
    1893             : /************************************************************************/
    1894             : /*                           GetUsageOfCol()                            */
    1895             : /************************************************************************/
    1896             : 
    1897             : /**
    1898             :  * \brief Fetch column usage value.
    1899             :  *
    1900             :  * @param iCol column index.
    1901             :  * @return usage.
    1902             :  */
    1903         159 : GDALRATFieldUsage GDALDefaultRasterAttributeTable::GetUsageOfCol(int iCol) const
    1904             : 
    1905             : {
    1906         159 :     if (iCol < 0 || iCol >= static_cast<int>(aoFields.size()))
    1907           0 :         return GFU_Generic;
    1908             : 
    1909         159 :     return aoFields[iCol].eUsage;
    1910             : }
    1911             : 
    1912             : /************************************************************************/
    1913             : /*                        GDALRATGetUsageOfCol()                        */
    1914             : /************************************************************************/
    1915             : 
    1916             : /**
    1917             :  * \brief Fetch column usage value.
    1918             :  *
    1919             :  * This function is the same as the C++ method
    1920             :  * GDALRasterAttributeTable::GetUsageOfCol()
    1921             :  * @param hRAT RAT handle.
    1922             :  * @param iCol column index.
    1923             :  * @return usage.
    1924             :  */
    1925             : GDALRATFieldUsage CPL_STDCALL
    1926          67 : GDALRATGetUsageOfCol(GDALRasterAttributeTableH hRAT, int iCol)
    1927             : 
    1928             : {
    1929          67 :     VALIDATE_POINTER1(hRAT, "GDALRATGetUsageOfCol", GFU_Generic);
    1930             : 
    1931          67 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetUsageOfCol(iCol);
    1932             : }
    1933             : 
    1934             : /************************************************************************/
    1935             : /*                            GetTypeOfCol()                            */
    1936             : /************************************************************************/
    1937             : 
    1938             : /**
    1939             :  * \brief Fetch column type.
    1940             :  *
    1941             :  * @param iCol column index.
    1942             :  * @return type.
    1943             :  */
    1944        1057 : GDALRATFieldType GDALDefaultRasterAttributeTable::GetTypeOfCol(int iCol) const
    1945             : 
    1946             : {
    1947        1057 :     if (iCol < 0 || iCol >= static_cast<int>(aoFields.size()))
    1948           2 :         return GFT_Integer;
    1949             : 
    1950        1055 :     return aoFields[iCol].eType;
    1951             : }
    1952             : 
    1953             : /************************************************************************/
    1954             : /*                        GDALRATGetTypeOfCol()                         */
    1955             : /************************************************************************/
    1956             : 
    1957             : /**
    1958             :  * \brief Fetch column type.
    1959             :  *
    1960             :  * This function is the same as the C++ method
    1961             :  * GDALRasterAttributeTable::GetTypeOfCol()
    1962             :  * @param hRAT RAT handle.
    1963             :  * @param iCol column index.
    1964             :  * @return type.
    1965             :  */
    1966         349 : GDALRATFieldType CPL_STDCALL GDALRATGetTypeOfCol(GDALRasterAttributeTableH hRAT,
    1967             :                                                  int iCol)
    1968             : 
    1969             : {
    1970         349 :     VALIDATE_POINTER1(hRAT, "GDALRATGetTypeOfCol", GFT_Integer);
    1971             : 
    1972         349 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetTypeOfCol(iCol);
    1973             : }
    1974             : 
    1975             : /************************************************************************/
    1976             : /*                           GetColOfUsage()                            */
    1977             : /************************************************************************/
    1978             : 
    1979             : /** Return the index of the column that corresponds to the passed usage.
    1980             :  * @param eUsage usage.
    1981             :  * @return column index, or -1 in case of error.
    1982             :  */
    1983           8 : int GDALDefaultRasterAttributeTable::GetColOfUsage(
    1984             :     GDALRATFieldUsage eUsage) const
    1985             : 
    1986             : {
    1987          16 :     for (unsigned int i = 0; i < aoFields.size(); i++)
    1988             :     {
    1989          12 :         if (aoFields[i].eUsage == eUsage)
    1990           4 :             return i;
    1991             :     }
    1992             : 
    1993           4 :     return -1;
    1994             : }
    1995             : 
    1996             : /************************************************************************/
    1997             : /*                        GDALRATGetColOfUsage()                        */
    1998             : /************************************************************************/
    1999             : 
    2000             : /**
    2001             :  * \brief Fetch column index for given usage.
    2002             :  *
    2003             :  * This function is the same as the C++ method
    2004             :  * GDALRasterAttributeTable::GetColOfUsage()
    2005             :  */
    2006          13 : int CPL_STDCALL GDALRATGetColOfUsage(GDALRasterAttributeTableH hRAT,
    2007             :                                      GDALRATFieldUsage eUsage)
    2008             : 
    2009             : {
    2010          13 :     VALIDATE_POINTER1(hRAT, "GDALRATGetColOfUsage", 0);
    2011             : 
    2012          13 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetColOfUsage(eUsage);
    2013             : }
    2014             : 
    2015             : /************************************************************************/
    2016             : /*                            GetRowCount()                             */
    2017             : /************************************************************************/
    2018             : 
    2019         349 : int GDALDefaultRasterAttributeTable::GetRowCount() const
    2020             : 
    2021             : {
    2022         349 :     return static_cast<int>(nRowCount);
    2023             : }
    2024             : 
    2025             : /************************************************************************/
    2026             : /*                        GDALRATGetUsageOfCol()                        */
    2027             : /************************************************************************/
    2028             : /**
    2029             :  * \brief Fetch row count.
    2030             :  *
    2031             :  * This function is the same as the C++ method
    2032             :  * GDALRasterAttributeTable::GetRowCount()
    2033             :  */
    2034         246 : int CPL_STDCALL GDALRATGetRowCount(GDALRasterAttributeTableH hRAT)
    2035             : 
    2036             : {
    2037         246 :     VALIDATE_POINTER1(hRAT, "GDALRATGetRowCount", 0);
    2038             : 
    2039         246 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetRowCount();
    2040             : }
    2041             : 
    2042             : /************************************************************************/
    2043             : /*                          GetValueAsString()                          */
    2044             : /************************************************************************/
    2045             : 
    2046         148 : const char *GDALDefaultRasterAttributeTable::GetValueAsString(int iRow,
    2047             :                                                               int iField) const
    2048             : 
    2049             : {
    2050         148 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2051             :     {
    2052           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2053             :                  iField);
    2054             : 
    2055           1 :         return "";
    2056             :     }
    2057             : 
    2058         147 :     if (iRow < 0 || iRow >= nRowCount)
    2059             :     {
    2060           3 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2061             : 
    2062           3 :         return "";
    2063             :     }
    2064             : 
    2065         144 :     switch (aoFields[iField].eType)
    2066             :     {
    2067           1 :         case GFT_Integer:
    2068             :         {
    2069             :             const_cast<GDALDefaultRasterAttributeTable *>(this)
    2070           1 :                 ->osWorkingResult.Printf("%d", aoFields[iField].anValues[iRow]);
    2071           1 :             return osWorkingResult;
    2072             :         }
    2073             : 
    2074           1 :         case GFT_Real:
    2075             :         {
    2076             :             const_cast<GDALDefaultRasterAttributeTable *>(this)
    2077             :                 ->osWorkingResult.Printf("%.16g",
    2078           1 :                                          aoFields[iField].adfValues[iRow]);
    2079           1 :             return osWorkingResult;
    2080             :         }
    2081             : 
    2082         121 :         case GFT_String:
    2083             :         {
    2084         121 :             return aoFields[iField].aosValues[iRow];
    2085             :         }
    2086             : 
    2087           2 :         case GFT_Boolean:
    2088             :         {
    2089           2 :             return aoFields[iField].abValues[iRow] ? "true" : "false";
    2090             :         }
    2091             : 
    2092           9 :         case GFT_DateTime:
    2093             :         {
    2094           9 :             const auto &sDateTime = aoFields[iField].asDateTimeValues[iRow];
    2095             :             const_cast<GDALDefaultRasterAttributeTable *>(this)
    2096           9 :                 ->osWorkingResult = DateTimeToString(sDateTime);
    2097           9 :             return osWorkingResult;
    2098             :         }
    2099             : 
    2100          10 :         case GFT_WKBGeometry:
    2101             :         {
    2102          10 :             OGRGeometry *poGeom = nullptr;
    2103          18 :             if (!aoFields[iField].aabyWKBGeometryValues[iRow].empty() &&
    2104          16 :                 OGRGeometryFactory::createFromWkb(
    2105           8 :                     aoFields[iField].aabyWKBGeometryValues[iRow].data(),
    2106             :                     nullptr, &poGeom,
    2107           8 :                     aoFields[iField].aabyWKBGeometryValues[iRow].size(),
    2108             :                     wkbVariantIso) == OGRERR_NONE)
    2109             :             {
    2110             :                 const_cast<GDALDefaultRasterAttributeTable *>(this)
    2111           8 :                     ->osWorkingResult = poGeom->exportToWkt();
    2112             :             }
    2113             :             else
    2114             :             {
    2115             :                 const_cast<GDALDefaultRasterAttributeTable *>(this)
    2116           2 :                     ->osWorkingResult.clear();
    2117             :             }
    2118          10 :             delete poGeom;
    2119          10 :             return osWorkingResult;
    2120             :         }
    2121             :     }
    2122             : 
    2123           0 :     return "";
    2124             : }
    2125             : 
    2126             : /************************************************************************/
    2127             : /*                      GDALRATGetValueAsString()                       */
    2128             : /************************************************************************/
    2129             : /**
    2130             :  * \brief Fetch field value as a string.
    2131             :  *
    2132             :  * This function is the same as the C++ method
    2133             :  * GDALRasterAttributeTable::GetValueAsString()
    2134             :  */
    2135         115 : const char *CPL_STDCALL GDALRATGetValueAsString(GDALRasterAttributeTableH hRAT,
    2136             :                                                 int iRow, int iField)
    2137             : 
    2138             : {
    2139         115 :     VALIDATE_POINTER1(hRAT, "GDALRATGetValueAsString", nullptr);
    2140             : 
    2141         230 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetValueAsString(iRow,
    2142         115 :                                                                         iField);
    2143             : }
    2144             : 
    2145             : /************************************************************************/
    2146             : /*                           GetValueAsInt()                            */
    2147             : /************************************************************************/
    2148             : 
    2149         354 : int GDALDefaultRasterAttributeTable::GetValueAsInt(int iRow, int iField) const
    2150             : 
    2151             : {
    2152         354 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2153             :     {
    2154          21 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2155             :                  iField);
    2156             : 
    2157          21 :         return 0;
    2158             :     }
    2159             : 
    2160         333 :     if (iRow < 0 || iRow >= nRowCount)
    2161             :     {
    2162           3 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2163             : 
    2164           3 :         return 0;
    2165             :     }
    2166             : 
    2167         330 :     switch (aoFields[iField].eType)
    2168             :     {
    2169         324 :         case GFT_Integer:
    2170         324 :             return aoFields[iField].anValues[iRow];
    2171             : 
    2172           1 :         case GFT_Real:
    2173           1 :             return static_cast<int>(aoFields[iField].adfValues[iRow]);
    2174             : 
    2175           1 :         case GFT_String:
    2176           1 :             return atoi(aoFields[iField].aosValues[iRow].c_str());
    2177             : 
    2178           2 :         case GFT_Boolean:
    2179           2 :             return aoFields[iField].abValues[iRow];
    2180             : 
    2181           2 :         case GFT_DateTime:
    2182             :         case GFT_WKBGeometry:
    2183           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2184             :                      "Incompatible RAT field type");
    2185           2 :             break;
    2186             :     }
    2187             : 
    2188           2 :     return 0;
    2189             : }
    2190             : 
    2191             : /************************************************************************/
    2192             : /*                        GDALRATGetValueAsInt()                        */
    2193             : /************************************************************************/
    2194             : 
    2195             : /**
    2196             :  * \brief Fetch field value as a integer.
    2197             :  *
    2198             :  * This function is the same as the C++ method
    2199             :  * GDALRasterAttributeTable::GetValueAsInt()
    2200             :  */
    2201         179 : int CPL_STDCALL GDALRATGetValueAsInt(GDALRasterAttributeTableH hRAT, int iRow,
    2202             :                                      int iField)
    2203             : 
    2204             : {
    2205         179 :     VALIDATE_POINTER1(hRAT, "GDALRATGetValueAsInt", 0);
    2206             : 
    2207         358 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetValueAsInt(iRow,
    2208         179 :                                                                      iField);
    2209             : }
    2210             : 
    2211             : /************************************************************************/
    2212             : /*                          GetValueAsDouble()                          */
    2213             : /************************************************************************/
    2214             : 
    2215         627 : double GDALDefaultRasterAttributeTable::GetValueAsDouble(int iRow,
    2216             :                                                          int iField) const
    2217             : 
    2218             : {
    2219         627 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2220             :     {
    2221           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2222             :                  iField);
    2223             : 
    2224           1 :         return 0;
    2225             :     }
    2226             : 
    2227         626 :     if (iRow < 0 || iRow >= nRowCount)
    2228             :     {
    2229           3 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2230             : 
    2231           3 :         return 0;
    2232             :     }
    2233             : 
    2234         623 :     switch (aoFields[iField].eType)
    2235             :     {
    2236           1 :         case GFT_Integer:
    2237           1 :             return aoFields[iField].anValues[iRow];
    2238             : 
    2239         618 :         case GFT_Real:
    2240         618 :             return aoFields[iField].adfValues[iRow];
    2241             : 
    2242           1 :         case GFT_String:
    2243           1 :             return CPLAtof(aoFields[iField].aosValues[iRow].c_str());
    2244             : 
    2245           1 :         case GFT_Boolean:
    2246           1 :             return aoFields[iField].abValues[iRow];
    2247             : 
    2248           2 :         case GFT_DateTime:
    2249             :         case GFT_WKBGeometry:
    2250           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2251             :                      "Incompatible RAT field type");
    2252           2 :             break;
    2253             :     }
    2254             : 
    2255           2 :     return 0;
    2256             : }
    2257             : 
    2258             : /************************************************************************/
    2259             : /*                      GDALRATGetValueAsDouble()                       */
    2260             : /************************************************************************/
    2261             : 
    2262             : /**
    2263             :  * \brief Fetch field value as a double.
    2264             :  *
    2265             :  * This function is the same as the C++ method
    2266             :  * GDALRasterAttributeTable::GetValueAsDouble()
    2267             :  */
    2268         105 : double CPL_STDCALL GDALRATGetValueAsDouble(GDALRasterAttributeTableH hRAT,
    2269             :                                            int iRow, int iField)
    2270             : 
    2271             : {
    2272         105 :     VALIDATE_POINTER1(hRAT, "GDALRATGetValueAsDouble", 0);
    2273             : 
    2274         210 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetValueAsDouble(iRow,
    2275         105 :                                                                         iField);
    2276             : }
    2277             : 
    2278             : /************************************************************************/
    2279             : /*                        GetValueAsBoolean()                           */
    2280             : /************************************************************************/
    2281             : 
    2282          45 : bool GDALDefaultRasterAttributeTable::GetValueAsBoolean(int iRow,
    2283             :                                                         int iField) const
    2284             : 
    2285             : {
    2286          45 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2287             :     {
    2288           6 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2289             :                  iField);
    2290             : 
    2291           6 :         return false;
    2292             :     }
    2293             : 
    2294          39 :     if (iRow < 0 || iRow >= nRowCount)
    2295             :     {
    2296           2 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2297             : 
    2298           2 :         return false;
    2299             :     }
    2300             : 
    2301          37 :     switch (aoFields[iField].eType)
    2302             :     {
    2303           5 :         case GFT_Integer:
    2304           5 :             return aoFields[iField].anValues[iRow] != 0;
    2305             : 
    2306           1 :         case GFT_Real:
    2307           1 :             return aoFields[iField].adfValues[iRow] != 0;
    2308             : 
    2309           1 :         case GFT_String:
    2310           1 :             return CPLTestBool(aoFields[iField].aosValues[iRow].c_str());
    2311             : 
    2312          28 :         case GFT_Boolean:
    2313          28 :             return aoFields[iField].abValues[iRow];
    2314             : 
    2315           2 :         case GFT_DateTime:
    2316             :         case GFT_WKBGeometry:
    2317           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2318             :                      "Incompatible RAT field type");
    2319           2 :             break;
    2320             :     }
    2321             : 
    2322           2 :     return false;
    2323             : }
    2324             : 
    2325             : /************************************************************************/
    2326             : /*                       GDALRATGetValueAsBoolean()                     */
    2327             : /************************************************************************/
    2328             : 
    2329             : /**
    2330             :  * \brief Fetch field value as a boolean.
    2331             :  *
    2332             :  * This function is the same as the C++ method
    2333             :  * GDALRasterAttributeTable::GetValueAsBoolean()
    2334             :  *
    2335             :  * \since 3.12
    2336             :  */
    2337          22 : bool GDALRATGetValueAsBoolean(GDALRasterAttributeTableH hRAT, int iRow,
    2338             :                               int iField)
    2339             : 
    2340             : {
    2341          22 :     VALIDATE_POINTER1(hRAT, "GDALRATGetValueAsBoolean", false);
    2342             : 
    2343          44 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetValueAsBoolean(
    2344          22 :         iRow, iField);
    2345             : }
    2346             : 
    2347             : /************************************************************************/
    2348             : /*                        GetValueAsDateTime()                          */
    2349             : /************************************************************************/
    2350             : 
    2351             : GDALRATDateTime
    2352          17 : GDALDefaultRasterAttributeTable::GetValueAsDateTime(int iRow, int iField) const
    2353             : 
    2354             : {
    2355          17 :     GDALRATDateTime dt;
    2356             : 
    2357          17 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2358             :     {
    2359           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2360             :                  iField);
    2361             : 
    2362           1 :         return dt;
    2363             :     }
    2364             : 
    2365          16 :     if (iRow < 0 || iRow >= nRowCount)
    2366             :     {
    2367           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2368             : 
    2369           1 :         return dt;
    2370             :     }
    2371             : 
    2372          15 :     switch (aoFields[iField].eType)
    2373             :     {
    2374           0 :         case GFT_String:
    2375           0 :             StringToDateTime(aoFields[iField].aosValues[iRow].c_str(), dt);
    2376           0 :             break;
    2377             : 
    2378          11 :         case GFT_DateTime:
    2379          11 :             dt = aoFields[iField].asDateTimeValues[iRow];
    2380          11 :             break;
    2381             : 
    2382           4 :         case GFT_Integer:
    2383             :         case GFT_Real:
    2384             :         case GFT_Boolean:
    2385             :         case GFT_WKBGeometry:
    2386           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    2387             :                      "Incompatible RAT field type");
    2388           4 :             break;
    2389             :     }
    2390             : 
    2391          15 :     return dt;
    2392             : }
    2393             : 
    2394             : /************************************************************************/
    2395             : /*                      GDALRATGetValueAsDateTime()                     */
    2396             : /************************************************************************/
    2397             : 
    2398             : /**
    2399             :  * \brief Fetch field value as a datetime.
    2400             :  *
    2401             :  * The value of the requested column in the requested row is returned
    2402             :  * as a datetime. Besides being called on a GFT_DateTime field, it
    2403             :  * is also possible to call this method on a string field that contains a
    2404             :  * ISO-8601 encoded datetime.
    2405             :  *
    2406             :  * This function is the same as the C++ method
    2407             :  * GDALRasterAttributeTable::GetValueAsDateTime()
    2408             :  *
    2409             :  * @param hRAT Raster attribute table handle. Must NOT be null.
    2410             :  * @param iRow Row index (0-based indexing)
    2411             :  * @param iField Field index (0-based indexing)
    2412             :  * @param[out] psDateTime Output date time struct. Must NOT be null.
    2413             :  * @return error code.
    2414             :  *
    2415             :  * \since 3.12
    2416             :  */
    2417          16 : CPLErr GDALRATGetValueAsDateTime(GDALRasterAttributeTableH hRAT, int iRow,
    2418             :                                  int iField, GDALRATDateTime *psDateTime)
    2419             : 
    2420             : {
    2421          16 :     VALIDATE_POINTER1(hRAT, "GDALRATGetValueAsBoolean", CE_Failure);
    2422          16 :     VALIDATE_POINTER1(psDateTime, "GDALRATGetValueAsBoolean", CE_Failure);
    2423             : 
    2424          16 :     const auto nErrorCounter = CPLGetErrorCounter();
    2425          16 :     *psDateTime =
    2426          16 :         GDALRasterAttributeTable::FromHandle(hRAT)->GetValueAsDateTime(iRow,
    2427          16 :                                                                        iField);
    2428          16 :     return nErrorCounter == CPLGetErrorCounter() ? CE_None : CE_Failure;
    2429             : }
    2430             : 
    2431             : /************************************************************************/
    2432             : /*                        GetValueAsWKBGeometry()                       */
    2433             : /************************************************************************/
    2434             : 
    2435             : const GByte *
    2436          14 : GDALDefaultRasterAttributeTable::GetValueAsWKBGeometry(int iRow, int iField,
    2437             :                                                        size_t &nWKBSize) const
    2438             : 
    2439             : {
    2440          14 :     nWKBSize = 0;
    2441             : 
    2442          14 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2443             :     {
    2444           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2445             :                  iField);
    2446             : 
    2447           1 :         return nullptr;
    2448             :     }
    2449             : 
    2450          13 :     if (iRow < 0 || iRow >= nRowCount)
    2451             :     {
    2452           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2453             : 
    2454           1 :         return nullptr;
    2455             :     }
    2456             : 
    2457          12 :     switch (aoFields[iField].eType)
    2458             :     {
    2459           0 :         case GFT_String:
    2460             :         {
    2461             :             auto abyWKB =
    2462           0 :                 WKTGeometryToWKB(aoFields[iField].aosValues[iRow].c_str());
    2463           0 :             if (!abyWKB.empty())
    2464             :             {
    2465           0 :                 nWKBSize = abyWKB.size();
    2466           0 :                 m_abyWKB = std::move(abyWKB);
    2467           0 :                 return m_abyWKB.data();
    2468             :             }
    2469           0 :             return nullptr;
    2470             :         }
    2471             : 
    2472           8 :         case GFT_WKBGeometry:
    2473             :         {
    2474           8 :             nWKBSize = aoFields[iField].aabyWKBGeometryValues[iRow].size();
    2475           8 :             return nWKBSize
    2476           8 :                        ? aoFields[iField].aabyWKBGeometryValues[iRow].data()
    2477           8 :                        : nullptr;
    2478             :         }
    2479             : 
    2480           4 :         case GFT_Integer:
    2481             :         case GFT_Real:
    2482             :         case GFT_Boolean:
    2483             :         case GFT_DateTime:
    2484           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    2485             :                      "Incompatible RAT field type");
    2486           4 :             break;
    2487             :     }
    2488             : 
    2489           4 :     return nullptr;
    2490             : }
    2491             : 
    2492             : /************************************************************************/
    2493             : /*                     GDALRATGetValueAsWKBGeometry()                   */
    2494             : /************************************************************************/
    2495             : 
    2496             : /**
    2497             :  * \brief Fetch field value as a WKB-encoded geometry.
    2498             :  *
    2499             :  * The value of the requested column in the requested row is returned
    2500             :  * as a WKB geometry. Besides being called on a GFT_WKBGeometry field, it
    2501             :  * is also possible to call this method on a string field that contains a WKT
    2502             :  * encoded geometry.
    2503             :  *
    2504             :  * The returned pointer may be invalidated by a following call  call to a method
    2505             :  * of this GDALRasterAttributeTable instance.
    2506             :  *
    2507             :  * This function is the same as the C++ method
    2508             :  * GDALRasterAttributeTable::GetValueAsWKBGeometry()
    2509             :  *
    2510             :  * \since 3.12
    2511             :  */
    2512          14 : const GByte *GDALRATGetValueAsWKBGeometry(GDALRasterAttributeTableH hRAT,
    2513             :                                           int iRow, int iField,
    2514             :                                           size_t *pnWKBSize)
    2515             : 
    2516             : {
    2517          14 :     VALIDATE_POINTER1(hRAT, "GDALRATGetValueAsWKBGeometry", nullptr);
    2518          14 :     VALIDATE_POINTER1(pnWKBSize, "GDALRATGetValueAsWKBGeometry", nullptr);
    2519             : 
    2520          28 :     return GDALRasterAttributeTable::FromHandle(hRAT)->GetValueAsWKBGeometry(
    2521          14 :         iRow, iField, *pnWKBSize);
    2522             : }
    2523             : 
    2524             : /************************************************************************/
    2525             : /*                            SetRowCount()                             */
    2526             : /************************************************************************/
    2527             : 
    2528             : /** Set row count.
    2529             :  * @param nNewCount new count.
    2530             :  */
    2531        2295 : void GDALDefaultRasterAttributeTable::SetRowCount(int nNewCount)
    2532             : 
    2533             : {
    2534        2295 :     if (nNewCount == nRowCount)
    2535          23 :         return;
    2536             : 
    2537        5947 :     for (auto &oField : aoFields)
    2538             :     {
    2539        3675 :         switch (oField.eType)
    2540             :         {
    2541         911 :             case GFT_Integer:
    2542         911 :                 oField.anValues.resize(nNewCount);
    2543         911 :                 break;
    2544             : 
    2545        2051 :             case GFT_Real:
    2546        2051 :                 oField.adfValues.resize(nNewCount);
    2547        2051 :                 break;
    2548             : 
    2549         680 :             case GFT_String:
    2550         680 :                 oField.aosValues.resize(nNewCount);
    2551         680 :                 break;
    2552             : 
    2553          11 :             case GFT_Boolean:
    2554          11 :                 oField.abValues.resize(nNewCount);
    2555          11 :                 break;
    2556             : 
    2557          11 :             case GFT_DateTime:
    2558          11 :                 oField.asDateTimeValues.resize(nNewCount);
    2559          11 :                 break;
    2560             : 
    2561          11 :             case GFT_WKBGeometry:
    2562          11 :                 oField.aabyWKBGeometryValues.resize(nNewCount);
    2563          11 :                 break;
    2564             :         }
    2565             :     }
    2566             : 
    2567        2272 :     nRowCount = nNewCount;
    2568             : }
    2569             : 
    2570             : /************************************************************************/
    2571             : /*                              SetValue()                              */
    2572             : /************************************************************************/
    2573             : 
    2574             : /** Set value
    2575             :  * @param iRow row index.
    2576             :  * @param iField field index.
    2577             :  * @param pszValue value.
    2578             :  */
    2579        3365 : CPLErr GDALDefaultRasterAttributeTable::SetValue(int iRow, int iField,
    2580             :                                                  const char *pszValue)
    2581             : 
    2582             : {
    2583        3365 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2584             :     {
    2585           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2586             :                  iField);
    2587             : 
    2588           1 :         return CE_Failure;
    2589             :     }
    2590             : 
    2591        3364 :     if (iRow == nRowCount)
    2592        1902 :         SetRowCount(nRowCount + 1);
    2593             : 
    2594        3364 :     if (iRow < 0 || iRow >= nRowCount)
    2595             :     {
    2596           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2597             : 
    2598           1 :         return CE_Failure;
    2599             :     }
    2600             : 
    2601        3363 :     switch (aoFields[iField].eType)
    2602             :     {
    2603         137 :         case GFT_Integer:
    2604         137 :             aoFields[iField].anValues[iRow] = atoi(pszValue);
    2605         137 :             break;
    2606             : 
    2607        1920 :         case GFT_Real:
    2608        1920 :             aoFields[iField].adfValues[iRow] = CPLAtof(pszValue);
    2609        1920 :             break;
    2610             : 
    2611        1271 :         case GFT_String:
    2612        1271 :             aoFields[iField].aosValues[iRow] = pszValue;
    2613        1271 :             break;
    2614             : 
    2615           9 :         case GFT_Boolean:
    2616           9 :             aoFields[iField].abValues[iRow] = CPLTestBool(pszValue);
    2617           9 :             break;
    2618             : 
    2619          13 :         case GFT_DateTime:
    2620             :         {
    2621          13 :             GDALRATDateTime sDateTime;
    2622          13 :             StringToDateTime(pszValue, sDateTime);
    2623          13 :             aoFields[iField].asDateTimeValues[iRow] = std::move(sDateTime);
    2624          13 :             break;
    2625             :         }
    2626             : 
    2627          13 :         case GFT_WKBGeometry:
    2628             :         {
    2629          26 :             auto abyWKB = WKTGeometryToWKB(pszValue);
    2630          13 :             aoFields[iField].aabyWKBGeometryValues[iRow] = std::move(abyWKB);
    2631          13 :             break;
    2632             :         }
    2633             :     }
    2634             : 
    2635        3363 :     return CE_None;
    2636             : }
    2637             : 
    2638             : /************************************************************************/
    2639             : /*                      GDALRATSetValueAsString()                       */
    2640             : /************************************************************************/
    2641             : 
    2642             : /**
    2643             :  * \brief Set field value from string.
    2644             :  *
    2645             :  * This function is the same as the C++ method
    2646             :  * GDALRasterAttributeTable::SetValue()
    2647             :  * @param hRAT RAT handle.
    2648             :  * @param iRow row index.
    2649             :  * @param iField field index.
    2650             :  * @param pszValue value.
    2651             :  */
    2652          66 : void CPL_STDCALL GDALRATSetValueAsString(GDALRasterAttributeTableH hRAT,
    2653             :                                          int iRow, int iField,
    2654             :                                          const char *pszValue)
    2655             : 
    2656             : {
    2657          66 :     VALIDATE_POINTER0(hRAT, "GDALRATSetValueAsString");
    2658             : 
    2659          66 :     GDALRasterAttributeTable::FromHandle(hRAT)->SetValue(iRow, iField,
    2660          66 :                                                          pszValue);
    2661             : }
    2662             : 
    2663             : /************************************************************************/
    2664             : /*                              SetValue()                              */
    2665             : /************************************************************************/
    2666             : 
    2667        1321 : CPLErr GDALDefaultRasterAttributeTable::SetValue(int iRow, int iField,
    2668             :                                                  int nValue)
    2669             : 
    2670             : {
    2671        1321 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2672             :     {
    2673           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2674             :                  iField);
    2675             : 
    2676           1 :         return CE_Failure;
    2677             :     }
    2678             : 
    2679        1320 :     if (iRow == nRowCount)
    2680         334 :         SetRowCount(nRowCount + 1);
    2681             : 
    2682        1320 :     if (iRow < 0 || iRow >= nRowCount)
    2683             :     {
    2684           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2685             : 
    2686           1 :         return CE_Failure;
    2687             :     }
    2688             : 
    2689        1319 :     switch (aoFields[iField].eType)
    2690             :     {
    2691        1302 :         case GFT_Integer:
    2692        1302 :             aoFields[iField].anValues[iRow] = nValue;
    2693        1302 :             break;
    2694             : 
    2695          13 :         case GFT_Real:
    2696          13 :             aoFields[iField].adfValues[iRow] = nValue;
    2697          13 :             break;
    2698             : 
    2699           1 :         case GFT_String:
    2700             :         {
    2701             :             char szValue[100];
    2702             : 
    2703           1 :             snprintf(szValue, sizeof(szValue), "%d", nValue);
    2704           1 :             aoFields[iField].aosValues[iRow] = szValue;
    2705           1 :             break;
    2706             :         }
    2707             : 
    2708           1 :         case GFT_Boolean:
    2709           1 :             aoFields[iField].abValues[iRow] = nValue != 0;
    2710           1 :             break;
    2711             : 
    2712           2 :         case GFT_DateTime:
    2713             :         case GFT_WKBGeometry:
    2714           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2715             :                      "Incompatible RAT field type");
    2716           2 :             return CE_Failure;
    2717             :     }
    2718             : 
    2719        1317 :     return CE_None;
    2720             : }
    2721             : 
    2722             : /************************************************************************/
    2723             : /*                        GDALRATSetValueAsInt()                        */
    2724             : /************************************************************************/
    2725             : 
    2726             : /**
    2727             :  * \brief Set field value from integer.
    2728             :  *
    2729             :  * This function is the same as the C++ method
    2730             :  * GDALRasterAttributeTable::SetValue()
    2731             :  */
    2732          64 : void CPL_STDCALL GDALRATSetValueAsInt(GDALRasterAttributeTableH hRAT, int iRow,
    2733             :                                       int iField, int nValue)
    2734             : 
    2735             : {
    2736          64 :     VALIDATE_POINTER0(hRAT, "GDALRATSetValueAsInt");
    2737             : 
    2738          64 :     GDALRasterAttributeTable::FromHandle(hRAT)->SetValue(iRow, iField, nValue);
    2739             : }
    2740             : 
    2741             : /************************************************************************/
    2742             : /*                              SetValue()                              */
    2743             : /************************************************************************/
    2744             : 
    2745        5794 : CPLErr GDALDefaultRasterAttributeTable::SetValue(int iRow, int iField,
    2746             :                                                  double dfValue)
    2747             : 
    2748             : {
    2749        5794 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2750             :     {
    2751           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2752             :                  iField);
    2753             : 
    2754           1 :         return CE_Failure;
    2755             :     }
    2756             : 
    2757        5793 :     if (iRow == nRowCount)
    2758           0 :         SetRowCount(nRowCount + 1);
    2759             : 
    2760        5793 :     if (iRow < 0 || iRow >= nRowCount)
    2761             :     {
    2762           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2763             : 
    2764           1 :         return CE_Failure;
    2765             :     }
    2766             : 
    2767        5792 :     switch (aoFields[iField].eType)
    2768             :     {
    2769        1609 :         case GFT_Integer:
    2770        1609 :             aoFields[iField].anValues[iRow] = static_cast<int>(dfValue);
    2771        1609 :             break;
    2772             : 
    2773        4177 :         case GFT_Real:
    2774        4177 :             aoFields[iField].adfValues[iRow] = dfValue;
    2775        4177 :             break;
    2776             : 
    2777           1 :         case GFT_String:
    2778             :         {
    2779           1 :             char szValue[100] = {'\0'};
    2780             : 
    2781           1 :             CPLsnprintf(szValue, sizeof(szValue), "%.15g", dfValue);
    2782           1 :             aoFields[iField].aosValues[iRow] = szValue;
    2783           1 :             break;
    2784             :         }
    2785             : 
    2786           1 :         case GFT_Boolean:
    2787           1 :             aoFields[iField].abValues[iRow] = dfValue != 0;
    2788           1 :             break;
    2789             : 
    2790           4 :         case GFT_DateTime:
    2791             :         case GFT_WKBGeometry:
    2792           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    2793             :                      "Incompatible RAT field type");
    2794           4 :             return CE_Failure;
    2795             :     }
    2796             : 
    2797        5788 :     return CE_None;
    2798             : }
    2799             : 
    2800             : /************************************************************************/
    2801             : /*                      GDALRATSetValueAsDouble()                       */
    2802             : /************************************************************************/
    2803             : 
    2804             : /**
    2805             :  * \brief Set field value from double.
    2806             :  *
    2807             :  * This function is the same as the C++ method
    2808             :  * GDALRasterAttributeTable::SetValue()
    2809             :  */
    2810          50 : void CPL_STDCALL GDALRATSetValueAsDouble(GDALRasterAttributeTableH hRAT,
    2811             :                                          int iRow, int iField, double dfValue)
    2812             : 
    2813             : {
    2814          50 :     VALIDATE_POINTER0(hRAT, "GDALRATSetValueAsDouble");
    2815             : 
    2816          50 :     GDALRasterAttributeTable::FromHandle(hRAT)->SetValue(iRow, iField, dfValue);
    2817             : }
    2818             : 
    2819             : /************************************************************************/
    2820             : /*                              SetValue()                              */
    2821             : /************************************************************************/
    2822             : 
    2823          27 : CPLErr GDALDefaultRasterAttributeTable::SetValue(int iRow, int iField,
    2824             :                                                  bool bValue)
    2825             : 
    2826             : {
    2827          27 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2828             :     {
    2829           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2830             :                  iField);
    2831             : 
    2832           1 :         return CE_Failure;
    2833             :     }
    2834             : 
    2835          26 :     if (iRow == nRowCount)
    2836           1 :         SetRowCount(nRowCount + 1);
    2837             : 
    2838          26 :     if (iRow < 0 || iRow >= nRowCount)
    2839             :     {
    2840           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2841             : 
    2842           1 :         return CE_Failure;
    2843             :     }
    2844             : 
    2845          25 :     switch (aoFields[iField].eType)
    2846             :     {
    2847           1 :         case GFT_Integer:
    2848             :         {
    2849           1 :             aoFields[iField].anValues[iRow] = bValue ? 1 : 0;
    2850           1 :             break;
    2851             :         }
    2852           1 :         case GFT_String:
    2853             :         {
    2854           1 :             aoFields[iField].aosValues[iRow] = bValue ? "true" : "false";
    2855           1 :             break;
    2856             :         }
    2857           1 :         case GFT_Real:
    2858             :         {
    2859           1 :             aoFields[iField].adfValues[iRow] = bValue ? 1 : 0;
    2860           1 :             break;
    2861             :         }
    2862          20 :         case GFT_Boolean:
    2863             :         {
    2864          20 :             aoFields[iField].abValues[iRow] = bValue;
    2865          20 :             break;
    2866             :         }
    2867           2 :         case GFT_DateTime:
    2868             :         case GFT_WKBGeometry:
    2869           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2870             :                      "Incompatible RAT field type");
    2871           2 :             return CE_Failure;
    2872             :     }
    2873             : 
    2874          23 :     return CE_None;
    2875             : }
    2876             : 
    2877             : /************************************************************************/
    2878             : /*                      GDALRATSetValueAsBoolean()                      */
    2879             : /************************************************************************/
    2880             : 
    2881             : /**
    2882             :  * \brief Set field value from a boolean value.
    2883             :  *
    2884             :  * This function is the same as the C++ method
    2885             :  * GDALRasterAttributeTable::SetValue()
    2886             :  *
    2887             :  * \since 3.12
    2888             :  */
    2889          27 : CPLErr GDALRATSetValueAsBoolean(GDALRasterAttributeTableH hRAT, int iRow,
    2890             :                                 int iField, bool bValue)
    2891             : 
    2892             : {
    2893          27 :     VALIDATE_POINTER1(hRAT, "GDALRATSetValueAsBoolean", CE_Failure);
    2894             : 
    2895          54 :     return GDALRasterAttributeTable::FromHandle(hRAT)->SetValue(iRow, iField,
    2896          27 :                                                                 bValue);
    2897             : }
    2898             : 
    2899             : /************************************************************************/
    2900             : /*                              SetValue()                              */
    2901             : /************************************************************************/
    2902             : 
    2903             : CPLErr
    2904          12 : GDALDefaultRasterAttributeTable::SetValue(int iRow, int iField,
    2905             :                                           const GDALRATDateTime &sDateTime)
    2906             : 
    2907             : {
    2908          12 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2909             :     {
    2910           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2911             :                  iField);
    2912             : 
    2913           1 :         return CE_Failure;
    2914             :     }
    2915             : 
    2916          11 :     if (iRow == nRowCount)
    2917           0 :         SetRowCount(nRowCount + 1);
    2918             : 
    2919          11 :     if (iRow < 0 || iRow >= nRowCount)
    2920             :     {
    2921           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    2922             : 
    2923           1 :         return CE_Failure;
    2924             :     }
    2925             : 
    2926          10 :     switch (aoFields[iField].eType)
    2927             :     {
    2928           1 :         case GFT_String:
    2929             :         {
    2930           1 :             aoFields[iField].aosValues[iRow] = DateTimeToString(sDateTime);
    2931           1 :             break;
    2932             :         }
    2933             : 
    2934           5 :         case GFT_DateTime:
    2935             :         {
    2936           5 :             aoFields[iField].asDateTimeValues[iRow] = sDateTime;
    2937           5 :             break;
    2938             :         }
    2939             : 
    2940           4 :         case GFT_Integer:
    2941             :         case GFT_Real:
    2942             :         case GFT_Boolean:
    2943             :         case GFT_WKBGeometry:
    2944           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    2945             :                      "Incompatible RAT field type");
    2946           4 :             return CE_Failure;
    2947             :     }
    2948             : 
    2949           6 :     return CE_None;
    2950             : }
    2951             : 
    2952             : /************************************************************************/
    2953             : /*                      GDALRATSetValueAsDateTime()                     */
    2954             : /************************************************************************/
    2955             : 
    2956             : /**
    2957             :  * \brief Set field value from datetime.
    2958             :  *
    2959             :  * Note that the GDALRATDateTime::bIsValid field must be set to true if
    2960             :  * the date time is valid.
    2961             :  *
    2962             :  * This function is the same as the C++ method
    2963             :  * GDALRasterAttributeTable::SetValue()
    2964             :  *
    2965             :  * \since 3.12
    2966             :  */
    2967          16 : CPLErr GDALRATSetValueAsDateTime(GDALRasterAttributeTableH hRAT, int iRow,
    2968             :                                  int iField, const GDALRATDateTime *psDateTime)
    2969             : 
    2970             : {
    2971          16 :     VALIDATE_POINTER1(hRAT, "GDALRATSetValueAsDateTime", CE_Failure);
    2972          16 :     VALIDATE_POINTER1(psDateTime, "GDALRATSetValueAsDateTime", CE_Failure);
    2973             : 
    2974          32 :     return GDALRasterAttributeTable::FromHandle(hRAT)->SetValue(iRow, iField,
    2975          16 :                                                                 *psDateTime);
    2976             : }
    2977             : 
    2978             : /************************************************************************/
    2979             : /*                              SetValue()                              */
    2980             : /************************************************************************/
    2981             : 
    2982           9 : CPLErr GDALDefaultRasterAttributeTable::SetValue(int iRow, int iField,
    2983             :                                                  const void *pabyWKB,
    2984             :                                                  size_t nWKBSize)
    2985             : 
    2986             : {
    2987           9 :     if (iField < 0 || iField >= static_cast<int>(aoFields.size()))
    2988             :     {
    2989           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iField (%d) out of range.",
    2990             :                  iField);
    2991             : 
    2992           1 :         return CE_Failure;
    2993             :     }
    2994             : 
    2995           8 :     if (iRow == nRowCount)
    2996           0 :         SetRowCount(nRowCount + 1);
    2997             : 
    2998           8 :     if (iRow < 0 || iRow >= nRowCount)
    2999             :     {
    3000           1 :         CPLError(CE_Failure, CPLE_AppDefined, "iRow (%d) out of range.", iRow);
    3001             : 
    3002           1 :         return CE_Failure;
    3003             :     }
    3004             : 
    3005           7 :     switch (aoFields[iField].eType)
    3006             :     {
    3007           1 :         case GFT_String:
    3008             :         {
    3009           1 :             aoFields[iField].aosValues[iRow] =
    3010           2 :                 WKBGeometryToWKT(pabyWKB, nWKBSize);
    3011           1 :             break;
    3012             :         }
    3013             : 
    3014           2 :         case GFT_WKBGeometry:
    3015             :         {
    3016           2 :             if (nWKBSize)
    3017           2 :                 aoFields[iField].aabyWKBGeometryValues[iRow].assign(
    3018             :                     static_cast<const GByte *>(pabyWKB),
    3019             :                     static_cast<const GByte *>(pabyWKB) + nWKBSize);
    3020             :             else
    3021           0 :                 aoFields[iField].aabyWKBGeometryValues[iRow].clear();
    3022           2 :             break;
    3023             :         }
    3024             : 
    3025           4 :         case GFT_Integer:
    3026             :         case GFT_Real:
    3027             :         case GFT_Boolean:
    3028             :         case GFT_DateTime:
    3029           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3030             :                      "Incompatible RAT field type");
    3031           4 :             return CE_Failure;
    3032             :     }
    3033             : 
    3034           3 :     return CE_None;
    3035             : }
    3036             : 
    3037             : /************************************************************************/
    3038             : /*                      GDALRATSetValueAsWKBGeometry()                  */
    3039             : /************************************************************************/
    3040             : 
    3041             : /**
    3042             :  * \brief Set field value from a WKB-encoded geometry.
    3043             :  *
    3044             :  * This function is the same as the C++ method
    3045             :  * GDALRasterAttributeTable::SetValue()
    3046             :  *
    3047             :  * \since 3.12
    3048             :  */
    3049          14 : CPLErr GDALRATSetValueAsWKBGeometry(GDALRasterAttributeTableH hRAT, int iRow,
    3050             :                                     int iField, const void *pabyWKB,
    3051             :                                     size_t nWKBSize)
    3052             : 
    3053             : {
    3054          14 :     VALIDATE_POINTER1(hRAT, "GDALRATSetValueAsWKBGeometry", CE_Failure);
    3055             : 
    3056          28 :     return GDALRasterAttributeTable::FromHandle(hRAT)->SetValue(
    3057          14 :         iRow, iField, pabyWKB, nWKBSize);
    3058             : }
    3059             : 
    3060             : /************************************************************************/
    3061             : /*                       ChangesAreWrittenToFile()                      */
    3062             : /************************************************************************/
    3063             : 
    3064           0 : int GDALDefaultRasterAttributeTable::ChangesAreWrittenToFile()
    3065             : {
    3066             :     // GDALRasterBand.SetDefaultRAT needs to be called on instances of
    3067             :     // GDALDefaultRasterAttributeTable since changes are just in-memory
    3068           0 :     return false;
    3069             : }
    3070             : 
    3071             : /************************************************************************/
    3072             : /*                   GDALRATChangesAreWrittenToFile()                   */
    3073             : /************************************************************************/
    3074             : 
    3075             : /**
    3076             :  * \brief Determine whether changes made to this RAT are reflected directly in
    3077             :  * the dataset
    3078             :  *
    3079             :  * This function is the same as the C++ method
    3080             :  * GDALRasterAttributeTable::ChangesAreWrittenToFile()
    3081             :  */
    3082           2 : int CPL_STDCALL GDALRATChangesAreWrittenToFile(GDALRasterAttributeTableH hRAT)
    3083             : {
    3084           2 :     VALIDATE_POINTER1(hRAT, "GDALRATChangesAreWrittenToFile", false);
    3085             : 
    3086           2 :     return GDALRasterAttributeTable::FromHandle(hRAT)
    3087           2 :         ->ChangesAreWrittenToFile();
    3088             : }
    3089             : 
    3090             : /************************************************************************/
    3091             : /*                           GetRowOfValue()                            */
    3092             : /************************************************************************/
    3093             : 
    3094           2 : int GDALDefaultRasterAttributeTable::GetRowOfValue(double dfValue) const
    3095             : 
    3096             : {
    3097             :     /* -------------------------------------------------------------------- */
    3098             :     /*      Handle case of regular binning.                                 */
    3099             :     /* -------------------------------------------------------------------- */
    3100           2 :     if (bLinearBinning)
    3101             :     {
    3102           0 :         const int iBin =
    3103           0 :             static_cast<int>(floor((dfValue - dfRow0Min) / dfBinSize));
    3104           0 :         if (iBin < 0 || iBin >= nRowCount)
    3105           0 :             return -1;
    3106             : 
    3107           0 :         return iBin;
    3108             :     }
    3109             : 
    3110             :     /* -------------------------------------------------------------------- */
    3111             :     /*      Do we have any information?                                     */
    3112             :     /* -------------------------------------------------------------------- */
    3113           2 :     if (!bColumnsAnalysed)
    3114           2 :         const_cast<GDALDefaultRasterAttributeTable *>(this)->AnalyseColumns();
    3115             : 
    3116           2 :     if (nMinCol == -1 && nMaxCol == -1)
    3117           0 :         return -1;
    3118             : 
    3119           2 :     const GDALRasterAttributeField *poMin = nullptr;
    3120           2 :     if (nMinCol != -1)
    3121           2 :         poMin = &(aoFields[nMinCol]);
    3122             :     else
    3123           0 :         poMin = nullptr;
    3124             : 
    3125           2 :     const GDALRasterAttributeField *poMax = nullptr;
    3126           2 :     if (nMaxCol != -1)
    3127           2 :         poMax = &(aoFields[nMaxCol]);
    3128             :     else
    3129           0 :         poMax = nullptr;
    3130             : 
    3131             :     /* -------------------------------------------------------------------- */
    3132             :     /*      Search through rows for match.                                  */
    3133             :     /* -------------------------------------------------------------------- */
    3134           4 :     for (int iRow = 0; iRow < nRowCount; iRow++)
    3135             :     {
    3136           4 :         if (poMin != nullptr)
    3137             :         {
    3138           4 :             if (poMin->eType == GFT_Integer)
    3139             :             {
    3140           4 :                 while (iRow < nRowCount && dfValue < poMin->anValues[iRow])
    3141           0 :                     iRow++;
    3142             :             }
    3143           0 :             else if (poMin->eType == GFT_Real)
    3144             :             {
    3145           0 :                 while (iRow < nRowCount && dfValue < poMin->adfValues[iRow])
    3146           0 :                     iRow++;
    3147             :             }
    3148             : 
    3149           4 :             if (iRow == nRowCount)
    3150           0 :                 break;
    3151             :         }
    3152             : 
    3153           4 :         if (poMax != nullptr)
    3154             :         {
    3155          14 :             if ((poMax->eType == GFT_Integer &&
    3156           6 :                  dfValue > poMax->anValues[iRow]) ||
    3157           2 :                 (poMax->eType == GFT_Real && dfValue > poMax->adfValues[iRow]))
    3158           2 :                 continue;
    3159             :         }
    3160             : 
    3161           2 :         return iRow;
    3162             :     }
    3163             : 
    3164           0 :     return -1;
    3165             : }
    3166             : 
    3167             : /************************************************************************/
    3168             : /*                           GetRowOfValue()                            */
    3169             : /*                                                                      */
    3170             : /*      Int arg for now just converted to double.  Perhaps we will      */
    3171             : /*      handle this in a special way some day?                          */
    3172             : /************************************************************************/
    3173             : 
    3174           0 : int GDALDefaultRasterAttributeTable::GetRowOfValue(int nValue) const
    3175             : 
    3176             : {
    3177           0 :     return GetRowOfValue(static_cast<double>(nValue));
    3178             : }
    3179             : 
    3180             : /************************************************************************/
    3181             : /*                          SetLinearBinning()                          */
    3182             : /************************************************************************/
    3183             : 
    3184          16 : CPLErr GDALDefaultRasterAttributeTable::SetLinearBinning(double dfRow0MinIn,
    3185             :                                                          double dfBinSizeIn)
    3186             : 
    3187             : {
    3188          16 :     bLinearBinning = true;
    3189          16 :     dfRow0Min = dfRow0MinIn;
    3190          16 :     dfBinSize = dfBinSizeIn;
    3191             : 
    3192          16 :     return CE_None;
    3193             : }
    3194             : 
    3195             : /************************************************************************/
    3196             : /*                          GetLinearBinning()                          */
    3197             : /************************************************************************/
    3198             : 
    3199          20 : int GDALDefaultRasterAttributeTable::GetLinearBinning(double *pdfRow0Min,
    3200             :                                                       double *pdfBinSize) const
    3201             : 
    3202             : {
    3203          20 :     if (!bLinearBinning)
    3204          18 :         return false;
    3205             : 
    3206           2 :     *pdfRow0Min = dfRow0Min;
    3207           2 :     *pdfBinSize = dfBinSize;
    3208             : 
    3209           2 :     return true;
    3210             : }
    3211             : 
    3212             : /************************************************************************/
    3213             : /*                          GetTableType()                              */
    3214             : /************************************************************************/
    3215             : 
    3216             : /**
    3217             :  * \brief Get RAT Table Type
    3218             :  *
    3219             :  * Returns whether table type is thematic or athematic
    3220             :  *
    3221             :  * This method is the same as the C function GDALRATGetTableType().
    3222             :  *
    3223             :  *
    3224             :  * @return GRTT_THEMATIC or GRTT_ATHEMATIC
    3225             :  */
    3226             : 
    3227          20 : GDALRATTableType GDALDefaultRasterAttributeTable::GetTableType() const
    3228             : {
    3229          20 :     return eTableType;
    3230             : }
    3231             : 
    3232             : /************************************************************************/
    3233             : /*                          SetTableType()                              */
    3234             : /************************************************************************/
    3235             : 
    3236             : /**
    3237             :  * \brief Set RAT Table Type
    3238             :  *
    3239             :  * Set whether table type is thematic or athematic
    3240             :  *
    3241             :  * This method is the same as the C function GDALRATSetTableType().
    3242             :  *
    3243             :  * @param eInTableType the new RAT table type (GRTT_THEMATIC or GRTT_ATHEMATIC)
    3244             :  *
    3245             :  *
    3246             :  * @return CE_None on success or CE_Failure on failure.
    3247             :  */
    3248             : 
    3249          46 : CPLErr GDALDefaultRasterAttributeTable::SetTableType(
    3250             :     const GDALRATTableType eInTableType)
    3251             : {
    3252          46 :     eTableType = eInTableType;
    3253          46 :     return CE_None;
    3254             : }
    3255             : 
    3256             : /************************************************************************/
    3257             : /*                            CreateColumn()                            */
    3258             : /************************************************************************/
    3259             : 
    3260             : CPLErr
    3261         486 : GDALDefaultRasterAttributeTable::CreateColumn(const char *pszFieldName,
    3262             :                                               GDALRATFieldType eFieldType,
    3263             :                                               GDALRATFieldUsage eFieldUsage)
    3264             : 
    3265             : {
    3266         486 :     const size_t iNewField = aoFields.size();
    3267             : 
    3268         486 :     aoFields.resize(iNewField + 1);
    3269             : 
    3270         486 :     aoFields[iNewField].sName = pszFieldName;
    3271             : 
    3272             :     // color columns should be int 0..255
    3273         486 :     if ((eFieldUsage == GFU_Red) || (eFieldUsage == GFU_Green) ||
    3274         426 :         (eFieldUsage == GFU_Blue) || (eFieldUsage == GFU_Alpha))
    3275             :     {
    3276          65 :         eFieldType = GFT_Integer;
    3277             :     }
    3278         486 :     aoFields[iNewField].eType = eFieldType;
    3279         486 :     aoFields[iNewField].eUsage = eFieldUsage;
    3280             : 
    3281         486 :     switch (eFieldType)
    3282             :     {
    3283         221 :         case GFT_Integer:
    3284         221 :             aoFields[iNewField].anValues.resize(nRowCount);
    3285         221 :             break;
    3286             : 
    3287          76 :         case GFT_Real:
    3288          76 :             aoFields[iNewField].adfValues.resize(nRowCount);
    3289          76 :             break;
    3290             : 
    3291         144 :         case GFT_String:
    3292         144 :             aoFields[iNewField].aosValues.resize(nRowCount);
    3293         144 :             break;
    3294             : 
    3295          16 :         case GFT_Boolean:
    3296          16 :             aoFields[iNewField].abValues.resize(nRowCount);
    3297          16 :             break;
    3298             : 
    3299          15 :         case GFT_DateTime:
    3300          15 :             aoFields[iNewField].asDateTimeValues.resize(nRowCount);
    3301          15 :             break;
    3302             : 
    3303          14 :         case GFT_WKBGeometry:
    3304          14 :             aoFields[iNewField].aabyWKBGeometryValues.resize(nRowCount);
    3305          14 :             break;
    3306             :     }
    3307         486 :     return CE_None;
    3308             : }
    3309             : 
    3310             : /************************************************************************/
    3311             : /*                            RemoveStatistics()                        */
    3312             : /************************************************************************/
    3313             : 
    3314             : /**
    3315             :  * \brief Remove Statistics from RAT
    3316             :  *
    3317             :  * Remove statistics (such as histogram) from the RAT. This is important
    3318             :  * if these have been invalidated, for example by cropping the image.
    3319             :  *
    3320             :  * This method is the same as the C function GDALRATRemoveStatistics().
    3321             :  *
    3322             :  */
    3323             : 
    3324           2 : void GDALDefaultRasterAttributeTable::RemoveStatistics()
    3325             : 
    3326             : {
    3327             :     // since we are storing the fields in a vector it will generally
    3328             :     // be faster to create a new vector and replace the old one
    3329             :     // rather than actually erasing columns.
    3330           4 :     std::vector<GDALRasterAttributeField> aoNewFields;
    3331           4 :     for (const auto &field : aoFields)
    3332             :     {
    3333           2 :         switch (field.eUsage)
    3334             :         {
    3335           1 :             case GFU_PixelCount:
    3336             :             case GFU_Min:
    3337             :             case GFU_Max:
    3338             :             case GFU_RedMin:
    3339             :             case GFU_GreenMin:
    3340             :             case GFU_BlueMin:
    3341             :             case GFU_AlphaMin:
    3342             :             case GFU_RedMax:
    3343             :             case GFU_GreenMax:
    3344             :             case GFU_BlueMax:
    3345             :             case GFU_AlphaMax:
    3346             :             {
    3347           1 :                 break;
    3348             :             }
    3349             : 
    3350           1 :             default:
    3351           1 :                 if (field.sName != "Histogram")
    3352             :                 {
    3353           1 :                     aoNewFields.push_back(field);
    3354             :                 }
    3355             :         }
    3356             :     }
    3357           2 :     aoFields = std::move(aoNewFields);
    3358           2 : }
    3359             : 
    3360             : /************************************************************************/
    3361             : /*                               Clone()                                */
    3362             : /************************************************************************/
    3363             : 
    3364          28 : GDALDefaultRasterAttributeTable *GDALDefaultRasterAttributeTable::Clone() const
    3365             : 
    3366             : {
    3367          28 :     return new GDALDefaultRasterAttributeTable(*this);
    3368             : }
    3369             : 
    3370             : /************************************************************************/
    3371             : /*                            GDALRATClone()                            */
    3372             : /************************************************************************/
    3373             : 
    3374             : /**
    3375             :  * \brief Copy Raster Attribute Table
    3376             :  *
    3377             :  * This function is the same as the C++ method GDALRasterAttributeTable::Clone()
    3378             :  */
    3379             : GDALRasterAttributeTableH CPL_STDCALL
    3380           6 : GDALRATClone(const GDALRasterAttributeTableH hRAT)
    3381             : 
    3382             : {
    3383           6 :     VALIDATE_POINTER1(hRAT, "GDALRATClone", nullptr);
    3384             : 
    3385           6 :     return GDALRasterAttributeTable::FromHandle(hRAT)->Clone();
    3386             : }
    3387             : 
    3388             : /************************************************************************/
    3389             : /*                            GDALRATSerializeJSON()                    */
    3390             : /************************************************************************/
    3391             : 
    3392             : /**
    3393             :  * \brief Serialize Raster Attribute Table in Json format
    3394             :  *
    3395             :  * This function is the same as the C++ method
    3396             :  * GDALRasterAttributeTable::SerializeJSON()
    3397             :  */
    3398           7 : void *CPL_STDCALL GDALRATSerializeJSON(GDALRasterAttributeTableH hRAT)
    3399             : 
    3400             : {
    3401           7 :     VALIDATE_POINTER1(hRAT, "GDALRATSerializeJSON", nullptr);
    3402             : 
    3403           7 :     return GDALRasterAttributeTable::FromHandle(hRAT)->SerializeJSON();
    3404             : }
    3405             : 
    3406             : /************************************************************************/
    3407             : /*                        GDALRATRemoveStatistics()                     */
    3408             : /************************************************************************/
    3409             : 
    3410             : /**
    3411             :  * \brief Remove Statistics from RAT
    3412             :  *
    3413             :  * This function is the same as the C++ method
    3414             :  * GDALRasterAttributeTable::RemoveStatistics()
    3415             :  *
    3416             :  */
    3417           1 : void CPL_STDCALL GDALRATRemoveStatistics(GDALRasterAttributeTableH hRAT)
    3418             : 
    3419             : {
    3420           1 :     VALIDATE_POINTER0(hRAT, "GDALRATRemoveStatistics");
    3421             : 
    3422           1 :     GDALRasterAttributeTable::FromHandle(hRAT)->RemoveStatistics();
    3423             : }

Generated by: LCOV version 1.14