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

Generated by: LCOV version 1.14