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