LCOV - code coverage report
Current view: top level - apps - gdalinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 966 1173 82.4 %
Date: 2026-09-11 05:09:32 Functions: 18 19 94.7 %

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

Generated by: LCOV version 1.14