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

Generated by: LCOV version 1.14