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