LCOV - code coverage report
Current view: top level - apps - gdalinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 946 1132 83.6 %
Date: 2026-04-23 19:47:11 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Utilities
       4             :  * Purpose:  Command line application to list info about a file.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  * ****************************************************************************
       8             :  * Copyright (c) 1998, Frank Warmerdam
       9             :  * Copyright (c) 2007-2015, Even Rouault <even.rouault at spatialys.com>
      10             :  * Copyright (c) 2015, Faza Mahamood
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "gdal_utils.h"
      17             : #include "gdal_utils_priv.h"
      18             : #include "gdalargumentparser.h"
      19             : 
      20             : #include <cmath>
      21             : #include <limits>
      22             : #include <stdarg.h>
      23             : #include <stdio.h>
      24             : #include <stdlib.h>
      25             : #include <string.h>
      26             : #include <new>
      27             : #include <string>
      28             : #include <vector>
      29             : 
      30             : #include "commonutils.h"
      31             : #include "cpl_conv.h"
      32             : #include "cpl_error.h"
      33             : #include "cpl_json_header.h"
      34             : #include "cpl_minixml.h"
      35             : #include "cpl_progress.h"
      36             : #include "cpl_string.h"
      37             : #include "cpl_vsi.h"
      38             : #include "gdal.h"
      39             : #include "gdal_alg.h"
      40             : #include "gdal_priv.h"
      41             : #include "gdal_rat.h"
      42             : #include "ogr_api.h"
      43             : #include "ogr_srs_api.h"
      44             : #include "ogr_spatialref.h"
      45             : #include "ogrlibjsonutils.h"
      46             : #include "ogrgeojsongeometry.h"
      47             : #include "ogrgeojsonwriter.h"
      48             : 
      49             : using std::vector;
      50             : 
      51             : /*! output format */
      52             : typedef enum
      53             : {
      54             :     /*! output in text format */ GDALINFO_FORMAT_TEXT = 0,
      55             :     /*! output in json format */ GDALINFO_FORMAT_JSON = 1
      56             : } GDALInfoFormat;
      57             : 
      58             : /************************************************************************/
      59             : /*                           GDALInfoOptions                            */
      60             : /************************************************************************/
      61             : 
      62             : /** Options for use with GDALInfo(). GDALInfoOptions* must be allocated and
      63             :  * freed with GDALInfoOptionsNew() and GDALInfoOptionsFree() respectively.
      64             :  */
      65             : struct GDALInfoOptions
      66             : {
      67             :     /*! output format */
      68             :     GDALInfoFormat eFormat = GDALINFO_FORMAT_TEXT;
      69             : 
      70             :     bool bComputeMinMax = false;
      71             : 
      72             :     /*! report histogram information for all bands */
      73             :     bool bReportHistograms = false;
      74             : 
      75             :     /*! report a PROJ.4 string corresponding to the file's coordinate system */
      76             :     bool bReportProj4 = false;
      77             : 
      78             :     /*! read and display image statistics. Force computation if no statistics
      79             :         are stored in an image */
      80             :     bool bStats = false;
      81             : 
      82             :     /*! read and display image statistics. Force computation if no statistics
      83             :         are stored in an image.  However, they may be computed based on
      84             :         overviews or a subset of all tiles. Useful if you are in a hurry and
      85             :         don't want precise stats. */
      86             :     bool bApproxStats = true;
      87             : 
      88             :     bool bSample = false;
      89             : 
      90             :     /*! force computation of the checksum for each band in the dataset */
      91             :     bool bComputeChecksum = false;
      92             : 
      93             :     /*! allow or suppress printing of nodata value */
      94             :     bool bShowNodata = true;
      95             : 
      96             :     /*! allow or suppress printing of mask information */
      97             :     bool bShowMask = true;
      98             : 
      99             :     /*! allow or suppress ground control points list printing. It may be useful
     100             :         for datasets with huge amount of GCPs, such as L1B AVHRR or HDF4 MODIS
     101             :         which contain thousands of them. */
     102             :     bool bShowGCPs = true;
     103             : 
     104             :     /*! allow or suppress metadata printing. Some datasets may contain a lot of
     105             :         metadata strings. */
     106             :     bool bShowMetadata = true;
     107             : 
     108             :     /*! allow or suppress printing of raster attribute table */
     109             :     bool bShowRAT = true;
     110             : 
     111             :     /*! allow or suppress printing of color table */
     112             :     bool bShowColorTable = true;
     113             : 
     114             :     /*! list all metadata domains available for the dataset */
     115             :     bool bListMDD = false;
     116             : 
     117             :     /*! display the file list or the first file of the file list */
     118             :     bool bShowFileList = true;
     119             : 
     120             :     /*! report metadata for the specified domains. "all" can be used to report
     121             :         metadata in all domains.
     122             :         */
     123             :     CPLStringList aosExtraMDDomains{};
     124             : 
     125             :     /*! WKT format used for SRS */
     126             :     std::string osWKTFormat = "WKT2";
     127             : 
     128             :     bool bStdoutOutput = false;
     129             : 
     130             :     /*! May be set to "gdal-raster-info" */
     131             :     std::string osInvokedFrom{};
     132             : 
     133             :     /*! Only used when osInvokedFrom is set to "gdal-raster-info" */
     134             :     std::string osCRSFormat{"AUTO"};
     135             : };
     136             : 
     137             : static int GDALInfoReportCorner(const GDALInfoOptions *psOptions,
     138             :                                 GDALDatasetH hDataset,
     139             :                                 OGRCoordinateTransformationH hTransform,
     140             :                                 const char *corner_name, double x, double y,
     141             :                                 bool bJson, json_object *poCornerCoordinates,
     142             :                                 json_object *poLongLatExtentCoordinates,
     143             :                                 CPLString &osStr);
     144             : 
     145             : static void GDALInfoReportMetadata(const GDALInfoOptions *psOptions,
     146             :                                    GDALMajorObjectH hObject, bool bIsBand,
     147             :                                    bool bJson, json_object *poMetadata,
     148             :                                    CPLString &osStr);
     149             : 
     150             : #ifndef Concat_defined
     151             : #define Concat_defined
     152             : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
     153             :                    ...) CPL_PRINT_FUNC_FORMAT(3, 4);
     154             : 
     155        3006 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
     156             :                    ...)
     157             : {
     158             :     va_list args;
     159        3006 :     va_start(args, pszFormat);
     160             : 
     161        3006 :     if (bStdoutOutput)
     162             :     {
     163        1973 :         vfprintf(stdout, pszFormat, args);
     164             :     }
     165             :     else
     166             :     {
     167             :         try
     168             :         {
     169        2066 :             CPLString osTarget;
     170        1033 :             osTarget.vPrintf(pszFormat, args);
     171             : 
     172        1033 :             osRet += osTarget;
     173             :         }
     174           0 :         catch (const std::bad_alloc &)
     175             :         {
     176           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
     177             :         }
     178             :     }
     179             : 
     180        3006 :     va_end(args);
     181        3006 : }
     182             : #endif
     183             : 
     184             : /************************************************************************/
     185             : /*         gdal_json_object_new_double_or_str_for_non_finite()          */
     186             : /************************************************************************/
     187             : 
     188             : static json_object *
     189         124 : gdal_json_object_new_double_or_str_for_non_finite(double dfVal, int nPrecision)
     190             : {
     191         124 :     if (std::isinf(dfVal))
     192           0 :         return json_object_new_string(dfVal < 0 ? "-Infinity" : "Infinity");
     193         124 :     else if (std::isnan(dfVal))
     194           0 :         return json_object_new_string("NaN");
     195             :     else
     196         124 :         return json_object_new_double_with_precision(dfVal, nPrecision);
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*           gdal_json_object_new_double_significant_digits()           */
     201             : /************************************************************************/
     202             : 
     203             : static json_object *
     204          26 : gdal_json_object_new_double_significant_digits(double dfVal,
     205             :                                                int nSignificantDigits)
     206             : {
     207          26 :     if (std::isinf(dfVal))
     208           0 :         return json_object_new_string(dfVal < 0 ? "-Infinity" : "Infinity");
     209          26 :     else if (std::isnan(dfVal))
     210           0 :         return json_object_new_string("NaN");
     211             :     else
     212          26 :         return json_object_new_double_with_significant_figures(
     213          26 :             dfVal, nSignificantDigits);
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                    GDALWarpAppOptionsGetParser()                     */
     218             : /************************************************************************/
     219             : 
     220             : static std::unique_ptr<GDALArgumentParser>
     221         156 : GDALInfoAppOptionsGetParser(GDALInfoOptions *psOptions,
     222             :                             GDALInfoOptionsForBinary *psOptionsForBinary)
     223             : {
     224             :     auto argParser = std::make_unique<GDALArgumentParser>(
     225         156 :         "gdalinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
     226             : 
     227         156 :     argParser->add_description(_("Raster dataset information utility."));
     228             : 
     229         156 :     argParser->add_epilog(
     230         156 :         _("For more details, consult https://gdal.org/programs/gdalinfo.html"));
     231             : 
     232             :     // Hidden: only for gdal raster info
     233         156 :     argParser->add_argument("--invoked-from")
     234         156 :         .store_into(psOptions->osInvokedFrom)
     235         156 :         .hidden();
     236             : 
     237             :     // Hidden: only for gdal raster info
     238         156 :     argParser->add_argument("--crs-format")
     239         156 :         .choices("AUTO", "WKT2", "PROJJSON")
     240         156 :         .store_into(psOptions->osCRSFormat)
     241         156 :         .hidden();
     242             : 
     243         156 :     argParser->add_argument("-json")
     244         156 :         .flag()
     245          95 :         .action([psOptions](const auto &)
     246         156 :                 { psOptions->eFormat = GDALINFO_FORMAT_JSON; })
     247         156 :         .help(_("Display the output in json format."));
     248             : 
     249         156 :     argParser->add_argument("-mm")
     250         156 :         .store_into(psOptions->bComputeMinMax)
     251             :         .help(_("Force computation of the actual min/max values for each band "
     252         156 :                 "in the dataset."));
     253             : 
     254             :     {
     255         156 :         auto &group = argParser->add_mutually_exclusive_group();
     256         156 :         group.add_argument("-stats")
     257         156 :             .store_into(psOptions->bStats)
     258             :             .help(_("Read and display image statistics computing exact values "
     259         156 :                     "if required."));
     260             : 
     261         156 :         group.add_argument("-approx_stats")
     262         156 :             .store_into(psOptions->bApproxStats)
     263             :             .help(
     264             :                 _("Read and display image statistics computing approximated "
     265         156 :                   "values on overviews or a subset of all tiles if required."));
     266             :     }
     267             : 
     268         156 :     argParser->add_argument("-hist")
     269         156 :         .store_into(psOptions->bReportHistograms)
     270         156 :         .help(_("Report histogram information for all bands."));
     271             : 
     272         156 :     argParser->add_usage_newline();
     273             : 
     274             :     argParser->add_inverted_logic_flag(
     275             :         "-nogcp", &psOptions->bShowGCPs,
     276         156 :         _("Suppress ground control points list printing."));
     277             : 
     278             :     argParser->add_inverted_logic_flag("-nomd", &psOptions->bShowMetadata,
     279         156 :                                        _("Suppress metadata printing."));
     280             : 
     281             :     argParser->add_inverted_logic_flag(
     282             :         "-norat", &psOptions->bShowRAT,
     283         156 :         _("Suppress printing of raster attribute table."));
     284             : 
     285             :     argParser->add_inverted_logic_flag("-noct", &psOptions->bShowColorTable,
     286         156 :                                        _("Suppress printing of color table."));
     287             : 
     288             :     argParser->add_inverted_logic_flag("-nofl", &psOptions->bShowFileList,
     289         156 :                                        _("Suppress display of the file list."));
     290             : 
     291             :     argParser->add_inverted_logic_flag(
     292             :         "-nonodata", &psOptions->bShowNodata,
     293         156 :         _("Suppress nodata printing (implies -nomask)."));
     294             : 
     295             :     argParser->add_inverted_logic_flag("-nomask", &psOptions->bShowMask,
     296         156 :                                        _("Suppress mask printing."));
     297             : 
     298         156 :     argParser->add_usage_newline();
     299             : 
     300         156 :     argParser->add_argument("-checksum")
     301         156 :         .flag()
     302         156 :         .store_into(psOptions->bComputeChecksum)
     303             :         .help(_(
     304         156 :             "Force computation of the checksum for each band in the dataset."));
     305             : 
     306         156 :     argParser->add_argument("-listmdd")
     307         156 :         .flag()
     308         156 :         .store_into(psOptions->bListMDD)
     309         156 :         .help(_("List all metadata domains available for the dataset."));
     310             : 
     311         156 :     argParser->add_argument("-proj4")
     312         156 :         .flag()
     313         156 :         .store_into(psOptions->bReportProj4)
     314             :         .help(_("Report a PROJ.4 string corresponding to the file's coordinate "
     315         156 :                 "system."));
     316             : 
     317         156 :     argParser->add_argument("-wkt_format")
     318         312 :         .metavar("<WKT1|WKT1_ESRI|WKT2|WKT2_2015|WKT2_2018|WKT2_2019>")
     319             :         .choices("WKT1", "WKT1_ESRI", "WKT2", "WKT2_2015", "WKT2_2018",
     320         156 :                  "WKT2_2019")
     321         156 :         .store_into(psOptions->osWKTFormat)
     322         156 :         .help(_("WKT format used for SRS."));
     323             : 
     324         156 :     if (psOptionsForBinary)
     325             :     {
     326          59 :         argParser->add_argument("-sd")
     327         118 :             .metavar("<n>")
     328          59 :             .store_into(psOptionsForBinary->nSubdataset)
     329             :             .help(_(
     330             :                 "Use subdataset of specified index (starting at 1), instead of "
     331          59 :                 "the source dataset itself."));
     332             :     }
     333             : 
     334         156 :     argParser->add_argument("-oo")
     335         312 :         .metavar("<NAME>=<VALUE>")
     336         156 :         .append()
     337             :         .action(
     338           2 :             [psOptionsForBinary](const std::string &s)
     339             :             {
     340           1 :                 if (psOptionsForBinary)
     341           1 :                     psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
     342         156 :             })
     343         156 :         .help(_("Open option(s) for dataset."));
     344             : 
     345             :     argParser->add_input_format_argument(
     346             :         psOptionsForBinary ? &psOptionsForBinary->aosAllowedInputDrivers
     347         156 :                            : nullptr);
     348             : 
     349         156 :     argParser->add_argument("-mdd")
     350         312 :         .metavar("<domain>|all")
     351             :         .action(
     352          27 :             [psOptions](const std::string &value)
     353             :             {
     354             :                 psOptions->aosExtraMDDomains =
     355           9 :                     CSLAddString(psOptions->aosExtraMDDomains, value.c_str());
     356         156 :             })
     357             :         .help(_("Report metadata for the specified domains. 'all' can be used "
     358         156 :                 "to report metadata in all domains."));
     359             : 
     360             :     /* Not documented: used by gdalinfo_bin.cpp only */
     361         156 :     argParser->add_argument("-stdout").flag().hidden().store_into(
     362         156 :         psOptions->bStdoutOutput);
     363             : 
     364         156 :     if (psOptionsForBinary)
     365             :     {
     366          59 :         argParser->add_argument("dataset_name")
     367         118 :             .metavar("<dataset_name>")
     368          59 :             .store_into(psOptionsForBinary->osFilename)
     369          59 :             .help("Input dataset.");
     370             :     }
     371             : 
     372         156 :     return argParser;
     373             : }
     374             : 
     375             : /************************************************************************/
     376             : /*                     GDALInfoAppGetParserUsage()                      */
     377             : /************************************************************************/
     378             : 
     379           0 : std::string GDALInfoAppGetParserUsage()
     380             : {
     381             :     try
     382             :     {
     383           0 :         GDALInfoOptions sOptions;
     384           0 :         GDALInfoOptionsForBinary sOptionsForBinary;
     385             :         auto argParser =
     386           0 :             GDALInfoAppOptionsGetParser(&sOptions, &sOptionsForBinary);
     387           0 :         return argParser->usage();
     388             :     }
     389           0 :     catch (const std::exception &err)
     390             :     {
     391           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
     392           0 :                  err.what());
     393           0 :         return std::string();
     394             :     }
     395             : }
     396             : 
     397             : /************************************************************************/
     398             : /*                        EmitTextDisplayOfCRS()                        */
     399             : /************************************************************************/
     400             : 
     401          22 : void EmitTextDisplayOfCRS(
     402             :     const OGRSpatialReference *poSRS, const std::string &osCRSFormat,
     403             :     const std::string &osIntroText,
     404             :     std::function<void(const std::string &)> printFunction)
     405             : {
     406          22 :     const char *pszAuthCode = poSRS->GetAuthorityCode();
     407          22 :     const char *pszAuthName = poSRS->GetAuthorityName();
     408             : 
     409          22 :     bool bCRSAlreadyEmitted = false;
     410          22 :     if (pszAuthName && pszAuthCode && osCRSFormat == "AUTO")
     411             :     {
     412          44 :         OGRSpatialReference oSRSFromAuthCode;
     413          44 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     414          22 :         const char *const apszComparisonCriteria[] = {
     415             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES",
     416             :             "CRITERION=EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS",
     417             :             "IGNORE_COORDINATE_EPOCH=YES", nullptr};
     418          66 :         if (oSRSFromAuthCode.SetFromUserInput(std::string(pszAuthName)
     419          44 :                                                   .append(":")
     420          22 :                                                   .append(pszAuthCode)
     421          88 :                                                   .c_str()) == OGRERR_NONE &&
     422          22 :             oSRSFromAuthCode.IsSame(poSRS, apszComparisonCriteria))
     423             :         {
     424          44 :             std::string osCRSId;
     425          22 :             if (STARTS_WITH_CI(pszAuthName, "IAU_"))
     426             :             {
     427           0 :                 osCRSId = "urn:ogc:def:crs:IAU:";
     428           0 :                 osCRSId += pszAuthName + strlen("IAU_");
     429           0 :                 osCRSId += ':';
     430           0 :                 osCRSId += pszAuthCode;
     431             :             }
     432          22 :             else if (strchr(pszAuthName, '_') == nullptr)
     433             :             {
     434          22 :                 osCRSId = pszAuthName;
     435          22 :                 osCRSId += ':';
     436          22 :                 osCRSId += pszAuthCode;
     437             :             }
     438             : 
     439          22 :             if (!osCRSId.empty())
     440             :             {
     441          22 :                 bCRSAlreadyEmitted = true;
     442          22 :                 printFunction(osIntroText);
     443          22 :                 printFunction(":\n");
     444          22 :                 printFunction(CPLSPrintf("  - name: %s\n", poSRS->GetName()));
     445             : 
     446          22 :                 printFunction(CPLSPrintf("  - ID: %s\n", osCRSId.c_str()));
     447             : 
     448          22 :                 const char *pszHorizType = "Other";
     449          22 :                 if (poSRS->IsGeographic())
     450             :                 {
     451           9 :                     if (poSRS->IsCompound())
     452           0 :                         pszHorizType = "Geographic";
     453           9 :                     else if (poSRS->GetAxesCount() == 3)
     454           0 :                         pszHorizType = "Geographic 3D";
     455             :                     else
     456           9 :                         pszHorizType = "Geographic 2D";
     457             :                 }
     458          13 :                 else if (poSRS->IsGeocentric())
     459           0 :                     pszHorizType = "Geocentric";
     460          13 :                 else if (poSRS->IsProjected())
     461          13 :                     pszHorizType = "Projected";
     462             : 
     463          22 :                 if (poSRS->IsCompound())
     464             :                 {
     465           0 :                     printFunction(
     466             :                         CPLSPrintf("  - type: Compound of %s\n", pszHorizType));
     467             :                 }
     468             :                 else
     469             :                 {
     470          22 :                     printFunction(CPLSPrintf("  - type: %s\n", pszHorizType));
     471             :                 }
     472             : 
     473          22 :                 if (poSRS->IsProjected())
     474             :                 {
     475             :                     // Create a copy since we want to force the internal
     476             :                     // WKT tree model to be WKT2 as we are going to
     477             :                     // request CONVERSION
     478          26 :                     OGRSpatialReference oSRS(*poSRS);
     479          13 :                     const char *pszConversion = oSRS.GetAttrValue("CONVERSION");
     480             :                     const std::string osConversion =
     481          26 :                         pszConversion ? pszConversion : "";
     482             :                     const char *pszMethod =
     483          13 :                         oSRS.GetAttrValue("CONVERSION|METHOD");
     484          26 :                     const std::string osMethod = pszMethod ? pszMethod : "";
     485          13 :                     if (!osConversion.empty() && !osMethod.empty())
     486             :                     {
     487             :                         // A bit of name laundering done to deal with EPSG:3857 where
     488             :                         // osConversion = "Popular Visualisation Pseudo-Mercator"
     489             :                         // osMethod = "Popular Visualisation Pseudo Mercator"
     490             :                         // A bit unfortunate to have to do that workaround, but as it
     491             :                         // is apparently ... popular ... let's do it.
     492          13 :                         if (CPLString(osConversion).replaceAll('-', ' ') ==
     493          26 :                             CPLString(osMethod).replaceAll('-', ' '))
     494             :                         {
     495           6 :                             printFunction(
     496             :                                 CPLSPrintf("  - projection type: %s\n",
     497             :                                            osConversion.c_str()));
     498             :                         }
     499             :                         else
     500             :                         {
     501           7 :                             printFunction(CPLSPrintf(
     502             :                                 "  - projection type: %s, %s\n",
     503             :                                 osConversion.c_str(), osMethod.c_str()));
     504             :                         }
     505             :                     }
     506          13 :                     const char *pszLinearUnits = nullptr;
     507          13 :                     poSRS->GetLinearUnits(&pszLinearUnits);
     508          13 :                     if (pszLinearUnits)
     509             :                     {
     510          13 :                         printFunction(CPLSPrintf("  - units: "
     511             :                                                  "%s\n",
     512             :                                                  pszLinearUnits));
     513             :                     }
     514             :                 }
     515          22 :                 double dfWest = 0;
     516          22 :                 double dfSouth = 0;
     517          22 :                 double dfEast = 0;
     518          22 :                 double dfNorth = 0;
     519          22 :                 const char *pszAreaName = nullptr;
     520          22 :                 if (poSRS->GetAreaOfUse(&dfWest, &dfSouth, &dfEast, &dfNorth,
     521             :                                         &pszAreaName))
     522             :                 {
     523           7 :                     if (pszAreaName && pszAreaName[0])
     524             :                     {
     525           7 :                         std::string osAreaOfUse(pszAreaName);
     526           7 :                         if (osAreaOfUse.back() == '.')
     527           0 :                             osAreaOfUse.pop_back();
     528           7 :                         if (osAreaOfUse.size() > 40)
     529             :                         {
     530           7 :                             auto nPos = osAreaOfUse.find(" - ");
     531           7 :                             if (nPos == std::string::npos)
     532           0 :                                 nPos = osAreaOfUse.find(", ");
     533           7 :                             if (nPos == std::string::npos)
     534           0 :                                 nPos = osAreaOfUse.find(' ');
     535           7 :                             if (nPos == std::string::npos)
     536           0 :                                 nPos = 40;
     537           7 :                             osAreaOfUse.resize(nPos);
     538           7 :                             osAreaOfUse += "...";
     539             :                         }
     540           7 :                         printFunction(
     541             :                             CPLSPrintf("  - area "
     542             :                                        "of use: %s, west %.2f, south %.2f, "
     543             :                                        "east %.2f, north %.2f\n",
     544             :                                        osAreaOfUse.c_str(), dfWest, dfSouth,
     545           7 :                                        dfEast, dfNorth));
     546             :                     }
     547             :                     else
     548             :                     {
     549           0 :                         printFunction(
     550             :                             CPLSPrintf("  - area "
     551             :                                        "of use: west %.2f, south %.2f, "
     552             :                                        "east %.2f, north %.2f\n",
     553             :                                        dfWest, dfSouth, dfEast, dfNorth));
     554             :                     }
     555             :                 }
     556             :             }
     557             :         }
     558             :     }
     559          22 :     if (!bCRSAlreadyEmitted)
     560             :     {
     561           0 :         if (osCRSFormat == "PROJJSON")
     562             :         {
     563           0 :             char *pszProjJson = nullptr;
     564           0 :             poSRS->exportToPROJJSON(&pszProjJson, nullptr);
     565           0 :             printFunction(osIntroText);
     566           0 :             printFunction(" PROJJSON:");
     567           0 :             if (pszProjJson)
     568             :             {
     569           0 :                 printFunction("\n");
     570           0 :                 printFunction(pszProjJson);
     571           0 :                 printFunction("\n");
     572             :             }
     573             :             else
     574             :             {
     575           0 :                 printFunction(" ERROR while exporting it to PROJJSON!\n");
     576             :             }
     577           0 :             CPLFree(pszProjJson);
     578             :         }
     579             :         else
     580             :         {
     581           0 :             const char *const apszWKTOptions[] = {"FORMAT=WKT2_2019",
     582             :                                                   "MULTILINE=YES", nullptr};
     583           0 :             printFunction(osIntroText);
     584           0 :             printFunction(" WKT:\n");
     585           0 :             printFunction(poSRS->exportToWkt(apszWKTOptions));
     586           0 :             printFunction("\n");
     587             :         }
     588             :     }
     589          22 : }
     590             : 
     591             : /************************************************************************/
     592             : /*                              GDALInfo()                              */
     593             : /************************************************************************/
     594             : 
     595             : /**
     596             :  * Lists various information about a GDAL supported raster dataset.
     597             :  *
     598             :  * This is the equivalent of the <a href="/programs/gdalinfo.html">gdalinfo</a>
     599             :  * utility.
     600             :  *
     601             :  * GDALInfoOptions* must be allocated and freed with GDALInfoOptionsNew()
     602             :  * and GDALInfoOptionsFree() respectively.
     603             :  *
     604             :  * @param hDataset the dataset handle.
     605             :  * @param psOptions the options structure returned by GDALInfoOptionsNew() or
     606             :  * NULL.
     607             :  * @return string corresponding to the information about the raster dataset
     608             :  * (must be freed with CPLFree()), or NULL in case of error.
     609             :  *
     610             :  * @since GDAL 2.1
     611             :  */
     612             : 
     613         151 : char *GDALInfo(GDALDatasetH hDataset, const GDALInfoOptions *psOptions)
     614             : {
     615         151 :     if (hDataset == nullptr)
     616           0 :         return nullptr;
     617             : 
     618         151 :     GDALInfoOptions *psOptionsToFree = nullptr;
     619         151 :     if (psOptions == nullptr)
     620             :     {
     621           0 :         psOptionsToFree = GDALInfoOptionsNew(nullptr, nullptr);
     622           0 :         psOptions = psOptionsToFree;
     623             :     }
     624             : 
     625         302 :     CPLString osStr;
     626         151 :     json_object *poJsonObject = nullptr;
     627         151 :     json_object *poBands = nullptr;
     628         151 :     json_object *poMetadata = nullptr;
     629         151 :     json_object *poStac = nullptr;
     630         151 :     json_object *poStacRasterBands = nullptr;
     631         151 :     json_object *poStacEOBands = nullptr;
     632             : 
     633         151 :     const bool bJson = psOptions->eFormat == GDALINFO_FORMAT_JSON;
     634             : 
     635             :     /* -------------------------------------------------------------------- */
     636             :     /*      Report general info.                                            */
     637             :     /* -------------------------------------------------------------------- */
     638         151 :     GDALDriverH hDriver = GDALGetDatasetDriver(hDataset);
     639         151 :     if (bJson)
     640             :     {
     641             :         json_object *poDescription =
     642          95 :             json_object_new_string(GDALGetDescription(hDataset));
     643          95 :         poJsonObject = json_object_new_object();
     644          95 :         poBands = json_object_new_array();
     645          95 :         poMetadata = json_object_new_object();
     646          95 :         poStac = json_object_new_object();
     647          95 :         poStacRasterBands = json_object_new_array();
     648          95 :         poStacEOBands = json_object_new_array();
     649             : 
     650          95 :         json_object_object_add(poJsonObject, "description", poDescription);
     651          95 :         if (hDriver)
     652             :         {
     653             :             json_object *poDriverShortName =
     654          94 :                 json_object_new_string(GDALGetDriverShortName(hDriver));
     655             :             json_object *poDriverLongName =
     656          94 :                 json_object_new_string(GDALGetDriverLongName(hDriver));
     657          94 :             json_object_object_add(poJsonObject, "driverShortName",
     658             :                                    poDriverShortName);
     659          94 :             json_object_object_add(poJsonObject, "driverLongName",
     660             :                                    poDriverLongName);
     661             :         }
     662             :     }
     663          56 :     else if (hDriver)
     664             :     {
     665          55 :         Concat(osStr, psOptions->bStdoutOutput, "Driver: %s/%s\n",
     666             :                GDALGetDriverShortName(hDriver), GDALGetDriverLongName(hDriver));
     667             :     }
     668             : 
     669         151 :     if (psOptions->bShowFileList)
     670             :     {
     671             :         // The list of files of a raster FileGDB is not super useful and potentially
     672             :         // super long, so omit it, unless the -json mode is enabled
     673             :         char **papszFileList =
     674          56 :             (!bJson && hDriver &&
     675          55 :              EQUAL(GDALGetDriverShortName(hDriver), "OpenFileGDB"))
     676         149 :                 ? nullptr
     677         149 :                 : GDALGetFileList(hDataset);
     678             : 
     679         149 :         if (!papszFileList || *papszFileList == nullptr)
     680             :         {
     681          28 :             if (bJson)
     682             :             {
     683          22 :                 json_object *poFiles = json_object_new_array();
     684          22 :                 json_object_object_add(poJsonObject, "files", poFiles);
     685             :             }
     686             :             else
     687             :             {
     688           6 :                 Concat(osStr, psOptions->bStdoutOutput,
     689             :                        "Files: none associated\n");
     690          28 :             }
     691             :         }
     692             :         else
     693             :         {
     694         121 :             if (bJson)
     695             :             {
     696          71 :                 json_object *poFiles = json_object_new_array();
     697             : 
     698         159 :                 for (int i = 0; papszFileList[i] != nullptr; i++)
     699             :                 {
     700             :                     json_object *poFile =
     701          88 :                         json_object_new_string(papszFileList[i]);
     702             : 
     703          88 :                     json_object_array_add(poFiles, poFile);
     704             :                 }
     705             : 
     706          71 :                 json_object_object_add(poJsonObject, "files", poFiles);
     707             :             }
     708             :             else
     709             :             {
     710          50 :                 Concat(osStr, psOptions->bStdoutOutput, "Files: %s\n",
     711             :                        papszFileList[0]);
     712          67 :                 for (int i = 1; papszFileList[i] != nullptr; i++)
     713          17 :                     Concat(osStr, psOptions->bStdoutOutput, "       %s\n",
     714          17 :                            papszFileList[i]);
     715             :             }
     716             :         }
     717         149 :         CSLDestroy(papszFileList);
     718             :     }
     719             : 
     720         151 :     if (bJson)
     721             :     {
     722             :         {
     723          95 :             json_object *poSize = json_object_new_array();
     724             :             json_object *poSizeX =
     725          95 :                 json_object_new_int(GDALGetRasterXSize(hDataset));
     726             :             json_object *poSizeY =
     727          95 :                 json_object_new_int(GDALGetRasterYSize(hDataset));
     728             : 
     729             :             // size is X, Y ordered
     730          95 :             json_object_array_add(poSize, poSizeX);
     731          95 :             json_object_array_add(poSize, poSizeY);
     732             : 
     733          95 :             json_object_object_add(poJsonObject, "size", poSize);
     734             :         }
     735             : 
     736             :         {
     737          95 :             json_object *poStacSize = json_object_new_array();
     738             :             json_object *poSizeX =
     739          95 :                 json_object_new_int(GDALGetRasterXSize(hDataset));
     740             :             json_object *poSizeY =
     741          95 :                 json_object_new_int(GDALGetRasterYSize(hDataset));
     742             : 
     743             :             // ... but ... proj:shape is Y, X ordered.
     744          95 :             json_object_array_add(poStacSize, poSizeY);
     745          95 :             json_object_array_add(poStacSize, poSizeX);
     746             : 
     747          95 :             json_object_object_add(poStac, "proj:shape", poStacSize);
     748             :         }
     749             :     }
     750             :     else
     751             :     {
     752          56 :         Concat(osStr, psOptions->bStdoutOutput, "Size is %d, %d\n",
     753             :                GDALGetRasterXSize(hDataset), GDALGetRasterYSize(hDataset));
     754             :     }
     755             : 
     756         302 :     CPLString osWKTFormat("FORMAT=");
     757         151 :     osWKTFormat += psOptions->osWKTFormat;
     758         151 :     const char *const apszWKTOptions[] = {osWKTFormat.c_str(), "MULTILINE=YES",
     759         151 :                                           nullptr};
     760             : 
     761             :     /* -------------------------------------------------------------------- */
     762             :     /*      Report projection.                                              */
     763             :     /* -------------------------------------------------------------------- */
     764         151 :     auto hSRS = GDALGetSpatialRef(hDataset);
     765         151 :     if (hSRS != nullptr)
     766             :     {
     767             :         const OGRSpatialReference *poSRS =
     768         128 :             OGRSpatialReference::FromHandle(hSRS);
     769             : 
     770         128 :         json_object *poCoordinateSystem = nullptr;
     771             : 
     772         128 :         if (bJson)
     773          80 :             poCoordinateSystem = json_object_new_object();
     774             : 
     775         256 :         const std::string osWkt = poSRS->exportToWkt(apszWKTOptions);
     776             : 
     777         256 :         const std::vector<int> anAxes = poSRS->GetDataAxisToSRSAxisMapping();
     778             : 
     779         128 :         const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
     780             : 
     781         128 :         const char *pszAuthCode = poSRS->GetAuthorityCode();
     782         128 :         const char *pszAuthName = poSRS->GetAuthorityName();
     783         128 :         if (bJson)
     784             :         {
     785          80 :             json_object *poWkt = json_object_new_string(osWkt.c_str());
     786          80 :             if (psOptions->osWKTFormat == "WKT2")
     787             :             {
     788          73 :                 json_object *poStacWkt = nullptr;
     789          73 :                 json_object_deep_copy(poWkt, &poStacWkt, nullptr);
     790          73 :                 json_object_object_add(poStac, "proj:wkt2", poStacWkt);
     791             :             }
     792          80 :             json_object_object_add(poCoordinateSystem, "wkt", poWkt);
     793             : 
     794          80 :             if (pszAuthCode && pszAuthName && EQUAL(pszAuthName, "EPSG"))
     795             :             {
     796          63 :                 json_object *poEPSG = json_object_new_int64(atoi(pszAuthCode));
     797          63 :                 json_object_object_add(poStac, "proj:epsg", poEPSG);
     798             :             }
     799             :             else
     800             :             {
     801             :                 // Setting it to null is mandated by the
     802             :                 // https://github.com/stac-extensions/projection#projepsg
     803             :                 // when setting proj:projjson or proj:wkt2
     804          17 :                 json_object_object_add(poStac, "proj:epsg", nullptr);
     805             :             }
     806             :             {
     807          80 :                 char *pszProjJson = nullptr;
     808          80 :                 OGRErr result = poSRS->exportToPROJJSON(&pszProjJson, nullptr);
     809          80 :                 if (result == OGRERR_NONE)
     810             :                 {
     811             :                     json_object *poStacProjJson =
     812          80 :                         json_tokener_parse(pszProjJson);
     813          80 :                     json_object_object_add(poStac, "proj:projjson",
     814             :                                            poStacProjJson);
     815          80 :                     CPLFree(pszProjJson);
     816             :                 }
     817             :             }
     818             : 
     819          80 :             json_object *poAxisMapping = json_object_new_array();
     820         242 :             for (int nMapping : anAxes)
     821             :             {
     822         162 :                 json_object_array_add(poAxisMapping,
     823             :                                       json_object_new_int(nMapping));
     824             :             }
     825          80 :             json_object_object_add(poCoordinateSystem,
     826             :                                    "dataAxisToSRSAxisMapping", poAxisMapping);
     827             : 
     828          80 :             if (dfCoordinateEpoch > 0)
     829             :             {
     830           2 :                 json_object_object_add(
     831             :                     poJsonObject, "coordinateEpoch",
     832             :                     json_object_new_double(dfCoordinateEpoch));
     833             :             }
     834             :         }
     835             :         else
     836             :         {
     837          48 :             if (psOptions->osInvokedFrom == "gdal-raster-info")
     838             :             {
     839          10 :                 EmitTextDisplayOfCRS(poSRS, psOptions->osCRSFormat,
     840             :                                      "Coordinate Reference System",
     841         148 :                                      [&osStr, psOptions](const std::string &s)
     842             :                                      {
     843          74 :                                          Concat(osStr, psOptions->bStdoutOutput,
     844             :                                                 "%s", s.c_str());
     845          74 :                                      });
     846             :             }
     847             :             else
     848             :             {
     849          38 :                 Concat(osStr, psOptions->bStdoutOutput,
     850             :                        "Coordinate System is:\n%s\n", osWkt.c_str());
     851             :             }
     852             : 
     853          48 :             Concat(osStr, psOptions->bStdoutOutput,
     854             :                    "Data axis to CRS axis mapping: ");
     855         146 :             for (size_t i = 0; i < anAxes.size(); i++)
     856             :             {
     857          98 :                 if (i > 0)
     858             :                 {
     859          50 :                     Concat(osStr, psOptions->bStdoutOutput, ",");
     860             :                 }
     861          98 :                 Concat(osStr, psOptions->bStdoutOutput, "%d", anAxes[i]);
     862             :             }
     863          48 :             Concat(osStr, psOptions->bStdoutOutput, "\n");
     864             : 
     865          48 :             if (dfCoordinateEpoch > 0)
     866             :             {
     867             :                 std::string osCoordinateEpoch =
     868           4 :                     CPLSPrintf("%f", dfCoordinateEpoch);
     869           2 :                 const size_t nDotPos = osCoordinateEpoch.find('.');
     870           2 :                 if (nDotPos != std::string::npos)
     871             :                 {
     872          22 :                     while (osCoordinateEpoch.size() > nDotPos + 2 &&
     873          10 :                            osCoordinateEpoch.back() == '0')
     874          10 :                         osCoordinateEpoch.pop_back();
     875             :                 }
     876           2 :                 Concat(osStr, psOptions->bStdoutOutput,
     877             :                        "Coordinate epoch: %s\n", osCoordinateEpoch.c_str());
     878             :             }
     879             :         }
     880             : 
     881         128 :         if (psOptions->bReportProj4)
     882             :         {
     883           2 :             char *pszProj4 = nullptr;
     884           2 :             OSRExportToProj4(hSRS, &pszProj4);
     885             : 
     886           2 :             if (bJson)
     887             :             {
     888           2 :                 json_object *proj4 = json_object_new_string(pszProj4);
     889           2 :                 json_object_object_add(poCoordinateSystem, "proj4", proj4);
     890             :             }
     891             :             else
     892           0 :                 Concat(osStr, psOptions->bStdoutOutput,
     893             :                        "PROJ.4 string is:\n\'%s\'\n", pszProj4);
     894           2 :             CPLFree(pszProj4);
     895             :         }
     896             : 
     897         128 :         if (bJson)
     898          80 :             json_object_object_add(poJsonObject, "coordinateSystem",
     899             :                                    poCoordinateSystem);
     900             :     }
     901             : 
     902             :     /* -------------------------------------------------------------------- */
     903             :     /*      Report Geotransform.                                            */
     904             :     /* -------------------------------------------------------------------- */
     905         151 :     double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
     906         151 :     if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
     907             :     {
     908         130 :         if (bJson)
     909             :         {
     910          80 :             json_object *poGeoTransform = json_object_new_array();
     911             : 
     912         560 :             for (int i = 0; i < 6; i++)
     913             :             {
     914             :                 json_object *poGeoTransformCoefficient =
     915         480 :                     json_object_new_double_with_precision(adfGeoTransform[i],
     916             :                                                           16);
     917         480 :                 json_object_array_add(poGeoTransform,
     918             :                                       poGeoTransformCoefficient);
     919             :             }
     920             : 
     921          80 :             json_object_object_add(poJsonObject, "geoTransform",
     922             :                                    poGeoTransform);
     923             : 
     924          80 :             json_object *poStacGeoTransform = json_object_new_array();
     925          80 :             json_object_array_add(
     926             :                 poStacGeoTransform,
     927             :                 json_object_new_double_with_precision(adfGeoTransform[1], 16));
     928          80 :             json_object_array_add(
     929             :                 poStacGeoTransform,
     930             :                 json_object_new_double_with_precision(adfGeoTransform[2], 16));
     931          80 :             json_object_array_add(
     932             :                 poStacGeoTransform,
     933             :                 json_object_new_double_with_precision(adfGeoTransform[0], 16));
     934          80 :             json_object_array_add(
     935             :                 poStacGeoTransform,
     936             :                 json_object_new_double_with_precision(adfGeoTransform[4], 16));
     937          80 :             json_object_array_add(
     938             :                 poStacGeoTransform,
     939             :                 json_object_new_double_with_precision(adfGeoTransform[5], 16));
     940          80 :             json_object_array_add(
     941             :                 poStacGeoTransform,
     942             :                 json_object_new_double_with_precision(adfGeoTransform[3], 16));
     943          80 :             json_object_object_add(poStac, "proj:transform",
     944             :                                    poStacGeoTransform);
     945             :         }
     946             :         else
     947             :         {
     948          50 :             if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
     949             :             {
     950          48 :                 Concat(osStr, psOptions->bStdoutOutput,
     951             :                        "Origin = (%.15f,%.15f)\n", adfGeoTransform[0],
     952             :                        adfGeoTransform[3]);
     953             : 
     954          48 :                 Concat(osStr, psOptions->bStdoutOutput,
     955             :                        "Pixel Size = (%.15f,%.15f)\n", adfGeoTransform[1],
     956             :                        adfGeoTransform[5]);
     957             :             }
     958             :             else
     959             :             {
     960           2 :                 Concat(osStr, psOptions->bStdoutOutput,
     961             :                        "GeoTransform =\n"
     962             :                        "  %.16g, %.16g, %.16g\n"
     963             :                        "  %.16g, %.16g, %.16g\n",
     964             :                        adfGeoTransform[0], adfGeoTransform[1],
     965             :                        adfGeoTransform[2], adfGeoTransform[3],
     966             :                        adfGeoTransform[4], adfGeoTransform[5]);
     967             :             }
     968             :         }
     969             :     }
     970             : 
     971             :     /* -------------------------------------------------------------------- */
     972             :     /*      Report GCPs.                                                    */
     973             :     /* -------------------------------------------------------------------- */
     974         151 :     if (psOptions->bShowGCPs && GDALGetGCPCount(hDataset) > 0)
     975             :     {
     976           2 :         json_object *const poGCPs = bJson ? json_object_new_object() : nullptr;
     977             : 
     978           2 :         hSRS = GDALGetGCPSpatialRef(hDataset);
     979           2 :         if (hSRS)
     980             :         {
     981           2 :             json_object *poGCPCoordinateSystem = nullptr;
     982             : 
     983           2 :             char *pszPrettyWkt = nullptr;
     984             : 
     985           2 :             int nAxesCount = 0;
     986             :             const int *panAxes =
     987           2 :                 OSRGetDataAxisToSRSAxisMapping(hSRS, &nAxesCount);
     988             : 
     989           2 :             OSRExportToWktEx(hSRS, &pszPrettyWkt, apszWKTOptions);
     990             : 
     991           2 :             if (bJson)
     992             :             {
     993           1 :                 json_object *poWkt = json_object_new_string(pszPrettyWkt);
     994           1 :                 poGCPCoordinateSystem = json_object_new_object();
     995             : 
     996           1 :                 json_object_object_add(poGCPCoordinateSystem, "wkt", poWkt);
     997             : 
     998           1 :                 json_object *poAxisMapping = json_object_new_array();
     999           3 :                 for (int i = 0; i < nAxesCount; i++)
    1000             :                 {
    1001           2 :                     json_object_array_add(poAxisMapping,
    1002           2 :                                           json_object_new_int(panAxes[i]));
    1003             :                 }
    1004           1 :                 json_object_object_add(poGCPCoordinateSystem,
    1005             :                                        "dataAxisToSRSAxisMapping",
    1006             :                                        poAxisMapping);
    1007             :             }
    1008             :             else
    1009             :             {
    1010           1 :                 Concat(osStr, psOptions->bStdoutOutput,
    1011             :                        "GCP Projection = \n%s\n", pszPrettyWkt);
    1012             : 
    1013           1 :                 Concat(osStr, psOptions->bStdoutOutput,
    1014             :                        "Data axis to CRS axis mapping: ");
    1015           3 :                 for (int i = 0; i < nAxesCount; i++)
    1016             :                 {
    1017           2 :                     if (i > 0)
    1018             :                     {
    1019           1 :                         Concat(osStr, psOptions->bStdoutOutput, ",");
    1020             :                     }
    1021           2 :                     Concat(osStr, psOptions->bStdoutOutput, "%d", panAxes[i]);
    1022             :                 }
    1023           1 :                 Concat(osStr, psOptions->bStdoutOutput, "\n");
    1024             :             }
    1025           2 :             CPLFree(pszPrettyWkt);
    1026             : 
    1027           2 :             if (bJson)
    1028           1 :                 json_object_object_add(poGCPs, "coordinateSystem",
    1029             :                                        poGCPCoordinateSystem);
    1030             :         }
    1031             : 
    1032             :         json_object *const poGCPList =
    1033           2 :             bJson ? json_object_new_array() : nullptr;
    1034             : 
    1035          10 :         for (int i = 0; i < GDALGetGCPCount(hDataset); i++)
    1036             :         {
    1037           8 :             const GDAL_GCP *psGCP = GDALGetGCPs(hDataset) + i;
    1038           8 :             if (bJson)
    1039             :             {
    1040           4 :                 json_object *poGCP = json_object_new_object();
    1041           4 :                 json_object *poId = json_object_new_string(psGCP->pszId);
    1042           4 :                 json_object *poInfo = json_object_new_string(psGCP->pszInfo);
    1043           8 :                 json_object *poPixel = json_object_new_double_with_precision(
    1044           4 :                     psGCP->dfGCPPixel, 15);
    1045             :                 json_object *poLine =
    1046           4 :                     json_object_new_double_with_precision(psGCP->dfGCPLine, 15);
    1047             :                 json_object *poX =
    1048           4 :                     json_object_new_double_with_precision(psGCP->dfGCPX, 15);
    1049             :                 json_object *poY =
    1050           4 :                     json_object_new_double_with_precision(psGCP->dfGCPY, 15);
    1051             :                 json_object *poZ =
    1052           4 :                     json_object_new_double_with_precision(psGCP->dfGCPZ, 15);
    1053             : 
    1054           4 :                 json_object_object_add(poGCP, "id", poId);
    1055           4 :                 json_object_object_add(poGCP, "info", poInfo);
    1056           4 :                 json_object_object_add(poGCP, "pixel", poPixel);
    1057           4 :                 json_object_object_add(poGCP, "line", poLine);
    1058           4 :                 json_object_object_add(poGCP, "x", poX);
    1059           4 :                 json_object_object_add(poGCP, "y", poY);
    1060           4 :                 json_object_object_add(poGCP, "z", poZ);
    1061           4 :                 json_object_array_add(poGCPList, poGCP);
    1062             :             }
    1063             :             else
    1064             :             {
    1065           4 :                 Concat(osStr, psOptions->bStdoutOutput,
    1066             :                        "GCP[%3d]: Id=%s, Info=%s\n"
    1067             :                        "          (%.15g,%.15g) -> (%.15g,%.15g,%.15g)\n",
    1068           4 :                        i, psGCP->pszId, psGCP->pszInfo, psGCP->dfGCPPixel,
    1069           4 :                        psGCP->dfGCPLine, psGCP->dfGCPX, psGCP->dfGCPY,
    1070           4 :                        psGCP->dfGCPZ);
    1071             :             }
    1072             :         }
    1073           2 :         if (bJson)
    1074             :         {
    1075           1 :             json_object_object_add(poGCPs, "gcpList", poGCPList);
    1076           1 :             json_object_object_add(poJsonObject, "gcps", poGCPs);
    1077             :         }
    1078             :     }
    1079             : 
    1080             :     /* -------------------------------------------------------------------- */
    1081             :     /*      Report metadata.                                                */
    1082             :     /* -------------------------------------------------------------------- */
    1083             : 
    1084         151 :     GDALInfoReportMetadata(psOptions, hDataset, false, bJson, poMetadata,
    1085             :                            osStr);
    1086         151 :     if (bJson)
    1087             :     {
    1088          95 :         if (psOptions->bShowMetadata)
    1089          92 :             json_object_object_add(poJsonObject, "metadata", poMetadata);
    1090             :         else
    1091           3 :             json_object_put(poMetadata);
    1092             : 
    1093             :         // Include eo:cloud_cover in stac output
    1094             :         const char *pszCloudCover =
    1095          95 :             GDALGetMetadataItem(hDataset, "CLOUDCOVER", "IMAGERY");
    1096          95 :         json_object *poValue = nullptr;
    1097          95 :         if (pszCloudCover)
    1098             :         {
    1099           1 :             poValue = json_object_new_int(atoi(pszCloudCover));
    1100           1 :             json_object_object_add(poStac, "eo:cloud_cover", poValue);
    1101             :         }
    1102             :     }
    1103             : 
    1104             :     /* -------------------------------------------------------------------- */
    1105             :     /*      Setup projected to lat/long transform if appropriate.           */
    1106             :     /* -------------------------------------------------------------------- */
    1107         151 :     OGRSpatialReferenceH hProj = nullptr;
    1108         151 :     if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
    1109         130 :         hProj = GDALGetSpatialRef(hDataset);
    1110             : 
    1111         151 :     OGRCoordinateTransformationH hTransform = nullptr;
    1112         151 :     bool bTransformToWGS84 = false;
    1113             : 
    1114         151 :     if (hProj)
    1115             :     {
    1116         127 :         OGRSpatialReferenceH hLatLong = nullptr;
    1117             : 
    1118         127 :         if (bJson)
    1119             :         {
    1120             :             // Check that it looks like Earth before trying to reproject to wgs84...
    1121             :             // OSRGetSemiMajor() may raise an error on CRS like Engineering CRS
    1122         158 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1123          79 :             OGRErr eErr = OGRERR_NONE;
    1124         157 :             if (fabs(OSRGetSemiMajor(hProj, &eErr) - 6378137.0) < 10000.0 &&
    1125          78 :                 eErr == OGRERR_NONE)
    1126             :             {
    1127          77 :                 bTransformToWGS84 = true;
    1128          77 :                 hLatLong = OSRNewSpatialReference(nullptr);
    1129          77 :                 OSRSetWellKnownGeogCS(hLatLong, "WGS84");
    1130             :             }
    1131             :         }
    1132             :         else
    1133             :         {
    1134          48 :             hLatLong = OSRCloneGeogCS(hProj);
    1135          48 :             if (hLatLong)
    1136             :             {
    1137             :                 // Override GEOGCS|UNIT child to be sure to output as degrees
    1138          48 :                 OSRSetAngularUnits(hLatLong, SRS_UA_DEGREE,
    1139             :                                    CPLAtof(SRS_UA_DEGREE_CONV));
    1140             :             }
    1141             :         }
    1142             : 
    1143         127 :         if (hLatLong != nullptr)
    1144             :         {
    1145         125 :             OSRSetAxisMappingStrategy(hLatLong, OAMS_TRADITIONAL_GIS_ORDER);
    1146         125 :             CPLPushErrorHandler(CPLQuietErrorHandler);
    1147         125 :             hTransform = OCTNewCoordinateTransformation(hProj, hLatLong);
    1148         125 :             CPLPopErrorHandler();
    1149             : 
    1150         125 :             OSRDestroySpatialReference(hLatLong);
    1151             :         }
    1152             :     }
    1153             : 
    1154             :     /* -------------------------------------------------------------------- */
    1155             :     /*      Report corners.                                                 */
    1156             :     /* -------------------------------------------------------------------- */
    1157         151 :     if (bJson && GDALGetRasterXSize(hDataset))
    1158             :     {
    1159         190 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1160             : 
    1161          95 :         json_object *poCornerCoordinates = json_object_new_object();
    1162          95 :         json_object *poLongLatExtentCoordinates = json_object_new_array();
    1163             : 
    1164          95 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
    1165             :                              0.0, bJson, poCornerCoordinates,
    1166             :                              poLongLatExtentCoordinates, osStr);
    1167         190 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "lowerLeft", 0.0,
    1168          95 :                              GDALGetRasterYSize(hDataset), bJson,
    1169             :                              poCornerCoordinates, poLongLatExtentCoordinates,
    1170             :                              osStr);
    1171         285 :         GDALInfoReportCorner(
    1172             :             psOptions, hDataset, hTransform, "lowerRight",
    1173          95 :             GDALGetRasterXSize(hDataset), GDALGetRasterYSize(hDataset), bJson,
    1174             :             poCornerCoordinates, poLongLatExtentCoordinates, osStr);
    1175         190 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperRight",
    1176          95 :                              GDALGetRasterXSize(hDataset), 0.0, bJson,
    1177             :                              poCornerCoordinates, poLongLatExtentCoordinates,
    1178             :                              osStr);
    1179         285 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "center",
    1180          95 :                              GDALGetRasterXSize(hDataset) / 2.0,
    1181          95 :                              GDALGetRasterYSize(hDataset) / 2.0, bJson,
    1182             :                              poCornerCoordinates, poLongLatExtentCoordinates,
    1183             :                              osStr);
    1184          95 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
    1185             :                              0.0, bJson, poCornerCoordinates,
    1186             :                              poLongLatExtentCoordinates, osStr);
    1187             : 
    1188          95 :         json_object_object_add(poJsonObject, "cornerCoordinates",
    1189             :                                poCornerCoordinates);
    1190             : 
    1191          95 :         if (json_object_array_length(poLongLatExtentCoordinates) > 0)
    1192             :         {
    1193          77 :             json_object *poLinearRing = json_object_new_array();
    1194          77 :             json_object *poLongLatExtent = json_object_new_object();
    1195             :             json_object *poLongLatExtentType =
    1196          77 :                 json_object_new_string("Polygon");
    1197          77 :             json_object_object_add(poLongLatExtent, "type",
    1198             :                                    poLongLatExtentType);
    1199          77 :             json_object_array_add(poLinearRing, poLongLatExtentCoordinates);
    1200          77 :             json_object_object_add(poLongLatExtent, "coordinates",
    1201             :                                    poLinearRing);
    1202          77 :             json_object_object_add(poJsonObject,
    1203             :                                    bTransformToWGS84 ? "wgs84Extent" : "extent",
    1204             :                                    poLongLatExtent);
    1205             :         }
    1206             :         else
    1207             :         {
    1208          18 :             json_object_put(poLongLatExtentCoordinates);
    1209             :         }
    1210             :     }
    1211          56 :     else if (GDALGetRasterXSize(hDataset))
    1212             :     {
    1213         112 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1214             : 
    1215          56 :         Concat(osStr, psOptions->bStdoutOutput, "Corner Coordinates:\n");
    1216          56 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Left", 0.0,
    1217             :                              0.0, bJson, nullptr, nullptr, osStr);
    1218         112 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Left", 0.0,
    1219          56 :                              GDALGetRasterYSize(hDataset), bJson, nullptr,
    1220             :                              nullptr, osStr);
    1221         112 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Right",
    1222          56 :                              GDALGetRasterXSize(hDataset), 0.0, bJson, nullptr,
    1223             :                              nullptr, osStr);
    1224         168 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Right",
    1225          56 :                              GDALGetRasterXSize(hDataset),
    1226          56 :                              GDALGetRasterYSize(hDataset), bJson, nullptr,
    1227             :                              nullptr, osStr);
    1228         168 :         GDALInfoReportCorner(psOptions, hDataset, hTransform, "Center",
    1229          56 :                              GDALGetRasterXSize(hDataset) / 2.0,
    1230          56 :                              GDALGetRasterYSize(hDataset) / 2.0, bJson, nullptr,
    1231             :                              nullptr, osStr);
    1232             :     }
    1233             : 
    1234         151 :     if (hTransform != nullptr)
    1235             :     {
    1236         125 :         OCTDestroyCoordinateTransformation(hTransform);
    1237         125 :         hTransform = nullptr;
    1238             :     }
    1239             : 
    1240             :     /* ==================================================================== */
    1241             :     /*      Loop over bands.                                                */
    1242             :     /* ==================================================================== */
    1243         333 :     for (int iBand = 0; iBand < GDALGetRasterCount(hDataset); iBand++)
    1244             :     {
    1245         182 :         json_object *poBand = nullptr;
    1246         182 :         json_object *poBandMetadata = nullptr;
    1247         182 :         json_object *poStacRasterBand = nullptr;
    1248         182 :         json_object *poStacEOBand = nullptr;
    1249             : 
    1250         182 :         if (bJson)
    1251             :         {
    1252         122 :             poBand = json_object_new_object();
    1253         122 :             poBandMetadata = json_object_new_object();
    1254         122 :             poStacRasterBand = json_object_new_object();
    1255         122 :             poStacEOBand = json_object_new_object();
    1256             :         }
    1257             : 
    1258         182 :         GDALRasterBandH const hBand = GDALGetRasterBand(hDataset, iBand + 1);
    1259         182 :         const auto eDT = GDALGetRasterDataType(hBand);
    1260             : 
    1261         182 :         if (psOptions->bSample)
    1262             :         {
    1263           0 :             vector<float> ofSample(10000, 0);
    1264           0 :             float *const pafSample = &ofSample[0];
    1265             :             const int nCount =
    1266           0 :                 GDALGetRandomRasterSample(hBand, 10000, pafSample);
    1267           0 :             if (!bJson)
    1268           0 :                 Concat(osStr, psOptions->bStdoutOutput, "Got %d samples.\n",
    1269             :                        nCount);
    1270             :         }
    1271             : 
    1272         182 :         int nBlockXSize = 0;
    1273         182 :         int nBlockYSize = 0;
    1274         182 :         GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
    1275         182 :         if (bJson)
    1276             :         {
    1277         122 :             json_object *poBandNumber = json_object_new_int(iBand + 1);
    1278         122 :             json_object *poBlock = json_object_new_array();
    1279             :             json_object *poType =
    1280         122 :                 json_object_new_string(GDALGetDataTypeName(eDT));
    1281             :             json_object *poColorInterp =
    1282         122 :                 json_object_new_string(GDALGetColorInterpretationName(
    1283             :                     GDALGetRasterColorInterpretation(hBand)));
    1284             : 
    1285         122 :             json_object_array_add(poBlock, json_object_new_int(nBlockXSize));
    1286         122 :             json_object_array_add(poBlock, json_object_new_int(nBlockYSize));
    1287         122 :             json_object_object_add(poBand, "band", poBandNumber);
    1288         122 :             json_object_object_add(poBand, "block", poBlock);
    1289         122 :             json_object_object_add(poBand, "type", poType);
    1290         122 :             json_object_object_add(poBand, "colorInterpretation",
    1291             :                                    poColorInterp);
    1292             : 
    1293         122 :             const char *stacDataType = nullptr;
    1294         122 :             switch (eDT)
    1295             :             {
    1296         103 :                 case GDT_UInt8:
    1297         103 :                     stacDataType = "uint8";
    1298         103 :                     break;
    1299           0 :                 case GDT_Int8:
    1300           0 :                     stacDataType = "int8";
    1301           0 :                     break;
    1302           3 :                 case GDT_UInt16:
    1303           3 :                     stacDataType = "uint16";
    1304           3 :                     break;
    1305           0 :                 case GDT_Int16:
    1306           0 :                     stacDataType = "int16";
    1307           0 :                     break;
    1308           2 :                 case GDT_UInt32:
    1309           2 :                     stacDataType = "uint32";
    1310           2 :                     break;
    1311           1 :                 case GDT_Int32:
    1312           1 :                     stacDataType = "int32";
    1313           1 :                     break;
    1314           0 :                 case GDT_UInt64:
    1315           0 :                     stacDataType = "uint64";
    1316           0 :                     break;
    1317           0 :                 case GDT_Int64:
    1318           0 :                     stacDataType = "int64";
    1319           0 :                     break;
    1320           0 :                 case GDT_Float16:
    1321           0 :                     stacDataType = "float16";
    1322           0 :                     break;
    1323          11 :                 case GDT_Float32:
    1324          11 :                     stacDataType = "float32";
    1325          11 :                     break;
    1326           2 :                 case GDT_Float64:
    1327           2 :                     stacDataType = "float64";
    1328           2 :                     break;
    1329           0 :                 case GDT_CInt16:
    1330           0 :                     stacDataType = "cint16";
    1331           0 :                     break;
    1332           0 :                 case GDT_CInt32:
    1333           0 :                     stacDataType = "cint32";
    1334           0 :                     break;
    1335           0 :                 case GDT_CFloat16:
    1336           0 :                     stacDataType = "cfloat16";
    1337           0 :                     break;
    1338           0 :                 case GDT_CFloat32:
    1339           0 :                     stacDataType = "cfloat32";
    1340           0 :                     break;
    1341           0 :                 case GDT_CFloat64:
    1342           0 :                     stacDataType = "cfloat64";
    1343           0 :                     break;
    1344           0 :                 case GDT_Unknown:
    1345             :                 case GDT_TypeCount:
    1346           0 :                     stacDataType = nullptr;
    1347             :             }
    1348         122 :             if (stacDataType)
    1349         122 :                 json_object_object_add(poStacRasterBand, "data_type",
    1350             :                                        json_object_new_string(stacDataType));
    1351             :         }
    1352             :         else
    1353             :         {
    1354          60 :             Concat(osStr, psOptions->bStdoutOutput,
    1355             :                    "Band %d Block=%dx%d Type=%s, ColorInterp=%s\n", iBand + 1,
    1356             :                    nBlockXSize, nBlockYSize, GDALGetDataTypeName(eDT),
    1357             :                    GDALGetColorInterpretationName(
    1358             :                        GDALGetRasterColorInterpretation(hBand)));
    1359             :         }
    1360             : 
    1361         182 :         if (bJson)
    1362             :         {
    1363             :             json_object *poBandName =
    1364         122 :                 json_object_new_string(CPLSPrintf("b%i", iBand + 1));
    1365         122 :             json_object_object_add(poStacEOBand, "name", poBandName);
    1366             :         }
    1367             : 
    1368         182 :         const char *pszBandDesc = GDALGetDescription(hBand);
    1369         182 :         if (pszBandDesc != nullptr && strlen(pszBandDesc) > 0)
    1370             :         {
    1371          41 :             if (bJson)
    1372             :             {
    1373          35 :                 json_object_object_add(poBand, "description",
    1374             :                                        json_object_new_string(pszBandDesc));
    1375             : 
    1376          35 :                 json_object_object_add(poStacEOBand, "description",
    1377             :                                        json_object_new_string(pszBandDesc));
    1378             :             }
    1379             :             else
    1380             :             {
    1381           6 :                 Concat(osStr, psOptions->bStdoutOutput, "  Description = %s\n",
    1382             :                        pszBandDesc);
    1383             :             }
    1384             :         }
    1385             :         else
    1386             :         {
    1387         141 :             if (bJson)
    1388             :             {
    1389             :                 json_object *poColorInterp =
    1390          87 :                     json_object_new_string(GDALGetColorInterpretationName(
    1391             :                         GDALGetRasterColorInterpretation(hBand)));
    1392          87 :                 json_object_object_add(poStacEOBand, "description",
    1393             :                                        poColorInterp);
    1394             :             }
    1395             :         }
    1396             : 
    1397         182 :         if (bJson)
    1398             :         {
    1399         122 :             const char *pszCommonName = GDALGetSTACCommonNameFromColorInterp(
    1400             :                 GDALGetRasterColorInterpretation(hBand));
    1401         122 :             if (pszCommonName)
    1402             :             {
    1403          25 :                 json_object_object_add(poStacEOBand, "common_name",
    1404             :                                        json_object_new_string(pszCommonName));
    1405             :             }
    1406             :         }
    1407             : 
    1408             :         {
    1409         182 :             int bGotMin = FALSE;
    1410         182 :             int bGotMax = FALSE;
    1411         182 :             const double dfMin = GDALGetRasterMinimum(hBand, &bGotMin);
    1412         182 :             const double dfMax = GDALGetRasterMaximum(hBand, &bGotMax);
    1413         182 :             if (bGotMin || bGotMax || psOptions->bComputeMinMax)
    1414             :             {
    1415          38 :                 if (!bJson)
    1416           8 :                     Concat(osStr, psOptions->bStdoutOutput, "  ");
    1417          38 :                 if (bGotMin)
    1418             :                 {
    1419          35 :                     if (bJson)
    1420             :                     {
    1421             :                         json_object *poMin =
    1422          28 :                             gdal_json_object_new_double_or_str_for_non_finite(
    1423             :                                 dfMin, 3);
    1424          28 :                         json_object_object_add(poBand, "min", poMin);
    1425             :                     }
    1426             :                     else
    1427             :                     {
    1428           7 :                         Concat(osStr, psOptions->bStdoutOutput, "Min=%.3f ",
    1429             :                                dfMin);
    1430             :                     }
    1431             :                 }
    1432          38 :                 if (bGotMax)
    1433             :                 {
    1434          35 :                     if (bJson)
    1435             :                     {
    1436             :                         json_object *poMax =
    1437          28 :                             gdal_json_object_new_double_or_str_for_non_finite(
    1438             :                                 dfMax, 3);
    1439          28 :                         json_object_object_add(poBand, "max", poMax);
    1440             :                     }
    1441             :                     else
    1442             :                     {
    1443           7 :                         Concat(osStr, psOptions->bStdoutOutput, "Max=%.3f ",
    1444             :                                dfMax);
    1445             :                     }
    1446             :                 }
    1447             : 
    1448          38 :                 if (psOptions->bComputeMinMax)
    1449             :                 {
    1450           4 :                     CPLErrorReset();
    1451           4 :                     double adfCMinMax[2] = {0.0, 0.0};
    1452           4 :                     GDALComputeRasterMinMax(hBand, FALSE, adfCMinMax);
    1453           4 :                     if (CPLGetLastErrorType() == CE_None)
    1454             :                     {
    1455           4 :                         if (bJson)
    1456             :                         {
    1457             :                             json_object *poComputedMin =
    1458           2 :                                 gdal_json_object_new_double_or_str_for_non_finite(
    1459             :                                     adfCMinMax[0], 3);
    1460             :                             json_object *poComputedMax =
    1461           2 :                                 gdal_json_object_new_double_or_str_for_non_finite(
    1462             :                                     adfCMinMax[1], 3);
    1463           2 :                             json_object_object_add(poBand, "computedMin",
    1464             :                                                    poComputedMin);
    1465           2 :                             json_object_object_add(poBand, "computedMax",
    1466             :                                                    poComputedMax);
    1467             :                         }
    1468             :                         else
    1469             :                         {
    1470           2 :                             Concat(osStr, psOptions->bStdoutOutput,
    1471             :                                    "  Computed Min/Max=%.3f,%.3f",
    1472             :                                    adfCMinMax[0], adfCMinMax[1]);
    1473             :                         }
    1474             :                     }
    1475             :                 }
    1476          38 :                 if (!bJson)
    1477           8 :                     Concat(osStr, psOptions->bStdoutOutput, "\n");
    1478             :             }
    1479             :         }
    1480             : 
    1481         182 :         double dfMinStat = 0.0;
    1482         182 :         double dfMaxStat = 0.0;
    1483         182 :         double dfMean = 0.0;
    1484         182 :         double dfStdDev = 0.0;
    1485         364 :         CPLErr eErr = GDALGetRasterStatistics(hBand, psOptions->bApproxStats,
    1486         182 :                                               psOptions->bStats, &dfMinStat,
    1487             :                                               &dfMaxStat, &dfMean, &dfStdDev);
    1488         182 :         if (eErr == CE_None)
    1489             :         {
    1490          17 :             if (bJson)
    1491             :             {
    1492           8 :                 json_object *poStacStats = json_object_new_object();
    1493             :                 json_object *poMinimum =
    1494           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
    1495             :                                                                       3);
    1496           8 :                 json_object_object_add(poBand, "minimum", poMinimum);
    1497             :                 json_object *poStacMinimum =
    1498           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
    1499             :                                                                       3);
    1500           8 :                 json_object_object_add(poStacStats, "minimum", poStacMinimum);
    1501             : 
    1502             :                 json_object *poMaximum =
    1503           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
    1504             :                                                                       3);
    1505           8 :                 json_object_object_add(poBand, "maximum", poMaximum);
    1506             :                 json_object *poStacMaximum =
    1507           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
    1508             :                                                                       3);
    1509           8 :                 json_object_object_add(poStacStats, "maximum", poStacMaximum);
    1510             : 
    1511             :                 json_object *poMean =
    1512           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfMean,
    1513             :                                                                       3);
    1514           8 :                 json_object_object_add(poBand, "mean", poMean);
    1515             :                 json_object *poStacMean =
    1516           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfMean,
    1517             :                                                                       3);
    1518           8 :                 json_object_object_add(poStacStats, "mean", poStacMean);
    1519             : 
    1520             :                 json_object *poStdDev =
    1521           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
    1522             :                                                                       3);
    1523           8 :                 json_object_object_add(poBand, "stdDev", poStdDev);
    1524             :                 json_object *poStacStdDev =
    1525           8 :                     gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
    1526             :                                                                       3);
    1527           8 :                 json_object_object_add(poStacStats, "stddev", poStacStdDev);
    1528             : 
    1529           8 :                 json_object_object_add(poStacRasterBand, "stats", poStacStats);
    1530             :             }
    1531             :             else
    1532             :             {
    1533           9 :                 Concat(osStr, psOptions->bStdoutOutput,
    1534             :                        "  Minimum=%.3f, Maximum=%.3f, Mean=%.3f, StdDev=%.3f\n",
    1535             :                        dfMinStat, dfMaxStat, dfMean, dfStdDev);
    1536             :             }
    1537             :         }
    1538             : 
    1539         182 :         if (psOptions->bReportHistograms)
    1540             :         {
    1541           5 :             int nBucketCount = 0;
    1542           5 :             GUIntBig *panHistogram = nullptr;
    1543             : 
    1544           5 :             if (bJson)
    1545           4 :                 eErr = GDALGetDefaultHistogramEx(
    1546             :                     hBand, &dfMinStat, &dfMaxStat, &nBucketCount, &panHistogram,
    1547             :                     TRUE, GDALDummyProgress, nullptr);
    1548             :             else
    1549           1 :                 eErr = GDALGetDefaultHistogramEx(
    1550             :                     hBand, &dfMinStat, &dfMaxStat, &nBucketCount, &panHistogram,
    1551             :                     TRUE, GDALTermProgress, nullptr);
    1552           5 :             if (eErr == CE_None)
    1553             :             {
    1554           5 :                 json_object *poHistogram = nullptr;
    1555           5 :                 json_object *poBuckets = nullptr;
    1556             : 
    1557           5 :                 if (bJson)
    1558             :                 {
    1559           4 :                     json_object *poCount = json_object_new_int(nBucketCount);
    1560           4 :                     json_object *poMin = json_object_new_double(dfMinStat);
    1561           4 :                     json_object *poMax = json_object_new_double(dfMaxStat);
    1562             : 
    1563           4 :                     poBuckets = json_object_new_array();
    1564           4 :                     poHistogram = json_object_new_object();
    1565           4 :                     json_object_object_add(poHistogram, "count", poCount);
    1566           4 :                     json_object_object_add(poHistogram, "min", poMin);
    1567           4 :                     json_object_object_add(poHistogram, "max", poMax);
    1568             :                 }
    1569             :                 else
    1570             :                 {
    1571           1 :                     Concat(osStr, psOptions->bStdoutOutput,
    1572             :                            "  %d buckets from %g to %g:\n  ", nBucketCount,
    1573             :                            dfMinStat, dfMaxStat);
    1574             :                 }
    1575             : 
    1576        1285 :                 for (int iBucket = 0; iBucket < nBucketCount; iBucket++)
    1577             :                 {
    1578        1280 :                     if (bJson)
    1579             :                     {
    1580             :                         json_object *poBucket =
    1581        1024 :                             json_object_new_int64(panHistogram[iBucket]);
    1582        1024 :                         json_object_array_add(poBuckets, poBucket);
    1583             :                     }
    1584             :                     else
    1585         256 :                         Concat(osStr, psOptions->bStdoutOutput,
    1586         256 :                                CPL_FRMT_GUIB " ", panHistogram[iBucket]);
    1587             :                 }
    1588           5 :                 if (bJson)
    1589             :                 {
    1590           4 :                     json_object_object_add(poHistogram, "buckets", poBuckets);
    1591           4 :                     json_object *poStacHistogram = nullptr;
    1592           4 :                     json_object_deep_copy(poHistogram, &poStacHistogram,
    1593             :                                           nullptr);
    1594           4 :                     json_object_object_add(poBand, "histogram", poHistogram);
    1595           4 :                     json_object_object_add(poStacRasterBand, "histogram",
    1596             :                                            poStacHistogram);
    1597             :                 }
    1598             :                 else
    1599             :                 {
    1600           1 :                     Concat(osStr, psOptions->bStdoutOutput, "\n");
    1601             :                 }
    1602           5 :                 CPLFree(panHistogram);
    1603             :             }
    1604             :         }
    1605             : 
    1606         182 :         if (psOptions->bComputeChecksum)
    1607             :         {
    1608             :             const int nBandChecksum =
    1609          42 :                 GDALChecksumImage(hBand, 0, 0, GDALGetRasterXSize(hDataset),
    1610             :                                   GDALGetRasterYSize(hDataset));
    1611          42 :             if (bJson)
    1612             :             {
    1613          32 :                 json_object *poChecksum = json_object_new_int(nBandChecksum);
    1614          32 :                 json_object_object_add(poBand, "checksum", poChecksum);
    1615             :             }
    1616             :             else
    1617             :             {
    1618          10 :                 Concat(osStr, psOptions->bStdoutOutput, "  Checksum=%d\n",
    1619             :                        nBandChecksum);
    1620             :             }
    1621             :         }
    1622             : 
    1623         182 :         int bGotNodata = FALSE;
    1624         182 :         if (!psOptions->bShowNodata)
    1625             :         {
    1626             :             // nothing to do
    1627             :         }
    1628         180 :         else if (eDT == GDT_Int64)
    1629             :         {
    1630             :             const auto nNoData =
    1631           0 :                 GDALGetRasterNoDataValueAsInt64(hBand, &bGotNodata);
    1632           0 :             if (bGotNodata)
    1633             :             {
    1634           0 :                 if (bJson)
    1635             :                 {
    1636           0 :                     json_object *poNoDataValue = json_object_new_int64(nNoData);
    1637           0 :                     json_object *poStacNoDataValue = nullptr;
    1638           0 :                     json_object_deep_copy(poNoDataValue, &poStacNoDataValue,
    1639             :                                           nullptr);
    1640           0 :                     json_object_object_add(poStacRasterBand, "nodata",
    1641             :                                            poStacNoDataValue);
    1642           0 :                     json_object_object_add(poBand, "noDataValue",
    1643             :                                            poNoDataValue);
    1644             :                 }
    1645             :                 else
    1646             :                 {
    1647           0 :                     Concat(osStr, psOptions->bStdoutOutput,
    1648             :                            "  NoData Value=" CPL_FRMT_GIB "\n",
    1649             :                            static_cast<GIntBig>(nNoData));
    1650             :                 }
    1651             :             }
    1652             :         }
    1653         180 :         else if (eDT == GDT_UInt64)
    1654             :         {
    1655             :             const auto nNoData =
    1656           0 :                 GDALGetRasterNoDataValueAsUInt64(hBand, &bGotNodata);
    1657           0 :             if (bGotNodata)
    1658             :             {
    1659           0 :                 if (bJson)
    1660             :                 {
    1661           0 :                     if (nNoData < static_cast<uint64_t>(
    1662           0 :                                       std::numeric_limits<int64_t>::max()))
    1663             :                     {
    1664           0 :                         json_object *poNoDataValue = json_object_new_int64(
    1665             :                             static_cast<int64_t>(nNoData));
    1666           0 :                         json_object *poStacNoDataValue = nullptr;
    1667           0 :                         json_object_deep_copy(poNoDataValue, &poStacNoDataValue,
    1668             :                                               nullptr);
    1669           0 :                         json_object_object_add(poStacRasterBand, "nodata",
    1670             :                                                poStacNoDataValue);
    1671           0 :                         json_object_object_add(poBand, "noDataValue",
    1672             :                                                poNoDataValue);
    1673             :                     }
    1674             :                     else
    1675             :                     {
    1676             :                         // not pretty to serialize as a string but there's no
    1677             :                         // way to serialize a uint64_t with libjson-c
    1678             :                         json_object *poNoDataValue =
    1679           0 :                             json_object_new_string(CPLSPrintf(
    1680             :                                 CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
    1681           0 :                         json_object_object_add(poBand, "noDataValue",
    1682             :                                                poNoDataValue);
    1683             :                     }
    1684             :                 }
    1685             :                 else
    1686             :                 {
    1687           0 :                     Concat(osStr, psOptions->bStdoutOutput,
    1688             :                            "  NoData Value=" CPL_FRMT_GUIB "\n",
    1689             :                            static_cast<GUIntBig>(nNoData));
    1690             :                 }
    1691             :             }
    1692             :         }
    1693             :         else
    1694             :         {
    1695             :             const double dfNoData =
    1696         180 :                 GDALGetRasterNoDataValue(hBand, &bGotNodata);
    1697         180 :             if (bGotNodata)
    1698             :             {
    1699          28 :                 const bool bIsNoDataFloat =
    1700          44 :                     eDT == GDT_Float32 &&
    1701          16 :                     static_cast<double>(static_cast<float>(dfNoData)) ==
    1702             :                         dfNoData;
    1703             :                 // Find the most compact decimal representation of the nodata
    1704             :                 // value that can be used to exactly represent the binary value
    1705          28 :                 int nSignificantDigits = bIsNoDataFloat ? 8 : 18;
    1706          28 :                 char szNoData[64] = {0};
    1707         324 :                 while (nSignificantDigits > 0)
    1708             :                 {
    1709             :                     char szCandidateNoData[64];
    1710             :                     char szFormat[16];
    1711         301 :                     snprintf(szFormat, sizeof(szFormat), "%%.%dg",
    1712             :                              nSignificantDigits);
    1713         301 :                     CPLsnprintf(szCandidateNoData, sizeof(szCandidateNoData),
    1714             :                                 szFormat, dfNoData);
    1715         273 :                     if (szNoData[0] == '\0' ||
    1716         112 :                         (bIsNoDataFloat &&
    1717         112 :                          static_cast<float>(CPLAtof(szCandidateNoData)) ==
    1718         574 :                              static_cast<float>(dfNoData)) ||
    1719         161 :                         (!bIsNoDataFloat &&
    1720         161 :                          CPLAtof(szCandidateNoData) == dfNoData))
    1721             :                     {
    1722         296 :                         strcpy(szNoData, szCandidateNoData);
    1723         296 :                         nSignificantDigits--;
    1724             :                     }
    1725             :                     else
    1726             :                     {
    1727           5 :                         nSignificantDigits++;
    1728           5 :                         break;
    1729             :                     }
    1730             :                 }
    1731             : 
    1732          28 :                 if (bJson)
    1733             :                 {
    1734             :                     json_object *poNoDataValue =
    1735          23 :                         (GDALDataTypeIsInteger(eDT) && dfNoData >= INT_MIN &&
    1736           5 :                          dfNoData <= INT_MAX &&
    1737           5 :                          static_cast<int>(dfNoData) == dfNoData)
    1738          23 :                             ? json_object_new_int(static_cast<int>(dfNoData))
    1739          13 :                             : gdal_json_object_new_double_significant_digits(
    1740          18 :                                   dfNoData, nSignificantDigits);
    1741             :                     json_object *poStacNoDataValue =
    1742          23 :                         (GDALDataTypeIsInteger(eDT) && dfNoData >= INT_MIN &&
    1743           5 :                          dfNoData <= INT_MAX &&
    1744           5 :                          static_cast<int>(dfNoData) == dfNoData)
    1745          23 :                             ? json_object_new_int(static_cast<int>(dfNoData))
    1746          13 :                             : gdal_json_object_new_double_significant_digits(
    1747          18 :                                   dfNoData, nSignificantDigits);
    1748          18 :                     json_object_object_add(poStacRasterBand, "nodata",
    1749             :                                            poStacNoDataValue);
    1750          18 :                     json_object_object_add(poBand, "noDataValue",
    1751             :                                            poNoDataValue);
    1752             :                 }
    1753          10 :                 else if (std::isnan(dfNoData))
    1754             :                 {
    1755           0 :                     Concat(osStr, psOptions->bStdoutOutput,
    1756             :                            "  NoData Value=nan\n");
    1757             :                 }
    1758             :                 else
    1759             :                 {
    1760          10 :                     Concat(osStr, psOptions->bStdoutOutput,
    1761             :                            "  NoData Value=%s\n", szNoData);
    1762             :                 }
    1763             :             }
    1764             :         }
    1765             : 
    1766         182 :         if (GDALGetOverviewCount(hBand) > 0)
    1767             :         {
    1768           9 :             json_object *poOverviews = nullptr;
    1769             : 
    1770           9 :             if (bJson)
    1771           8 :                 poOverviews = json_object_new_array();
    1772             :             else
    1773           1 :                 Concat(osStr, psOptions->bStdoutOutput, "  Overviews: ");
    1774             : 
    1775          35 :             for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
    1776             :                  iOverview++)
    1777             :             {
    1778          26 :                 if (!bJson)
    1779           1 :                     if (iOverview != 0)
    1780           0 :                         Concat(osStr, psOptions->bStdoutOutput, ", ");
    1781             : 
    1782          26 :                 GDALRasterBandH hOverview = GDALGetOverview(hBand, iOverview);
    1783          26 :                 if (hOverview != nullptr)
    1784             :                 {
    1785          26 :                     if (bJson)
    1786             :                     {
    1787          25 :                         json_object *poOverviewSize = json_object_new_array();
    1788          25 :                         json_object *poOverviewSizeX = json_object_new_int(
    1789             :                             GDALGetRasterBandXSize(hOverview));
    1790          25 :                         json_object *poOverviewSizeY = json_object_new_int(
    1791             :                             GDALGetRasterBandYSize(hOverview));
    1792             : 
    1793          25 :                         json_object *poOverview = json_object_new_object();
    1794          25 :                         json_object_array_add(poOverviewSize, poOverviewSizeX);
    1795          25 :                         json_object_array_add(poOverviewSize, poOverviewSizeY);
    1796          25 :                         json_object_object_add(poOverview, "size",
    1797             :                                                poOverviewSize);
    1798             : 
    1799          25 :                         if (psOptions->bComputeChecksum)
    1800             :                         {
    1801          16 :                             const int nOverviewChecksum = GDALChecksumImage(
    1802             :                                 hOverview, 0, 0,
    1803             :                                 GDALGetRasterBandXSize(hOverview),
    1804             :                                 GDALGetRasterBandYSize(hOverview));
    1805             :                             json_object *poOverviewChecksum =
    1806          16 :                                 json_object_new_int(nOverviewChecksum);
    1807          16 :                             json_object_object_add(poOverview, "checksum",
    1808             :                                                    poOverviewChecksum);
    1809             :                         }
    1810          25 :                         json_object_array_add(poOverviews, poOverview);
    1811             :                     }
    1812             :                     else
    1813             :                     {
    1814           1 :                         Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
    1815             :                                GDALGetRasterBandXSize(hOverview),
    1816             :                                GDALGetRasterBandYSize(hOverview));
    1817             :                     }
    1818             : 
    1819             :                     const char *pszResampling =
    1820          26 :                         GDALGetMetadataItem(hOverview, "RESAMPLING", "");
    1821             : 
    1822          26 :                     if (pszResampling != nullptr && !bJson &&
    1823           0 :                         STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
    1824           0 :                         Concat(osStr, psOptions->bStdoutOutput, "*");
    1825             :                 }
    1826             :                 else
    1827             :                 {
    1828           0 :                     if (!bJson)
    1829           0 :                         Concat(osStr, psOptions->bStdoutOutput, "(null)");
    1830             :                 }
    1831             :             }
    1832           9 :             if (bJson)
    1833           8 :                 json_object_object_add(poBand, "overviews", poOverviews);
    1834             :             else
    1835           1 :                 Concat(osStr, psOptions->bStdoutOutput, "\n");
    1836             : 
    1837           9 :             if (psOptions->bComputeChecksum && !bJson)
    1838             :             {
    1839           0 :                 Concat(osStr, psOptions->bStdoutOutput,
    1840             :                        "  Overviews checksum: ");
    1841             : 
    1842           0 :                 for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
    1843             :                      iOverview++)
    1844             :                 {
    1845             :                     GDALRasterBandH hOverview;
    1846             : 
    1847           0 :                     if (iOverview != 0)
    1848           0 :                         Concat(osStr, psOptions->bStdoutOutput, ", ");
    1849             : 
    1850           0 :                     hOverview = GDALGetOverview(hBand, iOverview);
    1851           0 :                     if (hOverview)
    1852             :                     {
    1853           0 :                         Concat(osStr, psOptions->bStdoutOutput, "%d",
    1854             :                                GDALChecksumImage(
    1855             :                                    hOverview, 0, 0,
    1856             :                                    GDALGetRasterBandXSize(hOverview),
    1857             :                                    GDALGetRasterBandYSize(hOverview)));
    1858             :                     }
    1859             :                     else
    1860             :                     {
    1861           0 :                         Concat(osStr, psOptions->bStdoutOutput, "(null)");
    1862             :                     }
    1863             :                 }
    1864           0 :                 Concat(osStr, psOptions->bStdoutOutput, "\n");
    1865             :             }
    1866             :         }
    1867             : 
    1868         182 :         if (GDALHasArbitraryOverviews(hBand) && !bJson)
    1869             :         {
    1870           0 :             Concat(osStr, psOptions->bStdoutOutput, "  Overviews: arbitrary\n");
    1871             :         }
    1872             : 
    1873             :         const int nMaskFlags =
    1874         182 :             psOptions->bShowMask ? GDALGetMaskFlags(hBand) : GMF_ALL_VALID;
    1875         182 :         if ((nMaskFlags & (GMF_NODATA | GMF_ALL_VALID)) == 0 ||
    1876             :             nMaskFlags == (GMF_NODATA | GMF_PER_DATASET))
    1877             :         {
    1878          21 :             GDALRasterBandH hMaskBand = GDALGetMaskBand(hBand);
    1879          21 :             json_object *poMask = nullptr;
    1880          21 :             json_object *poFlags = nullptr;
    1881          21 :             json_object *poMaskOverviews = nullptr;
    1882             : 
    1883          21 :             if (bJson)
    1884             :             {
    1885          17 :                 poMask = json_object_new_object();
    1886          17 :                 poFlags = json_object_new_array();
    1887             :             }
    1888             :             else
    1889           4 :                 Concat(osStr, psOptions->bStdoutOutput, "  Mask Flags: ");
    1890          21 :             if (nMaskFlags & GMF_PER_DATASET)
    1891             :             {
    1892          19 :                 if (bJson)
    1893             :                 {
    1894          16 :                     json_object *poFlag = json_object_new_string("PER_DATASET");
    1895          16 :                     json_object_array_add(poFlags, poFlag);
    1896             :                 }
    1897             :                 else
    1898           3 :                     Concat(osStr, psOptions->bStdoutOutput, "PER_DATASET ");
    1899             :             }
    1900          21 :             if (nMaskFlags & GMF_ALPHA)
    1901             :             {
    1902          15 :                 if (bJson)
    1903             :                 {
    1904          15 :                     json_object *poFlag = json_object_new_string("ALPHA");
    1905          15 :                     json_object_array_add(poFlags, poFlag);
    1906             :                 }
    1907             :                 else
    1908           0 :                     Concat(osStr, psOptions->bStdoutOutput, "ALPHA ");
    1909             :             }
    1910          21 :             if (nMaskFlags & GMF_NODATA)
    1911             :             {
    1912           3 :                 if (bJson)
    1913             :                 {
    1914           0 :                     json_object *poFlag = json_object_new_string("NODATA");
    1915           0 :                     json_object_array_add(poFlags, poFlag);
    1916             :                 }
    1917             :                 else
    1918             :                 {
    1919           3 :                     Concat(osStr, psOptions->bStdoutOutput, "NODATA ");
    1920             :                 }
    1921             :             }
    1922             : 
    1923          21 :             if (bJson)
    1924          17 :                 json_object_object_add(poMask, "flags", poFlags);
    1925             :             else
    1926           4 :                 Concat(osStr, psOptions->bStdoutOutput, "\n");
    1927             : 
    1928          21 :             if (bJson)
    1929          17 :                 poMaskOverviews = json_object_new_array();
    1930             : 
    1931          21 :             if (hMaskBand != nullptr && GDALGetOverviewCount(hMaskBand) > 0)
    1932             :             {
    1933           0 :                 if (!bJson)
    1934           0 :                     Concat(osStr, psOptions->bStdoutOutput,
    1935             :                            "  Overviews of mask band: ");
    1936             : 
    1937           0 :                 for (int iOverview = 0;
    1938           0 :                      iOverview < GDALGetOverviewCount(hMaskBand); iOverview++)
    1939             :                 {
    1940             :                     GDALRasterBandH hOverview =
    1941           0 :                         GDALGetOverview(hMaskBand, iOverview);
    1942           0 :                     if (!hOverview)
    1943           0 :                         break;
    1944           0 :                     json_object *poMaskOverview = nullptr;
    1945           0 :                     json_object *poMaskOverviewSize = nullptr;
    1946             : 
    1947           0 :                     if (bJson)
    1948             :                     {
    1949           0 :                         poMaskOverview = json_object_new_object();
    1950           0 :                         poMaskOverviewSize = json_object_new_array();
    1951             :                     }
    1952             :                     else
    1953             :                     {
    1954           0 :                         if (iOverview != 0)
    1955           0 :                             Concat(osStr, psOptions->bStdoutOutput, ", ");
    1956             :                     }
    1957             : 
    1958           0 :                     if (bJson)
    1959             :                     {
    1960           0 :                         json_object *poMaskOverviewSizeX = json_object_new_int(
    1961             :                             GDALGetRasterBandXSize(hOverview));
    1962           0 :                         json_object *poMaskOverviewSizeY = json_object_new_int(
    1963             :                             GDALGetRasterBandYSize(hOverview));
    1964             : 
    1965           0 :                         json_object_array_add(poMaskOverviewSize,
    1966             :                                               poMaskOverviewSizeX);
    1967           0 :                         json_object_array_add(poMaskOverviewSize,
    1968             :                                               poMaskOverviewSizeY);
    1969           0 :                         json_object_object_add(poMaskOverview, "size",
    1970             :                                                poMaskOverviewSize);
    1971           0 :                         json_object_array_add(poMaskOverviews, poMaskOverview);
    1972             :                     }
    1973             :                     else
    1974             :                     {
    1975           0 :                         Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
    1976             :                                GDALGetRasterBandXSize(hOverview),
    1977             :                                GDALGetRasterBandYSize(hOverview));
    1978             :                     }
    1979             :                 }
    1980           0 :                 if (!bJson)
    1981           0 :                     Concat(osStr, psOptions->bStdoutOutput, "\n");
    1982             :             }
    1983          21 :             if (bJson)
    1984             :             {
    1985          17 :                 json_object_object_add(poMask, "overviews", poMaskOverviews);
    1986          17 :                 json_object_object_add(poBand, "mask", poMask);
    1987             :             }
    1988             :         }
    1989             : 
    1990         182 :         if (strlen(GDALGetRasterUnitType(hBand)) > 0)
    1991             :         {
    1992           0 :             if (bJson)
    1993             :             {
    1994             :                 json_object *poUnit =
    1995           0 :                     json_object_new_string(GDALGetRasterUnitType(hBand));
    1996           0 :                 json_object *poStacUnit = nullptr;
    1997           0 :                 json_object_deep_copy(poUnit, &poStacUnit, nullptr);
    1998           0 :                 json_object_object_add(poStacRasterBand, "unit", poStacUnit);
    1999           0 :                 json_object_object_add(poBand, "unit", poUnit);
    2000             :             }
    2001             :             else
    2002             :             {
    2003           0 :                 Concat(osStr, psOptions->bStdoutOutput, "  Unit Type: %s\n",
    2004             :                        GDALGetRasterUnitType(hBand));
    2005             :             }
    2006             :         }
    2007             : 
    2008         182 :         if (GDALGetRasterCategoryNames(hBand) != nullptr)
    2009             :         {
    2010           0 :             char **papszCategories = GDALGetRasterCategoryNames(hBand);
    2011           0 :             json_object *poCategories = nullptr;
    2012             : 
    2013           0 :             if (bJson)
    2014           0 :                 poCategories = json_object_new_array();
    2015             :             else
    2016           0 :                 Concat(osStr, psOptions->bStdoutOutput, "  Categories:\n");
    2017             : 
    2018           0 :             for (int i = 0; papszCategories[i] != nullptr; i++)
    2019             :             {
    2020           0 :                 if (bJson)
    2021             :                 {
    2022             :                     json_object *poCategoryName =
    2023           0 :                         json_object_new_string(papszCategories[i]);
    2024           0 :                     json_object_array_add(poCategories, poCategoryName);
    2025             :                 }
    2026             :                 else
    2027           0 :                     Concat(osStr, psOptions->bStdoutOutput, "    %3d: %s\n", i,
    2028           0 :                            papszCategories[i]);
    2029             :             }
    2030           0 :             if (bJson)
    2031           0 :                 json_object_object_add(poBand, "categories", poCategories);
    2032             :         }
    2033             : 
    2034         182 :         int bSuccess = FALSE;
    2035         364 :         if (GDALGetRasterScale(hBand, &bSuccess) != 1.0 ||
    2036         182 :             GDALGetRasterOffset(hBand, &bSuccess) != 0.0)
    2037             :         {
    2038           0 :             if (bJson)
    2039             :             {
    2040           0 :                 json_object *poOffset = json_object_new_double_with_precision(
    2041             :                     GDALGetRasterOffset(hBand, &bSuccess), 15);
    2042           0 :                 json_object *poScale = json_object_new_double_with_precision(
    2043             :                     GDALGetRasterScale(hBand, &bSuccess), 15);
    2044           0 :                 json_object *poStacScale = nullptr;
    2045           0 :                 json_object *poStacOffset = nullptr;
    2046           0 :                 json_object_deep_copy(poScale, &poStacScale, nullptr);
    2047           0 :                 json_object_deep_copy(poOffset, &poStacOffset, nullptr);
    2048           0 :                 json_object_object_add(poStacRasterBand, "scale", poStacScale);
    2049           0 :                 json_object_object_add(poStacRasterBand, "offset",
    2050             :                                        poStacOffset);
    2051           0 :                 json_object_object_add(poBand, "offset", poOffset);
    2052           0 :                 json_object_object_add(poBand, "scale", poScale);
    2053             :             }
    2054             :             else
    2055             :             {
    2056           0 :                 Concat(osStr, psOptions->bStdoutOutput,
    2057             :                        "  Offset: %.15g,   Scale:%.15g\n",
    2058             :                        GDALGetRasterOffset(hBand, &bSuccess),
    2059             :                        GDALGetRasterScale(hBand, &bSuccess));
    2060             :             }
    2061             :         }
    2062             : 
    2063         182 :         GDALInfoReportMetadata(psOptions, hBand, true, bJson, poBandMetadata,
    2064             :                                osStr);
    2065         182 :         if (bJson)
    2066             :         {
    2067         122 :             if (psOptions->bShowMetadata)
    2068         119 :                 json_object_object_add(poBand, "metadata", poBandMetadata);
    2069             :             else
    2070           3 :                 json_object_put(poBandMetadata);
    2071             :         }
    2072             : 
    2073             :         GDALColorTableH hTable;
    2074         191 :         if (GDALGetRasterColorInterpretation(hBand) == GCI_PaletteIndex &&
    2075           9 :             (hTable = GDALGetRasterColorTable(hBand)) != nullptr)
    2076             :         {
    2077           9 :             if (!bJson)
    2078           2 :                 Concat(osStr, psOptions->bStdoutOutput,
    2079             :                        "  Color Table (%s with %d entries)\n",
    2080             :                        GDALGetPaletteInterpretationName(
    2081             :                            GDALGetPaletteInterpretation(hTable)),
    2082             :                        GDALGetColorEntryCount(hTable));
    2083             : 
    2084           9 :             if (psOptions->bShowColorTable)
    2085             :             {
    2086           7 :                 json_object *poEntries = nullptr;
    2087             : 
    2088           7 :                 if (bJson)
    2089             :                 {
    2090             :                     json_object *poPalette =
    2091           6 :                         json_object_new_string(GDALGetPaletteInterpretationName(
    2092             :                             GDALGetPaletteInterpretation(hTable)));
    2093             :                     json_object *poCount =
    2094           6 :                         json_object_new_int(GDALGetColorEntryCount(hTable));
    2095             : 
    2096           6 :                     json_object *poColorTable = json_object_new_object();
    2097             : 
    2098           6 :                     json_object_object_add(poColorTable, "palette", poPalette);
    2099           6 :                     json_object_object_add(poColorTable, "count", poCount);
    2100             : 
    2101           6 :                     poEntries = json_object_new_array();
    2102           6 :                     json_object_object_add(poColorTable, "entries", poEntries);
    2103           6 :                     json_object_object_add(poBand, "colorTable", poColorTable);
    2104             :                 }
    2105             : 
    2106         750 :                 for (int i = 0; i < GDALGetColorEntryCount(hTable); i++)
    2107             :                 {
    2108             :                     GDALColorEntry sEntry;
    2109             : 
    2110         743 :                     GDALGetColorEntryAsRGB(hTable, i, &sEntry);
    2111             : 
    2112         743 :                     if (bJson)
    2113             :                     {
    2114         727 :                         json_object *poEntry = json_object_new_array();
    2115         727 :                         json_object *poC1 = json_object_new_int(sEntry.c1);
    2116         727 :                         json_object *poC2 = json_object_new_int(sEntry.c2);
    2117         727 :                         json_object *poC3 = json_object_new_int(sEntry.c3);
    2118         727 :                         json_object *poC4 = json_object_new_int(sEntry.c4);
    2119             : 
    2120         727 :                         json_object_array_add(poEntry, poC1);
    2121         727 :                         json_object_array_add(poEntry, poC2);
    2122         727 :                         json_object_array_add(poEntry, poC3);
    2123         727 :                         json_object_array_add(poEntry, poC4);
    2124         727 :                         json_object_array_add(poEntries, poEntry);
    2125             :                     }
    2126             :                     else
    2127             :                     {
    2128          16 :                         Concat(osStr, psOptions->bStdoutOutput,
    2129          16 :                                "  %3d: %d,%d,%d,%d\n", i, sEntry.c1, sEntry.c2,
    2130          16 :                                sEntry.c3, sEntry.c4);
    2131             :                     }
    2132             :                 }
    2133             :             }
    2134             :         }
    2135             : 
    2136         182 :         if (psOptions->bShowRAT && GDALGetDefaultRAT(hBand) != nullptr)
    2137             :         {
    2138          10 :             GDALRasterAttributeTableH hRAT = GDALGetDefaultRAT(hBand);
    2139             : 
    2140          10 :             if (bJson)
    2141             :             {
    2142             :                 json_object *poRAT =
    2143           9 :                     static_cast<json_object *>(GDALRATSerializeJSON(hRAT));
    2144           9 :                 json_object_object_add(poBand, "rat", poRAT);
    2145             :             }
    2146             :             else
    2147             :             {
    2148             :                 CPLXMLNode *psTree =
    2149           1 :                     static_cast<GDALRasterAttributeTable *>(hRAT)->Serialize();
    2150           1 :                 char *pszXMLText = CPLSerializeXMLTree(psTree);
    2151           1 :                 CPLDestroyXMLNode(psTree);
    2152           1 :                 Concat(osStr, psOptions->bStdoutOutput, "%s\n", pszXMLText);
    2153           1 :                 CPLFree(pszXMLText);
    2154             :             }
    2155             :         }
    2156         182 :         if (bJson)
    2157             :         {
    2158         122 :             json_object_array_add(poBands, poBand);
    2159         122 :             json_object_array_add(poStacRasterBands, poStacRasterBand);
    2160         122 :             json_object_array_add(poStacEOBands, poStacEOBand);
    2161             :         }
    2162             :     }
    2163             : 
    2164         151 :     if (bJson)
    2165             :     {
    2166          95 :         json_object_object_add(poJsonObject, "bands", poBands);
    2167          95 :         json_object_object_add(poStac, "raster:bands", poStacRasterBands);
    2168          95 :         json_object_object_add(poStac, "eo:bands", poStacEOBands);
    2169          95 :         json_object_object_add(poJsonObject, "stac", poStac);
    2170          95 :         Concat(osStr, psOptions->bStdoutOutput, "%s",
    2171             :                json_object_to_json_string_ext(
    2172             :                    poJsonObject, JSON_C_TO_STRING_PRETTY
    2173             : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
    2174             :                                      | JSON_C_TO_STRING_NOSLASHESCAPE
    2175             : #endif
    2176             :                    ));
    2177          95 :         json_object_put(poJsonObject);
    2178          95 :         Concat(osStr, psOptions->bStdoutOutput, "\n");
    2179             :     }
    2180             : 
    2181         151 :     if (psOptionsToFree != nullptr)
    2182           0 :         GDALInfoOptionsFree(psOptionsToFree);
    2183             : 
    2184         151 :     return VSI_STRDUP_VERBOSE(osStr);
    2185             : }
    2186             : 
    2187             : /************************************************************************/
    2188             : /*                        GDALInfoReportCorner()                        */
    2189             : /************************************************************************/
    2190             : 
    2191         850 : static int GDALInfoReportCorner(const GDALInfoOptions *psOptions,
    2192             :                                 GDALDatasetH hDataset,
    2193             :                                 OGRCoordinateTransformationH hTransform,
    2194             :                                 const char *corner_name, double x, double y,
    2195             :                                 bool bJson, json_object *poCornerCoordinates,
    2196             :                                 json_object *poLongLatExtentCoordinates,
    2197             :                                 CPLString &osStr)
    2198             : 
    2199             : {
    2200         850 :     if (!bJson)
    2201         280 :         Concat(osStr, psOptions->bStdoutOutput, "%-11s ", corner_name);
    2202             : 
    2203             :     /* -------------------------------------------------------------------- */
    2204             :     /*      Transform the point into georeferenced coordinates.             */
    2205             :     /* -------------------------------------------------------------------- */
    2206         850 :     double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    2207         850 :     double dfGeoX = 0.0;
    2208         850 :     double dfGeoY = 0.0;
    2209             : 
    2210         850 :     if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
    2211             :     {
    2212         730 :         dfGeoX = adfGeoTransform[0] + adfGeoTransform[1] * x +
    2213         730 :                  adfGeoTransform[2] * y;
    2214         730 :         dfGeoY = adfGeoTransform[3] + adfGeoTransform[4] * x +
    2215         730 :                  adfGeoTransform[5] * y;
    2216             :     }
    2217             :     else
    2218             :     {
    2219         120 :         if (bJson)
    2220             :         {
    2221          90 :             json_object *const poCorner = json_object_new_array();
    2222             :             json_object *const poX =
    2223          90 :                 json_object_new_double_with_precision(x, 1);
    2224             :             json_object *const poY =
    2225          90 :                 json_object_new_double_with_precision(y, 1);
    2226          90 :             json_object_array_add(poCorner, poX);
    2227          90 :             json_object_array_add(poCorner, poY);
    2228          90 :             json_object_object_add(poCornerCoordinates, corner_name, poCorner);
    2229             :         }
    2230             :         else
    2231             :         {
    2232          30 :             Concat(osStr, psOptions->bStdoutOutput, "(%7.1f,%7.1f)\n", x, y);
    2233             :         }
    2234         120 :         return FALSE;
    2235             :     }
    2236             : 
    2237             :     /* -------------------------------------------------------------------- */
    2238             :     /*      Report the georeferenced coordinates.                           */
    2239             :     /* -------------------------------------------------------------------- */
    2240         730 :     if (std::abs(dfGeoX) < 181 && std::abs(dfGeoY) < 91)
    2241             :     {
    2242         100 :         if (bJson)
    2243             :         {
    2244          60 :             json_object *const poCorner = json_object_new_array();
    2245             :             json_object *const poX =
    2246          60 :                 json_object_new_double_with_precision(dfGeoX, 7);
    2247             :             json_object *const poY =
    2248          60 :                 json_object_new_double_with_precision(dfGeoY, 7);
    2249          60 :             json_object_array_add(poCorner, poX);
    2250          60 :             json_object_array_add(poCorner, poY);
    2251          60 :             json_object_object_add(poCornerCoordinates, corner_name, poCorner);
    2252             :         }
    2253             :         else
    2254             :         {
    2255          40 :             Concat(osStr, psOptions->bStdoutOutput, "(%12.7f,%12.7f) ", dfGeoX,
    2256             :                    dfGeoY);
    2257             :         }
    2258             :     }
    2259             :     else
    2260             :     {
    2261         630 :         if (bJson)
    2262             :         {
    2263         420 :             json_object *const poCorner = json_object_new_array();
    2264             :             json_object *const poX =
    2265         420 :                 json_object_new_double_with_precision(dfGeoX, 3);
    2266             :             json_object *const poY =
    2267         420 :                 json_object_new_double_with_precision(dfGeoY, 3);
    2268         420 :             json_object_array_add(poCorner, poX);
    2269         420 :             json_object_array_add(poCorner, poY);
    2270         420 :             json_object_object_add(poCornerCoordinates, corner_name, poCorner);
    2271             :         }
    2272             :         else
    2273             :         {
    2274         210 :             Concat(osStr, psOptions->bStdoutOutput, "(%12.3f,%12.3f) ", dfGeoX,
    2275             :                    dfGeoY);
    2276             :         }
    2277             :     }
    2278             : 
    2279             :     /* -------------------------------------------------------------------- */
    2280             :     /*      Transform to latlong and report.                                */
    2281             :     /* -------------------------------------------------------------------- */
    2282         730 :     if (bJson)
    2283             :     {
    2284         480 :         double dfZ = 0.0;
    2285         865 :         if (hTransform != nullptr && !EQUAL(corner_name, "center") &&
    2286         385 :             OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
    2287             :         {
    2288         385 :             json_object *const poCorner = json_object_new_array();
    2289             :             json_object *const poX =
    2290         385 :                 json_object_new_double_with_precision(dfGeoX, 7);
    2291             :             json_object *const poY =
    2292         385 :                 json_object_new_double_with_precision(dfGeoY, 7);
    2293         385 :             json_object_array_add(poCorner, poX);
    2294         385 :             json_object_array_add(poCorner, poY);
    2295         385 :             json_object_array_add(poLongLatExtentCoordinates, poCorner);
    2296             :         }
    2297             :     }
    2298             :     else
    2299             :     {
    2300         250 :         double dfZ = 0.0;
    2301         490 :         if (hTransform != nullptr &&
    2302         240 :             OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
    2303             :         {
    2304         240 :             Concat(osStr, psOptions->bStdoutOutput, "(%s,",
    2305             :                    GDALDecToDMS(dfGeoX, "Long", 2));
    2306         240 :             Concat(osStr, psOptions->bStdoutOutput, "%s)",
    2307             :                    GDALDecToDMS(dfGeoY, "Lat", 2));
    2308             :         }
    2309         250 :         Concat(osStr, psOptions->bStdoutOutput, "\n");
    2310             :     }
    2311             : 
    2312         730 :     return TRUE;
    2313             : }
    2314             : 
    2315             : /************************************************************************/
    2316             : /*                       GDALInfoPrintMetadata()                        */
    2317             : /************************************************************************/
    2318        1581 : static void GDALInfoPrintMetadata(const GDALInfoOptions *psOptions,
    2319             :                                   GDALMajorObjectH hObject,
    2320             :                                   const char *pszDomain,
    2321             :                                   const char *pszDisplayedname,
    2322             :                                   const char *pszIndent, int bJsonOutput,
    2323             :                                   json_object *poMetadata, CPLString &osStr)
    2324             : {
    2325        1581 :     const bool bIsxml =
    2326        1581 :         pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:");
    2327        1581 :     const bool bMDIsJson =
    2328        1581 :         pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:");
    2329             : 
    2330        1581 :     CSLConstList papszMetadata = GDALGetMetadata(hObject, pszDomain);
    2331        1581 :     if (papszMetadata != nullptr && *papszMetadata != nullptr)
    2332             :     {
    2333         149 :         json_object *poDomain = (bJsonOutput && !bIsxml && !bMDIsJson)
    2334         390 :                                     ? json_object_new_object()
    2335         241 :                                     : nullptr;
    2336             : 
    2337         241 :         if (!bJsonOutput)
    2338          92 :             Concat(osStr, psOptions->bStdoutOutput, "%s%s:\n", pszIndent,
    2339             :                    pszDisplayedname);
    2340             : 
    2341         241 :         json_object *poValue = nullptr;
    2342             : 
    2343        1105 :         for (int i = 0; papszMetadata[i] != nullptr; i++)
    2344             :         {
    2345         867 :             if (bJsonOutput)
    2346             :             {
    2347         565 :                 if (bIsxml)
    2348             :                 {
    2349           2 :                     poValue = json_object_new_string(papszMetadata[i]);
    2350           2 :                     break;
    2351             :                 }
    2352         563 :                 else if (bMDIsJson)
    2353             :                 {
    2354           1 :                     OGRJSonParse(papszMetadata[i], &poValue, true);
    2355           1 :                     break;
    2356             :                 }
    2357             :                 else
    2358             :                 {
    2359         562 :                     char *pszKey = nullptr;
    2360             :                     const char *pszValue =
    2361         562 :                         CPLParseNameValue(papszMetadata[i], &pszKey);
    2362         562 :                     if (pszKey)
    2363             :                     {
    2364         562 :                         poValue = json_object_new_string(pszValue);
    2365         562 :                         json_object_object_add(poDomain, pszKey, poValue);
    2366         562 :                         CPLFree(pszKey);
    2367             :                     }
    2368             :                 }
    2369             :             }
    2370             :             else
    2371             :             {
    2372         302 :                 if (bIsxml || bMDIsJson)
    2373           2 :                     Concat(osStr, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
    2374           2 :                            papszMetadata[i]);
    2375             :                 else
    2376         300 :                     Concat(osStr, psOptions->bStdoutOutput, "%s  %s\n",
    2377         300 :                            pszIndent, papszMetadata[i]);
    2378             :             }
    2379             :         }
    2380         241 :         if (bJsonOutput)
    2381             :         {
    2382         149 :             if (bIsxml || bMDIsJson)
    2383             :             {
    2384           3 :                 json_object_object_add(poMetadata, pszDomain, poValue);
    2385             :             }
    2386             :             else
    2387             :             {
    2388         146 :                 if (pszDomain == nullptr)
    2389          73 :                     json_object_object_add(poMetadata, "", poDomain);
    2390             :                 else
    2391          73 :                     json_object_object_add(poMetadata, pszDomain, poDomain);
    2392             :             }
    2393             :         }
    2394             :     }
    2395        1581 : }
    2396             : 
    2397             : /************************************************************************/
    2398             : /*                       GDALInfoReportMetadata()                       */
    2399             : /************************************************************************/
    2400         333 : static void GDALInfoReportMetadata(const GDALInfoOptions *psOptions,
    2401             :                                    GDALMajorObjectH hObject, bool bIsBand,
    2402             :                                    bool bJson, json_object *poMetadata,
    2403             :                                    CPLString &osStr)
    2404             : {
    2405         333 :     const char *const pszIndent = bIsBand ? "  " : "";
    2406             : 
    2407             :     /* -------------------------------------------------------------------- */
    2408             :     /*      Report list of Metadata domains                                 */
    2409             :     /* -------------------------------------------------------------------- */
    2410         333 :     if (psOptions->bListMDD)
    2411             :     {
    2412          16 :         const CPLStringList aosDomainList(GDALGetMetadataDomainList(hObject));
    2413           8 :         json_object *poMDD = nullptr;
    2414             :         json_object *const poListMDD =
    2415           8 :             bJson ? json_object_new_array() : nullptr;
    2416             : 
    2417           8 :         if (!aosDomainList.empty())
    2418             :         {
    2419           5 :             if (!bJson)
    2420           1 :                 Concat(osStr, psOptions->bStdoutOutput, "%sMetadata domains:\n",
    2421             :                        pszIndent);
    2422             :         }
    2423             : 
    2424          24 :         for (const char *pszDomain : aosDomainList)
    2425             :         {
    2426          16 :             if (EQUAL(pszDomain, ""))
    2427             :             {
    2428           5 :                 if (bJson)
    2429           4 :                     poMDD = json_object_new_string(pszDomain);
    2430             :                 else
    2431           1 :                     Concat(osStr, psOptions->bStdoutOutput, "%s  (default)\n",
    2432             :                            pszIndent);
    2433             :             }
    2434             :             else
    2435             :             {
    2436          11 :                 if (bJson)
    2437           8 :                     poMDD = json_object_new_string(pszDomain);
    2438             :                 else
    2439           3 :                     Concat(osStr, psOptions->bStdoutOutput, "%s  %s\n",
    2440             :                            pszIndent, pszDomain);
    2441             :             }
    2442          16 :             if (bJson)
    2443          12 :                 json_object_array_add(poListMDD, poMDD);
    2444             :         }
    2445           8 :         if (bJson)
    2446           6 :             json_object_object_add(poMetadata, "metadataDomains", poListMDD);
    2447             :     }
    2448             : 
    2449         333 :     if (!psOptions->bShowMetadata)
    2450           8 :         return;
    2451             : 
    2452             :     /* -------------------------------------------------------------------- */
    2453             :     /*      Report default Metadata domain.                                 */
    2454             :     /* -------------------------------------------------------------------- */
    2455         325 :     GDALInfoPrintMetadata(psOptions, hObject, nullptr, "Metadata", pszIndent,
    2456             :                           bJson, poMetadata, osStr);
    2457             : 
    2458             :     /* -------------------------------------------------------------------- */
    2459             :     /*      Report extra Metadata domains                                   */
    2460             :     /* -------------------------------------------------------------------- */
    2461         325 :     if (!psOptions->aosExtraMDDomains.empty())
    2462             :     {
    2463          36 :         CPLStringList aosExtraMDDomainsExpanded;
    2464             : 
    2465          26 :         if (EQUAL(psOptions->aosExtraMDDomains[0], "all") &&
    2466           8 :             psOptions->aosExtraMDDomains.Count() == 1)
    2467             :         {
    2468          16 :             const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
    2469          24 :             for (const char *pszDomain : aosMDDList)
    2470             :             {
    2471          16 :                 if (!EQUAL(pszDomain, "") &&
    2472          12 :                     !EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
    2473           8 :                     !EQUAL(pszDomain, "TILING_SCHEME") &&
    2474           8 :                     !EQUAL(pszDomain, "SUBDATASETS") &&
    2475           8 :                     !EQUAL(pszDomain, "GEOLOCATION") &&
    2476           8 :                     !EQUAL(pszDomain, "RPC"))
    2477             :                 {
    2478           8 :                     aosExtraMDDomainsExpanded.AddString(pszDomain);
    2479             :                 }
    2480             :             }
    2481             :         }
    2482             :         else
    2483             :         {
    2484          10 :             aosExtraMDDomainsExpanded = psOptions->aosExtraMDDomains;
    2485             :         }
    2486             : 
    2487          36 :         for (const char *pszDomain : aosExtraMDDomainsExpanded)
    2488             :         {
    2489          18 :             if (bJson)
    2490             :             {
    2491          12 :                 GDALInfoPrintMetadata(psOptions, hObject, pszDomain, pszDomain,
    2492             :                                       pszIndent, bJson, poMetadata, osStr);
    2493             :             }
    2494             :             else
    2495             :             {
    2496             :                 const std::string osDisplayedName =
    2497          18 :                     std::string("Metadata (").append(pszDomain).append(")");
    2498             : 
    2499           6 :                 GDALInfoPrintMetadata(psOptions, hObject, pszDomain,
    2500             :                                       osDisplayedName.c_str(), pszIndent, bJson,
    2501             :                                       poMetadata, osStr);
    2502             :             }
    2503             :         }
    2504             :     }
    2505             : 
    2506             :     /* -------------------------------------------------------------------- */
    2507             :     /*      Report various named metadata domains.                          */
    2508             :     /* -------------------------------------------------------------------- */
    2509         325 :     GDALInfoPrintMetadata(psOptions, hObject, "IMAGE_STRUCTURE",
    2510             :                           "Image Structure Metadata", pszIndent, bJson,
    2511             :                           poMetadata, osStr);
    2512             : 
    2513         325 :     if (!bIsBand)
    2514             :     {
    2515         147 :         GDALInfoPrintMetadata(psOptions, hObject, "TILING_SCHEME",
    2516             :                               "Tiling Scheme", pszIndent, bJson, poMetadata,
    2517             :                               osStr);
    2518         147 :         GDALInfoPrintMetadata(psOptions, hObject, "SUBDATASETS", "Subdatasets",
    2519             :                               pszIndent, bJson, poMetadata, osStr);
    2520         147 :         GDALInfoPrintMetadata(psOptions, hObject, "GEOLOCATION", "Geolocation",
    2521             :                               pszIndent, bJson, poMetadata, osStr);
    2522         147 :         GDALInfoPrintMetadata(psOptions, hObject, "RPC", "RPC Metadata",
    2523             :                               pszIndent, bJson, poMetadata, osStr);
    2524             :     }
    2525             : 
    2526         325 :     GDALInfoPrintMetadata(psOptions, hObject, "IMAGERY", "Imagery", pszIndent,
    2527             :                           bJson, poMetadata, osStr);
    2528             : }
    2529             : 
    2530             : /************************************************************************/
    2531             : /*                         GDALInfoOptionsNew()                         */
    2532             : /************************************************************************/
    2533             : 
    2534             : /**
    2535             :  * Allocates a GDALInfoOptions struct.
    2536             :  *
    2537             :  * @param papszArgv NULL terminated list of options (potentially including
    2538             :  * filename and open options too), or NULL. The accepted options are the ones of
    2539             :  * the <a href="/programs/gdalinfo.html">gdalinfo</a> utility.
    2540             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    2541             :  * NULL), otherwise (gdalinfo_bin.cpp use case) must be allocated with
    2542             :  *                           GDALInfoOptionsForBinaryNew() prior to this
    2543             :  * function. Will be filled with potentially present filename, open options,
    2544             :  * subdataset number...
    2545             :  * @return pointer to the allocated GDALInfoOptions struct. Must be freed with
    2546             :  * GDALInfoOptionsFree().
    2547             :  *
    2548             :  * @since GDAL 2.1
    2549             :  */
    2550             : 
    2551             : GDALInfoOptions *
    2552         156 : GDALInfoOptionsNew(char **papszArgv,
    2553             :                    GDALInfoOptionsForBinary *psOptionsForBinary)
    2554             : {
    2555         312 :     auto psOptions = std::make_unique<GDALInfoOptions>();
    2556             : 
    2557             :     /* -------------------------------------------------------------------- */
    2558             :     /*      Parse arguments.                                                */
    2559             :     /* -------------------------------------------------------------------- */
    2560             : 
    2561         312 :     CPLStringList aosArgv;
    2562             : 
    2563         156 :     if (papszArgv)
    2564             :     {
    2565         143 :         const int nArgc = CSLCount(papszArgv);
    2566         537 :         for (int i = 0; i < nArgc; i++)
    2567             :         {
    2568         394 :             aosArgv.AddString(papszArgv[i]);
    2569             :         }
    2570             :     }
    2571             : 
    2572             :     try
    2573             :     {
    2574             :         auto argParser =
    2575         312 :             GDALInfoAppOptionsGetParser(psOptions.get(), psOptionsForBinary);
    2576             : 
    2577         156 :         argParser->parse_args_without_binary_name(aosArgv.List());
    2578             : 
    2579         156 :         if (psOptions->bApproxStats)
    2580           2 :             psOptions->bStats = true;
    2581             :     }
    2582           0 :     catch (const std::exception &error)
    2583             :     {
    2584           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
    2585           0 :         return nullptr;
    2586             :     }
    2587             : 
    2588         156 :     if (!psOptions->bShowNodata)
    2589           2 :         psOptions->bShowMask = false;
    2590             : 
    2591         156 :     return psOptions.release();
    2592             : }
    2593             : 
    2594             : /************************************************************************/
    2595             : /*                        GDALInfoOptionsFree()                         */
    2596             : /************************************************************************/
    2597             : 
    2598             : /**
    2599             :  * Frees the GDALInfoOptions struct.
    2600             :  *
    2601             :  * @param psOptions the options struct for GDALInfo().
    2602             :  *
    2603             :  * @since GDAL 2.1
    2604             :  */
    2605             : 
    2606          97 : void GDALInfoOptionsFree(GDALInfoOptions *psOptions)
    2607             : {
    2608          97 :     delete psOptions;
    2609          97 : }

Generated by: LCOV version 1.14