Coverage Report

Created: 2026-07-25 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
        }
1202
0
        else
1203
0
        {
1204
0
            hLatLong = OSRCloneGeogCS(hProj);
1205
0
            if (hLatLong)
1206
0
            {
1207
                // Override GEOGCS|UNIT child to be sure to output as degrees
1208
0
                OSRSetAngularUnits(hLatLong, SRS_UA_DEGREE,
1209
0
                                   CPLAtof(SRS_UA_DEGREE_CONV));
1210
0
            }
1211
0
        }
1212
1213
0
        if (hLatLong != nullptr)
1214
0
        {
1215
0
            OSRSetAxisMappingStrategy(hLatLong, OAMS_TRADITIONAL_GIS_ORDER);
1216
0
            CPLPushErrorHandler(CPLQuietErrorHandler);
1217
0
            hTransform = OCTNewCoordinateTransformation(hProj, hLatLong);
1218
0
            CPLPopErrorHandler();
1219
1220
0
            OSRDestroySpatialReference(hLatLong);
1221
0
        }
1222
0
    }
1223
1224
    /* -------------------------------------------------------------------- */
1225
    /*      Report corners.                                                 */
1226
    /* -------------------------------------------------------------------- */
1227
0
    if (bJson && GDALGetRasterXSize(hDataset))
1228
0
    {
1229
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1230
1231
0
        json_object *poCornerCoordinates = json_object_new_object();
1232
0
        json_object *poLongLatExtentCoordinates = json_object_new_array();
1233
1234
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
1235
0
                             0.0, bJson, poCornerCoordinates,
1236
0
                             poLongLatExtentCoordinates, osStr);
1237
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "lowerLeft", 0.0,
1238
0
                             GDALGetRasterYSize(hDataset), bJson,
1239
0
                             poCornerCoordinates, poLongLatExtentCoordinates,
1240
0
                             osStr);
1241
0
        GDALInfoReportCorner(
1242
0
            psOptions, hDataset, hTransform, "lowerRight",
1243
0
            GDALGetRasterXSize(hDataset), GDALGetRasterYSize(hDataset), bJson,
1244
0
            poCornerCoordinates, poLongLatExtentCoordinates, osStr);
1245
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperRight",
1246
0
                             GDALGetRasterXSize(hDataset), 0.0, bJson,
1247
0
                             poCornerCoordinates, poLongLatExtentCoordinates,
1248
0
                             osStr);
1249
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "center",
1250
0
                             GDALGetRasterXSize(hDataset) / 2.0,
1251
0
                             GDALGetRasterYSize(hDataset) / 2.0, bJson,
1252
0
                             poCornerCoordinates, poLongLatExtentCoordinates,
1253
0
                             osStr);
1254
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
1255
0
                             0.0, bJson, poCornerCoordinates,
1256
0
                             poLongLatExtentCoordinates, osStr);
1257
1258
0
        json_object_object_add(poJsonObject, "cornerCoordinates",
1259
0
                               poCornerCoordinates);
1260
1261
0
        if (json_object_array_length(poLongLatExtentCoordinates) > 0)
1262
0
        {
1263
0
            json_object *poLinearRing = json_object_new_array();
1264
0
            json_object *poLongLatExtent = json_object_new_object();
1265
0
            json_object *poLongLatExtentType =
1266
0
                json_object_new_string("Polygon");
1267
0
            json_object_object_add(poLongLatExtent, "type",
1268
0
                                   poLongLatExtentType);
1269
0
            json_object_array_add(poLinearRing, poLongLatExtentCoordinates);
1270
0
            json_object_object_add(poLongLatExtent, "coordinates",
1271
0
                                   poLinearRing);
1272
0
            json_object_object_add(poJsonObject,
1273
0
                                   bTransformToWGS84 ? "wgs84Extent" : "extent",
1274
0
                                   poLongLatExtent);
1275
0
        }
1276
0
        else
1277
0
        {
1278
0
            json_object_put(poLongLatExtentCoordinates);
1279
0
        }
1280
0
    }
1281
0
    else if (GDALGetRasterXSize(hDataset))
1282
0
    {
1283
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1284
1285
0
        Concat(osStr, psOptions->bStdoutOutput, "Corner Coordinates:\n");
1286
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Left", 0.0,
1287
0
                             0.0, bJson, nullptr, nullptr, osStr);
1288
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Left", 0.0,
1289
0
                             GDALGetRasterYSize(hDataset), bJson, nullptr,
1290
0
                             nullptr, osStr);
1291
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Right",
1292
0
                             GDALGetRasterXSize(hDataset), 0.0, bJson, nullptr,
1293
0
                             nullptr, osStr);
1294
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Right",
1295
0
                             GDALGetRasterXSize(hDataset),
1296
0
                             GDALGetRasterYSize(hDataset), bJson, nullptr,
1297
0
                             nullptr, osStr);
1298
0
        GDALInfoReportCorner(psOptions, hDataset, hTransform, "Center",
1299
0
                             GDALGetRasterXSize(hDataset) / 2.0,
1300
0
                             GDALGetRasterYSize(hDataset) / 2.0, bJson, nullptr,
1301
0
                             nullptr, osStr);
1302
0
    }
1303
1304
0
    if (hTransform != nullptr)
1305
0
    {
1306
0
        OCTDestroyCoordinateTransformation(hTransform);
1307
0
        hTransform = nullptr;
1308
0
    }
1309
1310
    /* ==================================================================== */
1311
    /*      Loop over bands.                                                */
1312
    /* ==================================================================== */
1313
0
    for (int iBand = 0; iBand < GDALGetRasterCount(hDataset); iBand++)
1314
0
    {
1315
0
        json_object *poBand = nullptr;
1316
0
        json_object *poBandMetadata = nullptr;
1317
0
        json_object *poStacRasterBand = nullptr;
1318
0
        json_object *poStacEOBand = nullptr;
1319
1320
0
        if (bJson)
1321
0
        {
1322
0
            poBand = json_object_new_object();
1323
0
            poBandMetadata = json_object_new_object();
1324
0
            poStacRasterBand = json_object_new_object();
1325
0
            poStacEOBand = json_object_new_object();
1326
0
        }
1327
1328
0
        GDALRasterBandH const hBand = GDALGetRasterBand(hDataset, iBand + 1);
1329
0
        const auto eDT = GDALGetRasterDataType(hBand);
1330
1331
0
        if (psOptions->bSample)
1332
0
        {
1333
0
            vector<float> ofSample(10000, 0);
1334
0
            float *const pafSample = &ofSample[0];
1335
0
            const int nCount =
1336
0
                GDALGetRandomRasterSample(hBand, 10000, pafSample);
1337
0
            if (!bJson)
1338
0
                Concat(osStr, psOptions->bStdoutOutput, "Got %d samples.\n",
1339
0
                       nCount);
1340
0
        }
1341
1342
0
        int nBlockXSize = 0;
1343
0
        int nBlockYSize = 0;
1344
0
        GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
1345
0
        if (bJson)
1346
0
        {
1347
0
            json_object *poBandNumber = json_object_new_int(iBand + 1);
1348
0
            json_object *poBlock = json_object_new_array();
1349
0
            json_object *poType =
1350
0
                json_object_new_string(GDALGetDataTypeName(eDT));
1351
0
            json_object *poColorInterp =
1352
0
                json_object_new_string(GDALGetColorInterpretationName(
1353
0
                    GDALGetRasterColorInterpretation(hBand)));
1354
1355
0
            json_object_array_add(poBlock, json_object_new_int(nBlockXSize));
1356
0
            json_object_array_add(poBlock, json_object_new_int(nBlockYSize));
1357
0
            json_object_object_add(poBand, "band", poBandNumber);
1358
0
            json_object_object_add(poBand, "block", poBlock);
1359
0
            json_object_object_add(poBand, "type", poType);
1360
0
            json_object_object_add(poBand, "colorInterpretation",
1361
0
                                   poColorInterp);
1362
1363
0
            const char *stacDataType = nullptr;
1364
0
            switch (eDT)
1365
0
            {
1366
0
                case GDT_UInt8:
1367
0
                    stacDataType = "uint8";
1368
0
                    break;
1369
0
                case GDT_Int8:
1370
0
                    stacDataType = "int8";
1371
0
                    break;
1372
0
                case GDT_UInt16:
1373
0
                    stacDataType = "uint16";
1374
0
                    break;
1375
0
                case GDT_Int16:
1376
0
                    stacDataType = "int16";
1377
0
                    break;
1378
0
                case GDT_UInt32:
1379
0
                    stacDataType = "uint32";
1380
0
                    break;
1381
0
                case GDT_Int32:
1382
0
                    stacDataType = "int32";
1383
0
                    break;
1384
0
                case GDT_UInt64:
1385
0
                    stacDataType = "uint64";
1386
0
                    break;
1387
0
                case GDT_Int64:
1388
0
                    stacDataType = "int64";
1389
0
                    break;
1390
0
                case GDT_Float16:
1391
0
                    stacDataType = "float16";
1392
0
                    break;
1393
0
                case GDT_Float32:
1394
0
                    stacDataType = "float32";
1395
0
                    break;
1396
0
                case GDT_Float64:
1397
0
                    stacDataType = "float64";
1398
0
                    break;
1399
0
                case GDT_CInt16:
1400
0
                    stacDataType = "cint16";
1401
0
                    break;
1402
0
                case GDT_CInt32:
1403
0
                    stacDataType = "cint32";
1404
0
                    break;
1405
0
                case GDT_CFloat16:
1406
0
                    stacDataType = "cfloat16";
1407
0
                    break;
1408
0
                case GDT_CFloat32:
1409
0
                    stacDataType = "cfloat32";
1410
0
                    break;
1411
0
                case GDT_CFloat64:
1412
0
                    stacDataType = "cfloat64";
1413
0
                    break;
1414
0
                case GDT_Unknown:
1415
0
                case GDT_TypeCount:
1416
0
                    stacDataType = nullptr;
1417
0
            }
1418
0
            if (stacDataType)
1419
0
                json_object_object_add(poStacRasterBand, "data_type",
1420
0
                                       json_object_new_string(stacDataType));
1421
0
        }
1422
0
        else
1423
0
        {
1424
0
            Concat(osStr, psOptions->bStdoutOutput,
1425
0
                   "Band %d Block=%dx%d Type=%s, ColorInterp=%s\n", iBand + 1,
1426
0
                   nBlockXSize, nBlockYSize, GDALGetDataTypeName(eDT),
1427
0
                   GDALGetColorInterpretationName(
1428
0
                       GDALGetRasterColorInterpretation(hBand)));
1429
0
        }
1430
1431
0
        if (bJson)
1432
0
        {
1433
0
            json_object *poBandName =
1434
0
                json_object_new_string(CPLSPrintf("b%i", iBand + 1));
1435
0
            json_object_object_add(poStacEOBand, "name", poBandName);
1436
0
        }
1437
1438
0
        const char *pszBandDesc = GDALGetDescription(hBand);
1439
0
        if (pszBandDesc != nullptr && strlen(pszBandDesc) > 0)
1440
0
        {
1441
0
            if (bJson)
1442
0
            {
1443
0
                json_object_object_add(poBand, "description",
1444
0
                                       json_object_new_string(pszBandDesc));
1445
1446
0
                json_object_object_add(poStacEOBand, "description",
1447
0
                                       json_object_new_string(pszBandDesc));
1448
0
            }
1449
0
            else
1450
0
            {
1451
0
                Concat(osStr, psOptions->bStdoutOutput, "  Description = %s\n",
1452
0
                       pszBandDesc);
1453
0
            }
1454
0
        }
1455
0
        else
1456
0
        {
1457
0
            if (bJson)
1458
0
            {
1459
0
                json_object *poColorInterp =
1460
0
                    json_object_new_string(GDALGetColorInterpretationName(
1461
0
                        GDALGetRasterColorInterpretation(hBand)));
1462
0
                json_object_object_add(poStacEOBand, "description",
1463
0
                                       poColorInterp);
1464
0
            }
1465
0
        }
1466
1467
0
        if (bJson)
1468
0
        {
1469
0
            const char *pszCommonName = GDALGetSTACCommonNameFromColorInterp(
1470
0
                GDALGetRasterColorInterpretation(hBand));
1471
0
            if (pszCommonName)
1472
0
            {
1473
0
                json_object_object_add(poStacEOBand, "common_name",
1474
0
                                       json_object_new_string(pszCommonName));
1475
0
            }
1476
0
        }
1477
1478
0
        {
1479
0
            int bGotMin = FALSE;
1480
0
            int bGotMax = FALSE;
1481
0
            const double dfMin = GDALGetRasterMinimum(hBand, &bGotMin);
1482
0
            const double dfMax = GDALGetRasterMaximum(hBand, &bGotMax);
1483
0
            if (bGotMin || bGotMax || psOptions->bComputeMinMax)
1484
0
            {
1485
0
                if (!bJson)
1486
0
                    Concat(osStr, psOptions->bStdoutOutput, "  ");
1487
0
                if (bGotMin)
1488
0
                {
1489
0
                    if (bJson)
1490
0
                    {
1491
0
                        json_object *poMin =
1492
0
                            gdal_json_object_new_double_or_str_for_non_finite(
1493
0
                                dfMin, 3);
1494
0
                        json_object_object_add(poBand, "min", poMin);
1495
0
                    }
1496
0
                    else
1497
0
                    {
1498
0
                        Concat(osStr, psOptions->bStdoutOutput, "Min=%.3f ",
1499
0
                               dfMin);
1500
0
                    }
1501
0
                }
1502
0
                if (bGotMax)
1503
0
                {
1504
0
                    if (bJson)
1505
0
                    {
1506
0
                        json_object *poMax =
1507
0
                            gdal_json_object_new_double_or_str_for_non_finite(
1508
0
                                dfMax, 3);
1509
0
                        json_object_object_add(poBand, "max", poMax);
1510
0
                    }
1511
0
                    else
1512
0
                    {
1513
0
                        Concat(osStr, psOptions->bStdoutOutput, "Max=%.3f ",
1514
0
                               dfMax);
1515
0
                    }
1516
0
                }
1517
1518
0
                if (psOptions->bComputeMinMax)
1519
0
                {
1520
0
                    CPLErrorReset();
1521
0
                    double adfCMinMax[2] = {0.0, 0.0};
1522
0
                    GDALComputeRasterMinMax(hBand, FALSE, adfCMinMax);
1523
0
                    if (CPLGetLastErrorType() == CE_None)
1524
0
                    {
1525
0
                        if (bJson)
1526
0
                        {
1527
0
                            json_object *poComputedMin =
1528
0
                                gdal_json_object_new_double_or_str_for_non_finite(
1529
0
                                    adfCMinMax[0], 3);
1530
0
                            json_object *poComputedMax =
1531
0
                                gdal_json_object_new_double_or_str_for_non_finite(
1532
0
                                    adfCMinMax[1], 3);
1533
0
                            json_object_object_add(poBand, "computedMin",
1534
0
                                                   poComputedMin);
1535
0
                            json_object_object_add(poBand, "computedMax",
1536
0
                                                   poComputedMax);
1537
0
                        }
1538
0
                        else
1539
0
                        {
1540
0
                            Concat(osStr, psOptions->bStdoutOutput,
1541
0
                                   "  Computed Min/Max=%.3f,%.3f",
1542
0
                                   adfCMinMax[0], adfCMinMax[1]);
1543
0
                        }
1544
0
                    }
1545
0
                }
1546
0
                if (!bJson)
1547
0
                    Concat(osStr, psOptions->bStdoutOutput, "\n");
1548
0
            }
1549
0
        }
1550
1551
0
        double dfMinStat = 0.0;
1552
0
        double dfMaxStat = 0.0;
1553
0
        double dfMean = 0.0;
1554
0
        double dfStdDev = 0.0;
1555
0
        CPLErr eErr = GDALGetRasterStatistics(hBand, psOptions->bApproxStats,
1556
0
                                              psOptions->bStats, &dfMinStat,
1557
0
                                              &dfMaxStat, &dfMean, &dfStdDev);
1558
0
        if (eErr == CE_None)
1559
0
        {
1560
0
            if (bJson)
1561
0
            {
1562
0
                json_object *poStacStats = json_object_new_object();
1563
0
                json_object *poMinimum =
1564
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
1565
0
                                                                      3);
1566
0
                json_object_object_add(poBand, "minimum", poMinimum);
1567
0
                json_object *poStacMinimum =
1568
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
1569
0
                                                                      3);
1570
0
                json_object_object_add(poStacStats, "minimum", poStacMinimum);
1571
1572
0
                json_object *poMaximum =
1573
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
1574
0
                                                                      3);
1575
0
                json_object_object_add(poBand, "maximum", poMaximum);
1576
0
                json_object *poStacMaximum =
1577
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
1578
0
                                                                      3);
1579
0
                json_object_object_add(poStacStats, "maximum", poStacMaximum);
1580
1581
0
                json_object *poMean =
1582
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfMean,
1583
0
                                                                      3);
1584
0
                json_object_object_add(poBand, "mean", poMean);
1585
0
                json_object *poStacMean =
1586
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfMean,
1587
0
                                                                      3);
1588
0
                json_object_object_add(poStacStats, "mean", poStacMean);
1589
1590
0
                json_object *poStdDev =
1591
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
1592
0
                                                                      3);
1593
0
                json_object_object_add(poBand, "stdDev", poStdDev);
1594
0
                json_object *poStacStdDev =
1595
0
                    gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
1596
0
                                                                      3);
1597
0
                json_object_object_add(poStacStats, "stddev", poStacStdDev);
1598
1599
0
                json_object_object_add(poStacRasterBand, "stats", poStacStats);
1600
0
            }
1601
0
            else
1602
0
            {
1603
0
                Concat(osStr, psOptions->bStdoutOutput,
1604
0
                       "  Minimum=%.3f, Maximum=%.3f, Mean=%.3f, StdDev=%.3f\n",
1605
0
                       dfMinStat, dfMaxStat, dfMean, dfStdDev);
1606
0
            }
1607
0
        }
1608
1609
0
        if (psOptions->bReportHistograms)
1610
0
        {
1611
0
            int nBucketCount = 0;
1612
0
            GUIntBig *panHistogram = nullptr;
1613
1614
0
            if (bJson)
1615
0
                eErr = GDALGetDefaultHistogramEx(
1616
0
                    hBand, &dfMinStat, &dfMaxStat, &nBucketCount, &panHistogram,
1617
0
                    TRUE, GDALDummyProgress, nullptr);
1618
0
            else
1619
0
                eErr = GDALGetDefaultHistogramEx(
1620
0
                    hBand, &dfMinStat, &dfMaxStat, &nBucketCount, &panHistogram,
1621
0
                    TRUE, GDALTermProgress, nullptr);
1622
0
            if (eErr == CE_None)
1623
0
            {
1624
0
                json_object *poHistogram = nullptr;
1625
0
                json_object *poBuckets = nullptr;
1626
1627
0
                if (bJson)
1628
0
                {
1629
0
                    json_object *poCount = json_object_new_int(nBucketCount);
1630
0
                    json_object *poMin = json_object_new_double(dfMinStat);
1631
0
                    json_object *poMax = json_object_new_double(dfMaxStat);
1632
1633
0
                    poBuckets = json_object_new_array();
1634
0
                    poHistogram = json_object_new_object();
1635
0
                    json_object_object_add(poHistogram, "count", poCount);
1636
0
                    json_object_object_add(poHistogram, "min", poMin);
1637
0
                    json_object_object_add(poHistogram, "max", poMax);
1638
0
                }
1639
0
                else
1640
0
                {
1641
0
                    Concat(osStr, psOptions->bStdoutOutput,
1642
0
                           "  %d buckets from %g to %g:\n  ", nBucketCount,
1643
0
                           dfMinStat, dfMaxStat);
1644
0
                }
1645
1646
0
                for (int iBucket = 0; iBucket < nBucketCount; iBucket++)
1647
0
                {
1648
0
                    if (bJson)
1649
0
                    {
1650
0
                        json_object *poBucket =
1651
0
                            json_object_new_int64(panHistogram[iBucket]);
1652
0
                        json_object_array_add(poBuckets, poBucket);
1653
0
                    }
1654
0
                    else
1655
0
                        Concat(osStr, psOptions->bStdoutOutput,
1656
0
                               CPL_FRMT_GUIB " ", panHistogram[iBucket]);
1657
0
                }
1658
0
                if (bJson)
1659
0
                {
1660
0
                    json_object_object_add(poHistogram, "buckets", poBuckets);
1661
0
                    json_object *poStacHistogram = nullptr;
1662
0
                    json_object_deep_copy(poHistogram, &poStacHistogram,
1663
0
                                          nullptr);
1664
0
                    json_object_object_add(poBand, "histogram", poHistogram);
1665
0
                    json_object_object_add(poStacRasterBand, "histogram",
1666
0
                                           poStacHistogram);
1667
0
                }
1668
0
                else
1669
0
                {
1670
0
                    Concat(osStr, psOptions->bStdoutOutput, "\n");
1671
0
                }
1672
0
                CPLFree(panHistogram);
1673
0
            }
1674
0
        }
1675
1676
0
        if (psOptions->bComputeChecksum)
1677
0
        {
1678
0
            const int nBandChecksum =
1679
0
                GDALChecksumImage(hBand, 0, 0, GDALGetRasterXSize(hDataset),
1680
0
                                  GDALGetRasterYSize(hDataset));
1681
0
            if (bJson)
1682
0
            {
1683
0
                json_object *poChecksum = json_object_new_int(nBandChecksum);
1684
0
                json_object_object_add(poBand, "checksum", poChecksum);
1685
0
            }
1686
0
            else
1687
0
            {
1688
0
                Concat(osStr, psOptions->bStdoutOutput, "  Checksum=%d\n",
1689
0
                       nBandChecksum);
1690
0
            }
1691
0
        }
1692
1693
0
        int bGotNodata = FALSE;
1694
0
        if (!psOptions->bShowNodata)
1695
0
        {
1696
            // nothing to do
1697
0
        }
1698
0
        else if (eDT == GDT_Int64)
1699
0
        {
1700
0
            const auto nNoData =
1701
0
                GDALGetRasterNoDataValueAsInt64(hBand, &bGotNodata);
1702
0
            if (bGotNodata)
1703
0
            {
1704
0
                if (bJson)
1705
0
                {
1706
0
                    json_object *poNoDataValue = json_object_new_int64(nNoData);
1707
0
                    json_object *poStacNoDataValue = nullptr;
1708
0
                    json_object_deep_copy(poNoDataValue, &poStacNoDataValue,
1709
0
                                          nullptr);
1710
0
                    json_object_object_add(poStacRasterBand, "nodata",
1711
0
                                           poStacNoDataValue);
1712
0
                    json_object_object_add(poBand, "noDataValue",
1713
0
                                           poNoDataValue);
1714
0
                }
1715
0
                else
1716
0
                {
1717
0
                    Concat(osStr, psOptions->bStdoutOutput,
1718
0
                           "  NoData Value=" CPL_FRMT_GIB "\n",
1719
0
                           static_cast<GIntBig>(nNoData));
1720
0
                }
1721
0
            }
1722
0
        }
1723
0
        else if (eDT == GDT_UInt64)
1724
0
        {
1725
0
            const auto nNoData =
1726
0
                GDALGetRasterNoDataValueAsUInt64(hBand, &bGotNodata);
1727
0
            if (bGotNodata)
1728
0
            {
1729
0
                if (bJson)
1730
0
                {
1731
0
                    if (nNoData < static_cast<uint64_t>(
1732
0
                                      std::numeric_limits<int64_t>::max()))
1733
0
                    {
1734
0
                        json_object *poNoDataValue = json_object_new_int64(
1735
0
                            static_cast<int64_t>(nNoData));
1736
0
                        json_object *poStacNoDataValue = nullptr;
1737
0
                        json_object_deep_copy(poNoDataValue, &poStacNoDataValue,
1738
0
                                              nullptr);
1739
0
                        json_object_object_add(poStacRasterBand, "nodata",
1740
0
                                               poStacNoDataValue);
1741
0
                        json_object_object_add(poBand, "noDataValue",
1742
0
                                               poNoDataValue);
1743
0
                    }
1744
0
                    else
1745
0
                    {
1746
                        // not pretty to serialize as a string but there's no
1747
                        // way to serialize a uint64_t with libjson-c
1748
0
                        json_object *poNoDataValue =
1749
0
                            json_object_new_string(CPLSPrintf(
1750
0
                                CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
1751
0
                        json_object_object_add(poBand, "noDataValue",
1752
0
                                               poNoDataValue);
1753
0
                    }
1754
0
                }
1755
0
                else
1756
0
                {
1757
0
                    Concat(osStr, psOptions->bStdoutOutput,
1758
0
                           "  NoData Value=" CPL_FRMT_GUIB "\n",
1759
0
                           static_cast<GUIntBig>(nNoData));
1760
0
                }
1761
0
            }
1762
0
        }
1763
0
        else
1764
0
        {
1765
0
            const double dfNoData =
1766
0
                GDALGetRasterNoDataValue(hBand, &bGotNodata);
1767
0
            if (bGotNodata)
1768
0
            {
1769
0
                const bool bIsNoDataFloat =
1770
0
                    eDT == GDT_Float32 &&
1771
0
                    static_cast<double>(static_cast<float>(dfNoData)) ==
1772
0
                        dfNoData;
1773
                // Find the most compact decimal representation of the nodata
1774
                // value that can be used to exactly represent the binary value
1775
0
                int nSignificantDigits = bIsNoDataFloat ? 8 : 18;
1776
0
                char szNoData[64] = {0};
1777
0
                while (nSignificantDigits > 0)
1778
0
                {
1779
0
                    char szCandidateNoData[64];
1780
0
                    char szFormat[16];
1781
0
                    snprintf(szFormat, sizeof(szFormat), "%%.%dg",
1782
0
                             nSignificantDigits);
1783
0
                    CPLsnprintf(szCandidateNoData, sizeof(szCandidateNoData),
1784
0
                                szFormat, dfNoData);
1785
0
                    if (szNoData[0] == '\0' ||
1786
0
                        (bIsNoDataFloat &&
1787
0
                         static_cast<float>(CPLAtof(szCandidateNoData)) ==
1788
0
                             static_cast<float>(dfNoData)) ||
1789
0
                        (!bIsNoDataFloat &&
1790
0
                         CPLAtof(szCandidateNoData) == dfNoData))
1791
0
                    {
1792
0
                        strcpy(szNoData, szCandidateNoData);
1793
0
                        nSignificantDigits--;
1794
0
                    }
1795
0
                    else
1796
0
                    {
1797
0
                        nSignificantDigits++;
1798
0
                        break;
1799
0
                    }
1800
0
                }
1801
1802
0
                if (bJson)
1803
0
                {
1804
0
                    json_object *poNoDataValue =
1805
0
                        (GDALDataTypeIsInteger(eDT) && dfNoData >= INT_MIN &&
1806
0
                         dfNoData <= INT_MAX &&
1807
0
                         static_cast<int>(dfNoData) == dfNoData)
1808
0
                            ? json_object_new_int(static_cast<int>(dfNoData))
1809
0
                            : gdal_json_object_new_double_significant_digits(
1810
0
                                  dfNoData, nSignificantDigits);
1811
0
                    json_object *poStacNoDataValue =
1812
0
                        (GDALDataTypeIsInteger(eDT) && dfNoData >= INT_MIN &&
1813
0
                         dfNoData <= INT_MAX &&
1814
0
                         static_cast<int>(dfNoData) == dfNoData)
1815
0
                            ? json_object_new_int(static_cast<int>(dfNoData))
1816
0
                            : gdal_json_object_new_double_significant_digits(
1817
0
                                  dfNoData, nSignificantDigits);
1818
0
                    json_object_object_add(poStacRasterBand, "nodata",
1819
0
                                           poStacNoDataValue);
1820
0
                    json_object_object_add(poBand, "noDataValue",
1821
0
                                           poNoDataValue);
1822
0
                }
1823
0
                else if (std::isnan(dfNoData))
1824
0
                {
1825
0
                    Concat(osStr, psOptions->bStdoutOutput,
1826
0
                           "  NoData Value=nan\n");
1827
0
                }
1828
0
                else
1829
0
                {
1830
0
                    Concat(osStr, psOptions->bStdoutOutput,
1831
0
                           "  NoData Value=%s\n", szNoData);
1832
0
                }
1833
0
            }
1834
0
        }
1835
1836
0
        if (GDALGetOverviewCount(hBand) > 0)
1837
0
        {
1838
0
            json_object *poOverviews = nullptr;
1839
1840
0
            if (bJson)
1841
0
                poOverviews = json_object_new_array();
1842
0
            else
1843
0
                Concat(osStr, psOptions->bStdoutOutput, "  Overviews: ");
1844
1845
0
            for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
1846
0
                 iOverview++)
1847
0
            {
1848
0
                if (!bJson)
1849
0
                    if (iOverview != 0)
1850
0
                        Concat(osStr, psOptions->bStdoutOutput, ", ");
1851
1852
0
                GDALRasterBandH hOverview = GDALGetOverview(hBand, iOverview);
1853
0
                if (hOverview != nullptr)
1854
0
                {
1855
0
                    if (bJson)
1856
0
                    {
1857
0
                        json_object *poOverviewSize = json_object_new_array();
1858
0
                        json_object *poOverviewSizeX = json_object_new_int(
1859
0
                            GDALGetRasterBandXSize(hOverview));
1860
0
                        json_object *poOverviewSizeY = json_object_new_int(
1861
0
                            GDALGetRasterBandYSize(hOverview));
1862
1863
0
                        json_object *poOverview = json_object_new_object();
1864
0
                        json_object_array_add(poOverviewSize, poOverviewSizeX);
1865
0
                        json_object_array_add(poOverviewSize, poOverviewSizeY);
1866
0
                        json_object_object_add(poOverview, "size",
1867
0
                                               poOverviewSize);
1868
1869
0
                        if (psOptions->bComputeChecksum)
1870
0
                        {
1871
0
                            const int nOverviewChecksum = GDALChecksumImage(
1872
0
                                hOverview, 0, 0,
1873
0
                                GDALGetRasterBandXSize(hOverview),
1874
0
                                GDALGetRasterBandYSize(hOverview));
1875
0
                            json_object *poOverviewChecksum =
1876
0
                                json_object_new_int(nOverviewChecksum);
1877
0
                            json_object_object_add(poOverview, "checksum",
1878
0
                                                   poOverviewChecksum);
1879
0
                        }
1880
0
                        json_object_array_add(poOverviews, poOverview);
1881
0
                    }
1882
0
                    else
1883
0
                    {
1884
0
                        Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
1885
0
                               GDALGetRasterBandXSize(hOverview),
1886
0
                               GDALGetRasterBandYSize(hOverview));
1887
0
                    }
1888
1889
0
                    const char *pszResampling =
1890
0
                        GDALGetMetadataItem(hOverview, "RESAMPLING", "");
1891
1892
0
                    if (pszResampling != nullptr && !bJson &&
1893
0
                        STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
1894
0
                        Concat(osStr, psOptions->bStdoutOutput, "*");
1895
0
                }
1896
0
                else
1897
0
                {
1898
0
                    if (!bJson)
1899
0
                        Concat(osStr, psOptions->bStdoutOutput, "(null)");
1900
0
                }
1901
0
            }
1902
0
            if (bJson)
1903
0
                json_object_object_add(poBand, "overviews", poOverviews);
1904
0
            else
1905
0
                Concat(osStr, psOptions->bStdoutOutput, "\n");
1906
1907
0
            if (psOptions->bComputeChecksum && !bJson)
1908
0
            {
1909
0
                Concat(osStr, psOptions->bStdoutOutput,
1910
0
                       "  Overviews checksum: ");
1911
1912
0
                for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
1913
0
                     iOverview++)
1914
0
                {
1915
0
                    GDALRasterBandH hOverview;
1916
1917
0
                    if (iOverview != 0)
1918
0
                        Concat(osStr, psOptions->bStdoutOutput, ", ");
1919
1920
0
                    hOverview = GDALGetOverview(hBand, iOverview);
1921
0
                    if (hOverview)
1922
0
                    {
1923
0
                        Concat(osStr, psOptions->bStdoutOutput, "%d",
1924
0
                               GDALChecksumImage(
1925
0
                                   hOverview, 0, 0,
1926
0
                                   GDALGetRasterBandXSize(hOverview),
1927
0
                                   GDALGetRasterBandYSize(hOverview)));
1928
0
                    }
1929
0
                    else
1930
0
                    {
1931
0
                        Concat(osStr, psOptions->bStdoutOutput, "(null)");
1932
0
                    }
1933
0
                }
1934
0
                Concat(osStr, psOptions->bStdoutOutput, "\n");
1935
0
            }
1936
0
        }
1937
1938
0
        if (GDALHasArbitraryOverviews(hBand) && !bJson)
1939
0
        {
1940
0
            Concat(osStr, psOptions->bStdoutOutput, "  Overviews: arbitrary\n");
1941
0
        }
1942
1943
0
        const int nMaskFlags =
1944
0
            psOptions->bShowMask ? GDALGetMaskFlags(hBand) : GMF_ALL_VALID;
1945
0
        if ((nMaskFlags & (GMF_NODATA | GMF_ALL_VALID)) == 0 ||
1946
0
            nMaskFlags == (GMF_NODATA | GMF_PER_DATASET))
1947
0
        {
1948
0
            GDALRasterBandH hMaskBand = GDALGetMaskBand(hBand);
1949
0
            json_object *poMask = nullptr;
1950
0
            json_object *poFlags = nullptr;
1951
0
            json_object *poMaskOverviews = nullptr;
1952
1953
0
            if (bJson)
1954
0
            {
1955
0
                poMask = json_object_new_object();
1956
0
                poFlags = json_object_new_array();
1957
0
            }
1958
0
            else
1959
0
                Concat(osStr, psOptions->bStdoutOutput, "  Mask Flags: ");
1960
0
            if (nMaskFlags & GMF_PER_DATASET)
1961
0
            {
1962
0
                if (bJson)
1963
0
                {
1964
0
                    json_object *poFlag = json_object_new_string("PER_DATASET");
1965
0
                    json_object_array_add(poFlags, poFlag);
1966
0
                }
1967
0
                else
1968
0
                    Concat(osStr, psOptions->bStdoutOutput, "PER_DATASET ");
1969
0
            }
1970
0
            if (nMaskFlags & GMF_ALPHA)
1971
0
            {
1972
0
                if (bJson)
1973
0
                {
1974
0
                    json_object *poFlag = json_object_new_string("ALPHA");
1975
0
                    json_object_array_add(poFlags, poFlag);
1976
0
                }
1977
0
                else
1978
0
                    Concat(osStr, psOptions->bStdoutOutput, "ALPHA ");
1979
0
            }
1980
0
            if (nMaskFlags & GMF_NODATA)
1981
0
            {
1982
0
                if (bJson)
1983
0
                {
1984
0
                    json_object *poFlag = json_object_new_string("NODATA");
1985
0
                    json_object_array_add(poFlags, poFlag);
1986
0
                }
1987
0
                else
1988
0
                {
1989
0
                    Concat(osStr, psOptions->bStdoutOutput, "NODATA ");
1990
0
                }
1991
0
            }
1992
1993
0
            if (bJson)
1994
0
                json_object_object_add(poMask, "flags", poFlags);
1995
0
            else
1996
0
                Concat(osStr, psOptions->bStdoutOutput, "\n");
1997
1998
0
            if (bJson)
1999
0
                poMaskOverviews = json_object_new_array();
2000
2001
0
            if (hMaskBand != nullptr && GDALGetOverviewCount(hMaskBand) > 0)
2002
0
            {
2003
0
                if (!bJson)
2004
0
                    Concat(osStr, psOptions->bStdoutOutput,
2005
0
                           "  Overviews of mask band: ");
2006
2007
0
                for (int iOverview = 0;
2008
0
                     iOverview < GDALGetOverviewCount(hMaskBand); iOverview++)
2009
0
                {
2010
0
                    GDALRasterBandH hOverview =
2011
0
                        GDALGetOverview(hMaskBand, iOverview);
2012
0
                    if (!hOverview)
2013
0
                        break;
2014
0
                    json_object *poMaskOverview = nullptr;
2015
0
                    json_object *poMaskOverviewSize = nullptr;
2016
2017
0
                    if (bJson)
2018
0
                    {
2019
0
                        poMaskOverview = json_object_new_object();
2020
0
                        poMaskOverviewSize = json_object_new_array();
2021
0
                    }
2022
0
                    else
2023
0
                    {
2024
0
                        if (iOverview != 0)
2025
0
                            Concat(osStr, psOptions->bStdoutOutput, ", ");
2026
0
                    }
2027
2028
0
                    if (bJson)
2029
0
                    {
2030
0
                        json_object *poMaskOverviewSizeX = json_object_new_int(
2031
0
                            GDALGetRasterBandXSize(hOverview));
2032
0
                        json_object *poMaskOverviewSizeY = json_object_new_int(
2033
0
                            GDALGetRasterBandYSize(hOverview));
2034
2035
0
                        json_object_array_add(poMaskOverviewSize,
2036
0
                                              poMaskOverviewSizeX);
2037
0
                        json_object_array_add(poMaskOverviewSize,
2038
0
                                              poMaskOverviewSizeY);
2039
0
                        json_object_object_add(poMaskOverview, "size",
2040
0
                                               poMaskOverviewSize);
2041
0
                        json_object_array_add(poMaskOverviews, poMaskOverview);
2042
0
                    }
2043
0
                    else
2044
0
                    {
2045
0
                        Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
2046
0
                               GDALGetRasterBandXSize(hOverview),
2047
0
                               GDALGetRasterBandYSize(hOverview));
2048
0
                    }
2049
0
                }
2050
0
                if (!bJson)
2051
0
                    Concat(osStr, psOptions->bStdoutOutput, "\n");
2052
0
            }
2053
0
            if (bJson)
2054
0
            {
2055
0
                json_object_object_add(poMask, "overviews", poMaskOverviews);
2056
0
                json_object_object_add(poBand, "mask", poMask);
2057
0
            }
2058
0
        }
2059
2060
0
        if (strlen(GDALGetRasterUnitType(hBand)) > 0)
2061
0
        {
2062
0
            if (bJson)
2063
0
            {
2064
0
                json_object *poUnit =
2065
0
                    json_object_new_string(GDALGetRasterUnitType(hBand));
2066
0
                json_object *poStacUnit = nullptr;
2067
0
                json_object_deep_copy(poUnit, &poStacUnit, nullptr);
2068
0
                json_object_object_add(poStacRasterBand, "unit", poStacUnit);
2069
0
                json_object_object_add(poBand, "unit", poUnit);
2070
0
            }
2071
0
            else
2072
0
            {
2073
0
                Concat(osStr, psOptions->bStdoutOutput, "  Unit Type: %s\n",
2074
0
                       GDALGetRasterUnitType(hBand));
2075
0
            }
2076
0
        }
2077
2078
0
        if (GDALGetRasterCategoryNames(hBand) != nullptr)
2079
0
        {
2080
0
            char **papszCategories = GDALGetRasterCategoryNames(hBand);
2081
0
            json_object *poCategories = nullptr;
2082
2083
0
            if (bJson)
2084
0
                poCategories = json_object_new_array();
2085
0
            else
2086
0
                Concat(osStr, psOptions->bStdoutOutput, "  Categories:\n");
2087
2088
0
            for (int i = 0; papszCategories[i] != nullptr; i++)
2089
0
            {
2090
0
                if (bJson)
2091
0
                {
2092
0
                    json_object *poCategoryName =
2093
0
                        json_object_new_string(papszCategories[i]);
2094
0
                    json_object_array_add(poCategories, poCategoryName);
2095
0
                }
2096
0
                else
2097
0
                    Concat(osStr, psOptions->bStdoutOutput, "    %3d: %s\n", i,
2098
0
                           papszCategories[i]);
2099
0
            }
2100
0
            if (bJson)
2101
0
                json_object_object_add(poBand, "categories", poCategories);
2102
0
        }
2103
2104
0
        int bSuccess = FALSE;
2105
0
        if (GDALGetRasterScale(hBand, &bSuccess) != 1.0 ||
2106
0
            GDALGetRasterOffset(hBand, &bSuccess) != 0.0)
2107
0
        {
2108
0
            if (bJson)
2109
0
            {
2110
0
                json_object *poOffset = json_object_new_double_with_precision(
2111
0
                    GDALGetRasterOffset(hBand, &bSuccess), 15);
2112
0
                json_object *poScale = json_object_new_double_with_precision(
2113
0
                    GDALGetRasterScale(hBand, &bSuccess), 15);
2114
0
                json_object *poStacScale = nullptr;
2115
0
                json_object *poStacOffset = nullptr;
2116
0
                json_object_deep_copy(poScale, &poStacScale, nullptr);
2117
0
                json_object_deep_copy(poOffset, &poStacOffset, nullptr);
2118
0
                json_object_object_add(poStacRasterBand, "scale", poStacScale);
2119
0
                json_object_object_add(poStacRasterBand, "offset",
2120
0
                                       poStacOffset);
2121
0
                json_object_object_add(poBand, "offset", poOffset);
2122
0
                json_object_object_add(poBand, "scale", poScale);
2123
0
            }
2124
0
            else
2125
0
            {
2126
0
                Concat(osStr, psOptions->bStdoutOutput,
2127
0
                       "  Offset: %.15g,   Scale:%.15g\n",
2128
0
                       GDALGetRasterOffset(hBand, &bSuccess),
2129
0
                       GDALGetRasterScale(hBand, &bSuccess));
2130
0
            }
2131
0
        }
2132
2133
0
        GDALInfoReportMetadata(psOptions, hBand, true, bJson, poBandMetadata,
2134
0
                               osStr);
2135
0
        if (bJson)
2136
0
        {
2137
0
            if (psOptions->bShowMetadata)
2138
0
                json_object_object_add(poBand, "metadata", poBandMetadata);
2139
0
            else
2140
0
                json_object_put(poBandMetadata);
2141
0
        }
2142
2143
0
        GDALColorTableH hTable;
2144
0
        if (GDALGetRasterColorInterpretation(hBand) == GCI_PaletteIndex &&
2145
0
            (hTable = GDALGetRasterColorTable(hBand)) != nullptr)
2146
0
        {
2147
0
            if (!bJson)
2148
0
                Concat(osStr, psOptions->bStdoutOutput,
2149
0
                       "  Color Table (%s with %d entries)\n",
2150
0
                       GDALGetPaletteInterpretationName(
2151
0
                           GDALGetPaletteInterpretation(hTable)),
2152
0
                       GDALGetColorEntryCount(hTable));
2153
2154
0
            if (psOptions->bShowColorTable)
2155
0
            {
2156
0
                json_object *poEntries = nullptr;
2157
2158
0
                if (bJson)
2159
0
                {
2160
0
                    json_object *poPalette =
2161
0
                        json_object_new_string(GDALGetPaletteInterpretationName(
2162
0
                            GDALGetPaletteInterpretation(hTable)));
2163
0
                    json_object *poCount =
2164
0
                        json_object_new_int(GDALGetColorEntryCount(hTable));
2165
2166
0
                    json_object *poColorTable = json_object_new_object();
2167
2168
0
                    json_object_object_add(poColorTable, "palette", poPalette);
2169
0
                    json_object_object_add(poColorTable, "count", poCount);
2170
2171
0
                    poEntries = json_object_new_array();
2172
0
                    json_object_object_add(poColorTable, "entries", poEntries);
2173
0
                    json_object_object_add(poBand, "colorTable", poColorTable);
2174
0
                }
2175
2176
0
                for (int i = 0; i < GDALGetColorEntryCount(hTable); i++)
2177
0
                {
2178
0
                    GDALColorEntry sEntry;
2179
2180
0
                    GDALGetColorEntryAsRGB(hTable, i, &sEntry);
2181
2182
0
                    if (bJson)
2183
0
                    {
2184
0
                        json_object *poEntry = json_object_new_array();
2185
0
                        json_object *poC1 = json_object_new_int(sEntry.c1);
2186
0
                        json_object *poC2 = json_object_new_int(sEntry.c2);
2187
0
                        json_object *poC3 = json_object_new_int(sEntry.c3);
2188
0
                        json_object *poC4 = json_object_new_int(sEntry.c4);
2189
2190
0
                        json_object_array_add(poEntry, poC1);
2191
0
                        json_object_array_add(poEntry, poC2);
2192
0
                        json_object_array_add(poEntry, poC3);
2193
0
                        json_object_array_add(poEntry, poC4);
2194
0
                        json_object_array_add(poEntries, poEntry);
2195
0
                    }
2196
0
                    else
2197
0
                    {
2198
0
                        Concat(osStr, psOptions->bStdoutOutput,
2199
0
                               "  %3d: %d,%d,%d,%d\n", i, sEntry.c1, sEntry.c2,
2200
0
                               sEntry.c3, sEntry.c4);
2201
0
                    }
2202
0
                }
2203
0
            }
2204
0
        }
2205
2206
0
        if (psOptions->bShowRAT && GDALGetDefaultRAT(hBand) != nullptr)
2207
0
        {
2208
0
            GDALRasterAttributeTableH hRAT = GDALGetDefaultRAT(hBand);
2209
2210
0
            if (bJson)
2211
0
            {
2212
0
                json_object *poRAT =
2213
0
                    static_cast<json_object *>(GDALRATSerializeJSON(hRAT));
2214
0
                json_object_object_add(poBand, "rat", poRAT);
2215
0
            }
2216
0
            else
2217
0
            {
2218
0
                CPLXMLNode *psTree =
2219
0
                    static_cast<GDALRasterAttributeTable *>(hRAT)->Serialize();
2220
0
                char *pszXMLText = CPLSerializeXMLTree(psTree);
2221
0
                CPLDestroyXMLNode(psTree);
2222
0
                Concat(osStr, psOptions->bStdoutOutput, "%s\n", pszXMLText);
2223
0
                CPLFree(pszXMLText);
2224
0
            }
2225
0
        }
2226
0
        if (bJson)
2227
0
        {
2228
0
            json_object_array_add(poBands, poBand);
2229
0
            json_object_array_add(poStacRasterBands, poStacRasterBand);
2230
0
            json_object_array_add(poStacEOBands, poStacEOBand);
2231
0
        }
2232
0
    }
2233
2234
0
    if (bJson)
2235
0
    {
2236
0
        json_object_object_add(poJsonObject, "bands", poBands);
2237
0
        json_object_object_add(poStac, "raster:bands", poStacRasterBands);
2238
0
        json_object_object_add(poStac, "eo:bands", poStacEOBands);
2239
0
        json_object_object_add(poJsonObject, "stac", poStac);
2240
0
        Concat(osStr, psOptions->bStdoutOutput, "%s",
2241
0
               json_object_to_json_string_ext(
2242
0
                   poJsonObject, JSON_C_TO_STRING_PRETTY
2243
0
#ifdef JSON_C_TO_STRING_NOSLASHESCAPE
2244
0
                                     | JSON_C_TO_STRING_NOSLASHESCAPE
2245
0
#endif
2246
0
                   ));
2247
0
        json_object_put(poJsonObject);
2248
0
        Concat(osStr, psOptions->bStdoutOutput, "\n");
2249
0
    }
2250
2251
0
    if (psOptionsToFree != nullptr)
2252
0
        GDALInfoOptionsFree(psOptionsToFree);
2253
2254
0
    return VSI_STRDUP_VERBOSE(osStr);
2255
0
}
2256
2257
/************************************************************************/
2258
/*                        GDALInfoReportCorner()                        */
2259
/************************************************************************/
2260
2261
static int GDALInfoReportCorner(const GDALInfoOptions *psOptions,
2262
                                GDALDatasetH hDataset,
2263
                                OGRCoordinateTransformationH hTransform,
2264
                                const char *corner_name, double x, double y,
2265
                                bool bJson, json_object *poCornerCoordinates,
2266
                                json_object *poLongLatExtentCoordinates,
2267
                                CPLString &osStr)
2268
2269
0
{
2270
0
    if (!bJson)
2271
0
        Concat(osStr, psOptions->bStdoutOutput, "%-11s ", corner_name);
2272
2273
    /* -------------------------------------------------------------------- */
2274
    /*      Transform the point into georeferenced coordinates.             */
2275
    /* -------------------------------------------------------------------- */
2276
0
    double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
2277
0
    double dfGeoX = 0.0;
2278
0
    double dfGeoY = 0.0;
2279
2280
0
    if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
2281
0
    {
2282
0
        dfGeoX = adfGeoTransform[0] + adfGeoTransform[1] * x +
2283
0
                 adfGeoTransform[2] * y;
2284
0
        dfGeoY = adfGeoTransform[3] + adfGeoTransform[4] * x +
2285
0
                 adfGeoTransform[5] * y;
2286
0
    }
2287
0
    else
2288
0
    {
2289
0
        if (bJson)
2290
0
        {
2291
0
            json_object *const poCorner = json_object_new_array();
2292
0
            json_object *const poX =
2293
0
                json_object_new_double_with_precision(x, 1);
2294
0
            json_object *const poY =
2295
0
                json_object_new_double_with_precision(y, 1);
2296
0
            json_object_array_add(poCorner, poX);
2297
0
            json_object_array_add(poCorner, poY);
2298
0
            json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2299
0
        }
2300
0
        else
2301
0
        {
2302
0
            Concat(osStr, psOptions->bStdoutOutput, "(%7.1f,%7.1f)\n", x, y);
2303
0
        }
2304
0
        return FALSE;
2305
0
    }
2306
2307
    /* -------------------------------------------------------------------- */
2308
    /*      Report the georeferenced coordinates.                           */
2309
    /* -------------------------------------------------------------------- */
2310
0
    if (std::abs(dfGeoX) < 181 && std::abs(dfGeoY) < 91)
2311
0
    {
2312
0
        if (bJson)
2313
0
        {
2314
0
            json_object *const poCorner = json_object_new_array();
2315
0
            json_object *const poX =
2316
0
                json_object_new_double_with_precision(dfGeoX, 7);
2317
0
            json_object *const poY =
2318
0
                json_object_new_double_with_precision(dfGeoY, 7);
2319
0
            json_object_array_add(poCorner, poX);
2320
0
            json_object_array_add(poCorner, poY);
2321
0
            json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2322
0
        }
2323
0
        else
2324
0
        {
2325
0
            Concat(osStr, psOptions->bStdoutOutput, "(%12.7f,%12.7f) ", dfGeoX,
2326
0
                   dfGeoY);
2327
0
        }
2328
0
    }
2329
0
    else
2330
0
    {
2331
0
        if (bJson)
2332
0
        {
2333
0
            json_object *const poCorner = json_object_new_array();
2334
0
            json_object *const poX =
2335
0
                json_object_new_double_with_precision(dfGeoX, 3);
2336
0
            json_object *const poY =
2337
0
                json_object_new_double_with_precision(dfGeoY, 3);
2338
0
            json_object_array_add(poCorner, poX);
2339
0
            json_object_array_add(poCorner, poY);
2340
0
            json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2341
0
        }
2342
0
        else
2343
0
        {
2344
0
            Concat(osStr, psOptions->bStdoutOutput, "(%12.3f,%12.3f) ", dfGeoX,
2345
0
                   dfGeoY);
2346
0
        }
2347
0
    }
2348
2349
    /* -------------------------------------------------------------------- */
2350
    /*      Transform to latlong and report.                                */
2351
    /* -------------------------------------------------------------------- */
2352
0
    if (bJson)
2353
0
    {
2354
0
        double dfZ = 0.0;
2355
0
        if (hTransform != nullptr && !EQUAL(corner_name, "center") &&
2356
0
            OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
2357
0
        {
2358
0
            json_object *const poCorner = json_object_new_array();
2359
0
            json_object *const poX =
2360
0
                json_object_new_double_with_precision(dfGeoX, 7);
2361
0
            json_object *const poY =
2362
0
                json_object_new_double_with_precision(dfGeoY, 7);
2363
0
            json_object_array_add(poCorner, poX);
2364
0
            json_object_array_add(poCorner, poY);
2365
0
            json_object_array_add(poLongLatExtentCoordinates, poCorner);
2366
0
        }
2367
0
    }
2368
0
    else
2369
0
    {
2370
0
        double dfZ = 0.0;
2371
0
        if (hTransform != nullptr &&
2372
0
            OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
2373
0
        {
2374
0
            Concat(osStr, psOptions->bStdoutOutput, "(%s,",
2375
0
                   GDALDecToDMS(dfGeoX, "Long", 2));
2376
0
            Concat(osStr, psOptions->bStdoutOutput, "%s)",
2377
0
                   GDALDecToDMS(dfGeoY, "Lat", 2));
2378
0
        }
2379
0
        Concat(osStr, psOptions->bStdoutOutput, "\n");
2380
0
    }
2381
2382
0
    return TRUE;
2383
0
}
2384
2385
/************************************************************************/
2386
/*                       GDALInfoPrintMetadata()                        */
2387
/************************************************************************/
2388
static void GDALInfoPrintMetadata(const GDALInfoOptions *psOptions,
2389
                                  GDALMajorObjectH hObject,
2390
                                  const char *pszDomain,
2391
                                  const char *pszDisplayedname,
2392
                                  const char *pszIndent, int bJsonOutput,
2393
                                  json_object *poMetadata, CPLString &osStr)
2394
0
{
2395
0
    const bool bIsxml =
2396
0
        pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:");
2397
0
    const bool bMDIsJson =
2398
0
        pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:");
2399
2400
0
    CSLConstList papszMetadata = GDALGetMetadata(hObject, pszDomain);
2401
0
    if (papszMetadata != nullptr && *papszMetadata != nullptr)
2402
0
    {
2403
0
        json_object *poDomain = (bJsonOutput && !bIsxml && !bMDIsJson)
2404
0
                                    ? json_object_new_object()
2405
0
                                    : nullptr;
2406
2407
0
        if (!bJsonOutput)
2408
0
            Concat(osStr, psOptions->bStdoutOutput, "%s%s:\n", pszIndent,
2409
0
                   pszDisplayedname);
2410
2411
0
        json_object *poValue = nullptr;
2412
2413
0
        for (int i = 0; papszMetadata[i] != nullptr; i++)
2414
0
        {
2415
0
            if (bJsonOutput)
2416
0
            {
2417
0
                if (bIsxml)
2418
0
                {
2419
0
                    poValue = json_object_new_string(papszMetadata[i]);
2420
0
                    break;
2421
0
                }
2422
0
                else if (bMDIsJson)
2423
0
                {
2424
0
                    OGRJSonParse(papszMetadata[i], &poValue, true);
2425
0
                    break;
2426
0
                }
2427
0
                else
2428
0
                {
2429
0
                    char *pszKey = nullptr;
2430
0
                    const char *pszValue =
2431
0
                        CPLParseNameValue(papszMetadata[i], &pszKey);
2432
0
                    if (pszKey)
2433
0
                    {
2434
0
                        poValue = json_object_new_string(pszValue);
2435
0
                        json_object_object_add(poDomain, pszKey, poValue);
2436
0
                        CPLFree(pszKey);
2437
0
                    }
2438
0
                }
2439
0
            }
2440
0
            else
2441
0
            {
2442
0
                if (bIsxml || bMDIsJson)
2443
0
                    Concat(osStr, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
2444
0
                           papszMetadata[i]);
2445
0
                else
2446
0
                    Concat(osStr, psOptions->bStdoutOutput, "%s  %s\n",
2447
0
                           pszIndent, papszMetadata[i]);
2448
0
            }
2449
0
        }
2450
0
        if (bJsonOutput)
2451
0
        {
2452
0
            if (bIsxml || bMDIsJson)
2453
0
            {
2454
0
                json_object_object_add(poMetadata, pszDomain, poValue);
2455
0
            }
2456
0
            else
2457
0
            {
2458
0
                if (pszDomain == nullptr)
2459
0
                    json_object_object_add(poMetadata, "", poDomain);
2460
0
                else
2461
0
                    json_object_object_add(poMetadata, pszDomain, poDomain);
2462
0
            }
2463
0
        }
2464
0
    }
2465
0
}
2466
2467
/************************************************************************/
2468
/*                       GDALInfoReportMetadata()                       */
2469
/************************************************************************/
2470
static void GDALInfoReportMetadata(const GDALInfoOptions *psOptions,
2471
                                   GDALMajorObjectH hObject, bool bIsBand,
2472
                                   bool bJson, json_object *poMetadata,
2473
                                   CPLString &osStr)
2474
0
{
2475
0
    const char *const pszIndent = bIsBand ? "  " : "";
2476
2477
    /* -------------------------------------------------------------------- */
2478
    /*      Report list of Metadata domains                                 */
2479
    /* -------------------------------------------------------------------- */
2480
0
    if (psOptions->bListMDD)
2481
0
    {
2482
0
        const CPLStringList aosDomainList(GDALGetMetadataDomainList(hObject));
2483
0
        json_object *poMDD = nullptr;
2484
0
        json_object *const poListMDD =
2485
0
            bJson ? json_object_new_array() : nullptr;
2486
2487
0
        if (!aosDomainList.empty())
2488
0
        {
2489
0
            if (!bJson)
2490
0
                Concat(osStr, psOptions->bStdoutOutput, "%sMetadata domains:\n",
2491
0
                       pszIndent);
2492
0
        }
2493
2494
0
        for (const char *pszDomain : aosDomainList)
2495
0
        {
2496
0
            if (EQUAL(pszDomain, ""))
2497
0
            {
2498
0
                if (bJson)
2499
0
                    poMDD = json_object_new_string(pszDomain);
2500
0
                else
2501
0
                    Concat(osStr, psOptions->bStdoutOutput, "%s  (default)\n",
2502
0
                           pszIndent);
2503
0
            }
2504
0
            else
2505
0
            {
2506
0
                if (bJson)
2507
0
                    poMDD = json_object_new_string(pszDomain);
2508
0
                else
2509
0
                    Concat(osStr, psOptions->bStdoutOutput, "%s  %s\n",
2510
0
                           pszIndent, pszDomain);
2511
0
            }
2512
0
            if (bJson)
2513
0
                json_object_array_add(poListMDD, poMDD);
2514
0
        }
2515
0
        if (bJson)
2516
0
            json_object_object_add(poMetadata, "metadataDomains", poListMDD);
2517
0
    }
2518
2519
0
    if (!psOptions->bShowMetadata)
2520
0
        return;
2521
2522
    /* -------------------------------------------------------------------- */
2523
    /*      Report default Metadata domain.                                 */
2524
    /* -------------------------------------------------------------------- */
2525
0
    GDALInfoPrintMetadata(psOptions, hObject, nullptr, "Metadata", pszIndent,
2526
0
                          bJson, poMetadata, osStr);
2527
2528
    /* -------------------------------------------------------------------- */
2529
    /*      Report extra Metadata domains                                   */
2530
    /* -------------------------------------------------------------------- */
2531
0
    if (!psOptions->aosExtraMDDomains.empty())
2532
0
    {
2533
0
        CPLStringList aosExtraMDDomainsExpanded;
2534
2535
0
        if (EQUAL(psOptions->aosExtraMDDomains[0], "all") &&
2536
0
            psOptions->aosExtraMDDomains.Count() == 1)
2537
0
        {
2538
0
            const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
2539
0
            for (const char *pszDomain : aosMDDList)
2540
0
            {
2541
0
                if (!EQUAL(pszDomain, "") &&
2542
0
                    !EQUAL(pszDomain, GDAL_MDD_IMAGE_STRUCTURE) &&
2543
0
                    !EQUAL(pszDomain, "TILING_SCHEME") &&
2544
0
                    !EQUAL(pszDomain, GDAL_MDD_SUBDATASETS) &&
2545
0
                    !EQUAL(pszDomain, GDAL_MDD_GEOLOCATION) &&
2546
0
                    !EQUAL(pszDomain, GDAL_MDD_RPC))
2547
0
                {
2548
0
                    aosExtraMDDomainsExpanded.AddString(pszDomain);
2549
0
                }
2550
0
            }
2551
0
        }
2552
0
        else
2553
0
        {
2554
0
            aosExtraMDDomainsExpanded = psOptions->aosExtraMDDomains;
2555
0
        }
2556
2557
0
        for (const char *pszDomain : aosExtraMDDomainsExpanded)
2558
0
        {
2559
0
            if (bJson)
2560
0
            {
2561
0
                GDALInfoPrintMetadata(psOptions, hObject, pszDomain, pszDomain,
2562
0
                                      pszIndent, bJson, poMetadata, osStr);
2563
0
            }
2564
0
            else
2565
0
            {
2566
0
                const std::string osDisplayedName =
2567
0
                    std::string("Metadata (").append(pszDomain).append(")");
2568
2569
0
                GDALInfoPrintMetadata(psOptions, hObject, pszDomain,
2570
0
                                      osDisplayedName.c_str(), pszIndent, bJson,
2571
0
                                      poMetadata, osStr);
2572
0
            }
2573
0
        }
2574
0
    }
2575
2576
    /* -------------------------------------------------------------------- */
2577
    /*      Report various named metadata domains.                          */
2578
    /* -------------------------------------------------------------------- */
2579
0
    GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_IMAGE_STRUCTURE,
2580
0
                          "Image Structure Metadata", pszIndent, bJson,
2581
0
                          poMetadata, osStr);
2582
2583
0
    if (!bIsBand)
2584
0
    {
2585
0
        GDALInfoPrintMetadata(psOptions, hObject, "TILING_SCHEME",
2586
0
                              "Tiling Scheme", pszIndent, bJson, poMetadata,
2587
0
                              osStr);
2588
0
        GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_SUBDATASETS,
2589
0
                              "Subdatasets", pszIndent, bJson, poMetadata,
2590
0
                              osStr);
2591
0
        GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_GEOLOCATION,
2592
0
                              "Geolocation", pszIndent, bJson, poMetadata,
2593
0
                              osStr);
2594
0
        GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_RPC, "RPC Metadata",
2595
0
                              pszIndent, bJson, poMetadata, osStr);
2596
0
    }
2597
2598
0
    GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_IMAGERY, "Imagery",
2599
0
                          pszIndent, bJson, poMetadata, osStr);
2600
0
}
2601
2602
/************************************************************************/
2603
/*                         GDALInfoOptionsNew()                         */
2604
/************************************************************************/
2605
2606
/**
2607
 * Allocates a GDALInfoOptions struct.
2608
 *
2609
 * @param papszArgv NULL terminated list of options (potentially including
2610
 * filename and open options too), or NULL. The accepted options are the ones of
2611
 * the <a href="/programs/gdalinfo.html">gdalinfo</a> utility.
2612
 * @param psOptionsForBinary (output) may be NULL (and should generally be
2613
 * NULL), otherwise (gdalinfo_bin.cpp use case) must be allocated with
2614
 *                           GDALInfoOptionsForBinaryNew() prior to this
2615
 * function. Will be filled with potentially present filename, open options,
2616
 * subdataset number...
2617
 * @return pointer to the allocated GDALInfoOptions struct. Must be freed with
2618
 * GDALInfoOptionsFree().
2619
 *
2620
 * @since GDAL 2.1
2621
 */
2622
2623
GDALInfoOptions *
2624
GDALInfoOptionsNew(char **papszArgv,
2625
                   GDALInfoOptionsForBinary *psOptionsForBinary)
2626
0
{
2627
0
    auto psOptions = std::make_unique<GDALInfoOptions>();
2628
2629
    /* -------------------------------------------------------------------- */
2630
    /*      Parse arguments.                                                */
2631
    /* -------------------------------------------------------------------- */
2632
2633
0
    CPLStringList aosArgv;
2634
2635
0
    if (papszArgv)
2636
0
    {
2637
0
        const int nArgc = CSLCount(papszArgv);
2638
0
        for (int i = 0; i < nArgc; i++)
2639
0
        {
2640
0
            aosArgv.AddString(papszArgv[i]);
2641
0
        }
2642
0
    }
2643
2644
0
    try
2645
0
    {
2646
0
        auto argParser =
2647
0
            GDALInfoAppOptionsGetParser(psOptions.get(), psOptionsForBinary);
2648
2649
0
        argParser->parse_args_without_binary_name(aosArgv.List());
2650
2651
0
        if (psOptions->bApproxStats)
2652
0
            psOptions->bStats = true;
2653
0
    }
2654
0
    catch (const std::exception &error)
2655
0
    {
2656
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
2657
0
        return nullptr;
2658
0
    }
2659
2660
0
    if (!psOptions->bShowNodata)
2661
0
        psOptions->bShowMask = false;
2662
2663
0
    return psOptions.release();
2664
0
}
2665
2666
/************************************************************************/
2667
/*                        GDALInfoOptionsFree()                         */
2668
/************************************************************************/
2669
2670
/**
2671
 * Frees the GDALInfoOptions struct.
2672
 *
2673
 * @param psOptions the options struct for GDALInfo().
2674
 *
2675
 * @since GDAL 2.1
2676
 */
2677
2678
void GDALInfoOptionsFree(GDALInfoOptions *psOptions)
2679
0
{
2680
0
    delete psOptions;
2681
0
}