Coverage Report

Created: 2026-09-14 06:50

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