Coverage Report

Created: 2026-02-14 06:52

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