LCOV - code coverage report
Current view: top level - apps - gdalinfo_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 868 1025 84.7 %
Date: 2026-02-21 16:21:44 Functions: 13 14 92.9 %

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

Generated by: LCOV version 1.14