LCOV - code coverage report
Current view: top level - gcore - gdal_misc.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1577 2084 75.7 %
Date: 2024-05-03 15:49:35 Functions: 82 89 92.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Free standing functions for GDAL.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : 
      32             : #include <cctype>
      33             : #include <cerrno>
      34             : #include <clocale>
      35             : #include <cmath>
      36             : #include <cstddef>
      37             : #include <cstdio>
      38             : #include <cstdlib>
      39             : #include <cstring>
      40             : #include <fcntl.h>
      41             : 
      42             : #include <algorithm>
      43             : #include <iostream>
      44             : #include <limits>
      45             : #include <string>
      46             : 
      47             : #include "cpl_conv.h"
      48             : #include "cpl_error.h"
      49             : #include "cpl_minixml.h"
      50             : #include "cpl_multiproc.h"
      51             : #include "cpl_string.h"
      52             : #include "cpl_vsi.h"
      53             : #include "gdal_version_full/gdal_version.h"
      54             : #include "gdal.h"
      55             : #include "gdal_mdreader.h"
      56             : #include "gdal_priv.h"
      57             : #include "gdal_priv_templates.hpp"
      58             : #include "ogr_core.h"
      59             : #include "ogr_spatialref.h"
      60             : #include "ogr_geos.h"
      61             : 
      62             : #include "proj.h"
      63             : 
      64             : #ifdef HAVE_CURL
      65             : #include "cpl_curl_priv.h"
      66             : #endif
      67             : 
      68        3357 : static int GetMinBitsForPair(const bool pabSigned[], const bool pabFloating[],
      69             :                              const int panBits[])
      70             : {
      71        3357 :     if (pabFloating[0] != pabFloating[1])
      72             :     {
      73         371 :         const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
      74         371 :         const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
      75             : 
      76         371 :         return std::max(panBits[nFloatingTypeIndex],
      77         371 :                         2 * panBits[nNotFloatingTypeIndex]);
      78             :     }
      79             : 
      80        2986 :     if (pabSigned[0] != pabSigned[1])
      81             :     {
      82         428 :         const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0;
      83         428 :         const int nSignedTypeIndex = pabSigned[0] ? 0 : 1;
      84             : 
      85         428 :         return std::max(panBits[nSignedTypeIndex],
      86         428 :                         2 * panBits[nUnsignedTypeIndex]);
      87             :     }
      88             : 
      89        2558 :     return std::max(panBits[0], panBits[1]);
      90             : }
      91             : 
      92        6714 : static int GetDataTypeElementSizeBits(GDALDataType eDataType)
      93             : {
      94        6714 :     switch (eDataType)
      95             :     {
      96        4286 :         case GDT_Byte:
      97             :         case GDT_Int8:
      98        4286 :             return 8;
      99             : 
     100        1151 :         case GDT_UInt16:
     101             :         case GDT_Int16:
     102             :         case GDT_CInt16:
     103        1151 :             return 16;
     104             : 
     105         876 :         case GDT_UInt32:
     106             :         case GDT_Int32:
     107             :         case GDT_Float32:
     108             :         case GDT_CInt32:
     109             :         case GDT_CFloat32:
     110         876 :             return 32;
     111             : 
     112         401 :         case GDT_Float64:
     113             :         case GDT_CFloat64:
     114             :         case GDT_UInt64:
     115             :         case GDT_Int64:
     116         401 :             return 64;
     117             : 
     118           0 :         case GDT_Unknown:
     119             :         case GDT_TypeCount:
     120           0 :             break;
     121             :     }
     122           0 :     return 0;
     123             : }
     124             : 
     125             : /************************************************************************/
     126             : /*                         GDALDataTypeUnion()                          */
     127             : /************************************************************************/
     128             : 
     129             : /**
     130             :  * \brief Return the smallest data type that can fully express both input data
     131             :  * types.
     132             :  *
     133             :  * @param eType1 first data type.
     134             :  * @param eType2 second data type.
     135             :  *
     136             :  * @return a data type able to express eType1 and eType2.
     137             :  */
     138             : 
     139        3357 : GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1,
     140             :                                            GDALDataType eType2)
     141             : 
     142             : {
     143        3357 :     const int panBits[] = {GetDataTypeElementSizeBits(eType1),
     144        3357 :                            GetDataTypeElementSizeBits(eType2)};
     145             : 
     146        3357 :     if (panBits[0] == 0 || panBits[1] == 0)
     147           0 :         return GDT_Unknown;
     148             : 
     149        3357 :     const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)),
     150        3357 :                               CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))};
     151             : 
     152        3357 :     const bool bSigned = pabSigned[0] || pabSigned[1];
     153        3357 :     const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
     154        3357 :                                 CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))};
     155        3357 :     const bool bFloating = pabFloating[0] || pabFloating[1];
     156        6339 :     const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) ||
     157        2982 :                           CPL_TO_BOOL(GDALDataTypeIsComplex(eType2));
     158             : 
     159        3357 :     const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
     160             : 
     161        3357 :     return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
     162             : }
     163             : 
     164             : /************************************************************************/
     165             : /*                        GDALDataTypeUnionWithValue()                  */
     166             : /************************************************************************/
     167             : 
     168             : /**
     169             :  * \brief Union a data type with the one found for a value
     170             :  *
     171             :  * @param eDT the first data type
     172             :  * @param dValue the value for which to find a data type and union with eDT
     173             :  * @param bComplex if the value is complex
     174             :  *
     175             :  * @return a data type able to express eDT and dValue.
     176             :  * @since GDAL 2.3
     177             :  */
     178         248 : GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(GDALDataType eDT,
     179             :                                                     double dValue, int bComplex)
     180             : {
     181         248 :     if (eDT == GDT_Float32 && !bComplex && static_cast<float>(dValue) == dValue)
     182          47 :         return eDT;
     183             : 
     184         201 :     const GDALDataType eDT2 = GDALFindDataTypeForValue(dValue, bComplex);
     185         201 :     return GDALDataTypeUnion(eDT, eDT2);
     186             : }
     187             : 
     188             : /************************************************************************/
     189             : /*                        GetMinBitsForValue()                          */
     190             : /************************************************************************/
     191         201 : static int GetMinBitsForValue(double dValue)
     192             : {
     193         201 :     if (round(dValue) == dValue)
     194             :     {
     195         374 :         if (dValue <= std::numeric_limits<GByte>::max() &&
     196         184 :             dValue >= std::numeric_limits<GByte>::min())
     197         135 :             return 8;
     198             : 
     199         104 :         if (dValue <= std::numeric_limits<GInt8>::max() &&
     200          49 :             dValue >= std::numeric_limits<GInt8>::min())
     201           3 :             return 8;
     202             : 
     203         100 :         if (dValue <= std::numeric_limits<GInt16>::max() &&
     204          48 :             dValue >= std::numeric_limits<GInt16>::min())
     205          47 :             return 16;
     206             : 
     207          10 :         if (dValue <= std::numeric_limits<GUInt16>::max() &&
     208           5 :             dValue >= std::numeric_limits<GUInt16>::min())
     209           4 :             return 16;
     210             : 
     211           2 :         if (dValue <= std::numeric_limits<GInt32>::max() &&
     212           1 :             dValue >= std::numeric_limits<GInt32>::min())
     213           1 :             return 32;
     214             : 
     215           0 :         if (dValue <= std::numeric_limits<GUInt32>::max() &&
     216           0 :             dValue >= std::numeric_limits<GUInt32>::min())
     217           0 :             return 32;
     218             : 
     219           0 :         if (dValue <= static_cast<double>(
     220           0 :                           std::numeric_limits<std::uint64_t>::max()) &&
     221             :             dValue >=
     222           0 :                 static_cast<double>(std::numeric_limits<std::uint64_t>::min()))
     223           0 :             return 64;
     224             :     }
     225          11 :     else if (static_cast<float>(dValue) == dValue)
     226             :     {
     227           4 :         return 32;
     228             :     }
     229             : 
     230           7 :     return 64;
     231             : }
     232             : 
     233             : /************************************************************************/
     234             : /*                        GDALFindDataType()                            */
     235             : /************************************************************************/
     236             : 
     237             : /**
     238             :  * \brief Finds the smallest data type able to support the given
     239             :  *  requirements
     240             :  *
     241             :  * @param nBits number of bits necessary
     242             :  * @param bSigned if negative values are necessary
     243             :  * @param bFloating if non-integer values necessary
     244             :  * @param bComplex if complex values are necessary
     245             :  *
     246             :  * @return a best fit GDALDataType for supporting the requirements
     247             :  * @since GDAL 2.3
     248             :  */
     249        3576 : GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating,
     250             :                                           int bComplex)
     251             : {
     252        3576 :     if (bComplex)
     253             :     {
     254         723 :         nBits = std::max(nBits, !bSigned ? 32 : 16);
     255             :     }  // we don't have complex unsigned data types, so for a complex uint16,
     256             :        // promote to complex int32
     257        3576 :     if (bFloating)
     258             :     {
     259         545 :         nBits = std::max(nBits, 32);
     260             :     }
     261             : 
     262        3576 :     if (nBits <= 8)
     263             :     {
     264        1950 :         return bSigned ? GDT_Int8 : GDT_Byte;
     265             :     }
     266             : 
     267        1626 :     if (nBits <= 16)
     268             :     {
     269         712 :         if (bComplex)
     270         358 :             return GDT_CInt16;
     271         354 :         if (bSigned)
     272         244 :             return GDT_Int16;
     273         110 :         return GDT_UInt16;
     274             :     }
     275             : 
     276         914 :     if (nBits <= 32)
     277             :     {
     278         521 :         if (bFloating)
     279             :         {
     280         253 :             if (bComplex)
     281          95 :                 return GDT_CFloat32;
     282         158 :             return GDT_Float32;
     283             :         }
     284             : 
     285         268 :         if (bComplex)
     286          93 :             return GDT_CInt32;
     287         175 :         if (bSigned)
     288         116 :             return GDT_Int32;
     289          59 :         return GDT_UInt32;
     290             :     }
     291             : 
     292         393 :     if (nBits == 64 && !bFloating && !bComplex)
     293          57 :         return bSigned ? GDT_Int64 : GDT_UInt64;
     294             : 
     295         336 :     if (bComplex)
     296         177 :         return GDT_CFloat64;
     297             : 
     298         159 :     return GDT_Float64;
     299             : }
     300             : 
     301             : /************************************************************************/
     302             : /*                        GDALFindDataTypeForValue()                    */
     303             : /************************************************************************/
     304             : 
     305             : /**
     306             :  * \brief Finds the smallest data type able to support the provided value
     307             :  *
     308             :  * @param dValue value to support
     309             :  * @param bComplex is the value complex
     310             :  *
     311             :  * @return a best fit GDALDataType for supporting the value
     312             :  * @since GDAL 2.3
     313             :  */
     314         201 : GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex)
     315             : {
     316         201 :     const bool bFloating = round(dValue) != dValue;
     317         201 :     const bool bSigned = bFloating || dValue < 0;
     318         201 :     const int nBits = GetMinBitsForValue(dValue);
     319             : 
     320         201 :     return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                        GDALGetDataTypeSizeBytes()                    */
     325             : /************************************************************************/
     326             : 
     327             : /**
     328             :  * \brief Get data type size in <b>bytes</b>.
     329             :  *
     330             :  * Returns the size of a GDT_* type in bytes.  In contrast,
     331             :  * GDALGetDataTypeSize() returns the size in <b>bits</b>.
     332             :  *
     333             :  * @param eDataType type, such as GDT_Byte.
     334             :  * @return the number of bytes or zero if it is not recognised.
     335             :  */
     336             : 
     337   232608000 : int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType)
     338             : 
     339             : {
     340   232608000 :     switch (eDataType)
     341             :     {
     342    63334800 :         case GDT_Byte:
     343             :         case GDT_Int8:
     344    63334800 :             return 1;
     345             : 
     346    51942000 :         case GDT_UInt16:
     347             :         case GDT_Int16:
     348    51942000 :             return 2;
     349             : 
     350    80571100 :         case GDT_UInt32:
     351             :         case GDT_Int32:
     352             :         case GDT_Float32:
     353             :         case GDT_CInt16:
     354    80571100 :             return 4;
     355             : 
     356    36401500 :         case GDT_Float64:
     357             :         case GDT_CInt32:
     358             :         case GDT_CFloat32:
     359             :         case GDT_UInt64:
     360             :         case GDT_Int64:
     361    36401500 :             return 8;
     362             : 
     363      347727 :         case GDT_CFloat64:
     364      347727 :             return 16;
     365             : 
     366       13580 :         case GDT_Unknown:
     367             :         case GDT_TypeCount:
     368       13580 :             break;
     369             :     }
     370       11242 :     return 0;
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                        GDALGetDataTypeSizeBits()                     */
     375             : /************************************************************************/
     376             : 
     377             : /**
     378             :  * \brief Get data type size in <b>bits</b>.
     379             :  *
     380             :  * Returns the size of a GDT_* type in bits, <b>not bytes</b>!  Use
     381             :  * GDALGetDataTypeSizeBytes() for bytes.
     382             :  *
     383             :  * @param eDataType type, such as GDT_Byte.
     384             :  * @return the number of bits or zero if it is not recognised.
     385             :  */
     386             : 
     387        8915 : int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
     388             : 
     389             : {
     390        8915 :     return GDALGetDataTypeSizeBytes(eDataType) * 8;
     391             : }
     392             : 
     393             : /************************************************************************/
     394             : /*                        GDALGetDataTypeSize()                         */
     395             : /************************************************************************/
     396             : 
     397             : /**
     398             :  * \brief Get data type size in bits.  <b>Deprecated</b>.
     399             :  *
     400             :  * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
     401             :  *
     402             :  * Use GDALGetDataTypeSizeBytes() for bytes.
     403             :  * Use GDALGetDataTypeSizeBits() for bits.
     404             :  *
     405             :  * @param eDataType type, such as GDT_Byte.
     406             :  * @return the number of bits or zero if it is not recognised.
     407             :  */
     408             : 
     409     1194840 : int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
     410             : 
     411             : {
     412     1194840 :     return GDALGetDataTypeSizeBytes(eDataType) * 8;
     413             : }
     414             : 
     415             : /************************************************************************/
     416             : /*                       GDALDataTypeIsComplex()                        */
     417             : /************************************************************************/
     418             : 
     419             : /**
     420             :  * \brief Is data type complex?
     421             :  *
     422             :  * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
     423             :  * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
     424             :  * component.
     425             :  */
     426             : 
     427      622626 : int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
     428             : 
     429             : {
     430      622626 :     switch (eDataType)
     431             :     {
     432        7317 :         case GDT_CInt16:
     433             :         case GDT_CInt32:
     434             :         case GDT_CFloat32:
     435             :         case GDT_CFloat64:
     436        7317 :             return TRUE;
     437             : 
     438      615300 :         case GDT_Byte:
     439             :         case GDT_Int8:
     440             :         case GDT_Int16:
     441             :         case GDT_UInt16:
     442             :         case GDT_Int32:
     443             :         case GDT_UInt32:
     444             :         case GDT_Int64:
     445             :         case GDT_UInt64:
     446             :         case GDT_Float32:
     447             :         case GDT_Float64:
     448      615300 :             return FALSE;
     449             : 
     450           6 :         case GDT_Unknown:
     451             :         case GDT_TypeCount:
     452           6 :             break;
     453             :     }
     454           9 :     return FALSE;
     455             : }
     456             : 
     457             : /************************************************************************/
     458             : /*                       GDALDataTypeIsFloating()                       */
     459             : /************************************************************************/
     460             : 
     461             : /**
     462             :  * \brief Is data type floating? (might be complex)
     463             :  *
     464             :  * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float64,
     465             :  * GDT_CFloat32, GDT_CFloat64)
     466             :  * @since GDAL 2.3
     467             :  */
     468             : 
     469       46340 : int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType)
     470             : {
     471       46340 :     switch (eDataType)
     472             :     {
     473         974 :         case GDT_Float32:
     474             :         case GDT_Float64:
     475             :         case GDT_CFloat32:
     476             :         case GDT_CFloat64:
     477         974 :             return TRUE;
     478             : 
     479       45365 :         case GDT_Byte:
     480             :         case GDT_Int8:
     481             :         case GDT_Int16:
     482             :         case GDT_UInt16:
     483             :         case GDT_Int32:
     484             :         case GDT_UInt32:
     485             :         case GDT_Int64:
     486             :         case GDT_UInt64:
     487             :         case GDT_CInt16:
     488             :         case GDT_CInt32:
     489       45365 :             return FALSE;
     490             : 
     491           1 :         case GDT_Unknown:
     492             :         case GDT_TypeCount:
     493           1 :             break;
     494             :     }
     495           1 :     return FALSE;
     496             : }
     497             : 
     498             : /************************************************************************/
     499             : /*                       GDALDataTypeIsInteger()                        */
     500             : /************************************************************************/
     501             : 
     502             : /**
     503             :  * \brief Is data type integer? (might be complex)
     504             :  *
     505             :  * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16,
     506             :  * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
     507             :  * @since GDAL 2.3
     508             :  */
     509             : 
     510       43963 : int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
     511             : 
     512             : {
     513       43963 :     switch (eDataType)
     514             :     {
     515       43040 :         case GDT_Byte:
     516             :         case GDT_Int8:
     517             :         case GDT_Int16:
     518             :         case GDT_UInt16:
     519             :         case GDT_Int32:
     520             :         case GDT_UInt32:
     521             :         case GDT_CInt16:
     522             :         case GDT_CInt32:
     523             :         case GDT_UInt64:
     524             :         case GDT_Int64:
     525       43040 :             return TRUE;
     526             : 
     527         920 :         case GDT_Float32:
     528             :         case GDT_Float64:
     529             :         case GDT_CFloat32:
     530             :         case GDT_CFloat64:
     531         920 :             return FALSE;
     532             : 
     533           1 :         case GDT_Unknown:
     534             :         case GDT_TypeCount:
     535           1 :             break;
     536             :     }
     537           3 :     return FALSE;
     538             : }
     539             : 
     540             : /************************************************************************/
     541             : /*                       GDALDataTypeIsSigned()                         */
     542             : /************************************************************************/
     543             : 
     544             : /**
     545             :  * \brief Is data type signed?
     546             :  *
     547             :  * @return TRUE if the passed type is signed.
     548             :  * @since GDAL 2.3
     549             :  */
     550             : 
     551       84915 : int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
     552             : {
     553       84915 :     switch (eDataType)
     554             :     {
     555       82076 :         case GDT_Byte:
     556             :         case GDT_UInt16:
     557             :         case GDT_UInt32:
     558             :         case GDT_UInt64:
     559       82076 :             return FALSE;
     560             : 
     561        2839 :         case GDT_Int8:
     562             :         case GDT_Int16:
     563             :         case GDT_Int32:
     564             :         case GDT_Int64:
     565             :         case GDT_Float32:
     566             :         case GDT_Float64:
     567             :         case GDT_CInt16:
     568             :         case GDT_CInt32:
     569             :         case GDT_CFloat32:
     570             :         case GDT_CFloat64:
     571        2839 :             return TRUE;
     572             : 
     573           0 :         case GDT_Unknown:
     574             :         case GDT_TypeCount:
     575           0 :             break;
     576             :     }
     577           0 :     return FALSE;
     578             : }
     579             : 
     580             : /************************************************************************/
     581             : /*                    GDALDataTypeIsConversionLossy()                   */
     582             : /************************************************************************/
     583             : 
     584             : /**
     585             :  * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
     586             :  *
     587             :  * @param eTypeFrom input datatype
     588             :  * @param eTypeTo output datatype
     589             :  * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
     590             :  * @since GDAL 2.3
     591             :  */
     592             : 
     593       39378 : int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,
     594             :                                               GDALDataType eTypeTo)
     595             : {
     596             :     // E.g cfloat32 -> float32
     597       39378 :     if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo))
     598          36 :         return TRUE;
     599             : 
     600       39342 :     eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
     601       39342 :     eTypeTo = GDALGetNonComplexDataType(eTypeTo);
     602             : 
     603       39342 :     if (GDALDataTypeIsInteger(eTypeTo))
     604             :     {
     605             :         // E.g. float32 -> int32
     606       38856 :         if (GDALDataTypeIsFloating(eTypeFrom))
     607          28 :             return TRUE;
     608             : 
     609             :         // E.g. Int16 to UInt16
     610       38828 :         const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
     611       38828 :         const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
     612       38828 :         if (bIsFromSigned && !bIsToSigned)
     613          46 :             return TRUE;
     614             : 
     615             :         // E.g UInt32 to UInt16
     616       38782 :         const int nFromSize = GDALGetDataTypeSize(eTypeFrom);
     617       38782 :         const int nToSize = GDALGetDataTypeSize(eTypeTo);
     618       38782 :         if (nFromSize > nToSize)
     619          30 :             return TRUE;
     620             : 
     621             :         // E.g UInt16 to Int16
     622       38752 :         if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned)
     623          15 :             return TRUE;
     624             : 
     625       38737 :         return FALSE;
     626             :     }
     627             : 
     628         486 :     if (eTypeTo == GDT_Float32 &&
     629         390 :         (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
     630         386 :          eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
     631             :          eTypeFrom == GDT_Float64))
     632             :     {
     633          35 :         return TRUE;
     634             :     }
     635             : 
     636         451 :     if (eTypeTo == GDT_Float64 &&
     637          71 :         (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64))
     638             :     {
     639           4 :         return TRUE;
     640             :     }
     641             : 
     642         447 :     return FALSE;
     643             : }
     644             : 
     645             : /************************************************************************/
     646             : /*                        GDALGetDataTypeName()                         */
     647             : /************************************************************************/
     648             : 
     649             : /**
     650             :  * \brief Get name of data type.
     651             :  *
     652             :  * Returns a symbolic name for the data type.  This is essentially the
     653             :  * the enumerated item name with the GDT_ prefix removed.  So GDT_Byte returns
     654             :  * "Byte".  The returned strings are static strings and should not be modified
     655             :  * or freed by the application.  These strings are useful for reporting
     656             :  * datatypes in debug statements, errors and other user output.
     657             :  *
     658             :  * @param eDataType type to get name of.
     659             :  * @return string corresponding to existing data type
     660             :  *         or NULL pointer if invalid type given.
     661             :  */
     662             : 
     663       57888 : const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
     664             : 
     665             : {
     666       57888 :     switch (eDataType)
     667             :     {
     668        4581 :         case GDT_Unknown:
     669        4581 :             return "Unknown";
     670             : 
     671       25189 :         case GDT_Byte:
     672       25189 :             return "Byte";
     673             : 
     674         560 :         case GDT_Int8:
     675         560 :             return "Int8";
     676             : 
     677        4786 :         case GDT_UInt16:
     678        4786 :             return "UInt16";
     679             : 
     680        4934 :         case GDT_Int16:
     681        4934 :             return "Int16";
     682             : 
     683        3615 :         case GDT_UInt32:
     684        3615 :             return "UInt32";
     685             : 
     686        3588 :         case GDT_Int32:
     687        3588 :             return "Int32";
     688             : 
     689         565 :         case GDT_UInt64:
     690         565 :             return "UInt64";
     691             : 
     692         550 :         case GDT_Int64:
     693         550 :             return "Int64";
     694             : 
     695        3723 :         case GDT_Float32:
     696        3723 :             return "Float32";
     697             : 
     698        1810 :         case GDT_Float64:
     699        1810 :             return "Float64";
     700             : 
     701        1068 :         case GDT_CInt16:
     702        1068 :             return "CInt16";
     703             : 
     704        1002 :         case GDT_CInt32:
     705        1002 :             return "CInt32";
     706             : 
     707        1033 :         case GDT_CFloat32:
     708        1033 :             return "CFloat32";
     709             : 
     710         884 :         case GDT_CFloat64:
     711         884 :             return "CFloat64";
     712             : 
     713           0 :         case GDT_TypeCount:
     714           0 :             break;
     715             :     }
     716           0 :     return nullptr;
     717             : }
     718             : 
     719             : /************************************************************************/
     720             : /*                        GDALGetDataTypeByName()                       */
     721             : /************************************************************************/
     722             : 
     723             : /**
     724             :  * \brief Get data type by symbolic name.
     725             :  *
     726             :  * Returns a data type corresponding to the given symbolic name. This
     727             :  * function is opposite to the GDALGetDataTypeName().
     728             :  *
     729             :  * @param pszName string containing the symbolic name of the type.
     730             :  *
     731             :  * @return GDAL data type.
     732             :  */
     733             : 
     734        4540 : GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName)
     735             : 
     736             : {
     737        4540 :     VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown);
     738             : 
     739       12989 :     for (int iType = 1; iType < GDT_TypeCount; iType++)
     740             :     {
     741       12958 :         const auto eType = static_cast<GDALDataType>(iType);
     742       25916 :         if (GDALGetDataTypeName(eType) != nullptr &&
     743       12958 :             EQUAL(GDALGetDataTypeName(eType), pszName))
     744             :         {
     745        4509 :             return eType;
     746             :         }
     747             :     }
     748             : 
     749          31 :     return GDT_Unknown;
     750             : }
     751             : 
     752             : /************************************************************************/
     753             : /*                      GDALAdjustValueToDataType()                     */
     754             : /************************************************************************/
     755             : 
     756             : template <class T>
     757         147 : static inline void ClampAndRound(double &dfValue, bool &bClamped,
     758             :                                  bool &bRounded)
     759             : {
     760             :     // TODO(schwehr): Rework this template.  ::min() versus ::lowest.
     761             : 
     762         147 :     if (dfValue < static_cast<double>(std::numeric_limits<T>::min()))
     763             :     {
     764          10 :         bClamped = true;
     765          10 :         dfValue = static_cast<double>(std::numeric_limits<T>::min());
     766             :     }
     767         137 :     else if (dfValue > static_cast<double>(std::numeric_limits<T>::max()))
     768             :     {
     769          16 :         bClamped = true;
     770          16 :         dfValue = static_cast<double>(std::numeric_limits<T>::max());
     771             :     }
     772         121 :     else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
     773             :     {
     774           8 :         bRounded = true;
     775           8 :         dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
     776             :     }
     777         147 : }
     778             : 
     779             : /**
     780             :  * \brief Adjust a value to the output data type
     781             :  *
     782             :  * Adjustment consist in clamping to minimum/maximum values of the data type
     783             :  * and rounding for integral types.
     784             :  *
     785             :  * @param eDT target data type.
     786             :  * @param dfValue value to adjust.
     787             :  * @param pbClamped pointer to a integer(boolean) to indicate if clamping has
     788             :  * been made, or NULL
     789             :  * @param pbRounded pointer to a integer(boolean) to indicate if rounding has
     790             :  * been made, or NULL
     791             :  *
     792             :  * @return adjusted value
     793             :  * @since GDAL 2.1
     794             :  */
     795             : 
     796         198 : double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue,
     797             :                                  int *pbClamped, int *pbRounded)
     798             : {
     799         198 :     bool bClamped = false;
     800         198 :     bool bRounded = false;
     801         198 :     switch (eDT)
     802             :     {
     803          91 :         case GDT_Byte:
     804          91 :             ClampAndRound<GByte>(dfValue, bClamped, bRounded);
     805          91 :             break;
     806           6 :         case GDT_Int8:
     807           6 :             ClampAndRound<GInt8>(dfValue, bClamped, bRounded);
     808           6 :             break;
     809          20 :         case GDT_Int16:
     810          20 :             ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
     811          20 :             break;
     812          16 :         case GDT_UInt16:
     813          16 :             ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
     814          16 :             break;
     815           4 :         case GDT_Int32:
     816           4 :             ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
     817           4 :             break;
     818           5 :         case GDT_UInt32:
     819           5 :             ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
     820           5 :             break;
     821           2 :         case GDT_Int64:
     822           2 :             ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded);
     823           2 :             break;
     824           3 :         case GDT_UInt64:
     825           3 :             ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded);
     826           3 :             break;
     827          40 :         case GDT_Float32:
     828             :         {
     829          40 :             if (!CPLIsFinite(dfValue))
     830           4 :                 break;
     831             : 
     832             :             // TODO(schwehr): ::min() versus ::lowest.
     833             :             // Use ClampAndRound after it has been fixed.
     834          36 :             if (dfValue < -std::numeric_limits<float>::max())
     835             :             {
     836           1 :                 bClamped = TRUE;
     837           1 :                 dfValue =
     838           1 :                     static_cast<double>(-std::numeric_limits<float>::max());
     839             :             }
     840          35 :             else if (dfValue > std::numeric_limits<float>::max())
     841             :             {
     842           1 :                 bClamped = TRUE;
     843           1 :                 dfValue =
     844           1 :                     static_cast<double>(std::numeric_limits<float>::max());
     845             :             }
     846             :             else
     847             :             {
     848             :                 // Intentionally loose precision.
     849             :                 // TODO(schwehr): Is the double cast really necessary?
     850             :                 // If so, why?  What will fail?
     851          34 :                 dfValue = static_cast<double>(static_cast<float>(dfValue));
     852             :             }
     853          36 :             break;
     854             :         }
     855          11 :         case GDT_Float64:
     856             :         case GDT_CInt16:
     857             :         case GDT_CInt32:
     858             :         case GDT_CFloat32:
     859             :         case GDT_CFloat64:
     860             :         case GDT_Unknown:
     861             :         case GDT_TypeCount:
     862          11 :             break;
     863             :     }
     864         198 :     if (pbClamped)
     865         197 :         *pbClamped = bClamped;
     866         198 :     if (pbRounded)
     867         197 :         *pbRounded = bRounded;
     868         198 :     return dfValue;
     869             : }
     870             : 
     871             : /************************************************************************/
     872             : /*                        GDALGetNonComplexDataType()                */
     873             : /************************************************************************/
     874             : /**
     875             :  * \brief Return the base data type for the specified input.
     876             :  *
     877             :  * If the input data type is complex this function returns the base type
     878             :  * i.e. the data type of the real and imaginary parts (non-complex).
     879             :  * If the input data type is already non-complex, then it is returned
     880             :  * unchanged.
     881             :  *
     882             :  * @param eDataType type, such as GDT_CFloat32.
     883             :  *
     884             :  * @return GDAL data type.
     885             :  */
     886       78751 : GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType)
     887             : {
     888       78751 :     switch (eDataType)
     889             :     {
     890          70 :         case GDT_CInt16:
     891          70 :             return GDT_Int16;
     892          23 :         case GDT_CInt32:
     893          23 :             return GDT_Int32;
     894          47 :         case GDT_CFloat32:
     895          47 :             return GDT_Float32;
     896          50 :         case GDT_CFloat64:
     897          50 :             return GDT_Float64;
     898             : 
     899       78561 :         case GDT_Byte:
     900             :         case GDT_UInt16:
     901             :         case GDT_UInt32:
     902             :         case GDT_UInt64:
     903             :         case GDT_Int8:
     904             :         case GDT_Int16:
     905             :         case GDT_Int32:
     906             :         case GDT_Int64:
     907             :         case GDT_Float32:
     908             :         case GDT_Float64:
     909       78561 :             break;
     910             : 
     911           0 :         case GDT_Unknown:
     912             :         case GDT_TypeCount:
     913           0 :             break;
     914             :     }
     915       78561 :     return eDataType;
     916             : }
     917             : 
     918             : /************************************************************************/
     919             : /*                        GDALGetAsyncStatusTypeByName()                */
     920             : /************************************************************************/
     921             : /**
     922             :  * Get AsyncStatusType by symbolic name.
     923             :  *
     924             :  * Returns a data type corresponding to the given symbolic name. This
     925             :  * function is opposite to the GDALGetAsyncStatusTypeName().
     926             :  *
     927             :  * @param pszName string containing the symbolic name of the type.
     928             :  *
     929             :  * @return GDAL AsyncStatus type.
     930             :  */
     931             : GDALAsyncStatusType CPL_DLL CPL_STDCALL
     932           0 : GDALGetAsyncStatusTypeByName(const char *pszName)
     933             : {
     934           0 :     VALIDATE_POINTER1(pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
     935             : 
     936           0 :     for (int iType = 0; iType < GARIO_TypeCount; iType++)
     937             :     {
     938           0 :         const auto eType = static_cast<GDALAsyncStatusType>(iType);
     939           0 :         if (GDALGetAsyncStatusTypeName(eType) != nullptr &&
     940           0 :             EQUAL(GDALGetAsyncStatusTypeName(eType), pszName))
     941             :         {
     942           0 :             return eType;
     943             :         }
     944             :     }
     945             : 
     946           0 :     return GARIO_ERROR;
     947             : }
     948             : 
     949             : /************************************************************************/
     950             : /*                        GDALGetAsyncStatusTypeName()                 */
     951             : /************************************************************************/
     952             : 
     953             : /**
     954             :  * Get name of AsyncStatus data type.
     955             :  *
     956             :  * Returns a symbolic name for the AsyncStatus data type.  This is essentially
     957             :  * the enumerated item name with the GARIO_ prefix removed.  So
     958             :  * GARIO_COMPLETE returns "COMPLETE".  The returned strings are static strings
     959             :  * and should not be modified or freed by the application.  These strings are
     960             :  * useful for reporting datatypes in debug statements, errors and other user
     961             :  * output.
     962             :  *
     963             :  * @param eAsyncStatusType type to get name of.
     964             :  * @return string corresponding to type.
     965             :  */
     966             : 
     967             : const char *CPL_STDCALL
     968           0 : GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)
     969             : 
     970             : {
     971           0 :     switch (eAsyncStatusType)
     972             :     {
     973           0 :         case GARIO_PENDING:
     974           0 :             return "PENDING";
     975             : 
     976           0 :         case GARIO_UPDATE:
     977           0 :             return "UPDATE";
     978             : 
     979           0 :         case GARIO_ERROR:
     980           0 :             return "ERROR";
     981             : 
     982           0 :         case GARIO_COMPLETE:
     983           0 :             return "COMPLETE";
     984             : 
     985           0 :         default:
     986           0 :             return nullptr;
     987             :     }
     988             : }
     989             : 
     990             : /************************************************************************/
     991             : /*                  GDALGetPaletteInterpretationName()                  */
     992             : /************************************************************************/
     993             : 
     994             : /**
     995             :  * \brief Get name of palette interpretation
     996             :  *
     997             :  * Returns a symbolic name for the palette interpretation.  This is the
     998             :  * the enumerated item name with the GPI_ prefix removed.  So GPI_Gray returns
     999             :  * "Gray".  The returned strings are static strings and should not be modified
    1000             :  * or freed by the application.
    1001             :  *
    1002             :  * @param eInterp palette interpretation to get name of.
    1003             :  * @return string corresponding to palette interpretation.
    1004             :  */
    1005             : 
    1006           9 : const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)
    1007             : 
    1008             : {
    1009           9 :     switch (eInterp)
    1010             :     {
    1011           0 :         case GPI_Gray:
    1012           0 :             return "Gray";
    1013             : 
    1014           9 :         case GPI_RGB:
    1015           9 :             return "RGB";
    1016             : 
    1017           0 :         case GPI_CMYK:
    1018           0 :             return "CMYK";
    1019             : 
    1020           0 :         case GPI_HLS:
    1021           0 :             return "HLS";
    1022             : 
    1023           0 :         default:
    1024           0 :             return "Unknown";
    1025             :     }
    1026             : }
    1027             : 
    1028             : /************************************************************************/
    1029             : /*                   GDALGetColorInterpretationName()                   */
    1030             : /************************************************************************/
    1031             : 
    1032             : /**
    1033             :  * \brief Get name of color interpretation
    1034             :  *
    1035             :  * Returns a symbolic name for the color interpretation.  This is derived from
    1036             :  * the enumerated item name with the GCI_ prefix removed, but there are some
    1037             :  * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
    1038             :  * The returned strings are static strings and should not be modified
    1039             :  * or freed by the application.
    1040             :  *
    1041             :  * @param eInterp color interpretation to get name of.
    1042             :  * @return string corresponding to color interpretation
    1043             :  *         or NULL pointer if invalid enumerator given.
    1044             :  */
    1045             : 
    1046        8403 : const char *GDALGetColorInterpretationName(GDALColorInterp eInterp)
    1047             : 
    1048             : {
    1049        8403 :     switch (eInterp)
    1050             :     {
    1051        2109 :         case GCI_Undefined:
    1052        2109 :             return "Undefined";
    1053             : 
    1054        2271 :         case GCI_GrayIndex:
    1055        2271 :             return "Gray";
    1056             : 
    1057         935 :         case GCI_PaletteIndex:
    1058         935 :             return "Palette";
    1059             : 
    1060        1032 :         case GCI_RedBand:
    1061        1032 :             return "Red";
    1062             : 
    1063         770 :         case GCI_GreenBand:
    1064         770 :             return "Green";
    1065             : 
    1066         522 :         case GCI_BlueBand:
    1067         522 :             return "Blue";
    1068             : 
    1069         282 :         case GCI_AlphaBand:
    1070         282 :             return "Alpha";
    1071             : 
    1072          76 :         case GCI_HueBand:
    1073          76 :             return "Hue";
    1074             : 
    1075          76 :         case GCI_SaturationBand:
    1076          76 :             return "Saturation";
    1077             : 
    1078          76 :         case GCI_LightnessBand:
    1079          76 :             return "Lightness";
    1080             : 
    1081          84 :         case GCI_CyanBand:
    1082          84 :             return "Cyan";
    1083             : 
    1084          67 :         case GCI_MagentaBand:
    1085          67 :             return "Magenta";
    1086             : 
    1087          50 :         case GCI_YellowBand:
    1088          50 :             return "Yellow";
    1089             : 
    1090          32 :         case GCI_BlackBand:
    1091          32 :             return "Black";
    1092             : 
    1093           9 :         case GCI_YCbCr_YBand:
    1094           9 :             return "YCbCr_Y";
    1095             : 
    1096           7 :         case GCI_YCbCr_CbBand:
    1097           7 :             return "YCbCr_Cb";
    1098             : 
    1099           5 :         case GCI_YCbCr_CrBand:
    1100           5 :             return "YCbCr_Cr";
    1101             : 
    1102           0 :         default:
    1103           0 :             return "Unknown";
    1104             :     }
    1105             : }
    1106             : 
    1107             : /************************************************************************/
    1108             : /*                GDALGetColorInterpretationByName()                    */
    1109             : /************************************************************************/
    1110             : 
    1111             : /**
    1112             :  * \brief Get color interpretation by symbolic name.
    1113             :  *
    1114             :  * Returns a color interpretation corresponding to the given symbolic name. This
    1115             :  * function is opposite to the GDALGetColorInterpretationName().
    1116             :  *
    1117             :  * @param pszName string containing the symbolic name of the color
    1118             :  * interpretation.
    1119             :  *
    1120             :  * @return GDAL color interpretation.
    1121             :  *
    1122             :  * @since GDAL 1.7.0
    1123             :  */
    1124             : 
    1125        1951 : GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
    1126             : 
    1127             : {
    1128        1951 :     VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
    1129             :                       GCI_Undefined);
    1130             : 
    1131        7212 :     for (int iType = 0; iType <= GCI_Max; iType++)
    1132             :     {
    1133        7210 :         if (EQUAL(GDALGetColorInterpretationName(
    1134             :                       static_cast<GDALColorInterp>(iType)),
    1135             :                   pszName))
    1136             :         {
    1137        1949 :             return static_cast<GDALColorInterp>(iType);
    1138             :         }
    1139             :     }
    1140             : 
    1141           2 :     return GCI_Undefined;
    1142             : }
    1143             : 
    1144             : /************************************************************************/
    1145             : /*                     GDALGetRandomRasterSample()                      */
    1146             : /************************************************************************/
    1147             : 
    1148             : /** Undocumented
    1149             :  * @param hBand undocumented.
    1150             :  * @param nSamples undocumented.
    1151             :  * @param pafSampleBuf undocumented.
    1152             :  * @return undocumented
    1153             :  */
    1154           0 : int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples,
    1155             :                                           float *pafSampleBuf)
    1156             : 
    1157             : {
    1158           0 :     VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0);
    1159             : 
    1160             :     GDALRasterBand *poBand;
    1161             : 
    1162           0 :     poBand = GDALRasterBand::FromHandle(
    1163             :         GDALGetRasterSampleOverview(hBand, nSamples));
    1164           0 :     CPLAssert(nullptr != poBand);
    1165             : 
    1166             :     /* -------------------------------------------------------------------- */
    1167             :     /*      Figure out the ratio of blocks we will read to get an           */
    1168             :     /*      approximate value.                                              */
    1169             :     /* -------------------------------------------------------------------- */
    1170           0 :     int bGotNoDataValue = FALSE;
    1171             : 
    1172           0 :     double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue);
    1173             : 
    1174           0 :     int nBlockXSize = 0;
    1175           0 :     int nBlockYSize = 0;
    1176           0 :     poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1177             : 
    1178             :     const int nBlocksPerRow =
    1179           0 :         (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize;
    1180             :     const int nBlocksPerColumn =
    1181           0 :         (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize;
    1182             : 
    1183           0 :     const int nBlockPixels = nBlockXSize * nBlockYSize;
    1184           0 :     const int nBlockCount = nBlocksPerRow * nBlocksPerColumn;
    1185             : 
    1186           0 :     if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 ||
    1187             :         nBlockCount == 0)
    1188             :     {
    1189           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1190             :                  "GDALGetRandomRasterSample(): returning because band"
    1191             :                  " appears degenerate.");
    1192             : 
    1193           0 :         return FALSE;
    1194             :     }
    1195             : 
    1196             :     int nSampleRate = static_cast<int>(
    1197           0 :         std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0));
    1198             : 
    1199           0 :     if (nSampleRate == nBlocksPerRow && nSampleRate > 1)
    1200           0 :         nSampleRate--;
    1201             : 
    1202           0 :     while (nSampleRate > 1 &&
    1203           0 :            ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples)
    1204           0 :         nSampleRate--;
    1205             : 
    1206           0 :     int nBlockSampleRate = 1;
    1207             : 
    1208           0 :     if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0)
    1209           0 :         nBlockSampleRate =
    1210           0 :             std::max(1, nBlockPixels /
    1211           0 :                             (nSamples / ((nBlockCount - 1) / nSampleRate + 1)));
    1212             : 
    1213           0 :     int nActualSamples = 0;
    1214             : 
    1215           0 :     for (int iSampleBlock = 0; iSampleBlock < nBlockCount;
    1216           0 :          iSampleBlock += nSampleRate)
    1217             :     {
    1218             : 
    1219           0 :         const int iYBlock = iSampleBlock / nBlocksPerRow;
    1220           0 :         const int iXBlock = iSampleBlock - nBlocksPerRow * iYBlock;
    1221             : 
    1222             :         GDALRasterBlock *const poBlock =
    1223           0 :             poBand->GetLockedBlockRef(iXBlock, iYBlock);
    1224           0 :         if (poBlock == nullptr)
    1225           0 :             continue;
    1226           0 :         void *pDataRef = poBlock->GetDataRef();
    1227             : 
    1228           0 :         int iXValid = nBlockXSize;
    1229           0 :         if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize())
    1230           0 :             iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
    1231             : 
    1232           0 :         int iYValid = nBlockYSize;
    1233           0 :         if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize())
    1234           0 :             iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
    1235             : 
    1236           0 :         int iRemainder = 0;
    1237             : 
    1238           0 :         for (int iY = 0; iY < iYValid; iY++)
    1239             :         {
    1240           0 :             int iX = iRemainder;  // Used after for.
    1241           0 :             for (; iX < iXValid; iX += nBlockSampleRate)
    1242             :             {
    1243           0 :                 double dfValue = 0.0;
    1244           0 :                 const int iOffset = iX + iY * nBlockXSize;
    1245             : 
    1246           0 :                 switch (poBlock->GetDataType())
    1247             :                 {
    1248           0 :                     case GDT_Byte:
    1249           0 :                         dfValue =
    1250           0 :                             reinterpret_cast<const GByte *>(pDataRef)[iOffset];
    1251           0 :                         break;
    1252           0 :                     case GDT_Int8:
    1253           0 :                         dfValue =
    1254           0 :                             reinterpret_cast<const GInt8 *>(pDataRef)[iOffset];
    1255           0 :                         break;
    1256           0 :                     case GDT_UInt16:
    1257           0 :                         dfValue = reinterpret_cast<const GUInt16 *>(
    1258           0 :                             pDataRef)[iOffset];
    1259           0 :                         break;
    1260           0 :                     case GDT_Int16:
    1261           0 :                         dfValue =
    1262           0 :                             reinterpret_cast<const GInt16 *>(pDataRef)[iOffset];
    1263           0 :                         break;
    1264           0 :                     case GDT_UInt32:
    1265           0 :                         dfValue = reinterpret_cast<const GUInt32 *>(
    1266           0 :                             pDataRef)[iOffset];
    1267           0 :                         break;
    1268           0 :                     case GDT_Int32:
    1269           0 :                         dfValue =
    1270           0 :                             reinterpret_cast<const GInt32 *>(pDataRef)[iOffset];
    1271           0 :                         break;
    1272           0 :                     case GDT_UInt64:
    1273           0 :                         dfValue = static_cast<double>(
    1274             :                             reinterpret_cast<const std::uint64_t *>(
    1275           0 :                                 pDataRef)[iOffset]);
    1276           0 :                         break;
    1277           0 :                     case GDT_Int64:
    1278           0 :                         dfValue = static_cast<double>(
    1279             :                             reinterpret_cast<const std::int64_t *>(
    1280           0 :                                 pDataRef)[iOffset]);
    1281           0 :                         break;
    1282           0 :                     case GDT_Float32:
    1283           0 :                         dfValue =
    1284           0 :                             reinterpret_cast<const float *>(pDataRef)[iOffset];
    1285           0 :                         break;
    1286           0 :                     case GDT_Float64:
    1287           0 :                         dfValue =
    1288           0 :                             reinterpret_cast<const double *>(pDataRef)[iOffset];
    1289           0 :                         break;
    1290           0 :                     case GDT_CInt16:
    1291             :                     {
    1292             :                         // TODO(schwehr): Clean up casts.
    1293           0 :                         const double dfReal = reinterpret_cast<const GInt16 *>(
    1294           0 :                             pDataRef)[iOffset * 2];
    1295           0 :                         const double dfImag = reinterpret_cast<const GInt16 *>(
    1296           0 :                             pDataRef)[iOffset * 2 + 1];
    1297           0 :                         dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
    1298           0 :                         break;
    1299             :                     }
    1300           0 :                     case GDT_CInt32:
    1301             :                     {
    1302           0 :                         const double dfReal = reinterpret_cast<const GInt32 *>(
    1303           0 :                             pDataRef)[iOffset * 2];
    1304           0 :                         const double dfImag = reinterpret_cast<const GInt32 *>(
    1305           0 :                             pDataRef)[iOffset * 2 + 1];
    1306           0 :                         dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
    1307           0 :                         break;
    1308             :                     }
    1309           0 :                     case GDT_CFloat32:
    1310             :                     {
    1311           0 :                         const double dfReal = reinterpret_cast<const float *>(
    1312           0 :                             pDataRef)[iOffset * 2];
    1313           0 :                         const double dfImag = reinterpret_cast<const float *>(
    1314           0 :                             pDataRef)[iOffset * 2 + 1];
    1315           0 :                         dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
    1316           0 :                         break;
    1317             :                     }
    1318           0 :                     case GDT_CFloat64:
    1319             :                     {
    1320           0 :                         const double dfReal = reinterpret_cast<const double *>(
    1321           0 :                             pDataRef)[iOffset * 2];
    1322           0 :                         const double dfImag = reinterpret_cast<const double *>(
    1323           0 :                             pDataRef)[iOffset * 2 + 1];
    1324           0 :                         dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
    1325           0 :                         break;
    1326             :                     }
    1327           0 :                     case GDT_Unknown:
    1328             :                     case GDT_TypeCount:
    1329           0 :                         CPLAssert(false);
    1330             :                 }
    1331             : 
    1332           0 :                 if (bGotNoDataValue && dfValue == dfNoDataValue)
    1333           0 :                     continue;
    1334             : 
    1335           0 :                 if (nActualSamples < nSamples)
    1336           0 :                     pafSampleBuf[nActualSamples++] =
    1337           0 :                         static_cast<float>(dfValue);
    1338             :             }
    1339             : 
    1340           0 :             iRemainder = iX - iXValid;
    1341             :         }
    1342             : 
    1343           0 :         poBlock->DropLock();
    1344             :     }
    1345             : 
    1346           0 :     return nActualSamples;
    1347             : }
    1348             : 
    1349             : /************************************************************************/
    1350             : /*                             gdal::GCP                                */
    1351             : /************************************************************************/
    1352             : 
    1353             : namespace gdal
    1354             : {
    1355             : /** Constructor. */
    1356        3574 : GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine,
    1357        3574 :          double dfX, double dfY, double dfZ)
    1358        3574 :     : gcp{CPLStrdup(pszId ? pszId : ""),
    1359        3574 :           CPLStrdup(pszInfo ? pszInfo : ""),
    1360             :           dfPixel,
    1361             :           dfLine,
    1362             :           dfX,
    1363             :           dfY,
    1364        3574 :           dfZ}
    1365             : {
    1366             :     static_assert(sizeof(GCP) == sizeof(GDAL_GCP));
    1367        3574 : }
    1368             : 
    1369             : /** Destructor. */
    1370       29638 : GCP::~GCP()
    1371             : {
    1372       14819 :     CPLFree(gcp.pszId);
    1373       14819 :     CPLFree(gcp.pszInfo);
    1374       14819 : }
    1375             : 
    1376             : /** Constructor from a C GDAL_GCP instance. */
    1377        8564 : GCP::GCP(const GDAL_GCP &other)
    1378        8564 :     : gcp{CPLStrdup(other.pszId),
    1379       17128 :           CPLStrdup(other.pszInfo),
    1380        8564 :           other.dfGCPPixel,
    1381        8564 :           other.dfGCPLine,
    1382        8564 :           other.dfGCPX,
    1383        8564 :           other.dfGCPY,
    1384        8564 :           other.dfGCPZ}
    1385             : {
    1386        8564 : }
    1387             : 
    1388             : /** Copy constructor. */
    1389        5135 : GCP::GCP(const GCP &other) : GCP(other.gcp)
    1390             : {
    1391        5135 : }
    1392             : 
    1393             : /** Move constructor. */
    1394        2681 : GCP::GCP(GCP &&other)
    1395        2681 :     : gcp{other.gcp.pszId,     other.gcp.pszInfo, other.gcp.dfGCPPixel,
    1396        2681 :           other.gcp.dfGCPLine, other.gcp.dfGCPX,  other.gcp.dfGCPY,
    1397        2681 :           other.gcp.dfGCPZ}
    1398             : {
    1399        2681 :     other.gcp.pszId = nullptr;
    1400        2681 :     other.gcp.pszInfo = nullptr;
    1401        2681 : }
    1402             : 
    1403             : /** Copy assignment operator. */
    1404           1 : GCP &GCP::operator=(const GCP &other)
    1405             : {
    1406           1 :     if (this != &other)
    1407             :     {
    1408           1 :         CPLFree(gcp.pszId);
    1409           1 :         CPLFree(gcp.pszInfo);
    1410           1 :         gcp = other.gcp;
    1411           1 :         gcp.pszId = CPLStrdup(other.gcp.pszId);
    1412           1 :         gcp.pszInfo = CPLStrdup(other.gcp.pszInfo);
    1413             :     }
    1414           1 :     return *this;
    1415             : }
    1416             : 
    1417             : /** Move assignment operator. */
    1418           1 : GCP &GCP::operator=(GCP &&other)
    1419             : {
    1420           1 :     if (this != &other)
    1421             :     {
    1422           1 :         CPLFree(gcp.pszId);
    1423           1 :         CPLFree(gcp.pszInfo);
    1424           1 :         gcp = other.gcp;
    1425           1 :         other.gcp.pszId = nullptr;
    1426           1 :         other.gcp.pszInfo = nullptr;
    1427             :     }
    1428           1 :     return *this;
    1429             : }
    1430             : 
    1431             : /** Set the 'id' member of the GCP. */
    1432        2651 : void GCP::SetId(const char *pszId)
    1433             : {
    1434        2651 :     CPLFree(gcp.pszId);
    1435        2651 :     gcp.pszId = CPLStrdup(pszId ? pszId : "");
    1436        2651 : }
    1437             : 
    1438             : /** Set the 'info' member of the GCP. */
    1439        2651 : void GCP::SetInfo(const char *pszInfo)
    1440             : {
    1441        2651 :     CPLFree(gcp.pszInfo);
    1442        2651 :     gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : "");
    1443        2651 : }
    1444             : 
    1445             : /** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */
    1446             : /*static */
    1447         285 : const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs)
    1448             : {
    1449         285 :     return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr();
    1450             : }
    1451             : 
    1452             : /** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */
    1453             : /*static*/
    1454         320 : std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount)
    1455             : {
    1456         320 :     return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount);
    1457             : }
    1458             : 
    1459             : } /* namespace gdal */
    1460             : 
    1461             : /************************************************************************/
    1462             : /*                            GDALInitGCPs()                            */
    1463             : /************************************************************************/
    1464             : 
    1465             : /** Initialize an array of GCPs.
    1466             :  *
    1467             :  * Numeric values are initialized to 0 and strings to the empty string ""
    1468             :  * allocated with CPLStrdup()
    1469             :  * An array initialized with GDALInitGCPs() must be de-initialized with
    1470             :  * GDALDeinitGCPs().
    1471             :  *
    1472             :  * @param nCount number of GCPs in psGCP
    1473             :  * @param psGCP array of GCPs of size nCount.
    1474             :  */
    1475        1276 : void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP)
    1476             : 
    1477             : {
    1478        1276 :     if (nCount > 0)
    1479             :     {
    1480         637 :         VALIDATE_POINTER0(psGCP, "GDALInitGCPs");
    1481             :     }
    1482             : 
    1483        5906 :     for (int iGCP = 0; iGCP < nCount; iGCP++)
    1484             :     {
    1485        4630 :         memset(psGCP, 0, sizeof(GDAL_GCP));
    1486        4630 :         psGCP->pszId = CPLStrdup("");
    1487        4630 :         psGCP->pszInfo = CPLStrdup("");
    1488        4630 :         psGCP++;
    1489             :     }
    1490             : }
    1491             : 
    1492             : /************************************************************************/
    1493             : /*                           GDALDeinitGCPs()                           */
    1494             : /************************************************************************/
    1495             : 
    1496             : /** De-initialize an array of GCPs (initialized with GDALInitGCPs())
    1497             :  *
    1498             :  * @param nCount number of GCPs in psGCP
    1499             :  * @param psGCP array of GCPs of size nCount.
    1500             :  */
    1501        1331 : void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP)
    1502             : 
    1503             : {
    1504        1331 :     if (nCount > 0)
    1505             :     {
    1506         429 :         VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs");
    1507             :     }
    1508             : 
    1509        6090 :     for (int iGCP = 0; iGCP < nCount; iGCP++)
    1510             :     {
    1511        4759 :         CPLFree(psGCP->pszId);
    1512        4759 :         CPLFree(psGCP->pszInfo);
    1513        4759 :         psGCP++;
    1514             :     }
    1515             : }
    1516             : 
    1517             : /************************************************************************/
    1518             : /*                         GDALDuplicateGCPs()                          */
    1519             : /************************************************************************/
    1520             : 
    1521             : /** Duplicate an array of GCPs
    1522             :  *
    1523             :  * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
    1524             :  *
    1525             :  * @param nCount number of GCPs in psGCP
    1526             :  * @param pasGCPList array of GCPs of size nCount.
    1527             :  */
    1528         723 : GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList)
    1529             : 
    1530             : {
    1531             :     GDAL_GCP *pasReturn =
    1532         723 :         static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount));
    1533         723 :     GDALInitGCPs(nCount, pasReturn);
    1534             : 
    1535        3952 :     for (int iGCP = 0; iGCP < nCount; iGCP++)
    1536             :     {
    1537        3229 :         CPLFree(pasReturn[iGCP].pszId);
    1538        3229 :         pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId);
    1539             : 
    1540        3229 :         CPLFree(pasReturn[iGCP].pszInfo);
    1541        3229 :         pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo);
    1542             : 
    1543        3229 :         pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
    1544        3229 :         pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
    1545        3229 :         pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
    1546        3229 :         pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
    1547        3229 :         pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
    1548             :     }
    1549             : 
    1550         723 :     return pasReturn;
    1551             : }
    1552             : 
    1553             : /************************************************************************/
    1554             : /*                       GDALFindAssociatedFile()                       */
    1555             : /************************************************************************/
    1556             : 
    1557             : /**
    1558             :  * \brief Find file with alternate extension.
    1559             :  *
    1560             :  * Finds the file with the indicated extension, substituting it in place
    1561             :  * of the extension of the base filename.  Generally used to search for
    1562             :  * associated files like world files .RPB files, etc.  If necessary, the
    1563             :  * extension will be tried in both upper and lower case.  If a sibling file
    1564             :  * list is available it will be used instead of doing VSIStatExL() calls to
    1565             :  * probe the file system.
    1566             :  *
    1567             :  * Note that the result is a dynamic CPLString so this method should not
    1568             :  * be used in a situation where there could be cross heap issues.  It is
    1569             :  * generally imprudent for application built on GDAL to use this function
    1570             :  * unless they are sure they will always use the same runtime heap as GDAL.
    1571             :  *
    1572             :  * @param pszBaseFilename the filename relative to which to search.
    1573             :  * @param pszExt the target extension in either upper or lower case.
    1574             :  * @param papszSiblingFiles the list of files in the same directory as
    1575             :  * pszBaseFilename or NULL if they are not known.
    1576             :  * @param nFlags special options controlling search.  None defined yet, just
    1577             :  * pass 0.
    1578             :  *
    1579             :  * @return an empty string if the target is not found, otherwise the target
    1580             :  * file with similar path style as the pszBaseFilename.
    1581             :  */
    1582             : 
    1583             : /**/
    1584             : /**/
    1585             : 
    1586       35401 : CPLString GDALFindAssociatedFile(const char *pszBaseFilename,
    1587             :                                  const char *pszExt,
    1588             :                                  CSLConstList papszSiblingFiles,
    1589             :                                  CPL_UNUSED int nFlags)
    1590             : 
    1591             : {
    1592       70802 :     CPLString osTarget = CPLResetExtension(pszBaseFilename, pszExt);
    1593             : 
    1594       70594 :     if (papszSiblingFiles == nullptr ||
    1595             :         // cppcheck-suppress knownConditionTrueFalse
    1596       35193 :         !GDALCanReliablyUseSiblingFileList(osTarget.c_str()))
    1597             :     {
    1598             :         VSIStatBufL sStatBuf;
    1599             : 
    1600         208 :         if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
    1601             :         {
    1602         193 :             CPLString osAltExt = pszExt;
    1603             : 
    1604         193 :             if (islower(static_cast<unsigned char>(pszExt[0])))
    1605           0 :                 osAltExt.toupper();
    1606             :             else
    1607         193 :                 osAltExt.tolower();
    1608             : 
    1609         193 :             osTarget = CPLResetExtension(pszBaseFilename, osAltExt);
    1610             : 
    1611         193 :             if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
    1612         191 :                 return "";
    1613             :         }
    1614             :     }
    1615             :     else
    1616             :     {
    1617             :         const int iSibling =
    1618       35193 :             CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget));
    1619       35193 :         if (iSibling < 0)
    1620       35142 :             return "";
    1621             : 
    1622          51 :         osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
    1623          51 :         osTarget += papszSiblingFiles[iSibling];
    1624             :     }
    1625             : 
    1626          68 :     return osTarget;
    1627             : }
    1628             : 
    1629             : /************************************************************************/
    1630             : /*                         GDALLoadOziMapFile()                         */
    1631             : /************************************************************************/
    1632             : 
    1633             : /** Helper function for translator implementer wanting support for OZI .map
    1634             :  *
    1635             :  * @param pszFilename filename of .tab file
    1636             :  * @param padfGeoTransform output geotransform. Must hold 6 doubles.
    1637             :  * @param ppszWKT output pointer to a string that will be allocated with
    1638             :  * CPLMalloc().
    1639             :  * @param pnGCPCount output pointer to GCP count.
    1640             :  * @param ppasGCPs outputer pointer to an array of GCPs.
    1641             :  * @return TRUE in case of success, FALSE otherwise.
    1642             :  */
    1643           0 : int CPL_STDCALL GDALLoadOziMapFile(const char *pszFilename,
    1644             :                                    double *padfGeoTransform, char **ppszWKT,
    1645             :                                    int *pnGCPCount, GDAL_GCP **ppasGCPs)
    1646             : 
    1647             : {
    1648           0 :     VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE);
    1649           0 :     VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE);
    1650           0 :     VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE);
    1651           0 :     VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE);
    1652             : 
    1653           0 :     char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
    1654             : 
    1655           0 :     if (!papszLines)
    1656           0 :         return FALSE;
    1657             : 
    1658           0 :     int nLines = CSLCount(papszLines);
    1659             : 
    1660             :     // Check the OziExplorer Map file signature
    1661           0 :     if (nLines < 5 ||
    1662           0 :         !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version "))
    1663             :     {
    1664           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1665             :                  "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map "
    1666             :                  "format.",
    1667             :                  pszFilename);
    1668           0 :         CSLDestroy(papszLines);
    1669           0 :         return FALSE;
    1670             :     }
    1671             : 
    1672           0 :     OGRSpatialReference oSRS;
    1673           0 :     OGRErr eErr = OGRERR_NONE;
    1674             : 
    1675             :     /* The Map Scale Factor has been introduced recently on the 6th line */
    1676             :     /* and is a trick that is used to just change that line without changing */
    1677             :     /* the rest of the MAP file but providing an imagery that is smaller or
    1678             :      * larger */
    1679             :     /* so we have to correct the pixel/line values read in the .MAP file so they
    1680             :      */
    1681             :     /* match the actual imagery dimension. Well, this is a bad summary of what
    1682             :      */
    1683             :     /* is explained at
    1684             :      * http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
    1685           0 :     double dfMSF = 1;
    1686             : 
    1687           0 :     for (int iLine = 5; iLine < nLines; iLine++)
    1688             :     {
    1689           0 :         if (STARTS_WITH_CI(papszLines[iLine], "MSF,"))
    1690             :         {
    1691           0 :             dfMSF = CPLAtof(papszLines[iLine] + 4);
    1692           0 :             if (dfMSF <= 0.01) /* Suspicious values */
    1693             :             {
    1694           0 :                 CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
    1695           0 :                 dfMSF = 1;
    1696             :             }
    1697             :         }
    1698             :     }
    1699             : 
    1700           0 :     eErr = oSRS.importFromOzi(papszLines);
    1701           0 :     if (eErr == OGRERR_NONE)
    1702             :     {
    1703           0 :         if (ppszWKT != nullptr)
    1704           0 :             oSRS.exportToWkt(ppszWKT);
    1705             :     }
    1706             : 
    1707           0 :     int nCoordinateCount = 0;
    1708             :     // TODO(schwehr): Initialize asGCPs.
    1709             :     GDAL_GCP asGCPs[30];
    1710             : 
    1711             :     // Iterate all lines in the MAP-file
    1712           0 :     for (int iLine = 5; iLine < nLines; iLine++)
    1713             :     {
    1714           0 :         char **papszTok = CSLTokenizeString2(
    1715           0 :             papszLines[iLine], ",",
    1716             :             CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
    1717             : 
    1718           0 :         if (CSLCount(papszTok) < 12)
    1719             :         {
    1720           0 :             CSLDestroy(papszTok);
    1721           0 :             continue;
    1722             :         }
    1723             : 
    1724           0 :         if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") &&
    1725           0 :             !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") &&
    1726             :             nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
    1727             :         {
    1728           0 :             bool bReadOk = false;
    1729           0 :             double dfLon = 0.0;
    1730           0 :             double dfLat = 0.0;
    1731             : 
    1732           0 :             if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") &&
    1733           0 :                 !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], ""))
    1734             :             {
    1735             :                 // Set geographical coordinates of the pixels
    1736           0 :                 dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
    1737           0 :                 dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
    1738           0 :                 if (EQUAL(papszTok[11], "W"))
    1739           0 :                     dfLon = -dfLon;
    1740           0 :                 if (EQUAL(papszTok[8], "S"))
    1741           0 :                     dfLat = -dfLat;
    1742             : 
    1743             :                 // Transform from the geographical coordinates into projected
    1744             :                 // coordinates.
    1745           0 :                 if (eErr == OGRERR_NONE)
    1746             :                 {
    1747           0 :                     OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
    1748             : 
    1749           0 :                     if (poLongLat)
    1750             :                     {
    1751           0 :                         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1752           0 :                         poLongLat->SetAxisMappingStrategy(
    1753             :                             OAMS_TRADITIONAL_GIS_ORDER);
    1754             : 
    1755             :                         OGRCoordinateTransformation *poTransform =
    1756           0 :                             OGRCreateCoordinateTransformation(poLongLat, &oSRS);
    1757           0 :                         if (poTransform)
    1758             :                         {
    1759           0 :                             bReadOk = CPL_TO_BOOL(
    1760             :                                 poTransform->Transform(1, &dfLon, &dfLat));
    1761           0 :                             delete poTransform;
    1762             :                         }
    1763           0 :                         delete poLongLat;
    1764             :                     }
    1765           0 :                 }
    1766             :             }
    1767           0 :             else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], ""))
    1768             :             {
    1769             :                 // Set cartesian coordinates of the pixels.
    1770           0 :                 dfLon = CPLAtofM(papszTok[14]);
    1771           0 :                 dfLat = CPLAtofM(papszTok[15]);
    1772           0 :                 bReadOk = true;
    1773             : 
    1774             :                 // if ( EQUAL(papszTok[16], "S") )
    1775             :                 //     dfLat = -dfLat;
    1776             :             }
    1777             : 
    1778           0 :             if (bReadOk)
    1779             :             {
    1780           0 :                 GDALInitGCPs(1, asGCPs + nCoordinateCount);
    1781             : 
    1782             :                 // Set pixel/line part
    1783           0 :                 asGCPs[nCoordinateCount].dfGCPPixel =
    1784           0 :                     CPLAtofM(papszTok[2]) / dfMSF;
    1785           0 :                 asGCPs[nCoordinateCount].dfGCPLine =
    1786           0 :                     CPLAtofM(papszTok[3]) / dfMSF;
    1787             : 
    1788           0 :                 asGCPs[nCoordinateCount].dfGCPX = dfLon;
    1789           0 :                 asGCPs[nCoordinateCount].dfGCPY = dfLat;
    1790             : 
    1791           0 :                 nCoordinateCount++;
    1792             :             }
    1793             :         }
    1794             : 
    1795           0 :         CSLDestroy(papszTok);
    1796             :     }
    1797             : 
    1798           0 :     CSLDestroy(papszLines);
    1799             : 
    1800           0 :     if (nCoordinateCount == 0)
    1801             :     {
    1802           0 :         CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
    1803             :                  pszFilename);
    1804           0 :         return FALSE;
    1805             :     }
    1806             : 
    1807             :     /* -------------------------------------------------------------------- */
    1808             :     /*      Try to convert the GCPs into a geotransform definition, if      */
    1809             :     /*      possible.  Otherwise we will need to use them as GCPs.          */
    1810             :     /* -------------------------------------------------------------------- */
    1811           0 :     if (!GDALGCPsToGeoTransform(
    1812             :             nCoordinateCount, asGCPs, padfGeoTransform,
    1813           0 :             CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO"))))
    1814             :     {
    1815           0 :         if (pnGCPCount && ppasGCPs)
    1816             :         {
    1817           0 :             CPLDebug(
    1818             :                 "GDAL",
    1819             :                 "GDALLoadOziMapFile(%s) found file, was not able to derive a\n"
    1820             :                 "first order geotransform.  Using points as GCPs.",
    1821             :                 pszFilename);
    1822             : 
    1823           0 :             *ppasGCPs = static_cast<GDAL_GCP *>(
    1824           0 :                 CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
    1825           0 :             memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
    1826           0 :             *pnGCPCount = nCoordinateCount;
    1827             :         }
    1828             :     }
    1829             :     else
    1830             :     {
    1831           0 :         GDALDeinitGCPs(nCoordinateCount, asGCPs);
    1832             :     }
    1833             : 
    1834           0 :     return TRUE;
    1835             : }
    1836             : 
    1837             : /************************************************************************/
    1838             : /*                       GDALReadOziMapFile()                           */
    1839             : /************************************************************************/
    1840             : 
    1841             : /** Helper function for translator implementer wanting support for OZI .map
    1842             :  *
    1843             :  * @param pszBaseFilename filename whose basename will help building the .map
    1844             :  * filename.
    1845             :  * @param padfGeoTransform output geotransform. Must hold 6 doubles.
    1846             :  * @param ppszWKT output pointer to a string that will be allocated with
    1847             :  * CPLMalloc().
    1848             :  * @param pnGCPCount output pointer to GCP count.
    1849             :  * @param ppasGCPs outputer pointer to an array of GCPs.
    1850             :  * @return TRUE in case of success, FALSE otherwise.
    1851             :  */
    1852           0 : int CPL_STDCALL GDALReadOziMapFile(const char *pszBaseFilename,
    1853             :                                    double *padfGeoTransform, char **ppszWKT,
    1854             :                                    int *pnGCPCount, GDAL_GCP **ppasGCPs)
    1855             : 
    1856             : {
    1857             :     /* -------------------------------------------------------------------- */
    1858             :     /*      Try lower case, then upper case.                                */
    1859             :     /* -------------------------------------------------------------------- */
    1860           0 :     const char *pszOzi = CPLResetExtension(pszBaseFilename, "map");
    1861             : 
    1862           0 :     VSILFILE *fpOzi = VSIFOpenL(pszOzi, "rt");
    1863             : 
    1864           0 :     if (fpOzi == nullptr && VSIIsCaseSensitiveFS(pszOzi))
    1865             :     {
    1866           0 :         pszOzi = CPLResetExtension(pszBaseFilename, "MAP");
    1867           0 :         fpOzi = VSIFOpenL(pszOzi, "rt");
    1868             :     }
    1869             : 
    1870           0 :     if (fpOzi == nullptr)
    1871           0 :         return FALSE;
    1872             : 
    1873           0 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
    1874             : 
    1875             :     /* -------------------------------------------------------------------- */
    1876             :     /*      We found the file, now load and parse it.                       */
    1877             :     /* -------------------------------------------------------------------- */
    1878           0 :     return GDALLoadOziMapFile(pszOzi, padfGeoTransform, ppszWKT, pnGCPCount,
    1879           0 :                               ppasGCPs);
    1880             : }
    1881             : 
    1882             : /************************************************************************/
    1883             : /*                         GDALLoadTabFile()                            */
    1884             : /*                                                                      */
    1885             : /************************************************************************/
    1886             : 
    1887             : /** Helper function for translator implementer wanting support for MapInfo
    1888             :  * .tab files.
    1889             :  *
    1890             :  * @param pszFilename filename of .tab
    1891             :  * @param padfGeoTransform output geotransform. Must hold 6 doubles.
    1892             :  * @param ppszWKT output pointer to a string that will be allocated with
    1893             :  * CPLMalloc().
    1894             :  * @param pnGCPCount output pointer to GCP count.
    1895             :  * @param ppasGCPs outputer pointer to an array of GCPs.
    1896             :  * @return TRUE in case of success, FALSE otherwise.
    1897             :  */
    1898          14 : int CPL_STDCALL GDALLoadTabFile(const char *pszFilename,
    1899             :                                 double *padfGeoTransform, char **ppszWKT,
    1900             :                                 int *pnGCPCount, GDAL_GCP **ppasGCPs)
    1901             : 
    1902             : {
    1903          14 :     char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
    1904             : 
    1905          14 :     if (!papszLines)
    1906           0 :         return FALSE;
    1907             : 
    1908          14 :     char **papszTok = nullptr;
    1909          14 :     bool bTypeRasterFound = false;
    1910          14 :     bool bInsideTableDef = false;
    1911          14 :     int nCoordinateCount = 0;
    1912             :     GDAL_GCP asGCPs[256];  // TODO(schwehr): Initialize.
    1913          14 :     const int numLines = CSLCount(papszLines);
    1914             : 
    1915             :     // Iterate all lines in the TAB-file
    1916         196 :     for (int iLine = 0; iLine < numLines; iLine++)
    1917             :     {
    1918         182 :         CSLDestroy(papszTok);
    1919             :         papszTok =
    1920         182 :             CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE);
    1921             : 
    1922         182 :         if (CSLCount(papszTok) < 2)
    1923          28 :             continue;
    1924             : 
    1925             :         // Did we find table definition
    1926         154 :         if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table"))
    1927             :         {
    1928          14 :             bInsideTableDef = TRUE;
    1929             :         }
    1930         140 :         else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")))
    1931             :         {
    1932             :             // Only RASTER-type will be handled
    1933          14 :             if (EQUAL(papszTok[1], "RASTER"))
    1934             :             {
    1935          14 :                 bTypeRasterFound = true;
    1936             :             }
    1937             :             else
    1938             :             {
    1939           0 :                 CSLDestroy(papszTok);
    1940           0 :                 CSLDestroy(papszLines);
    1941           0 :                 return FALSE;
    1942             :             }
    1943             :         }
    1944          84 :         else if (bTypeRasterFound && bInsideTableDef &&
    1945         210 :                  CSLCount(papszTok) > 4 && EQUAL(papszTok[4], "Label") &&
    1946             :                  nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
    1947             :         {
    1948          56 :             GDALInitGCPs(1, asGCPs + nCoordinateCount);
    1949             : 
    1950          56 :             asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
    1951          56 :             asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
    1952          56 :             asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
    1953          56 :             asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
    1954          56 :             if (papszTok[5] != nullptr)
    1955             :             {
    1956          56 :                 CPLFree(asGCPs[nCoordinateCount].pszId);
    1957          56 :                 asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
    1958             :             }
    1959             : 
    1960          56 :             nCoordinateCount++;
    1961             :         }
    1962          70 :         else if (bTypeRasterFound && bInsideTableDef &&
    1963          28 :                  EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr)
    1964             :         {
    1965          28 :             OGRSpatialReference oSRS;
    1966             : 
    1967          14 :             if (oSRS.importFromMICoordSys(papszLines[iLine]) == OGRERR_NONE)
    1968          28 :                 oSRS.exportToWkt(ppszWKT);
    1969             :         }
    1970          70 :         else if (EQUAL(papszTok[0], "Units") && CSLCount(papszTok) > 1 &&
    1971          14 :                  EQUAL(papszTok[1], "degree"))
    1972             :         {
    1973             :             /*
    1974             :             ** If we have units of "degree", but a projected coordinate
    1975             :             ** system we need to convert it to geographic.  See to01_02.TAB.
    1976             :             */
    1977           0 :             if (ppszWKT != nullptr && *ppszWKT != nullptr &&
    1978           0 :                 STARTS_WITH_CI(*ppszWKT, "PROJCS"))
    1979             :             {
    1980           0 :                 OGRSpatialReference oSRS;
    1981           0 :                 oSRS.importFromWkt(*ppszWKT);
    1982             : 
    1983           0 :                 OGRSpatialReference oSRSGeogCS;
    1984           0 :                 oSRSGeogCS.CopyGeogCSFrom(&oSRS);
    1985           0 :                 CPLFree(*ppszWKT);
    1986             : 
    1987           0 :                 oSRSGeogCS.exportToWkt(ppszWKT);
    1988             :             }
    1989             :         }
    1990             :     }
    1991             : 
    1992          14 :     CSLDestroy(papszTok);
    1993          14 :     CSLDestroy(papszLines);
    1994             : 
    1995          14 :     if (nCoordinateCount == 0)
    1996             :     {
    1997           0 :         CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
    1998             :                  pszFilename);
    1999           0 :         return FALSE;
    2000             :     }
    2001             : 
    2002             :     /* -------------------------------------------------------------------- */
    2003             :     /*      Try to convert the GCPs into a geotransform definition, if      */
    2004             :     /*      possible.  Otherwise we will need to use them as GCPs.          */
    2005             :     /* -------------------------------------------------------------------- */
    2006          14 :     if (!GDALGCPsToGeoTransform(
    2007             :             nCoordinateCount, asGCPs, padfGeoTransform,
    2008          14 :             CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO"))))
    2009             :     {
    2010           0 :         if (pnGCPCount && ppasGCPs)
    2011             :         {
    2012           0 :             CPLDebug("GDAL",
    2013             :                      "GDALLoadTabFile(%s) found file, was not able to derive a "
    2014             :                      "first order geotransform.  Using points as GCPs.",
    2015             :                      pszFilename);
    2016             : 
    2017           0 :             *ppasGCPs = static_cast<GDAL_GCP *>(
    2018           0 :                 CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
    2019           0 :             memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
    2020           0 :             *pnGCPCount = nCoordinateCount;
    2021             :         }
    2022             :     }
    2023             :     else
    2024             :     {
    2025          14 :         GDALDeinitGCPs(nCoordinateCount, asGCPs);
    2026             :     }
    2027             : 
    2028          14 :     return TRUE;
    2029             : }
    2030             : 
    2031             : /************************************************************************/
    2032             : /*                         GDALReadTabFile()                            */
    2033             : /************************************************************************/
    2034             : 
    2035             : /** Helper function for translator implementer wanting support for MapInfo
    2036             :  * .tab files.
    2037             :  *
    2038             :  * @param pszBaseFilename filename whose basename will help building the .tab
    2039             :  * filename.
    2040             :  * @param padfGeoTransform output geotransform. Must hold 6 doubles.
    2041             :  * @param ppszWKT output pointer to a string that will be allocated with
    2042             :  * CPLMalloc().
    2043             :  * @param pnGCPCount output pointer to GCP count.
    2044             :  * @param ppasGCPs outputer pointer to an array of GCPs.
    2045             :  * @return TRUE in case of success, FALSE otherwise.
    2046             :  */
    2047           0 : int CPL_STDCALL GDALReadTabFile(const char *pszBaseFilename,
    2048             :                                 double *padfGeoTransform, char **ppszWKT,
    2049             :                                 int *pnGCPCount, GDAL_GCP **ppasGCPs)
    2050             : 
    2051             : {
    2052           0 :     return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT,
    2053           0 :                             pnGCPCount, ppasGCPs, nullptr, nullptr);
    2054             : }
    2055             : 
    2056        3617 : int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform,
    2057             :                      char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs,
    2058             :                      char **papszSiblingFiles, char **ppszTabFileNameOut)
    2059             : {
    2060        3617 :     if (ppszTabFileNameOut)
    2061        3617 :         *ppszTabFileNameOut = nullptr;
    2062             : 
    2063        3617 :     if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
    2064           0 :         return FALSE;
    2065             : 
    2066        3617 :     const char *pszTAB = CPLResetExtension(pszBaseFilename, "tab");
    2067             : 
    2068        7188 :     if (papszSiblingFiles &&
    2069             :         // cppcheck-suppress knownConditionTrueFalse
    2070        3571 :         GDALCanReliablyUseSiblingFileList(pszTAB))
    2071             :     {
    2072        3571 :         int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTAB));
    2073        3571 :         if (iSibling >= 0)
    2074             :         {
    2075          14 :             CPLString osTabFilename = pszBaseFilename;
    2076          28 :             osTabFilename.resize(strlen(pszBaseFilename) -
    2077          14 :                                  strlen(CPLGetFilename(pszBaseFilename)));
    2078          14 :             osTabFilename += papszSiblingFiles[iSibling];
    2079          14 :             if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
    2080          14 :                                 pnGCPCount, ppasGCPs))
    2081             :             {
    2082          14 :                 if (ppszTabFileNameOut)
    2083          14 :                     *ppszTabFileNameOut = CPLStrdup(osTabFilename);
    2084          14 :                 return TRUE;
    2085             :             }
    2086             :         }
    2087        3557 :         return FALSE;
    2088             :     }
    2089             : 
    2090             :     /* -------------------------------------------------------------------- */
    2091             :     /*      Try lower case, then upper case.                                */
    2092             :     /* -------------------------------------------------------------------- */
    2093             : 
    2094          46 :     VSILFILE *fpTAB = VSIFOpenL(pszTAB, "rt");
    2095             : 
    2096          46 :     if (fpTAB == nullptr && VSIIsCaseSensitiveFS(pszTAB))
    2097             :     {
    2098          46 :         pszTAB = CPLResetExtension(pszBaseFilename, "TAB");
    2099          46 :         fpTAB = VSIFOpenL(pszTAB, "rt");
    2100             :     }
    2101             : 
    2102          46 :     if (fpTAB == nullptr)
    2103          46 :         return FALSE;
    2104             : 
    2105           0 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
    2106             : 
    2107             :     /* -------------------------------------------------------------------- */
    2108             :     /*      We found the file, now load and parse it.                       */
    2109             :     /* -------------------------------------------------------------------- */
    2110           0 :     if (GDALLoadTabFile(pszTAB, padfGeoTransform, ppszWKT, pnGCPCount,
    2111           0 :                         ppasGCPs))
    2112             :     {
    2113           0 :         if (ppszTabFileNameOut)
    2114           0 :             *ppszTabFileNameOut = CPLStrdup(pszTAB);
    2115           0 :         return TRUE;
    2116             :     }
    2117           0 :     return FALSE;
    2118             : }
    2119             : 
    2120             : /************************************************************************/
    2121             : /*                         GDALLoadWorldFile()                          */
    2122             : /************************************************************************/
    2123             : 
    2124             : /**
    2125             :  * \brief Read ESRI world file.
    2126             :  *
    2127             :  * This function reads an ESRI style world file, and formats a geotransform
    2128             :  * from its contents.
    2129             :  *
    2130             :  * The world file contains an affine transformation with the parameters
    2131             :  * in a different order than in a geotransform array.
    2132             :  *
    2133             :  * <ul>
    2134             :  * <li> geotransform[1] : width of pixel
    2135             :  * <li> geotransform[4] : rotational coefficient, zero for north up images.
    2136             :  * <li> geotransform[2] : rotational coefficient, zero for north up images.
    2137             :  * <li> geotransform[5] : height of pixel (but negative)
    2138             :  * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
    2139             :  * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
    2140             :  * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
    2141             :  * pixel.
    2142             :  * </ul>
    2143             :  *
    2144             :  * @param pszFilename the world file name.
    2145             :  * @param padfGeoTransform the six double array into which the
    2146             :  * geotransformation should be placed.
    2147             :  *
    2148             :  * @return TRUE on success or FALSE on failure.
    2149             :  */
    2150             : 
    2151          67 : int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
    2152             :                                   double *padfGeoTransform)
    2153             : 
    2154             : {
    2155          67 :     VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
    2156          67 :     VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
    2157             : 
    2158          67 :     char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
    2159             : 
    2160          67 :     if (!papszLines)
    2161           0 :         return FALSE;
    2162             : 
    2163          67 :     double world[6] = {0.0};
    2164             :     // reads the first 6 non-empty lines
    2165          67 :     int nLines = 0;
    2166          67 :     const int nLinesCount = CSLCount(papszLines);
    2167         469 :     for (int i = 0;
    2168         469 :          i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
    2169             :          ++i)
    2170             :     {
    2171         402 :         CPLString line(papszLines[i]);
    2172         402 :         if (line.Trim().empty())
    2173           0 :             continue;
    2174             : 
    2175         402 :         world[nLines] = CPLAtofM(line);
    2176         402 :         ++nLines;
    2177             :     }
    2178             : 
    2179          67 :     if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
    2180          67 :         (world[3] != 0.0 || world[1] != 0.0))
    2181             :     {
    2182          67 :         padfGeoTransform[0] = world[4];
    2183          67 :         padfGeoTransform[1] = world[0];
    2184          67 :         padfGeoTransform[2] = world[2];
    2185          67 :         padfGeoTransform[3] = world[5];
    2186          67 :         padfGeoTransform[4] = world[1];
    2187          67 :         padfGeoTransform[5] = world[3];
    2188             : 
    2189             :         // correct for center of pixel vs. top left of pixel
    2190          67 :         padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
    2191          67 :         padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
    2192          67 :         padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
    2193          67 :         padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
    2194             : 
    2195          67 :         CSLDestroy(papszLines);
    2196             : 
    2197          67 :         return TRUE;
    2198             :     }
    2199             :     else
    2200             :     {
    2201           0 :         CPLDebug("GDAL",
    2202             :                  "GDALLoadWorldFile(%s) found file, but it was corrupt.",
    2203             :                  pszFilename);
    2204           0 :         CSLDestroy(papszLines);
    2205           0 :         return FALSE;
    2206             :     }
    2207             : }
    2208             : 
    2209             : /************************************************************************/
    2210             : /*                         GDALReadWorldFile()                          */
    2211             : /************************************************************************/
    2212             : 
    2213             : /**
    2214             :  * \brief Read ESRI world file.
    2215             :  *
    2216             :  * This function reads an ESRI style world file, and formats a geotransform
    2217             :  * from its contents.  It does the same as GDALLoadWorldFile() function, but
    2218             :  * it will form the filename for the worldfile from the filename of the raster
    2219             :  * file referred and the suggested extension.  If no extension is provided,
    2220             :  * the code will internally try the unix style and windows style world file
    2221             :  * extensions (eg. for .tif these would be .tfw and .tifw).
    2222             :  *
    2223             :  * The world file contains an affine transformation with the parameters
    2224             :  * in a different order than in a geotransform array.
    2225             :  *
    2226             :  * <ul>
    2227             :  * <li> geotransform[1] : width of pixel
    2228             :  * <li> geotransform[4] : rotational coefficient, zero for north up images.
    2229             :  * <li> geotransform[2] : rotational coefficient, zero for north up images.
    2230             :  * <li> geotransform[5] : height of pixel (but negative)
    2231             :  * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
    2232             :  * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
    2233             :  * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
    2234             :  * pixel.
    2235             :  * </ul>
    2236             :  *
    2237             :  * @param pszBaseFilename the target raster file.
    2238             :  * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
    2239             :  * from the pszBaseFilename
    2240             :  * @param padfGeoTransform the six double array into which the
    2241             :  * geotransformation should be placed.
    2242             :  *
    2243             :  * @return TRUE on success or FALSE on failure.
    2244             :  */
    2245             : 
    2246         908 : int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
    2247             :                                   const char *pszExtension,
    2248             :                                   double *padfGeoTransform)
    2249             : 
    2250             : {
    2251         908 :     return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
    2252         908 :                               nullptr, nullptr);
    2253             : }
    2254             : 
    2255       18408 : int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
    2256             :                        double *padfGeoTransform, char **papszSiblingFiles,
    2257             :                        char **ppszWorldFileNameOut)
    2258             : {
    2259       18408 :     VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
    2260       18408 :     VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
    2261             : 
    2262       18408 :     if (ppszWorldFileNameOut)
    2263       16555 :         *ppszWorldFileNameOut = nullptr;
    2264             : 
    2265       18408 :     if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
    2266         167 :         return FALSE;
    2267             : 
    2268             :     /* -------------------------------------------------------------------- */
    2269             :     /*      If we aren't given an extension, try both the unix and          */
    2270             :     /*      windows style extensions.                                       */
    2271             :     /* -------------------------------------------------------------------- */
    2272       18241 :     if (pszExtension == nullptr)
    2273             :     {
    2274        8680 :         const std::string oBaseExt = CPLGetExtension(pszBaseFilename);
    2275             : 
    2276        4340 :         if (oBaseExt.length() < 2)
    2277         217 :             return FALSE;
    2278             : 
    2279             :         // windows version - first + last + 'w'
    2280        4123 :         char szDerivedExtension[100] = {'\0'};
    2281        4123 :         szDerivedExtension[0] = oBaseExt[0];
    2282        4123 :         szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
    2283        4123 :         szDerivedExtension[2] = 'w';
    2284        4123 :         szDerivedExtension[3] = '\0';
    2285             : 
    2286        4123 :         if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
    2287             :                                padfGeoTransform, papszSiblingFiles,
    2288        4123 :                                ppszWorldFileNameOut))
    2289          52 :             return TRUE;
    2290             : 
    2291             :         // unix version - extension + 'w'
    2292        4071 :         if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
    2293           0 :             return FALSE;
    2294             : 
    2295        4071 :         snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
    2296             :                  oBaseExt.c_str());
    2297        4071 :         return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
    2298             :                                   padfGeoTransform, papszSiblingFiles,
    2299        4071 :                                   ppszWorldFileNameOut);
    2300             :     }
    2301             : 
    2302             :     /* -------------------------------------------------------------------- */
    2303             :     /*      Skip the leading period in the extension if there is one.       */
    2304             :     /* -------------------------------------------------------------------- */
    2305       13901 :     if (*pszExtension == '.')
    2306         991 :         pszExtension++;
    2307             : 
    2308             :     /* -------------------------------------------------------------------- */
    2309             :     /*      Generate upper and lower case versions of the extension.        */
    2310             :     /* -------------------------------------------------------------------- */
    2311       13901 :     char szExtUpper[32] = {'\0'};
    2312       13901 :     char szExtLower[32] = {'\0'};
    2313       13901 :     CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
    2314       13901 :     CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
    2315             : 
    2316       59715 :     for (int i = 0; szExtUpper[i] != '\0'; i++)
    2317             :     {
    2318       45814 :         szExtUpper[i] = static_cast<char>(
    2319       45814 :             CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
    2320       45814 :         szExtLower[i] = static_cast<char>(
    2321       45814 :             CPLTolower(static_cast<unsigned char>(szExtLower[i])));
    2322             :     }
    2323             : 
    2324       13901 :     const char *pszTFW = CPLResetExtension(pszBaseFilename, szExtLower);
    2325             : 
    2326       26661 :     if (papszSiblingFiles &&
    2327             :         // cppcheck-suppress knownConditionTrueFalse
    2328       12760 :         GDALCanReliablyUseSiblingFileList(pszTFW))
    2329             :     {
    2330             :         const int iSibling =
    2331       12760 :             CSLFindString(papszSiblingFiles, CPLGetFilename(pszTFW));
    2332       12760 :         if (iSibling >= 0)
    2333             :         {
    2334          65 :             CPLString osTFWFilename = pszBaseFilename;
    2335         130 :             osTFWFilename.resize(strlen(pszBaseFilename) -
    2336          65 :                                  strlen(CPLGetFilename(pszBaseFilename)));
    2337          65 :             osTFWFilename += papszSiblingFiles[iSibling];
    2338          65 :             if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
    2339             :             {
    2340          65 :                 if (ppszWorldFileNameOut)
    2341          62 :                     *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
    2342          65 :                 return TRUE;
    2343             :             }
    2344             :         }
    2345       12695 :         return FALSE;
    2346             :     }
    2347             : 
    2348             :     /* -------------------------------------------------------------------- */
    2349             :     /*      Try lower case, then upper case.                                */
    2350             :     /* -------------------------------------------------------------------- */
    2351             : 
    2352             :     VSIStatBufL sStatBuf;
    2353        1141 :     bool bGotTFW = VSIStatExL(pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
    2354             : 
    2355        1141 :     if (!bGotTFW && VSIIsCaseSensitiveFS(pszTFW))
    2356             :     {
    2357        1139 :         pszTFW = CPLResetExtension(pszBaseFilename, szExtUpper);
    2358        1139 :         bGotTFW = VSIStatExL(pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
    2359             :     }
    2360             : 
    2361        1141 :     if (!bGotTFW)
    2362        1139 :         return FALSE;
    2363             : 
    2364             :     /* -------------------------------------------------------------------- */
    2365             :     /*      We found the file, now load and parse it.                       */
    2366             :     /* -------------------------------------------------------------------- */
    2367           2 :     if (GDALLoadWorldFile(pszTFW, padfGeoTransform))
    2368             :     {
    2369           2 :         if (ppszWorldFileNameOut)
    2370           1 :             *ppszWorldFileNameOut = CPLStrdup(pszTFW);
    2371           2 :         return TRUE;
    2372             :     }
    2373           0 :     return FALSE;
    2374             : }
    2375             : 
    2376             : /************************************************************************/
    2377             : /*                         GDALWriteWorldFile()                         */
    2378             : /*                                                                      */
    2379             : /*      Helper function for translator implementer wanting              */
    2380             : /*      support for ESRI world files.                                   */
    2381             : /************************************************************************/
    2382             : 
    2383             : /**
    2384             :  * \brief Write ESRI world file.
    2385             :  *
    2386             :  * This function writes an ESRI style world file from the passed geotransform.
    2387             :  *
    2388             :  * The world file contains an affine transformation with the parameters
    2389             :  * in a different order than in a geotransform array.
    2390             :  *
    2391             :  * <ul>
    2392             :  * <li> geotransform[1] : width of pixel
    2393             :  * <li> geotransform[4] : rotational coefficient, zero for north up images.
    2394             :  * <li> geotransform[2] : rotational coefficient, zero for north up images.
    2395             :  * <li> geotransform[5] : height of pixel (but negative)
    2396             :  * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
    2397             :  * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
    2398             :  * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
    2399             :  * pixel.
    2400             :  * </ul>
    2401             :  *
    2402             :  * @param pszBaseFilename the target raster file.
    2403             :  * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
    2404             :  * @param padfGeoTransform the six double array from which the
    2405             :  * geotransformation should be read.
    2406             :  *
    2407             :  * @return TRUE on success or FALSE on failure.
    2408             :  */
    2409             : 
    2410          13 : int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
    2411             :                                    const char *pszExtension,
    2412             :                                    double *padfGeoTransform)
    2413             : 
    2414             : {
    2415          13 :     VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
    2416          13 :     VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
    2417          13 :     VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
    2418             : 
    2419             :     /* -------------------------------------------------------------------- */
    2420             :     /*      Prepare the text to write to the file.                          */
    2421             :     /* -------------------------------------------------------------------- */
    2422          26 :     CPLString osTFWText;
    2423             : 
    2424             :     osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
    2425          13 :                      padfGeoTransform[1], padfGeoTransform[4],
    2426          13 :                      padfGeoTransform[2], padfGeoTransform[5],
    2427          13 :                      padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
    2428          13 :                          0.5 * padfGeoTransform[2],
    2429          13 :                      padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
    2430          13 :                          0.5 * padfGeoTransform[5]);
    2431             : 
    2432             :     /* -------------------------------------------------------------------- */
    2433             :     /*      Update extension, and write to disk.                            */
    2434             :     /* -------------------------------------------------------------------- */
    2435          13 :     const char *pszTFW = CPLResetExtension(pszBaseFilename, pszExtension);
    2436          13 :     VSILFILE *const fpTFW = VSIFOpenL(pszTFW, "wt");
    2437          13 :     if (fpTFW == nullptr)
    2438           0 :         return FALSE;
    2439             : 
    2440             :     const int bRet =
    2441          13 :         VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
    2442          13 :     if (VSIFCloseL(fpTFW) != 0)
    2443           0 :         return FALSE;
    2444             : 
    2445          13 :     return bRet;
    2446             : }
    2447             : 
    2448             : /************************************************************************/
    2449             : /*                          GDALVersionInfo()                           */
    2450             : /************************************************************************/
    2451             : 
    2452             : /**
    2453             :  * \brief Get runtime version information.
    2454             :  *
    2455             :  * Available pszRequest values:
    2456             :  * <ul>
    2457             :  * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string.  i.e.
    2458             :  * "30603000", e.g for GDAL 3.6.3.0</li>
    2459             :  * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a
    2460             :  * string. i.e. "20230312".</li>
    2461             :  * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li>
    2462             :  * <li> "--version": Returns one line version message suitable for
    2463             :  * use in response to --version requests.  i.e. "GDAL 3.6.3, released
    2464             :  * 2023/03/12"</li>
    2465             :  * <li> "LICENSE": Returns the content of the LICENSE.TXT file from
    2466             :  * the GDAL_DATA directory.
    2467             :  * </li>
    2468             :  * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines
    2469             :  * with information on build time options.</li>
    2470             :  * </ul>
    2471             :  *
    2472             :  * @param pszRequest the type of version info desired, as listed above.
    2473             :  *
    2474             :  * @return an internal string containing the requested information.
    2475             :  */
    2476             : 
    2477        4443 : const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
    2478             : 
    2479             : {
    2480             :     /* -------------------------------------------------------------------- */
    2481             :     /*      Try to capture as much build information as practical.          */
    2482             :     /* -------------------------------------------------------------------- */
    2483        4443 :     if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
    2484             :     {
    2485         994 :         CPLString osBuildInfo;
    2486             : 
    2487             : #define STRINGIFY_HELPER(x) #x
    2488             : #define STRINGIFY(x) STRINGIFY_HELPER(x)
    2489             : 
    2490             : #ifdef ESRI_BUILD
    2491             :         osBuildInfo += "ESRI_BUILD=YES\n";
    2492             : #endif
    2493             : #ifdef PAM_ENABLED
    2494         497 :         osBuildInfo += "PAM_ENABLED=YES\n";
    2495             : #endif
    2496         497 :         osBuildInfo += "OGR_ENABLED=YES\n";  // Deprecated.  Always yes.
    2497             : #ifdef HAVE_CURL
    2498         497 :         osBuildInfo += "CURL_ENABLED=YES\n";
    2499         497 :         osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
    2500             : #endif
    2501             : #ifdef HAVE_GEOS
    2502         497 :         osBuildInfo += "GEOS_ENABLED=YES\n";
    2503             : #ifdef GEOS_CAPI_VERSION
    2504         497 :         osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
    2505             : #endif
    2506             : #endif
    2507             :         osBuildInfo +=
    2508             :             "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
    2509         497 :                 PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
    2510         497 :         osBuildInfo += "PROJ_RUNTIME_VERSION=";
    2511         497 :         osBuildInfo += proj_info().version;
    2512         497 :         osBuildInfo += '\n';
    2513             : 
    2514             : #ifdef __VERSION__
    2515             : #ifdef __clang_version__
    2516             :         osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
    2517             : #elif defined(__GNUC__)
    2518         497 :         osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
    2519             : #elif defined(__INTEL_COMPILER)
    2520             :         osBuildInfo += "COMPILER=" __VERSION__ "\n";
    2521             : #else
    2522             :         // STRINGIFY() as we're not sure if its a int or a string
    2523             :         osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
    2524             : #endif
    2525             : #elif defined(_MSC_FULL_VER)
    2526             :         osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
    2527             : #elif defined(__INTEL_COMPILER)
    2528             :         osBuildInfo +=
    2529             :             "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
    2530             : #endif
    2531             : #ifdef CMAKE_UNITY_BUILD
    2532             :         osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
    2533             : #endif
    2534             : 
    2535             : #undef STRINGIFY_HELPER
    2536             : #undef STRINGIFY
    2537             : 
    2538         497 :         CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
    2539         497 :         CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
    2540         497 :         return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
    2541             :     }
    2542             : 
    2543             :     /* -------------------------------------------------------------------- */
    2544             :     /*      LICENSE is a special case. We try to find and read the          */
    2545             :     /*      LICENSE.TXT file from the GDAL_DATA directory and return it     */
    2546             :     /* -------------------------------------------------------------------- */
    2547        3946 :     if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
    2548             :     {
    2549             :         char *pszResultLicence =
    2550           4 :             reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
    2551           4 :         if (pszResultLicence != nullptr)
    2552             :         {
    2553           0 :             return pszResultLicence;
    2554             :         }
    2555             : 
    2556           4 :         const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT");
    2557           4 :         VSILFILE *fp = nullptr;
    2558             : 
    2559           4 :         if (pszFilename != nullptr)
    2560           4 :             fp = VSIFOpenL(pszFilename, "r");
    2561             : 
    2562           4 :         if (fp != nullptr)
    2563             :         {
    2564           4 :             if (VSIFSeekL(fp, 0, SEEK_END) == 0)
    2565             :             {
    2566             :                 // TODO(schwehr): Handle if VSITellL returns a value too large
    2567             :                 // for size_t.
    2568           4 :                 const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1);
    2569           4 :                 if (VSIFSeekL(fp, SEEK_SET, 0) == 0)
    2570             :                 {
    2571             :                     pszResultLicence =
    2572           4 :                         static_cast<char *>(VSICalloc(1, nLength));
    2573           4 :                     if (pszResultLicence)
    2574           4 :                         CPL_IGNORE_RET_VAL(
    2575           4 :                             VSIFReadL(pszResultLicence, 1, nLength - 1, fp));
    2576             :                 }
    2577             :             }
    2578             : 
    2579           4 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    2580             :         }
    2581             : 
    2582           4 :         if (!pszResultLicence)
    2583             :         {
    2584             :             pszResultLicence =
    2585           0 :                 CPLStrdup("GDAL/OGR is released under the MIT license.\n"
    2586             :                           "The LICENSE.TXT distributed with GDAL/OGR should\n"
    2587             :                           "contain additional details.\n");
    2588             :         }
    2589             : 
    2590           4 :         CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
    2591           4 :         return pszResultLicence;
    2592             :     }
    2593             : 
    2594             :     /* -------------------------------------------------------------------- */
    2595             :     /*      All other strings are fairly small.                             */
    2596             :     /* -------------------------------------------------------------------- */
    2597        7884 :     CPLString osVersionInfo;
    2598             : 
    2599        3942 :     if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM"))
    2600        2426 :         osVersionInfo.Printf("%d", GDAL_VERSION_NUM);
    2601        1516 :     else if (EQUAL(pszRequest, "RELEASE_DATE"))
    2602           1 :         osVersionInfo.Printf("%d", GDAL_RELEASE_DATE);
    2603        1515 :     else if (EQUAL(pszRequest, "RELEASE_NAME"))
    2604        1216 :         osVersionInfo.Printf(GDAL_RELEASE_NAME);
    2605             :     else  // --version
    2606             :     {
    2607             :         osVersionInfo.Printf("GDAL %s, released %d/%02d/%02d",
    2608             :                              GDAL_RELEASE_NAME, GDAL_RELEASE_DATE / 10000,
    2609             :                              (GDAL_RELEASE_DATE % 10000) / 100,
    2610         299 :                              GDAL_RELEASE_DATE % 100);
    2611             : #if defined(__GNUC__) && !defined(__OPTIMIZE__)
    2612             :         // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
    2613             :         // also true for CLang
    2614         299 :         osVersionInfo += " (debug build)";
    2615             : #elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2
    2616             :         // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170
    2617             :         // In release mode, the compiler generates an error if you specify
    2618             :         // _ITERATOR_DEBUG_LEVEL as 2.
    2619             :         osVersionInfo += " (debug build)";
    2620             : #endif
    2621             :     }
    2622             : 
    2623        3942 :     CPLFree(CPLGetTLS(CTLS_VERSIONINFO));  // clear old value.
    2624        3942 :     CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
    2625        3942 :     return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
    2626             : }
    2627             : 
    2628             : /************************************************************************/
    2629             : /*                         GDALCheckVersion()                           */
    2630             : /************************************************************************/
    2631             : 
    2632             : /** Return TRUE if GDAL library version at runtime matches
    2633             :    nVersionMajor.nVersionMinor.
    2634             : 
    2635             :     The purpose of this method is to ensure that calling code will run
    2636             :     with the GDAL version it is compiled for. It is primarily intended
    2637             :     for external plugins.
    2638             : 
    2639             :     @param nVersionMajor Major version to be tested against
    2640             :     @param nVersionMinor Minor version to be tested against
    2641             :     @param pszCallingComponentName If not NULL, in case of version mismatch, the
    2642             :    method will issue a failure mentioning the name of the calling component.
    2643             : 
    2644             :     @return TRUE if GDAL library version at runtime matches
    2645             :     nVersionMajor.nVersionMinor, FALSE otherwise.
    2646             :   */
    2647       25142 : int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
    2648             :                                  const char *pszCallingComponentName)
    2649             : {
    2650       25142 :     if (nVersionMajor == GDAL_VERSION_MAJOR &&
    2651             :         nVersionMinor == GDAL_VERSION_MINOR)
    2652       25142 :         return TRUE;
    2653             : 
    2654           0 :     if (pszCallingComponentName)
    2655             :     {
    2656           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2657             :                  "%s was compiled against GDAL %d.%d, but "
    2658             :                  "the current library version is %d.%d",
    2659             :                  pszCallingComponentName, nVersionMajor, nVersionMinor,
    2660             :                  GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
    2661             :     }
    2662           0 :     return FALSE;
    2663             : }
    2664             : 
    2665             : /************************************************************************/
    2666             : /*                            GDALDecToDMS()                            */
    2667             : /************************************************************************/
    2668             : 
    2669             : /** Translate a decimal degrees value to a DMS string with hemisphere.
    2670             :  */
    2671         500 : const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
    2672             :                                      int nPrecision)
    2673             : 
    2674             : {
    2675         500 :     return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
    2676             : }
    2677             : 
    2678             : /************************************************************************/
    2679             : /*                         GDALPackedDMSToDec()                         */
    2680             : /************************************************************************/
    2681             : 
    2682             : /**
    2683             :  * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
    2684             :  *
    2685             :  * See CPLPackedDMSToDec().
    2686             :  */
    2687             : 
    2688           4 : double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
    2689             : 
    2690             : {
    2691           4 :     return CPLPackedDMSToDec(dfPacked);
    2692             : }
    2693             : 
    2694             : /************************************************************************/
    2695             : /*                         GDALDecToPackedDMS()                         */
    2696             : /************************************************************************/
    2697             : 
    2698             : /**
    2699             :  * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
    2700             :  *
    2701             :  * See CPLDecToPackedDMS().
    2702             :  */
    2703             : 
    2704           4 : double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
    2705             : 
    2706             : {
    2707           4 :     return CPLDecToPackedDMS(dfDec);
    2708             : }
    2709             : 
    2710             : /************************************************************************/
    2711             : /*                       GDALGCPsToGeoTransform()                       */
    2712             : /************************************************************************/
    2713             : 
    2714             : /**
    2715             :  * \brief Generate Geotransform from GCPs.
    2716             :  *
    2717             :  * Given a set of GCPs perform first order fit as a geotransform.
    2718             :  *
    2719             :  * Due to imprecision in the calculations the fit algorithm will often
    2720             :  * return non-zero rotational coefficients even if given perfectly non-rotated
    2721             :  * inputs.  A special case has been implemented for corner corner coordinates
    2722             :  * given in TL, TR, BR, BL order.  So when using this to get a geotransform
    2723             :  * from 4 corner coordinates, pass them in this order.
    2724             :  *
    2725             :  * Starting with GDAL 2.2.2, if bApproxOK = FALSE, the
    2726             :  * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
    2727             :  * set to YES, then bApproxOK will be overridden with TRUE.
    2728             :  * Starting with GDAL 2.2.2, when exact fit is asked, the
    2729             :  * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
    2730             :  * give the maximum error threshold in pixel. The default is 0.25.
    2731             :  *
    2732             :  * @param nGCPCount the number of GCPs being passed in.
    2733             :  * @param pasGCPs the list of GCP structures.
    2734             :  * @param padfGeoTransform the six double array in which the affine
    2735             :  * geotransformation will be returned.
    2736             :  * @param bApproxOK If FALSE the function will fail if the geotransform is not
    2737             :  * essentially an exact fit (within 0.25 pixel) for all GCPs.
    2738             :  *
    2739             :  * @return TRUE on success or FALSE if there aren't enough points to prepare a
    2740             :  * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
    2741             :  * and the fit is poor.
    2742             :  */
    2743             : 
    2744             : // TODO(schwehr): Add consts to args.
    2745         561 : int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
    2746             :                                        double *padfGeoTransform, int bApproxOK)
    2747             : 
    2748             : {
    2749         561 :     double dfPixelThreshold = 0.25;
    2750         561 :     if (!bApproxOK)
    2751             :     {
    2752         551 :         bApproxOK = CPLTestBool(
    2753             :             CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
    2754         551 :         if (!bApproxOK)
    2755             :         {
    2756             :             // coverity[tainted_data]
    2757         551 :             dfPixelThreshold = CPLAtof(CPLGetConfigOption(
    2758             :                 "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25"));
    2759             :         }
    2760             :     }
    2761             : 
    2762             :     /* -------------------------------------------------------------------- */
    2763             :     /*      Recognise a few special cases.                                  */
    2764             :     /* -------------------------------------------------------------------- */
    2765         561 :     if (nGCPCount < 2)
    2766          15 :         return FALSE;
    2767             : 
    2768         546 :     if (nGCPCount == 2)
    2769             :     {
    2770           1 :         if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
    2771           1 :             pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
    2772           0 :             return FALSE;
    2773             : 
    2774           1 :         padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
    2775           1 :                               (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
    2776           1 :         padfGeoTransform[2] = 0.0;
    2777             : 
    2778           1 :         padfGeoTransform[4] = 0.0;
    2779           1 :         padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
    2780           1 :                               (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
    2781             : 
    2782           1 :         padfGeoTransform[0] = pasGCPs[0].dfGCPX -
    2783           1 :                               pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
    2784           1 :                               pasGCPs[0].dfGCPLine * padfGeoTransform[2];
    2785             : 
    2786           1 :         padfGeoTransform[3] = pasGCPs[0].dfGCPY -
    2787           1 :                               pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
    2788           1 :                               pasGCPs[0].dfGCPLine * padfGeoTransform[5];
    2789             : 
    2790           1 :         return TRUE;
    2791             :     }
    2792             : 
    2793             :     /* -------------------------------------------------------------------- */
    2794             :     /*      Special case of 4 corner coordinates of a non-rotated           */
    2795             :     /*      image.  The points must be in TL-TR-BR-BL order for now.        */
    2796             :     /*      This case helps avoid some imprecision in the general           */
    2797             :     /*      calculations.                                                   */
    2798             :     /* -------------------------------------------------------------------- */
    2799         545 :     if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine &&
    2800         254 :         pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine &&
    2801         254 :         pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel &&
    2802         253 :         pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel &&
    2803         253 :         pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine &&
    2804         250 :         pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel &&
    2805         250 :         pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY &&
    2806         224 :         pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY &&
    2807         222 :         pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX &&
    2808         222 :         pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX &&
    2809         222 :         pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY &&
    2810         159 :         pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX)
    2811             :     {
    2812         159 :         padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
    2813         159 :                               (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
    2814         159 :         padfGeoTransform[2] = 0.0;
    2815         159 :         padfGeoTransform[4] = 0.0;
    2816         159 :         padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) /
    2817         159 :                               (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
    2818             : 
    2819         159 :         padfGeoTransform[0] =
    2820         159 :             pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
    2821         159 :         padfGeoTransform[3] =
    2822         159 :             pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
    2823         159 :         return TRUE;
    2824             :     }
    2825             : 
    2826             :     /* -------------------------------------------------------------------- */
    2827             :     /*      Compute source and destination ranges so we can normalize       */
    2828             :     /*      the values to make the least squares computation more stable.   */
    2829             :     /* -------------------------------------------------------------------- */
    2830         386 :     double min_pixel = pasGCPs[0].dfGCPPixel;
    2831         386 :     double max_pixel = pasGCPs[0].dfGCPPixel;
    2832         386 :     double min_line = pasGCPs[0].dfGCPLine;
    2833         386 :     double max_line = pasGCPs[0].dfGCPLine;
    2834         386 :     double min_geox = pasGCPs[0].dfGCPX;
    2835         386 :     double max_geox = pasGCPs[0].dfGCPX;
    2836         386 :     double min_geoy = pasGCPs[0].dfGCPY;
    2837         386 :     double max_geoy = pasGCPs[0].dfGCPY;
    2838             : 
    2839        1647 :     for (int i = 1; i < nGCPCount; ++i)
    2840             :     {
    2841        1261 :         min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
    2842        1261 :         max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
    2843        1261 :         min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
    2844        1261 :         max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
    2845        1261 :         min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
    2846        1261 :         max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
    2847        1261 :         min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
    2848        1261 :         max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
    2849             :     }
    2850             : 
    2851         386 :     double EPS = 1.0e-12;
    2852             : 
    2853         769 :     if (std::abs(max_pixel - min_pixel) < EPS ||
    2854         766 :         std::abs(max_line - min_line) < EPS ||
    2855        1152 :         std::abs(max_geox - min_geox) < EPS ||
    2856         320 :         std::abs(max_geoy - min_geoy) < EPS)
    2857             :     {
    2858          66 :         return FALSE;  // degenerate in at least one dimension.
    2859             :     }
    2860             : 
    2861             :     double pl_normalize[6], geo_normalize[6];
    2862             : 
    2863         320 :     pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
    2864         320 :     pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
    2865         320 :     pl_normalize[2] = 0.0;
    2866         320 :     pl_normalize[3] = -min_line / (max_line - min_line);
    2867         320 :     pl_normalize[4] = 0.0;
    2868         320 :     pl_normalize[5] = 1.0 / (max_line - min_line);
    2869             : 
    2870         320 :     geo_normalize[0] = -min_geox / (max_geox - min_geox);
    2871         320 :     geo_normalize[1] = 1.0 / (max_geox - min_geox);
    2872         320 :     geo_normalize[2] = 0.0;
    2873         320 :     geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
    2874         320 :     geo_normalize[4] = 0.0;
    2875         320 :     geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
    2876             : 
    2877             :     /* -------------------------------------------------------------------- */
    2878             :     /* In the general case, do a least squares error approximation by       */
    2879             :     /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum          */
    2880             :     /* -------------------------------------------------------------------- */
    2881             : 
    2882         320 :     double sum_x = 0.0;
    2883         320 :     double sum_y = 0.0;
    2884         320 :     double sum_xy = 0.0;
    2885         320 :     double sum_xx = 0.0;
    2886         320 :     double sum_yy = 0.0;
    2887         320 :     double sum_Lon = 0.0;
    2888         320 :     double sum_Lonx = 0.0;
    2889         320 :     double sum_Lony = 0.0;
    2890         320 :     double sum_Lat = 0.0;
    2891         320 :     double sum_Latx = 0.0;
    2892         320 :     double sum_Laty = 0.0;
    2893             : 
    2894        1703 :     for (int i = 0; i < nGCPCount; ++i)
    2895             :     {
    2896             :         double pixel, line, geox, geoy;
    2897             : 
    2898        1383 :         GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
    2899        1383 :                               pasGCPs[i].dfGCPLine, &pixel, &line);
    2900        1383 :         GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
    2901        1383 :                               pasGCPs[i].dfGCPY, &geox, &geoy);
    2902             : 
    2903        1383 :         sum_x += pixel;
    2904        1383 :         sum_y += line;
    2905        1383 :         sum_xy += pixel * line;
    2906        1383 :         sum_xx += pixel * pixel;
    2907        1383 :         sum_yy += line * line;
    2908        1383 :         sum_Lon += geox;
    2909        1383 :         sum_Lonx += geox * pixel;
    2910        1383 :         sum_Lony += geox * line;
    2911        1383 :         sum_Lat += geoy;
    2912        1383 :         sum_Latx += geoy * pixel;
    2913        1383 :         sum_Laty += geoy * line;
    2914             :     }
    2915             : 
    2916         320 :     const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
    2917         320 :                            2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
    2918         320 :                            sum_x * sum_x * sum_yy;
    2919             : 
    2920             :     /* -------------------------------------------------------------------- */
    2921             :     /*      If the divisor is zero, there is no valid solution.             */
    2922             :     /* -------------------------------------------------------------------- */
    2923         320 :     if (divisor == 0.0)
    2924           0 :         return FALSE;
    2925             : 
    2926             :     /* -------------------------------------------------------------------- */
    2927             :     /*      Compute top/left origin.                                        */
    2928             :     /* -------------------------------------------------------------------- */
    2929         320 :     double gt_normalized[6] = {0.0};
    2930         320 :     gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
    2931         320 :                         sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
    2932         320 :                         sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
    2933             :                        divisor;
    2934             : 
    2935         320 :     gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
    2936         320 :                         sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
    2937         320 :                         sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
    2938             :                        divisor;
    2939             : 
    2940             :     /* -------------------------------------------------------------------- */
    2941             :     /*      Compute X related coefficients.                                 */
    2942             :     /* -------------------------------------------------------------------- */
    2943         320 :     gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
    2944         320 :                         sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
    2945         320 :                         sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
    2946             :                        divisor;
    2947             : 
    2948         320 :     gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
    2949         320 :                         sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
    2950         320 :                         sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
    2951             :                        divisor;
    2952             : 
    2953             :     /* -------------------------------------------------------------------- */
    2954             :     /*      Compute Y related coefficients.                                 */
    2955             :     /* -------------------------------------------------------------------- */
    2956         320 :     gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
    2957         320 :                         sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
    2958         320 :                         sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
    2959             :                        divisor;
    2960             : 
    2961         320 :     gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
    2962         320 :                         sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
    2963         320 :                         sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
    2964             :                        divisor;
    2965             : 
    2966             :     /* -------------------------------------------------------------------- */
    2967             :     /*      Compose the resulting transformation with the normalization     */
    2968             :     /*      geotransformations.                                             */
    2969             :     /* -------------------------------------------------------------------- */
    2970         320 :     double gt1p2[6] = {0.0};
    2971         320 :     double inv_geo_normalize[6] = {0.0};
    2972         320 :     if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
    2973           0 :         return FALSE;
    2974             : 
    2975         320 :     GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
    2976         320 :     GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
    2977             : 
    2978             :     /* -------------------------------------------------------------------- */
    2979             :     /*      Now check if any of the input points fit this poorly.           */
    2980             :     /* -------------------------------------------------------------------- */
    2981         320 :     if (!bApproxOK)
    2982             :     {
    2983             :         // FIXME? Not sure if it is the more accurate way of computing
    2984             :         // pixel size
    2985             :         double dfPixelSize =
    2986             :             0.5 *
    2987         313 :             (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) +
    2988         313 :              std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5]));
    2989         313 :         if (dfPixelSize == 0.0)
    2990             :         {
    2991           0 :             CPLDebug("GDAL", "dfPixelSize = 0");
    2992           0 :             return FALSE;
    2993             :         }
    2994             : 
    2995        1641 :         for (int i = 0; i < nGCPCount; i++)
    2996             :         {
    2997        1334 :             const double dfErrorX =
    2998        1334 :                 (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
    2999        1334 :                  pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
    3000        1334 :                  padfGeoTransform[0]) -
    3001        1334 :                 pasGCPs[i].dfGCPX;
    3002        1334 :             const double dfErrorY =
    3003        1334 :                 (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
    3004        1334 :                  pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
    3005        1334 :                  padfGeoTransform[3]) -
    3006        1334 :                 pasGCPs[i].dfGCPY;
    3007             : 
    3008        2665 :             if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
    3009        1331 :                 std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
    3010             :             {
    3011           6 :                 CPLDebug("GDAL",
    3012             :                          "dfErrorX/dfPixelSize = %.2f, "
    3013             :                          "dfErrorY/dfPixelSize = %.2f",
    3014           6 :                          std::abs(dfErrorX) / dfPixelSize,
    3015           6 :                          std::abs(dfErrorY) / dfPixelSize);
    3016           6 :                 return FALSE;
    3017             :             }
    3018             :         }
    3019             :     }
    3020             : 
    3021         314 :     return TRUE;
    3022             : }
    3023             : 
    3024             : /************************************************************************/
    3025             : /*                      GDALComposeGeoTransforms()                      */
    3026             : /************************************************************************/
    3027             : 
    3028             : /**
    3029             :  * \brief Compose two geotransforms.
    3030             :  *
    3031             :  * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
    3032             :  * being applied to a point.
    3033             :  *
    3034             :  * @param padfGT1 the first geotransform, six values.
    3035             :  * @param padfGT2 the second geotransform, six values.
    3036             :  * @param padfGTOut the output geotransform, six values, may safely be the same
    3037             :  * array as padfGT1 or padfGT2.
    3038             :  */
    3039             : 
    3040         640 : void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
    3041             :                               double *padfGTOut)
    3042             : 
    3043             : {
    3044         640 :     double gtwrk[6] = {0.0};
    3045             :     // We need to think of the geotransform in a more normal form to do
    3046             :     // the matrix multiple:
    3047             :     //
    3048             :     //  __                     __
    3049             :     //  | gt[1]   gt[2]   gt[0] |
    3050             :     //  | gt[4]   gt[5]   gt[3] |
    3051             :     //  |  0.0     0.0     1.0  |
    3052             :     //  --                     --
    3053             :     //
    3054             :     // Then we can use normal matrix multiplication to produce the
    3055             :     // composed transformation.  I don't actually reform the matrix
    3056             :     // explicitly which is why the following may seem kind of spagettish.
    3057             : 
    3058         640 :     gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
    3059         640 :     gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
    3060         640 :     gtwrk[0] =
    3061         640 :         padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
    3062             : 
    3063         640 :     gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
    3064         640 :     gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
    3065         640 :     gtwrk[3] =
    3066         640 :         padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
    3067         640 :     memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
    3068         640 : }
    3069             : 
    3070             : /************************************************************************/
    3071             : /*                      StripIrrelevantOptions()                        */
    3072             : /************************************************************************/
    3073             : 
    3074           8 : static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
    3075             : {
    3076           8 :     if (psCOL == nullptr)
    3077           0 :         return;
    3078           8 :     if (nOptions == 0)
    3079           5 :         nOptions = GDAL_OF_RASTER;
    3080           8 :     if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
    3081           0 :         return;
    3082             : 
    3083           8 :     CPLXMLNode *psPrev = nullptr;
    3084         173 :     for (CPLXMLNode *psIter = psCOL->psChild; psIter;)
    3085             :     {
    3086         165 :         if (psIter->eType == CXT_Element)
    3087             :         {
    3088         165 :             CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope");
    3089         165 :             bool bStrip = false;
    3090         165 :             if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild &&
    3091          35 :                 psScope->psChild->pszValue &&
    3092          35 :                 EQUAL(psScope->psChild->pszValue, "vector"))
    3093             :             {
    3094           1 :                 bStrip = true;
    3095             :             }
    3096         164 :             else if (nOptions == GDAL_OF_VECTOR && psScope &&
    3097          35 :                      psScope->psChild && psScope->psChild->pszValue &&
    3098          35 :                      EQUAL(psScope->psChild->pszValue, "raster"))
    3099             :             {
    3100          33 :                 bStrip = true;
    3101             :             }
    3102         165 :             if (psScope)
    3103             :             {
    3104          70 :                 CPLRemoveXMLChild(psIter, psScope);
    3105          70 :                 CPLDestroyXMLNode(psScope);
    3106             :             }
    3107             : 
    3108         165 :             CPLXMLNode *psNext = psIter->psNext;
    3109         165 :             if (bStrip)
    3110             :             {
    3111          34 :                 if (psPrev)
    3112          13 :                     psPrev->psNext = psNext;
    3113          21 :                 else if (psCOL->psChild == psIter)
    3114          21 :                     psCOL->psChild = psNext;
    3115          34 :                 psIter->psNext = nullptr;
    3116          34 :                 CPLDestroyXMLNode(psIter);
    3117          34 :                 psIter = psNext;
    3118             :             }
    3119             :             else
    3120             :             {
    3121         131 :                 psPrev = psIter;
    3122         131 :                 psIter = psNext;
    3123             :             }
    3124             :         }
    3125             :         else
    3126             :         {
    3127           0 :             psIter = psIter->psNext;
    3128             :         }
    3129             :     }
    3130             : }
    3131             : 
    3132             : /************************************************************************/
    3133             : /*                    GDALGeneralCmdLineProcessor()                     */
    3134             : /************************************************************************/
    3135             : 
    3136             : /**
    3137             :  * \brief General utility option processing.
    3138             :  *
    3139             :  * This function is intended to provide a variety of generic commandline
    3140             :  * options for all GDAL commandline utilities.  It takes care of the following
    3141             :  * commandline options:
    3142             :  *
    3143             :  *  --version: report version of GDAL in use.
    3144             :  *  --build: report build info about GDAL in use.
    3145             :  *  --license: report GDAL license info.
    3146             :  *  --formats: report all format drivers configured.
    3147             :  *  --format [format]: report details of one format driver.
    3148             :  *  --optfile filename: expand an option file into the argument list.
    3149             :  *  --config key value: set system configuration option.
    3150             :  *  --config key=value: set system configuration option (since GDAL 3.9)
    3151             :  *  --debug [on/off/value]: set debug level.
    3152             :  *  --mempreload dir: preload directory contents into /vsimem
    3153             :  *  --pause: Pause for user input (allows time to attach debugger)
    3154             :  *  --locale [locale]: Install a locale using setlocale() (debugging)
    3155             :  *  --help-general: report detailed help on general options.
    3156             :  *
    3157             :  * The argument array is replaced "in place" and should be freed with
    3158             :  * CSLDestroy() when no longer needed.  The typical usage looks something
    3159             :  * like the following.  Note that the formats should be registered so that
    3160             :  * the --formats and --format options will work properly.
    3161             :  *
    3162             :  *  int main( int argc, char ** argv )
    3163             :  *  {
    3164             :  *    GDALAllRegister();
    3165             :  *
    3166             :  *    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
    3167             :  *    if( argc < 1 )
    3168             :  *        exit( -argc );
    3169             :  *
    3170             :  * @param nArgc number of values in the argument list.
    3171             :  * @param ppapszArgv pointer to the argument list array (will be updated in
    3172             :  * place).
    3173             :  * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
    3174             :  *                 to determine which drivers should be displayed by --formats.
    3175             :  *                 If set to 0, GDAL_OF_RASTER is assumed.
    3176             :  *
    3177             :  * @return updated nArgc argument count.  Return of 0 requests terminate
    3178             :  * without error, return of -1 requests exit with error code.
    3179             :  */
    3180             : 
    3181        1192 : int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
    3182             :                                             int nOptions)
    3183             : 
    3184             : {
    3185        2384 :     CPLStringList aosReturn;
    3186             :     int iArg;
    3187        1192 :     char **papszArgv = *ppapszArgv;
    3188             : 
    3189             :     /* -------------------------------------------------------------------- */
    3190             :     /*      Preserve the program name.                                      */
    3191             :     /* -------------------------------------------------------------------- */
    3192        1192 :     aosReturn.AddString(papszArgv[0]);
    3193             : 
    3194             :     /* ==================================================================== */
    3195             :     /*      Loop over all arguments.                                        */
    3196             :     /* ==================================================================== */
    3197        7282 :     for (iArg = 1; iArg < nArgc; iArg++)
    3198             :     {
    3199             :         /* --------------------------------------------------------------------
    3200             :          */
    3201             :         /*      --version */
    3202             :         /* --------------------------------------------------------------------
    3203             :          */
    3204        6134 :         if (EQUAL(papszArgv[iArg], "--version"))
    3205             :         {
    3206          19 :             printf("%s\n", GDALVersionInfo("--version")); /*ok*/
    3207          19 :             return 0;
    3208             :         }
    3209             : 
    3210             :         /* --------------------------------------------------------------------
    3211             :          */
    3212             :         /*      --build */
    3213             :         /* --------------------------------------------------------------------
    3214             :          */
    3215        6115 :         else if (EQUAL(papszArgv[iArg], "--build"))
    3216             :         {
    3217           1 :             printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
    3218           1 :             return 0;
    3219             :         }
    3220             : 
    3221             :         /* --------------------------------------------------------------------
    3222             :          */
    3223             :         /*      --license */
    3224             :         /* --------------------------------------------------------------------
    3225             :          */
    3226        6114 :         else if (EQUAL(papszArgv[iArg], "--license"))
    3227             :         {
    3228           1 :             printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
    3229           1 :             return 0;
    3230             :         }
    3231             : 
    3232             :         /* --------------------------------------------------------------------
    3233             :          */
    3234             :         /*      --config */
    3235             :         /* --------------------------------------------------------------------
    3236             :          */
    3237        6113 :         else if (EQUAL(papszArgv[iArg], "--config"))
    3238             :         {
    3239          43 :             if (iArg + 1 >= nArgc)
    3240             :             {
    3241           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3242             :                          "--config option given without a key=value argument.");
    3243           2 :                 return -1;
    3244             :             }
    3245             : 
    3246          41 :             const char *pszArg = papszArgv[iArg + 1];
    3247          41 :             if (strchr(pszArg, '=') != nullptr)
    3248             :             {
    3249           1 :                 char *pszKey = nullptr;
    3250           1 :                 const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
    3251           1 :                 if (pszKey && pszValue)
    3252             :                 {
    3253           1 :                     CPLSetConfigOption(pszKey, pszValue);
    3254             :                 }
    3255           1 :                 CPLFree(pszKey);
    3256           1 :                 ++iArg;
    3257             :             }
    3258             :             else
    3259             :             {
    3260          40 :                 if (iArg + 2 >= nArgc)
    3261             :                 {
    3262           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3263             :                              "--config option given without a key and value "
    3264             :                              "argument.");
    3265           2 :                     return -1;
    3266             :                 }
    3267             : 
    3268          38 :                 CPLSetConfigOption(papszArgv[iArg + 1], papszArgv[iArg + 2]);
    3269             : 
    3270          38 :                 iArg += 2;
    3271             :             }
    3272             :         }
    3273             : 
    3274             :         /* --------------------------------------------------------------------
    3275             :          */
    3276             :         /*      --mempreload */
    3277             :         /* --------------------------------------------------------------------
    3278             :          */
    3279        6070 :         else if (EQUAL(papszArgv[iArg], "--mempreload"))
    3280             :         {
    3281           4 :             if (iArg + 1 >= nArgc)
    3282             :             {
    3283           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3284             :                          "--mempreload option given without directory path.");
    3285           2 :                 return -1;
    3286             :             }
    3287             : 
    3288           2 :             char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
    3289           2 :             if (CSLCount(papszFiles) == 0)
    3290             :             {
    3291           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3292             :                          "--mempreload given invalid or empty directory.");
    3293           0 :                 return -1;
    3294             :             }
    3295             : 
    3296         488 :             for (int i = 0; papszFiles[i] != nullptr; i++)
    3297             :             {
    3298         486 :                 if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
    3299          71 :                     continue;
    3300             : 
    3301         482 :                 CPLString osOldPath, osNewPath;
    3302         482 :                 osOldPath = CPLFormFilename(papszArgv[iArg + 1], papszFiles[i],
    3303         482 :                                             nullptr);
    3304         482 :                 osNewPath.Printf("/vsimem/%s", papszFiles[i]);
    3305             : 
    3306             :                 VSIStatBufL sStatBuf;
    3307         964 :                 if (VSIStatL(osOldPath, &sStatBuf) != 0 ||
    3308         482 :                     VSI_ISDIR(sStatBuf.st_mode))
    3309             :                 {
    3310          67 :                     CPLDebug("VSI", "Skipping preload of %s.",
    3311             :                              osOldPath.c_str());
    3312          67 :                     continue;
    3313             :                 }
    3314             : 
    3315         415 :                 CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
    3316             :                          osNewPath.c_str());
    3317             : 
    3318         415 :                 if (CPLCopyFile(osNewPath, osOldPath) != 0)
    3319             :                 {
    3320           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3321             :                              "Failed to copy %s to /vsimem", osOldPath.c_str());
    3322           0 :                     return -1;
    3323             :                 }
    3324             :             }
    3325             : 
    3326           2 :             CSLDestroy(papszFiles);
    3327           2 :             iArg += 1;
    3328             :         }
    3329             : 
    3330             :         /* --------------------------------------------------------------------
    3331             :          */
    3332             :         /*      --debug */
    3333             :         /* --------------------------------------------------------------------
    3334             :          */
    3335        6066 :         else if (EQUAL(papszArgv[iArg], "--debug"))
    3336             :         {
    3337          15 :             if (iArg + 1 >= nArgc)
    3338             :             {
    3339           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3340             :                          "--debug option given without debug level.");
    3341           2 :                 return -1;
    3342             :             }
    3343             : 
    3344          13 :             CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
    3345          13 :             iArg += 1;
    3346             :         }
    3347             : 
    3348             :         /* --------------------------------------------------------------------
    3349             :          */
    3350             :         /*      --optfile */
    3351             :         /* --------------------------------------------------------------------
    3352             :          */
    3353        6051 :         else if (EQUAL(papszArgv[iArg], "--optfile"))
    3354             :         {
    3355          11 :             if (iArg + 1 >= nArgc)
    3356             :             {
    3357           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3358             :                          "--optfile option given without filename.");
    3359           5 :                 return -1;
    3360             :             }
    3361             : 
    3362           9 :             VSILFILE *fpOptFile = VSIFOpenL(papszArgv[iArg + 1], "rb");
    3363             : 
    3364           9 :             if (fpOptFile == nullptr)
    3365             :             {
    3366           4 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3367             :                          "Unable to open optfile '%s'.\n%s",
    3368           2 :                          papszArgv[iArg + 1], VSIStrerror(errno));
    3369           2 :                 return -1;
    3370             :             }
    3371             : 
    3372             :             const char *pszLine;
    3373           7 :             CPLStringList aosArgvOptfile;
    3374             :             // dummy value as first argument to please
    3375             :             // GDALGeneralCmdLineProcessor()
    3376           7 :             aosArgvOptfile.AddString("");
    3377           7 :             bool bHasOptfile = false;
    3378          23 :             while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
    3379             :             {
    3380          16 :                 if (pszLine[0] == '#' || strlen(pszLine) == 0)
    3381           3 :                     continue;
    3382             : 
    3383          13 :                 char **papszTokens = CSLTokenizeString(pszLine);
    3384          13 :                 for (int i = 0;
    3385          45 :                      papszTokens != nullptr && papszTokens[i] != nullptr; i++)
    3386             :                 {
    3387          32 :                     if (EQUAL(papszTokens[i], "--optfile"))
    3388             :                     {
    3389             :                         // To avoid potential recursion
    3390           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    3391             :                                  "--optfile not supported in a option file");
    3392           0 :                         bHasOptfile = true;
    3393             :                     }
    3394          32 :                     aosArgvOptfile.AddStringDirectly(papszTokens[i]);
    3395          32 :                     papszTokens[i] = nullptr;
    3396             :                 }
    3397          13 :                 CSLDestroy(papszTokens);
    3398             :             }
    3399             : 
    3400           7 :             VSIFCloseL(fpOptFile);
    3401             : 
    3402           7 :             char **papszArgvOptfile = aosArgvOptfile.StealList();
    3403           7 :             if (!bHasOptfile)
    3404             :             {
    3405           7 :                 char **papszArgvOptfileBefore = papszArgvOptfile;
    3406           7 :                 if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
    3407             :                                                 &papszArgvOptfile,
    3408           7 :                                                 nOptions) < 0)
    3409             :                 {
    3410           1 :                     CSLDestroy(papszArgvOptfile);
    3411           1 :                     return -1;
    3412             :                 }
    3413           6 :                 CSLDestroy(papszArgvOptfileBefore);
    3414             :             }
    3415             : 
    3416           6 :             char **papszIter = papszArgvOptfile + 1;
    3417          36 :             while (*papszIter)
    3418             :             {
    3419          30 :                 aosReturn.AddString(*papszIter);
    3420          30 :                 ++papszIter;
    3421             :             }
    3422           6 :             CSLDestroy(papszArgvOptfile);
    3423             : 
    3424           6 :             iArg += 1;
    3425             :         }
    3426             : 
    3427             :         /* --------------------------------------------------------------------
    3428             :          */
    3429             :         /*      --formats */
    3430             :         /* --------------------------------------------------------------------
    3431             :          */
    3432        6040 :         else if (EQUAL(papszArgv[iArg], "--formats"))
    3433             :         {
    3434           3 :             if (nOptions == 0)
    3435           1 :                 nOptions = GDAL_OF_RASTER;
    3436             : 
    3437           3 :             printf(/*ok*/
    3438             :                    "Supported Formats: (ro:read-only, rw:read-write, +:update, "
    3439             :                    "v:virtual-I/O s:subdatasets)\n");
    3440         702 :             for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
    3441             :             {
    3442         699 :                 GDALDriverH hDriver = GDALGetDriver(iDr);
    3443             : 
    3444         699 :                 const char *pszRFlag = "", *pszWFlag, *pszVirtualIO,
    3445             :                            *pszSubdatasets;
    3446         699 :                 char **papszMD = GDALGetMetadata(hDriver, nullptr);
    3447             : 
    3448         932 :                 if (nOptions == GDAL_OF_RASTER &&
    3449         233 :                     !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
    3450         359 :                     continue;
    3451        1092 :                 if (nOptions == GDAL_OF_VECTOR &&
    3452         466 :                     !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
    3453         286 :                     continue;
    3454         340 :                 if (nOptions == GDAL_OF_GNM &&
    3455           0 :                     !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
    3456           0 :                     continue;
    3457         340 :                 if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
    3458           0 :                     !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
    3459           0 :                     continue;
    3460             : 
    3461         340 :                 if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
    3462         337 :                     pszRFlag = "r";
    3463             : 
    3464         340 :                 if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
    3465         171 :                     pszWFlag = "w+";
    3466         169 :                 else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
    3467          35 :                     pszWFlag = "w";
    3468             :                 else
    3469         134 :                     pszWFlag = "o";
    3470             : 
    3471         340 :                 if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
    3472         275 :                     pszVirtualIO = "v";
    3473             :                 else
    3474          65 :                     pszVirtualIO = "";
    3475             : 
    3476         340 :                 if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
    3477          44 :                     pszSubdatasets = "s";
    3478             :                 else
    3479         296 :                     pszSubdatasets = "";
    3480             : 
    3481         680 :                 CPLString osKind;
    3482         340 :                 if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
    3483         198 :                     osKind = "raster";
    3484         340 :                 if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
    3485             :                 {
    3486          18 :                     if (!osKind.empty())
    3487          18 :                         osKind += ',';
    3488          18 :                     osKind += "multidimensional raster";
    3489             :                 }
    3490         340 :                 if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
    3491             :                 {
    3492         199 :                     if (!osKind.empty())
    3493          57 :                         osKind += ',';
    3494         199 :                     osKind += "vector";
    3495             :                 }
    3496         340 :                 if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
    3497             :                 {
    3498           0 :                     if (!osKind.empty())
    3499           0 :                         osKind += ',';
    3500           0 :                     osKind += "geography network";
    3501             :                 }
    3502         340 :                 if (osKind.empty())
    3503           0 :                     osKind = "unknown kind";
    3504             : 
    3505         680 :                 std::string osExtensions;
    3506         340 :                 if (const char *pszExtensions = CSLFetchNameValueDef(
    3507             :                         papszMD, GDAL_DMD_EXTENSIONS,
    3508             :                         CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
    3509             :                 {
    3510             :                     const CPLStringList aosExt(
    3511         452 :                         CSLTokenizeString2(pszExtensions, " ", 0));
    3512         542 :                     for (int i = 0; i < aosExt.size(); ++i)
    3513             :                     {
    3514         316 :                         if (i == 0)
    3515         224 :                             osExtensions = " (*.";
    3516             :                         else
    3517          92 :                             osExtensions += ", *.";
    3518         316 :                         osExtensions += aosExt[i];
    3519             :                     }
    3520         226 :                     if (!osExtensions.empty())
    3521         224 :                         osExtensions += ')';
    3522             :                 }
    3523             : 
    3524         340 :                 printf("  %s -%s- (%s%s%s%s): %s%s\n", /*ok*/
    3525             :                        GDALGetDriverShortName(hDriver), osKind.c_str(),
    3526             :                        pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets,
    3527             :                        GDALGetDriverLongName(hDriver), osExtensions.c_str());
    3528             :             }
    3529             : 
    3530           3 :             return 0;
    3531             :         }
    3532             : 
    3533             :         /* --------------------------------------------------------------------
    3534             :          */
    3535             :         /*      --format */
    3536             :         /* --------------------------------------------------------------------
    3537             :          */
    3538        6037 :         else if (EQUAL(papszArgv[iArg], "--format"))
    3539             :         {
    3540             :             GDALDriverH hDriver;
    3541             :             char **papszMD;
    3542             : 
    3543           5 :             if (iArg + 1 >= nArgc)
    3544             :             {
    3545           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3546             :                          "--format option given without a format code.");
    3547           1 :                 return -1;
    3548             :             }
    3549             : 
    3550           4 :             hDriver = GDALGetDriverByName(papszArgv[iArg + 1]);
    3551           4 :             if (hDriver == nullptr)
    3552             :             {
    3553           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3554             :                          "--format option given with format '%s', but that "
    3555             :                          "format not\nrecognised.  Use the --formats option "
    3556             :                          "to get a list of available formats,\n"
    3557             :                          "and use the short code (i.e. GTiff or HFA) as the "
    3558             :                          "format identifier.\n",
    3559           1 :                          papszArgv[iArg + 1]);
    3560           1 :                 return -1;
    3561             :             }
    3562             : 
    3563           3 :             printf("Format Details:\n"); /*ok*/
    3564           3 :             printf(/*ok*/ "  Short Name: %s\n",
    3565             :                    GDALGetDriverShortName(hDriver));
    3566           3 :             printf(/*ok*/ "  Long Name: %s\n", GDALGetDriverLongName(hDriver));
    3567             : 
    3568           3 :             papszMD = GDALGetMetadata(hDriver, nullptr);
    3569           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
    3570           3 :                 printf("  Supports: Raster\n"); /*ok*/
    3571           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
    3572           0 :                 printf("  Supports: Multidimensional raster\n"); /*ok*/
    3573           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
    3574           2 :                 printf("  Supports: Vector\n"); /*ok*/
    3575           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
    3576           0 :                 printf("  Supports: Geography Network\n"); /*ok*/
    3577             : 
    3578             :             const char *pszExt =
    3579           3 :                 CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
    3580           3 :             if (pszExt != nullptr)
    3581           3 :                 printf("  Extension%s: %s\n", /*ok*/
    3582           3 :                        (strchr(pszExt, ' ') ? "s" : ""), pszExt);
    3583             : 
    3584           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
    3585           1 :                 printf("  Mime Type: %s\n", /*ok*/
    3586             :                        CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
    3587           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
    3588           3 :                 printf("  Help Topic: %s\n", /*ok*/
    3589             :                        CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
    3590             : 
    3591           3 :             if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
    3592           3 :                 printf("  Supports: Raster subdatasets\n"); /*ok*/
    3593           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
    3594           3 :                 printf("  Supports: Open() - Open existing dataset.\n"); /*ok*/
    3595           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
    3596           3 :                 printf(/*ok*/
    3597             :                        "  Supports: Create() - Create writable dataset.\n");
    3598           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false))
    3599           0 :                 printf(/*ok*/ "  Supports: CreateMultiDimensional() - Create "
    3600             :                               "multidimensional dataset.\n");
    3601           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
    3602           3 :                 printf(/*ok*/ "  Supports: CreateCopy() - Create dataset by "
    3603             :                               "copying "
    3604             :                               "another.\n");
    3605           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
    3606           3 :                 printf("  Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/
    3607           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES))
    3608           3 :                 printf("  Creation Datatypes: %s\n", /*ok*/
    3609             :                        CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES));
    3610           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES))
    3611           2 :                 printf("  Creation Field Datatypes: %s\n", /*ok*/
    3612             :                        CSLFetchNameValue(papszMD,
    3613             :                                          GDAL_DMD_CREATIONFIELDDATATYPES));
    3614           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES))
    3615           2 :                 printf("  Creation Field Data Sub-types: %s\n", /*ok*/
    3616             :                        CSLFetchNameValue(papszMD,
    3617             :                                          GDAL_DMD_CREATIONFIELDDATASUBTYPES));
    3618           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false))
    3619           2 :                 printf(/*ok*/ "  Supports: Creating fields with NOT NULL "
    3620             :                               "constraint.\n");
    3621           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false))
    3622           2 :                 printf(/*ok*/
    3623             :                        "  Supports: Creating fields with UNIQUE constraint.\n");
    3624           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false))
    3625           2 :                 printf(/*ok*/
    3626             :                        "  Supports: Creating fields with DEFAULT values.\n");
    3627           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false))
    3628           2 :                 /*ok*/ printf(
    3629             :                     "  Supports: Creating geometry fields with NOT NULL "
    3630             :                     "constraint.\n");
    3631           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION,
    3632             :                              false))
    3633           2 :                 /*ok*/ printf("  Supports: Writing geometries with given "
    3634             :                               "coordinate precision\n");
    3635           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false))
    3636           0 :                 printf("  Supports: Reading feature styles.\n"); /*ok*/
    3637           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false))
    3638           0 :                 printf("  Supports: Writing feature styles.\n"); /*ok*/
    3639           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false))
    3640           1 :                 printf("  Supports: Coordinate epoch.\n"); /*ok*/
    3641           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false))
    3642           2 :                 printf("  Supports: Multiple vector layers.\n"); /*ok*/
    3643           3 :             if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false))
    3644           2 :                 printf("  Supports: Reading field domains.\n"); /*ok*/
    3645           3 :             if (CSLFetchNameValue(papszMD,
    3646           3 :                                   GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES))
    3647           2 :                 printf("  Creation field domain types: %s\n", /*ok*/
    3648             :                        CSLFetchNameValue(papszMD,
    3649             :                                          GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES));
    3650           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS))
    3651           2 :                 printf("  Supported SQL dialects: %s\n", /*ok*/
    3652             :                        CSLFetchNameValue(papszMD,
    3653             :                                          GDAL_DMD_SUPPORTED_SQL_DIALECTS));
    3654             : 
    3655          24 :             for (const char *key :
    3656             :                  {GDAL_DMD_CREATIONOPTIONLIST,
    3657             :                   GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
    3658             :                   GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
    3659             :                   GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
    3660             :                   GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
    3661             :                   GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
    3662             :                   GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
    3663          27 :                   GDAL_DS_LAYER_CREATIONOPTIONLIST})
    3664             :             {
    3665          24 :                 if (CSLFetchNameValue(papszMD, key))
    3666             :                 {
    3667             :                     CPLXMLNode *psCOL =
    3668           5 :                         CPLParseXMLString(CSLFetchNameValue(papszMD, key));
    3669           5 :                     StripIrrelevantOptions(psCOL, nOptions);
    3670           5 :                     char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
    3671             : 
    3672           5 :                     CPLDestroyXMLNode(psCOL);
    3673             : 
    3674           5 :                     printf("\n%s\n", pszFormattedXML); /*ok*/
    3675           5 :                     CPLFree(pszFormattedXML);
    3676             :                 }
    3677             :             }
    3678             : 
    3679           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
    3680           0 :                 printf("  Connection prefix: %s\n", /*ok*/
    3681             :                        CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
    3682             : 
    3683           3 :             if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
    3684             :             {
    3685           3 :                 CPLXMLNode *psCOL = CPLParseXMLString(
    3686             :                     CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
    3687           3 :                 StripIrrelevantOptions(psCOL, nOptions);
    3688           3 :                 char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
    3689             : 
    3690           3 :                 CPLDestroyXMLNode(psCOL);
    3691             : 
    3692           3 :                 printf("%s\n", pszFormattedXML); /*ok*/
    3693           3 :                 CPLFree(pszFormattedXML);
    3694             :             }
    3695             : 
    3696           3 :             bool bFirstOtherOption = true;
    3697         112 :             for (char **papszIter = papszMD; papszIter && *papszIter;
    3698             :                  ++papszIter)
    3699             :             {
    3700         109 :                 if (!STARTS_WITH(*papszIter, "DCAP_") &&
    3701          49 :                     !STARTS_WITH(*papszIter, "DMD_") &&
    3702          14 :                     !STARTS_WITH(*papszIter, "DS_") &&
    3703          12 :                     !STARTS_WITH(*papszIter, "OGR_DRIVER="))
    3704             :                 {
    3705          12 :                     if (bFirstOtherOption)
    3706           3 :                         printf("  Other metadata items:\n"); /*ok*/
    3707          12 :                     bFirstOtherOption = false;
    3708          12 :                     printf("    %s\n", *papszIter); /*ok*/
    3709             :                 }
    3710             :             }
    3711             : 
    3712           3 :             return 0;
    3713             :         }
    3714             : 
    3715             :         /* --------------------------------------------------------------------
    3716             :          */
    3717             :         /*      --help-general */
    3718             :         /* --------------------------------------------------------------------
    3719             :          */
    3720        6032 :         else if (EQUAL(papszArgv[iArg], "--help-general"))
    3721             :         {
    3722           2 :             printf("Generic GDAL utility command options:\n");       /*ok*/
    3723           2 :             printf("  --version: report version of GDAL in use.\n"); /*ok*/
    3724           2 :             /*ok*/ printf(
    3725             :                 "  --build: report detailed information about GDAL in "
    3726             :                 "use.\n");
    3727           2 :             printf("  --license: report GDAL license info.\n"); /*ok*/
    3728           2 :             printf(                                             /*ok*/
    3729             :                    "  --formats: report all configured format drivers.\n"); /*ok*/
    3730           2 :             printf("  --format [<format>]: details of one format.\n"); /*ok*/
    3731           2 :             /*ok*/ printf(
    3732             :                 "  --optfile filename: expand an option file into the "
    3733             :                 "argument list.\n");
    3734           2 :             printf(/*ok*/
    3735             :                    "  --config <key> <value> or --config <key>=<value>: set "
    3736             :                    "system configuration option.\n");               /*ok*/
    3737           2 :             printf("  --debug [on/off/value]: set debug level.\n"); /*ok*/
    3738           2 :             /*ok*/ printf(                                          /*ok*/
    3739             :                           "  --pause: wait for user input, time to attach "
    3740             :                           "debugger\n");
    3741           2 :             printf("  --locale [<locale>]: install locale for debugging " /*ok*/
    3742             :                    "(i.e. en_US.UTF-8)\n");
    3743           2 :             printf("  --help-general: report detailed help on general " /*ok*/
    3744             :                    "options.\n");
    3745             : 
    3746           2 :             return 0;
    3747             :         }
    3748             : 
    3749             :         /* --------------------------------------------------------------------
    3750             :          */
    3751             :         /*      --locale */
    3752             :         /* --------------------------------------------------------------------
    3753             :          */
    3754        6030 :         else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
    3755             :         {
    3756           2 :             CPLsetlocale(LC_ALL, papszArgv[++iArg]);
    3757             :         }
    3758             : 
    3759             :         /* --------------------------------------------------------------------
    3760             :          */
    3761             :         /*      --pause */
    3762             :         /* --------------------------------------------------------------------
    3763             :          */
    3764        6028 :         else if (EQUAL(papszArgv[iArg], "--pause"))
    3765             :         {
    3766           0 :             std::cout << "Hit <ENTER> to Continue." << std::endl;
    3767           0 :             std::cin.clear();
    3768           0 :             std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    3769             :         }
    3770             : 
    3771             :         /* --------------------------------------------------------------------
    3772             :          */
    3773             :         /*      Carry through unrecognized options. */
    3774             :         /* --------------------------------------------------------------------
    3775             :          */
    3776             :         else
    3777             :         {
    3778        6028 :             aosReturn.AddString(papszArgv[iArg]);
    3779             :         }
    3780             :     }
    3781             : 
    3782        1148 :     const int nSize = aosReturn.size();
    3783        1148 :     *ppapszArgv = aosReturn.StealList();
    3784             : 
    3785        1148 :     return nSize;
    3786             : }
    3787             : 
    3788             : /************************************************************************/
    3789             : /*                          _FetchDblFromMD()                           */
    3790             : /************************************************************************/
    3791             : 
    3792        1660 : static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
    3793             :                             double *padfTarget, int nCount, double dfDefault)
    3794             : 
    3795             : {
    3796             :     char szFullKey[200];
    3797             : 
    3798        1660 :     snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
    3799             : 
    3800        1660 :     const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
    3801             : 
    3802        9628 :     for (int i = 0; i < nCount; i++)
    3803        7968 :         padfTarget[i] = dfDefault;
    3804             : 
    3805        1660 :     if (pszValue == nullptr)
    3806         408 :         return false;
    3807             : 
    3808        1252 :     if (nCount == 1)
    3809             :     {
    3810         920 :         *padfTarget = CPLAtofM(pszValue);
    3811         920 :         return true;
    3812             :     }
    3813             : 
    3814         332 :     char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
    3815             : 
    3816         332 :     if (CSLCount(papszTokens) != nCount)
    3817             :     {
    3818           0 :         CSLDestroy(papszTokens);
    3819           0 :         return false;
    3820             :     }
    3821             : 
    3822        6972 :     for (int i = 0; i < nCount; i++)
    3823        6640 :         padfTarget[i] = CPLAtofM(papszTokens[i]);
    3824             : 
    3825         332 :     CSLDestroy(papszTokens);
    3826             : 
    3827         332 :     return true;
    3828             : }
    3829             : 
    3830             : /************************************************************************/
    3831             : /*                         GDALExtractRPCInfo()                         */
    3832             : /************************************************************************/
    3833             : 
    3834             : /** Extract RPC info from metadata, and apply to an RPCInfo structure.
    3835             :  *
    3836             :  * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
    3837             :  *
    3838             :  * @param papszMD Dictionary of metadata representing RPC
    3839             :  * @param psRPC (output) Pointer to structure to hold the RPC values.
    3840             :  * @return TRUE in case of success. FALSE in case of failure.
    3841             :  */
    3842           0 : int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
    3843             : 
    3844             : {
    3845             :     GDALRPCInfoV2 sRPC;
    3846           0 :     if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
    3847           0 :         return FALSE;
    3848           0 :     memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
    3849           0 :     return TRUE;
    3850             : }
    3851             : 
    3852             : /** Extract RPC info from metadata, and apply to an RPCInfo structure.
    3853             :  *
    3854             :  * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
    3855             :  *
    3856             :  * @param papszMD Dictionary of metadata representing RPC
    3857             :  * @param psRPC (output) Pointer to structure to hold the RPC values.
    3858             :  * @return TRUE in case of success. FALSE in case of failure.
    3859             :  */
    3860          83 : int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
    3861             : 
    3862             : {
    3863          83 :     if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
    3864           0 :         return FALSE;
    3865             : 
    3866          83 :     if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
    3867          83 :         CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
    3868         249 :         CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
    3869          83 :         CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
    3870             :     {
    3871           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3872             :                  "Some required RPC metadata missing in GDALExtractRPCInfo()");
    3873           0 :         return FALSE;
    3874             :     }
    3875             : 
    3876          83 :     _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
    3877          83 :     _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
    3878          83 :     _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
    3879          83 :     _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
    3880          83 :     _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
    3881          83 :     _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
    3882          83 :     _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
    3883          83 :     _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
    3884             :                     1.0);
    3885          83 :     _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
    3886          83 :     _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
    3887          83 :     _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
    3888          83 :     _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
    3889             : 
    3890          83 :     _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
    3891             :                     0.0);
    3892          83 :     _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
    3893             :                     0.0);
    3894          83 :     _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
    3895             :                     0.0);
    3896          83 :     _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
    3897             :                     0.0);
    3898             : 
    3899          83 :     _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
    3900          83 :     _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
    3901          83 :     _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
    3902          83 :     _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
    3903             : 
    3904          83 :     return TRUE;
    3905             : }
    3906             : 
    3907             : /************************************************************************/
    3908             : /*                     GDALFindAssociatedAuxFile()                      */
    3909             : /************************************************************************/
    3910             : 
    3911       10827 : GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
    3912             :                                        GDALAccess eAccess,
    3913             :                                        GDALDataset *poDependentDS)
    3914             : 
    3915             : {
    3916       10827 :     const char *pszAuxSuffixLC = "aux";
    3917       10827 :     const char *pszAuxSuffixUC = "AUX";
    3918             : 
    3919       10827 :     if (EQUAL(CPLGetExtension(pszBasename), pszAuxSuffixLC))
    3920          34 :         return nullptr;
    3921             : 
    3922             :     /* -------------------------------------------------------------------- */
    3923             :     /*      Don't even try to look for an .aux file if we don't have a      */
    3924             :     /*      path of any kind.                                               */
    3925             :     /* -------------------------------------------------------------------- */
    3926       10793 :     if (strlen(pszBasename) == 0)
    3927          20 :         return nullptr;
    3928             : 
    3929             :     /* -------------------------------------------------------------------- */
    3930             :     /*      We didn't find that, so try and find a corresponding aux        */
    3931             :     /*      file.  Check that we are the dependent file of the aux          */
    3932             :     /*      file, or if we aren't verify that the dependent file does       */
    3933             :     /*      not exist, likely mean it is us but some sort of renaming       */
    3934             :     /*      has occurred.                                                   */
    3935             :     /* -------------------------------------------------------------------- */
    3936       21546 :     CPLString osJustFile = CPLGetFilename(pszBasename);  // without dir
    3937       10773 :     CPLString osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixLC);
    3938       10773 :     GDALDataset *poODS = nullptr;
    3939             :     GByte abyHeader[32];
    3940             : 
    3941       10773 :     VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
    3942             : 
    3943       10773 :     if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
    3944             :     {
    3945             :         // Can't found file with lower case suffix. Try the upper case one.
    3946       10724 :         osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixUC);
    3947       10724 :         fp = VSIFOpenL(osAuxFilename, "rb");
    3948             :     }
    3949             : 
    3950       10773 :     if (fp != nullptr)
    3951             :     {
    3952          98 :         if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
    3953          49 :             STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
    3954             :                            "EHFA_HEADER_TAG"))
    3955             :         {
    3956             :             /* Avoid causing failure in opening of main file from SWIG bindings
    3957             :              */
    3958             :             /* when auxiliary file cannot be opened (#3269) */
    3959          19 :             CPLTurnFailureIntoWarning(TRUE);
    3960          19 :             if (poDependentDS != nullptr && poDependentDS->GetShared())
    3961           0 :                 poODS = GDALDataset::FromHandle(
    3962             :                     GDALOpenShared(osAuxFilename, eAccess));
    3963             :             else
    3964             :                 poODS =
    3965          19 :                     GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess));
    3966          19 :             CPLTurnFailureIntoWarning(FALSE);
    3967             :         }
    3968          49 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    3969             :     }
    3970             : 
    3971             :     /* -------------------------------------------------------------------- */
    3972             :     /*      Try replacing extension with .aux                               */
    3973             :     /* -------------------------------------------------------------------- */
    3974       10773 :     if (poODS != nullptr)
    3975             :     {
    3976             :         const char *pszDep =
    3977          19 :             poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
    3978          19 :         if (pszDep == nullptr)
    3979             :         {
    3980           0 :             CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
    3981             :                      osAuxFilename.c_str());
    3982           0 :             GDALClose(poODS);
    3983           0 :             poODS = nullptr;
    3984             :         }
    3985          19 :         else if (!EQUAL(pszDep, osJustFile))
    3986             :         {
    3987             :             VSIStatBufL sStatBuf;
    3988             : 
    3989           0 :             if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
    3990             :             {
    3991           0 :                 CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
    3992             :                          osAuxFilename.c_str(), pszDep, osJustFile.c_str());
    3993           0 :                 GDALClose(poODS);
    3994           0 :                 poODS = nullptr;
    3995             :             }
    3996             :             else
    3997             :             {
    3998           0 :                 CPLDebug("AUX",
    3999             :                          "%s is for file %s, not %s, but since\n"
    4000             :                          "%s does not exist, we will use .aux file as our own.",
    4001             :                          osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
    4002             :                          pszDep);
    4003             :             }
    4004             :         }
    4005             : 
    4006             :         /* --------------------------------------------------------------------
    4007             :          */
    4008             :         /*      Confirm that the aux file matches the configuration of the */
    4009             :         /*      dependent dataset. */
    4010             :         /* --------------------------------------------------------------------
    4011             :          */
    4012          38 :         if (poODS != nullptr && poDependentDS != nullptr &&
    4013          19 :             (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
    4014          19 :              poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
    4015          16 :              poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
    4016             :         {
    4017           3 :             CPLDebug("AUX",
    4018             :                      "Ignoring aux file %s as its raster configuration\n"
    4019             :                      "(%dP x %dL x %dB) does not match master file (%dP x %dL "
    4020             :                      "x %dB)",
    4021             :                      osAuxFilename.c_str(), poODS->GetRasterXSize(),
    4022             :                      poODS->GetRasterYSize(), poODS->GetRasterCount(),
    4023             :                      poDependentDS->GetRasterXSize(),
    4024             :                      poDependentDS->GetRasterYSize(),
    4025             :                      poDependentDS->GetRasterCount());
    4026             : 
    4027           3 :             GDALClose(poODS);
    4028           3 :             poODS = nullptr;
    4029             :         }
    4030             :     }
    4031             : 
    4032             :     /* -------------------------------------------------------------------- */
    4033             :     /*      Try appending .aux to the end of the filename.                  */
    4034             :     /* -------------------------------------------------------------------- */
    4035       10773 :     if (poODS == nullptr)
    4036             :     {
    4037       10757 :         osAuxFilename = pszBasename;
    4038       10757 :         osAuxFilename += ".";
    4039       10757 :         osAuxFilename += pszAuxSuffixLC;
    4040       10757 :         fp = VSIFOpenL(osAuxFilename, "rb");
    4041       10757 :         if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
    4042             :         {
    4043             :             // Can't found file with lower case suffix. Try the upper case one.
    4044       10734 :             osAuxFilename = pszBasename;
    4045       10734 :             osAuxFilename += ".";
    4046       10734 :             osAuxFilename += pszAuxSuffixUC;
    4047       10734 :             fp = VSIFOpenL(osAuxFilename, "rb");
    4048             :         }
    4049             : 
    4050       10757 :         if (fp != nullptr)
    4051             :         {
    4052          46 :             if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
    4053          23 :                 STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
    4054             :                                "EHFA_HEADER_TAG"))
    4055             :             {
    4056             :                 /* Avoid causing failure in opening of main file from SWIG
    4057             :                  * bindings */
    4058             :                 /* when auxiliary file cannot be opened (#3269) */
    4059           0 :                 CPLTurnFailureIntoWarning(TRUE);
    4060           0 :                 if (poDependentDS != nullptr && poDependentDS->GetShared())
    4061           0 :                     poODS = GDALDataset::FromHandle(
    4062             :                         GDALOpenShared(osAuxFilename, eAccess));
    4063             :                 else
    4064           0 :                     poODS = GDALDataset::FromHandle(
    4065             :                         GDALOpen(osAuxFilename, eAccess));
    4066           0 :                 CPLTurnFailureIntoWarning(FALSE);
    4067             :             }
    4068          23 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    4069             :         }
    4070             : 
    4071       10757 :         if (poODS != nullptr)
    4072             :         {
    4073             :             const char *pszDep =
    4074           0 :                 poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
    4075           0 :             if (pszDep == nullptr)
    4076             :             {
    4077           0 :                 CPLDebug("AUX",
    4078             :                          "Found %s but it has no dependent file, ignoring.",
    4079             :                          osAuxFilename.c_str());
    4080           0 :                 GDALClose(poODS);
    4081           0 :                 poODS = nullptr;
    4082             :             }
    4083           0 :             else if (!EQUAL(pszDep, osJustFile))
    4084             :             {
    4085             :                 VSIStatBufL sStatBuf;
    4086             : 
    4087           0 :                 if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
    4088             :                 {
    4089           0 :                     CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
    4090             :                              osAuxFilename.c_str(), pszDep, osJustFile.c_str());
    4091           0 :                     GDALClose(poODS);
    4092           0 :                     poODS = nullptr;
    4093             :                 }
    4094             :                 else
    4095             :                 {
    4096           0 :                     CPLDebug(
    4097             :                         "AUX",
    4098             :                         "%s is for file %s, not %s, but since\n"
    4099             :                         "%s does not exist, we will use .aux file as our own.",
    4100             :                         osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
    4101             :                         pszDep);
    4102             :                 }
    4103             :             }
    4104             :         }
    4105             :     }
    4106             : 
    4107             :     /* -------------------------------------------------------------------- */
    4108             :     /*      Confirm that the aux file matches the configuration of the      */
    4109             :     /*      dependent dataset.                                              */
    4110             :     /* -------------------------------------------------------------------- */
    4111       10789 :     if (poODS != nullptr && poDependentDS != nullptr &&
    4112          16 :         (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
    4113          16 :          poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
    4114          16 :          poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
    4115             :     {
    4116           0 :         CPLDebug(
    4117             :             "AUX",
    4118             :             "Ignoring aux file %s as its raster configuration\n"
    4119             :             "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
    4120             :             osAuxFilename.c_str(), poODS->GetRasterXSize(),
    4121             :             poODS->GetRasterYSize(), poODS->GetRasterCount(),
    4122             :             poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
    4123             :             poDependentDS->GetRasterCount());
    4124             : 
    4125           0 :         GDALClose(poODS);
    4126           0 :         poODS = nullptr;
    4127             :     }
    4128             : 
    4129       10773 :     return poODS;
    4130             : }
    4131             : 
    4132             : /************************************************************************/
    4133             : /* Infrastructure to check that dataset characteristics are valid       */
    4134             : /************************************************************************/
    4135             : 
    4136             : CPL_C_START
    4137             : 
    4138             : /**
    4139             :  * \brief Return TRUE if the dataset dimensions are valid.
    4140             :  *
    4141             :  * @param nXSize raster width
    4142             :  * @param nYSize raster height
    4143             :  *
    4144             :  * @since GDAL 1.7.0
    4145             :  */
    4146        4537 : int GDALCheckDatasetDimensions(int nXSize, int nYSize)
    4147             : {
    4148        4537 :     if (nXSize <= 0 || nYSize <= 0)
    4149             :     {
    4150           8 :         CPLError(CE_Failure, CPLE_AppDefined,
    4151             :                  "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
    4152           8 :         return FALSE;
    4153             :     }
    4154        4529 :     return TRUE;
    4155             : }
    4156             : 
    4157             : /**
    4158             :  * \brief Return TRUE if the band count is valid.
    4159             :  *
    4160             :  * If the configuration option GDAL_MAX_BAND_COUNT is defined,
    4161             :  * the band count will be compared to the maximum number of band allowed.
    4162             :  * If not defined, the maximum number allowed is 65536.
    4163             :  *
    4164             :  * @param nBands the band count
    4165             :  * @param bIsZeroAllowed TRUE if band count == 0 is allowed
    4166             :  *
    4167             :  * @since GDAL 1.7.0
    4168             :  */
    4169             : 
    4170        3943 : int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
    4171             : {
    4172        3943 :     if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
    4173             :     {
    4174          12 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
    4175             :                  nBands);
    4176          12 :         return FALSE;
    4177             :     }
    4178             :     const char *pszMaxBandCount =
    4179        3931 :         CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
    4180             :     /* coverity[tainted_data] */
    4181        3931 :     int nMaxBands = atoi(pszMaxBandCount);
    4182        3931 :     if (nBands > nMaxBands)
    4183             :     {
    4184           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4185             :                  "Invalid band count : %d. Maximum allowed currently is %d. "
    4186             :                  "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
    4187             :                  "legitimate number.",
    4188             :                  nBands, nMaxBands);
    4189           1 :         return FALSE;
    4190             :     }
    4191        3930 :     return TRUE;
    4192             : }
    4193             : 
    4194             : CPL_C_END
    4195             : 
    4196             : /************************************************************************/
    4197             : /*                     GDALSerializeGCPListToXML()                      */
    4198             : /************************************************************************/
    4199             : 
    4200          22 : void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
    4201             :                                const std::vector<gdal::GCP> &asGCPs,
    4202             :                                const OGRSpatialReference *poGCP_SRS)
    4203             : {
    4204          44 :     CPLString oFmt;
    4205             : 
    4206             :     CPLXMLNode *psPamGCPList =
    4207          22 :         CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
    4208             : 
    4209          22 :     CPLXMLNode *psLastChild = nullptr;
    4210             : 
    4211          22 :     if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
    4212             :     {
    4213           9 :         char *pszWKT = nullptr;
    4214           9 :         poGCP_SRS->exportToWkt(&pszWKT);
    4215           9 :         CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
    4216           9 :         CPLFree(pszWKT);
    4217           9 :         const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
    4218           9 :         CPLString osMapping;
    4219          27 :         for (size_t i = 0; i < mapping.size(); ++i)
    4220             :         {
    4221          18 :             if (!osMapping.empty())
    4222           9 :                 osMapping += ",";
    4223          18 :             osMapping += CPLSPrintf("%d", mapping[i]);
    4224             :         }
    4225           9 :         CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
    4226             :                        osMapping.c_str());
    4227             : 
    4228           9 :         psLastChild = psPamGCPList->psChild->psNext;
    4229             :     }
    4230             : 
    4231          88 :     for (const gdal::GCP &gcp : asGCPs)
    4232             :     {
    4233          66 :         CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
    4234             : 
    4235          66 :         if (psLastChild == nullptr)
    4236          13 :             psPamGCPList->psChild = psXMLGCP;
    4237             :         else
    4238          53 :             psLastChild->psNext = psXMLGCP;
    4239          66 :         psLastChild = psXMLGCP;
    4240             : 
    4241          66 :         CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
    4242             : 
    4243          66 :         if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
    4244           0 :             CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
    4245             : 
    4246          66 :         CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel()));
    4247             : 
    4248          66 :         CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line()));
    4249             : 
    4250          66 :         CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X()));
    4251             : 
    4252          66 :         CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y()));
    4253             : 
    4254             :         /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it
    4255             :          * back */
    4256          66 :         if (gcp.Z() != 0.0)
    4257          14 :             CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
    4258             :     }
    4259          22 : }
    4260             : 
    4261             : /************************************************************************/
    4262             : /*                     GDALDeserializeGCPListFromXML()                  */
    4263             : /************************************************************************/
    4264             : 
    4265          93 : void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
    4266             :                                    std::vector<gdal::GCP> &asGCPs,
    4267             :                                    OGRSpatialReference **ppoGCP_SRS)
    4268             : {
    4269          93 :     if (ppoGCP_SRS)
    4270             :     {
    4271             :         const char *pszRawProj =
    4272          78 :             CPLGetXMLValue(psGCPList, "Projection", nullptr);
    4273             : 
    4274          78 :         *ppoGCP_SRS = nullptr;
    4275          78 :         if (pszRawProj && pszRawProj[0])
    4276             :         {
    4277          62 :             *ppoGCP_SRS = new OGRSpatialReference();
    4278             :             (*ppoGCP_SRS)
    4279          62 :                 ->SetFromUserInput(
    4280             :                     pszRawProj,
    4281             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
    4282             : 
    4283             :             const char *pszMapping =
    4284          62 :                 CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
    4285          62 :             if (pszMapping)
    4286             :             {
    4287             :                 char **papszTokens =
    4288          14 :                     CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
    4289          28 :                 std::vector<int> anMapping;
    4290          42 :                 for (int i = 0; papszTokens && papszTokens[i]; i++)
    4291             :                 {
    4292          28 :                     anMapping.push_back(atoi(papszTokens[i]));
    4293             :                 }
    4294          14 :                 CSLDestroy(papszTokens);
    4295          14 :                 (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
    4296             :             }
    4297             :             else
    4298             :             {
    4299             :                 (*ppoGCP_SRS)
    4300          48 :                     ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4301             :             }
    4302             :         }
    4303             :     }
    4304             : 
    4305          93 :     asGCPs.clear();
    4306        2823 :     for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
    4307        2730 :          psXMLGCP = psXMLGCP->psNext)
    4308             :     {
    4309        2730 :         if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
    4310          86 :             continue;
    4311             : 
    4312        5288 :         gdal::GCP gcp;
    4313        2644 :         gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
    4314        2644 :         gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
    4315             : 
    4316             :         const auto ParseDoubleValue =
    4317       10576 :             [psXMLGCP](const char *pszParameter, double &dfVal)
    4318             :         {
    4319             :             const char *pszVal =
    4320       10576 :                 CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
    4321       10576 :             if (!pszVal)
    4322             :             {
    4323           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
    4324             :                          pszParameter);
    4325           0 :                 return false;
    4326             :             }
    4327       10576 :             char *endptr = nullptr;
    4328       10576 :             dfVal = CPLStrtod(pszVal, &endptr);
    4329       10576 :             if (endptr == pszVal)
    4330             :             {
    4331           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    4332             :                          "GCP#%s=%s is an invalid value", pszParameter, pszVal);
    4333           0 :                 return false;
    4334             :             }
    4335       10576 :             return true;
    4336        2644 :         };
    4337             : 
    4338        2644 :         bool bOK = true;
    4339        2644 :         if (!ParseDoubleValue("Pixel", gcp.Pixel()))
    4340           0 :             bOK = false;
    4341        2644 :         if (!ParseDoubleValue("Line", gcp.Line()))
    4342           0 :             bOK = false;
    4343        2644 :         if (!ParseDoubleValue("X", gcp.X()))
    4344           0 :             bOK = false;
    4345        2644 :         if (!ParseDoubleValue("Y", gcp.Y()))
    4346           0 :             bOK = false;
    4347        2644 :         const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr);
    4348        2644 :         if (pszZ == nullptr)
    4349             :         {
    4350             :             // Note: GDAL 1.10.1 and older generated #GCPZ,
    4351             :             // but could not read it back.
    4352        2422 :             pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0");
    4353             :         }
    4354        2644 :         char *endptr = nullptr;
    4355        2644 :         gcp.Z() = CPLStrtod(pszZ, &endptr);
    4356        2644 :         if (endptr == pszZ)
    4357             :         {
    4358           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4359             :                      "GCP#Z=%s is an invalid value", pszZ);
    4360           0 :             bOK = false;
    4361             :         }
    4362             : 
    4363        2644 :         if (bOK)
    4364             :         {
    4365        2644 :             asGCPs.emplace_back(std::move(gcp));
    4366             :         }
    4367             :     }
    4368          93 : }
    4369             : 
    4370             : /************************************************************************/
    4371             : /*                   GDALSerializeOpenOptionsToXML()                    */
    4372             : /************************************************************************/
    4373             : 
    4374        1006 : void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
    4375             :                                    char **papszOpenOptions)
    4376             : {
    4377        1006 :     if (papszOpenOptions != nullptr)
    4378             :     {
    4379             :         CPLXMLNode *psOpenOptions =
    4380           3 :             CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
    4381           3 :         CPLXMLNode *psLastChild = nullptr;
    4382             : 
    4383           6 :         for (char **papszIter = papszOpenOptions; *papszIter != nullptr;
    4384             :              papszIter++)
    4385             :         {
    4386             :             const char *pszRawValue;
    4387           3 :             char *pszKey = nullptr;
    4388             :             CPLXMLNode *psOOI;
    4389             : 
    4390           3 :             pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
    4391             : 
    4392           3 :             psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
    4393           3 :             if (psLastChild == nullptr)
    4394           3 :                 psOpenOptions->psChild = psOOI;
    4395             :             else
    4396           0 :                 psLastChild->psNext = psOOI;
    4397           3 :             psLastChild = psOOI;
    4398             : 
    4399           3 :             CPLSetXMLValue(psOOI, "#key", pszKey);
    4400           3 :             CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
    4401             : 
    4402           3 :             CPLFree(pszKey);
    4403             :         }
    4404             :     }
    4405        1006 : }
    4406             : 
    4407             : /************************************************************************/
    4408             : /*                  GDALDeserializeOpenOptionsFromXML()                 */
    4409             : /************************************************************************/
    4410             : 
    4411        2228 : char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
    4412             : {
    4413        2228 :     char **papszOpenOptions = nullptr;
    4414             :     const CPLXMLNode *psOpenOptions =
    4415        2228 :         CPLGetXMLNode(psParentNode, "OpenOptions");
    4416        2228 :     if (psOpenOptions != nullptr)
    4417             :     {
    4418             :         const CPLXMLNode *psOOI;
    4419          22 :         for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
    4420          11 :              psOOI = psOOI->psNext)
    4421             :         {
    4422          11 :             if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
    4423          11 :                 psOOI->psChild == nullptr ||
    4424          11 :                 psOOI->psChild->psNext == nullptr ||
    4425          11 :                 psOOI->psChild->eType != CXT_Attribute ||
    4426          11 :                 psOOI->psChild->psChild == nullptr)
    4427           0 :                 continue;
    4428             : 
    4429          11 :             char *pszName = psOOI->psChild->psChild->pszValue;
    4430          11 :             char *pszValue = psOOI->psChild->psNext->pszValue;
    4431          11 :             if (pszName != nullptr && pszValue != nullptr)
    4432             :                 papszOpenOptions =
    4433          11 :                     CSLSetNameValue(papszOpenOptions, pszName, pszValue);
    4434             :         }
    4435             :     }
    4436        2228 :     return papszOpenOptions;
    4437             : }
    4438             : 
    4439             : /************************************************************************/
    4440             : /*                    GDALRasterIOGetResampleAlg()                      */
    4441             : /************************************************************************/
    4442             : 
    4443        2163 : GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
    4444             : {
    4445        2163 :     GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
    4446        2163 :     if (STARTS_WITH_CI(pszResampling, "NEAR"))
    4447          18 :         eResampleAlg = GRIORA_NearestNeighbour;
    4448        2145 :     else if (EQUAL(pszResampling, "BILINEAR"))
    4449        2016 :         eResampleAlg = GRIORA_Bilinear;
    4450         129 :     else if (EQUAL(pszResampling, "CUBIC"))
    4451          78 :         eResampleAlg = GRIORA_Cubic;
    4452          51 :     else if (EQUAL(pszResampling, "CUBICSPLINE"))
    4453           1 :         eResampleAlg = GRIORA_CubicSpline;
    4454          50 :     else if (EQUAL(pszResampling, "LANCZOS"))
    4455           1 :         eResampleAlg = GRIORA_Lanczos;
    4456          49 :     else if (EQUAL(pszResampling, "AVERAGE"))
    4457          43 :         eResampleAlg = GRIORA_Average;
    4458           6 :     else if (EQUAL(pszResampling, "RMS"))
    4459           1 :         eResampleAlg = GRIORA_RMS;
    4460           5 :     else if (EQUAL(pszResampling, "MODE"))
    4461           4 :         eResampleAlg = GRIORA_Mode;
    4462           1 :     else if (EQUAL(pszResampling, "GAUSS"))
    4463           1 :         eResampleAlg = GRIORA_Gauss;
    4464             :     else
    4465           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    4466             :                  "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
    4467        2163 :     return eResampleAlg;
    4468             : }
    4469             : 
    4470             : /************************************************************************/
    4471             : /*                    GDALRasterIOGetResampleAlgStr()                   */
    4472             : /************************************************************************/
    4473             : 
    4474           1 : const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
    4475             : {
    4476           1 :     switch (eResampleAlg)
    4477             :     {
    4478           0 :         case GRIORA_NearestNeighbour:
    4479           0 :             return "NearestNeighbour";
    4480           0 :         case GRIORA_Bilinear:
    4481           0 :             return "Bilinear";
    4482           1 :         case GRIORA_Cubic:
    4483           1 :             return "Cubic";
    4484           0 :         case GRIORA_CubicSpline:
    4485           0 :             return "CubicSpline";
    4486           0 :         case GRIORA_Lanczos:
    4487           0 :             return "Lanczos";
    4488           0 :         case GRIORA_Average:
    4489           0 :             return "Average";
    4490           0 :         case GRIORA_RMS:
    4491           0 :             return "RMS";
    4492           0 :         case GRIORA_Mode:
    4493           0 :             return "Mode";
    4494           0 :         case GRIORA_Gauss:
    4495           0 :             return "Gauss";
    4496           0 :         default:
    4497           0 :             CPLAssert(false);
    4498             :             return "Unknown";
    4499             :     }
    4500             : }
    4501             : 
    4502             : /************************************************************************/
    4503             : /*                   GDALRasterIOExtraArgSetResampleAlg()               */
    4504             : /************************************************************************/
    4505             : 
    4506     3461830 : void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
    4507             :                                         int nXSize, int nYSize, int nBufXSize,
    4508             :                                         int nBufYSize)
    4509             : {
    4510     3461830 :     if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
    4511      371525 :         psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
    4512             :     {
    4513             :         const char *pszResampling =
    4514      368896 :             CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
    4515      368896 :         if (pszResampling != nullptr)
    4516             :         {
    4517           1 :             psExtraArg->eResampleAlg =
    4518           1 :                 GDALRasterIOGetResampleAlg(pszResampling);
    4519             :         }
    4520             :     }
    4521     3461830 : }
    4522             : 
    4523             : /************************************************************************/
    4524             : /*                     GDALCanFileAcceptSidecarFile()                   */
    4525             : /************************************************************************/
    4526             : 
    4527       92834 : int GDALCanFileAcceptSidecarFile(const char *pszFilename)
    4528             : {
    4529       92834 :     if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
    4530           0 :         return FALSE;
    4531             :     // Do no attempt reading side-car files on /vsisubfile/ (#6241)
    4532       92834 :     if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
    4533         321 :         return FALSE;
    4534       92513 :     return TRUE;
    4535             : }
    4536             : 
    4537             : /************************************************************************/
    4538             : /*                   GDALCanReliablyUseSiblingFileList()                */
    4539             : /************************************************************************/
    4540             : 
    4541             : /* Try to address https://github.com/OSGeo/gdal/issues/2903 */
    4542             : /* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
    4543             : /*   (normalization form decomposed). The filesystem takes care of converting */
    4544             : /*   precomposed form as often coming from user interface to this NFD variant */
    4545             : /*   See
    4546             :  * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x
    4547             :  */
    4548             : /*   And readdir() will return such NFD variant encoding. Consequently comparing
    4549             :  */
    4550             : /*   the user filename with ones with readdir() is not reliable */
    4551             : /* - APFS preserves both case and normalization of the filename on disk in all
    4552             :  */
    4553             : /*   variants. In macOS High Sierra, APFS is normalization-insensitive in both
    4554             :  */
    4555             : /*   the case-insensitive and case-sensitive variants, using a hash-based native
    4556             :  */
    4557             : /*   normalization scheme. APFS preserves the normalization of the filename and
    4558             :  */
    4559             : /*   uses hashes of the normalized form of the filename to provide normalization
    4560             :  */
    4561             : /*   insensitivity. */
    4562             : /*   From
    4563             :  * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
    4564             :  */
    4565             : /*   Issues might still arise if the file has been created using one of the
    4566             :  * UTF-8 */
    4567             : /*   encoding (likely the decomposed one if using MacOS specific API), but the
    4568             :  */
    4569             : /*   string passed to GDAL for opening would be with another one (likely the
    4570             :  * precomposed one) */
    4571      102949 : bool GDALCanReliablyUseSiblingFileList(const char *pszFilename)
    4572             : {
    4573             : #ifdef __APPLE__
    4574             :     for (int i = 0; pszFilename[i] != 0; ++i)
    4575             :     {
    4576             :         if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127)
    4577             :         {
    4578             :             // non-ASCII character found
    4579             : 
    4580             :             // if this is a network storage, assume no issue
    4581             :             if (!VSIIsLocal(pszFilename))
    4582             :             {
    4583             :                 return true;
    4584             :             }
    4585             :             return false;
    4586             :         }
    4587             :     }
    4588             :     return true;
    4589             : #else
    4590             :     (void)pszFilename;
    4591      102949 :     return true;
    4592             : #endif
    4593             : }
    4594             : 
    4595             : /************************************************************************/
    4596             : /*                    GDALAdjustNoDataCloseToFloatMax()                 */
    4597             : /************************************************************************/
    4598             : 
    4599        1089 : double GDALAdjustNoDataCloseToFloatMax(double dfVal)
    4600             : {
    4601        1089 :     const auto kMaxFloat = std::numeric_limits<float>::max();
    4602        1089 :     if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
    4603          33 :         return -kMaxFloat;
    4604        1056 :     if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
    4605           7 :         return kMaxFloat;
    4606        1049 :     return dfVal;
    4607             : }
    4608             : 
    4609             : /************************************************************************/
    4610             : /*                        GDALCopyNoDataValue()                         */
    4611             : /************************************************************************/
    4612             : 
    4613        2576 : void GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand)
    4614             : {
    4615             : 
    4616             :     int bSuccess;
    4617        2576 :     const auto eSrcDataType = poSrcBand->GetRasterDataType();
    4618        2576 :     const auto eDstDataType = poDstBand->GetRasterDataType();
    4619        2576 :     if (eSrcDataType == GDT_Int64)
    4620             :     {
    4621           3 :         const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
    4622           3 :         if (bSuccess)
    4623             :         {
    4624           3 :             if (eDstDataType == GDT_Int64)
    4625             :             {
    4626           3 :                 poDstBand->SetNoDataValueAsInt64(nNoData);
    4627             :             }
    4628           0 :             else if (eDstDataType == GDT_UInt64)
    4629             :             {
    4630           0 :                 if (nNoData >= 0)
    4631           0 :                     poDstBand->SetNoDataValueAsUInt64(
    4632           0 :                         static_cast<uint64_t>(nNoData));
    4633             :             }
    4634           0 :             else if (nNoData ==
    4635           0 :                      static_cast<int64_t>(static_cast<double>(nNoData)))
    4636             :             {
    4637           0 :                 poDstBand->SetNoDataValue(static_cast<double>(nNoData));
    4638             :             }
    4639             :         }
    4640             :     }
    4641        2573 :     else if (eSrcDataType == GDT_UInt64)
    4642             :     {
    4643           3 :         const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
    4644           3 :         if (bSuccess)
    4645             :         {
    4646           3 :             if (eDstDataType == GDT_UInt64)
    4647             :             {
    4648           3 :                 poDstBand->SetNoDataValueAsUInt64(nNoData);
    4649             :             }
    4650           0 :             else if (eDstDataType == GDT_Int64)
    4651             :             {
    4652           0 :                 if (nNoData <
    4653           0 :                     static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
    4654             :                 {
    4655           0 :                     poDstBand->SetNoDataValueAsInt64(
    4656           0 :                         static_cast<int64_t>(nNoData));
    4657             :                 }
    4658             :             }
    4659           0 :             else if (nNoData ==
    4660           0 :                      static_cast<uint64_t>(static_cast<double>(nNoData)))
    4661             :             {
    4662           0 :                 poDstBand->SetNoDataValue(static_cast<double>(nNoData));
    4663             :             }
    4664             :         }
    4665             :     }
    4666             :     else
    4667             :     {
    4668        2570 :         const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
    4669        2570 :         if (bSuccess)
    4670             :         {
    4671         155 :             if (eDstDataType == GDT_Int64)
    4672             :             {
    4673           0 :                 if (dfNoData >= static_cast<double>(
    4674           0 :                                     std::numeric_limits<int64_t>::min()) &&
    4675           0 :                     dfNoData <= static_cast<double>(
    4676           0 :                                     std::numeric_limits<int64_t>::max()) &&
    4677             :                     dfNoData ==
    4678           0 :                         static_cast<double>(static_cast<int64_t>(dfNoData)))
    4679             :                 {
    4680           0 :                     poDstBand->SetNoDataValueAsInt64(
    4681           0 :                         static_cast<int64_t>(dfNoData));
    4682             :                 }
    4683             :             }
    4684         155 :             else if (eDstDataType == GDT_UInt64)
    4685             :             {
    4686           0 :                 if (dfNoData >= static_cast<double>(
    4687           0 :                                     std::numeric_limits<uint64_t>::min()) &&
    4688           0 :                     dfNoData <= static_cast<double>(
    4689           0 :                                     std::numeric_limits<uint64_t>::max()) &&
    4690             :                     dfNoData ==
    4691           0 :                         static_cast<double>(static_cast<uint64_t>(dfNoData)))
    4692             :                 {
    4693           0 :                     poDstBand->SetNoDataValueAsInt64(
    4694           0 :                         static_cast<uint64_t>(dfNoData));
    4695             :                 }
    4696             :             }
    4697             :             else
    4698             :             {
    4699         155 :                 poDstBand->SetNoDataValue(dfNoData);
    4700             :             }
    4701             :         }
    4702             :     }
    4703        2576 : }
    4704             : 
    4705             : /************************************************************************/
    4706             : /*                     GDALGetNoDataValueCastToDouble()                 */
    4707             : /************************************************************************/
    4708             : 
    4709           1 : double GDALGetNoDataValueCastToDouble(int64_t nVal)
    4710             : {
    4711           1 :     const double dfVal = static_cast<double>(nVal);
    4712           1 :     if (static_cast<int64_t>(dfVal) != nVal)
    4713             :     {
    4714           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    4715             :                  "GetNoDataValue() returns an approximate value of the "
    4716             :                  "true nodata value = " CPL_FRMT_GIB ". Use "
    4717             :                  "GetNoDataValueAsInt64() instead",
    4718             :                  static_cast<GIntBig>(nVal));
    4719             :     }
    4720           1 :     return dfVal;
    4721             : }
    4722             : 
    4723           1 : double GDALGetNoDataValueCastToDouble(uint64_t nVal)
    4724             : {
    4725           1 :     const double dfVal = static_cast<double>(nVal);
    4726           1 :     if (static_cast<uint64_t>(dfVal) != nVal)
    4727             :     {
    4728           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    4729             :                  "GetNoDataValue() returns an approximate value of the "
    4730             :                  "true nodata value = " CPL_FRMT_GUIB ". Use "
    4731             :                  "GetNoDataValueAsUInt64() instead",
    4732             :                  static_cast<GUIntBig>(nVal));
    4733             :     }
    4734           1 :     return dfVal;
    4735             : }
    4736             : 
    4737             : /************************************************************************/
    4738             : /*                GDALGetCompressionFormatForJPEG()                     */
    4739             : /************************************************************************/
    4740             : 
    4741             : //! @cond Doxygen_Suppress
    4742          23 : std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp)
    4743             : {
    4744          23 :     std::string osRet;
    4745          23 :     const auto nSavedPos = VSIFTellL(fp);
    4746             :     GByte abyMarkerHeader[4];
    4747          23 :     if (VSIFSeekL(fp, 0, SEEK_SET) == 0 &&
    4748          23 :         VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 &&
    4749          46 :         abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8)
    4750             :     {
    4751          23 :         osRet = "JPEG";
    4752          23 :         bool bHasAPP14Adobe = false;
    4753          23 :         GByte abyAPP14AdobeMarkerData[14 - 2] = {0};
    4754          23 :         int nNumComponents = 0;
    4755             :         while (true)
    4756             :         {
    4757         171 :             const auto nCurPos = VSIFTellL(fp);
    4758         171 :             if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1)
    4759           0 :                 break;
    4760         171 :             if (abyMarkerHeader[0] != 0xFF)
    4761           0 :                 break;
    4762         171 :             const GByte markerType = abyMarkerHeader[1];
    4763         171 :             const size_t nMarkerSize =
    4764         171 :                 abyMarkerHeader[2] * 256 + abyMarkerHeader[3];
    4765         171 :             if (nMarkerSize < 2)
    4766           0 :                 break;
    4767         171 :             if (markerType >= 0xC0 && markerType <= 0xCF &&
    4768          23 :                 markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC)
    4769             :             {
    4770          23 :                 switch (markerType)
    4771             :                 {
    4772          21 :                     case 0xC0:
    4773          21 :                         osRet += ";frame_type=SOF0_baseline";
    4774          21 :                         break;
    4775           2 :                     case 0xC1:
    4776           2 :                         osRet += ";frame_type=SOF1_extended_sequential";
    4777           2 :                         break;
    4778           0 :                     case 0xC2:
    4779           0 :                         osRet += ";frame_type=SOF2_progressive_huffman";
    4780           0 :                         break;
    4781           0 :                     case 0xC3:
    4782             :                         osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_"
    4783           0 :                                  "supported=no";
    4784           0 :                         break;
    4785           0 :                     case 0xC5:
    4786             :                         osRet += ";frame_type="
    4787             :                                  "SOF5_differential_sequential_huffman;"
    4788           0 :                                  "libjpeg_supported=no";
    4789           0 :                         break;
    4790           0 :                     case 0xC6:
    4791             :                         osRet += ";frame_type=SOF6_differential_progressive_"
    4792           0 :                                  "huffman;libjpeg_supported=no";
    4793           0 :                         break;
    4794           0 :                     case 0xC7:
    4795             :                         osRet += ";frame_type="
    4796             :                                  "SOF7_differential_lossless_huffman;"
    4797           0 :                                  "libjpeg_supported=no";
    4798           0 :                         break;
    4799           0 :                     case 0xC9:
    4800             :                         osRet += ";frame_type="
    4801           0 :                                  "SOF9_extended_sequential_arithmetic";
    4802           0 :                         break;
    4803           0 :                     case 0xCA:
    4804           0 :                         osRet += ";frame_type=SOF10_progressive_arithmetic";
    4805           0 :                         break;
    4806           0 :                     case 0xCB:
    4807             :                         osRet += ";frame_type="
    4808             :                                  "SOF11_lossless_arithmetic;libjpeg_"
    4809           0 :                                  "supported=no";
    4810           0 :                         break;
    4811           0 :                     case 0xCD:
    4812             :                         osRet += ";frame_type=SOF13_differential_sequential_"
    4813           0 :                                  "arithmetic;libjpeg_supported=no";
    4814           0 :                         break;
    4815           0 :                     case 0xCE:
    4816             :                         osRet += ";frame_type=SOF14_differential_progressive_"
    4817           0 :                                  "arithmetic;libjpeg_supported=no";
    4818           0 :                         break;
    4819           0 :                     case 0xCF:
    4820             :                         osRet += ";frame_type=SOF15_differential_lossless_"
    4821           0 :                                  "arithmetic;libjpeg_supported=no";
    4822           0 :                         break;
    4823           0 :                     default:
    4824           0 :                         break;
    4825             :                 }
    4826             :                 GByte abySegmentBegin[6];
    4827          23 :                 if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1,
    4828          23 :                               fp) != 1)
    4829           0 :                     break;
    4830          23 :                 osRet += ";bit_depth=";
    4831          23 :                 osRet += CPLSPrintf("%d", abySegmentBegin[0]);
    4832          23 :                 nNumComponents = abySegmentBegin[5];
    4833          23 :                 osRet += ";num_components=";
    4834          23 :                 osRet += CPLSPrintf("%d", nNumComponents);
    4835          23 :                 if (nNumComponents == 3)
    4836             :                 {
    4837             :                     GByte abySegmentNext[3 * 3];
    4838          13 :                     if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1,
    4839          13 :                                   fp) != 1)
    4840           0 :                         break;
    4841          13 :                     if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 &&
    4842           0 :                         abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 &&
    4843           0 :                         abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11)
    4844             :                     {
    4845             :                         // no subsampling
    4846           0 :                         osRet += ";subsampling=4:4:4";
    4847             :                     }
    4848          13 :                     else if (abySegmentNext[0] == 1 &&
    4849          11 :                              abySegmentNext[1] == 0x22 &&
    4850          11 :                              abySegmentNext[3] == 2 &&
    4851          11 :                              abySegmentNext[4] == 0x11 &&
    4852          11 :                              abySegmentNext[6] == 3 &&
    4853          11 :                              abySegmentNext[7] == 0x11)
    4854             :                     {
    4855             :                         // classic subsampling
    4856          11 :                         osRet += ";subsampling=4:2:0";
    4857             :                     }
    4858           2 :                     else if (abySegmentNext[0] == 1 &&
    4859           0 :                              abySegmentNext[1] == 0x21 &&
    4860           0 :                              abySegmentNext[3] == 2 &&
    4861           0 :                              abySegmentNext[4] == 0x11 &&
    4862           0 :                              abySegmentNext[6] == 3 &&
    4863           0 :                              abySegmentNext[7] == 0x11)
    4864             :                     {
    4865           0 :                         osRet += ";subsampling=4:2:2";
    4866             :                     }
    4867          23 :                 }
    4868             :             }
    4869         148 :             else if (markerType == 0xEE && nMarkerSize == 14)
    4870             :             {
    4871           1 :                 if (VSIFReadL(abyAPP14AdobeMarkerData,
    4872           2 :                               sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 &&
    4873           1 :                     memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) ==
    4874             :                         0)
    4875             :                 {
    4876           1 :                     bHasAPP14Adobe = true;
    4877             :                 }
    4878             :             }
    4879         147 :             else if (markerType == 0xDA)
    4880             :             {
    4881             :                 // Start of scan
    4882          23 :                 break;
    4883             :             }
    4884         148 :             VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET);
    4885         148 :         }
    4886          46 :         std::string osColorspace;
    4887          23 :         if (bHasAPP14Adobe)
    4888             :         {
    4889           1 :             if (abyAPP14AdobeMarkerData[11] == 0)
    4890             :             {
    4891           1 :                 if (nNumComponents == 3)
    4892           0 :                     osColorspace = "RGB";
    4893           1 :                 else if (nNumComponents == 4)
    4894           1 :                     osColorspace = "CMYK";
    4895             :             }
    4896           0 :             else if (abyAPP14AdobeMarkerData[11] == 1)
    4897             :             {
    4898           0 :                 osColorspace = "YCbCr";
    4899             :             }
    4900           0 :             else if (abyAPP14AdobeMarkerData[11] == 2)
    4901             :             {
    4902           0 :                 osColorspace = "YCCK";
    4903             :             }
    4904             :         }
    4905             :         else
    4906             :         {
    4907          22 :             if (nNumComponents == 3)
    4908          13 :                 osColorspace = "YCbCr";
    4909           9 :             else if (nNumComponents == 4)
    4910           1 :                 osColorspace = "CMYK";
    4911             :         }
    4912          23 :         osRet += ";colorspace=";
    4913          23 :         if (!osColorspace.empty())
    4914          15 :             osRet += osColorspace;
    4915             :         else
    4916           8 :             osRet += "unknown";
    4917             :     }
    4918          23 :     if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0)
    4919             :     {
    4920           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4921             :                  "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed");
    4922             :     }
    4923          46 :     return osRet;
    4924             : }
    4925             : 
    4926          16 : std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
    4927             :                                             size_t nBufferSize)
    4928             : {
    4929          16 :     VSILFILE *fp = VSIFileFromMemBuffer(
    4930             :         nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
    4931             :         false);
    4932          16 :     std::string osRet = GDALGetCompressionFormatForJPEG(fp);
    4933          16 :     VSIFCloseL(fp);
    4934          16 :     return osRet;
    4935             : }
    4936             : 
    4937             : //! @endcond
    4938             : 
    4939             : /************************************************************************/
    4940             : /*                      GDALGetNoDataReplacementValue()                 */
    4941             : /************************************************************************/
    4942             : 
    4943             : /**
    4944             :  * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
    4945             :  *        is out of range for the specified data type (dt).
    4946             :  *        For UInt64 and Int64 data type this function cannot reliably trusted
    4947             :  *        because their nodata values might not always be representable exactly
    4948             :  *        as a double, in particular the maximum absolute value for those types
    4949             :  *        is 2^53.
    4950             :  *
    4951             :  * The replacement value is a value that can be used in a computation
    4952             :  * whose result would match by accident the nodata value, whereas it is
    4953             :  * meant to be valid. For example, for a dataset with a nodata value of 0,
    4954             :  * when averaging -1 and 1, one would get normally a value of 0. The
    4955             :  * replacement nodata value can then be substituted to that 0 value to still
    4956             :  * get a valid value, as close as practical to the true value, while being
    4957             :  * different from the nodata value.
    4958             :  *
    4959             :  * @param dt Data type
    4960             :  * @param dfNoDataValue The no data value
    4961             : 
    4962             :  * @since GDAL 3.9
    4963             :  */
    4964         155 : double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
    4965             : {
    4966             : 
    4967             :     // The logic here is to check if the value is out of range for the
    4968             :     // specified data type and return a replacement value if it is, return
    4969             :     // 0 otherwise.
    4970         155 :     double dfReplacementVal = dfNoDataValue;
    4971         155 :     if (dt == GDT_Byte)
    4972             :     {
    4973          59 :         if (GDALClampDoubleValue(dfNoDataValue,
    4974          59 :                                  std::numeric_limits<uint8_t>::lowest(),
    4975          59 :                                  std::numeric_limits<uint8_t>::max()))
    4976             :         {
    4977           2 :             return 0;
    4978             :         }
    4979          57 :         if (dfNoDataValue == std::numeric_limits<unsigned char>::max())
    4980           3 :             dfReplacementVal = std::numeric_limits<unsigned char>::max() - 1;
    4981             :         else
    4982          54 :             dfReplacementVal = dfNoDataValue + 1;
    4983             :     }
    4984          96 :     else if (dt == GDT_Int8)
    4985             :     {
    4986           4 :         if (GDALClampDoubleValue(dfNoDataValue,
    4987           4 :                                  std::numeric_limits<int8_t>::lowest(),
    4988           4 :                                  std::numeric_limits<int8_t>::max()))
    4989             :         {
    4990           2 :             return 0;
    4991             :         }
    4992           2 :         if (dfNoDataValue == std::numeric_limits<GInt8>::max())
    4993           1 :             dfReplacementVal = std::numeric_limits<GInt8>::max() - 1;
    4994             :         else
    4995           1 :             dfReplacementVal = dfNoDataValue + 1;
    4996             :     }
    4997          92 :     else if (dt == GDT_UInt16)
    4998             :     {
    4999           6 :         if (GDALClampDoubleValue(dfNoDataValue,
    5000           6 :                                  std::numeric_limits<uint16_t>::lowest(),
    5001           6 :                                  std::numeric_limits<uint16_t>::max()))
    5002             :         {
    5003           2 :             return 0;
    5004             :         }
    5005           4 :         if (dfNoDataValue == std::numeric_limits<GUInt16>::max())
    5006           1 :             dfReplacementVal = std::numeric_limits<GUInt16>::max() - 1;
    5007             :         else
    5008           3 :             dfReplacementVal = dfNoDataValue + 1;
    5009             :     }
    5010          86 :     else if (dt == GDT_Int16)
    5011             :     {
    5012           4 :         if (GDALClampDoubleValue(dfNoDataValue,
    5013           4 :                                  std::numeric_limits<int16_t>::lowest(),
    5014           4 :                                  std::numeric_limits<int16_t>::max()))
    5015             :         {
    5016           2 :             return 0;
    5017             :         }
    5018           2 :         if (dfNoDataValue == std::numeric_limits<GInt16>::max())
    5019           1 :             dfReplacementVal = std::numeric_limits<GInt16>::max() - 1;
    5020             :         else
    5021           1 :             dfReplacementVal = dfNoDataValue + 1;
    5022             :     }
    5023          82 :     else if (dt == GDT_UInt32)
    5024             :     {
    5025           5 :         if (GDALClampDoubleValue(dfNoDataValue,
    5026             :                                  std::numeric_limits<uint32_t>::lowest(),
    5027             :                                  std::numeric_limits<uint32_t>::max()))
    5028             :         {
    5029           2 :             return 0;
    5030             :         }
    5031           3 :         if (dfNoDataValue == std::numeric_limits<GUInt32>::max())
    5032           1 :             dfReplacementVal = std::numeric_limits<GUInt32>::max() - 1;
    5033             :         else
    5034           2 :             dfReplacementVal = dfNoDataValue + 1;
    5035             :     }
    5036          77 :     else if (dt == GDT_Int32)
    5037             :     {
    5038           6 :         if (GDALClampDoubleValue(dfNoDataValue,
    5039             :                                  std::numeric_limits<int32_t>::lowest(),
    5040             :                                  std::numeric_limits<int32_t>::max()))
    5041             :         {
    5042           2 :             return 0;
    5043             :         }
    5044           4 :         if (dfNoDataValue == std::numeric_limits<int32_t>::max())
    5045           1 :             dfReplacementVal = std::numeric_limits<int32_t>::max() - 1;
    5046             :         else
    5047           3 :             dfReplacementVal = dfNoDataValue + 1;
    5048             :     }
    5049          71 :     else if (dt == GDT_UInt64)
    5050             :     {
    5051             :         // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
    5052             :         // so we take the next lower value representable as a double 18446744073709549567
    5053             :         static const double dfMaxUInt64Value{
    5054             :             std::nextafter(
    5055             :                 static_cast<double>(std::numeric_limits<uint64_t>::max()), 0) -
    5056             :             1};
    5057             : 
    5058           4 :         if (GDALClampDoubleValue(dfNoDataValue,
    5059             :                                  std::numeric_limits<uint64_t>::lowest(),
    5060             :                                  std::numeric_limits<uint64_t>::max()))
    5061             :         {
    5062           2 :             return 0;
    5063             :         }
    5064             : 
    5065           2 :         if (dfNoDataValue >=
    5066           2 :             static_cast<double>(std::numeric_limits<uint64_t>::max()))
    5067           1 :             dfReplacementVal = dfMaxUInt64Value;
    5068             :         else
    5069           1 :             dfReplacementVal = dfNoDataValue + 1;
    5070             :     }
    5071          67 :     else if (dt == GDT_Int64)
    5072             :     {
    5073             :         // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
    5074             :         // so we take the next lower value representable as a double 9223372036854774784
    5075             :         static const double dfMaxInt64Value{
    5076             :             std::nextafter(
    5077             :                 static_cast<double>(std::numeric_limits<int64_t>::max()), 0) -
    5078             :             1};
    5079             : 
    5080           4 :         if (GDALClampDoubleValue(dfNoDataValue,
    5081             :                                  std::numeric_limits<int64_t>::lowest(),
    5082             :                                  std::numeric_limits<int64_t>::max()))
    5083             :         {
    5084           2 :             return 0;
    5085             :         }
    5086             : 
    5087           2 :         if (dfNoDataValue >=
    5088           2 :             static_cast<double>(std::numeric_limits<int64_t>::max()))
    5089           1 :             dfReplacementVal = dfMaxInt64Value;
    5090             :         else
    5091           1 :             dfReplacementVal = dfNoDataValue + 1;
    5092             :     }
    5093          63 :     else if (dt == GDT_Float32)
    5094             :     {
    5095             : 
    5096          32 :         if (GDALClampDoubleValue(dfNoDataValue,
    5097             :                                  std::numeric_limits<float>::lowest(),
    5098             :                                  std::numeric_limits<float>::max()))
    5099             :         {
    5100           4 :             return 0;
    5101             :         }
    5102             : 
    5103          28 :         if (dfNoDataValue == std::numeric_limits<float>::max())
    5104             :         {
    5105           1 :             dfReplacementVal =
    5106           1 :                 std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
    5107             :         }
    5108             :         else
    5109             :         {
    5110          27 :             dfReplacementVal =
    5111          27 :                 std::nextafter(static_cast<float>(dfNoDataValue),
    5112             :                                std::numeric_limits<float>::max());
    5113             :         }
    5114             :     }
    5115          31 :     else if (dt == GDT_Float64)
    5116             :     {
    5117          31 :         if (GDALClampDoubleValue(dfNoDataValue,
    5118             :                                  std::numeric_limits<double>::lowest(),
    5119             :                                  std::numeric_limits<double>::max()))
    5120             :         {
    5121           2 :             return 0;
    5122             :         }
    5123             : 
    5124          29 :         if (dfNoDataValue == std::numeric_limits<double>::max())
    5125             :         {
    5126           2 :             dfReplacementVal = std::nextafter(dfNoDataValue, 0.0f);
    5127             :         }
    5128             :         else
    5129             :         {
    5130          27 :             dfReplacementVal = std::nextafter(
    5131             :                 dfNoDataValue, std::numeric_limits<double>::max());
    5132             :         }
    5133             :     }
    5134             : 
    5135         133 :     return dfReplacementVal;
    5136             : }

Generated by: LCOV version 1.14