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 2793 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
150 : ...)
151 : {
152 : va_list args;
153 2793 : va_start(args, pszFormat);
154 :
155 2793 : if (bStdoutOutput)
156 : {
157 1896 : vfprintf(stdout, pszFormat, args);
158 : }
159 : else
160 : {
161 : try
162 : {
163 1794 : CPLString osTarget;
164 897 : osTarget.vPrintf(pszFormat, args);
165 :
166 897 : 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 2793 : va_end(args);
175 2793 : }
176 : #endif
177 :
178 : /************************************************************************/
179 : /* gdal_json_object_new_double_or_str_for_non_finite() */
180 : /************************************************************************/
181 :
182 : static json_object *
183 114 : gdal_json_object_new_double_or_str_for_non_finite(double dfVal, int nPrecision)
184 : {
185 114 : if (std::isinf(dfVal))
186 0 : return json_object_new_string(dfVal < 0 ? "-Infinity" : "Infinity");
187 114 : else if (std::isnan(dfVal))
188 0 : return json_object_new_string("NaN");
189 : else
190 114 : 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 139 : GDALInfoAppOptionsGetParser(GDALInfoOptions *psOptions,
216 : GDALInfoOptionsForBinary *psOptionsForBinary)
217 : {
218 : auto argParser = std::make_unique<GDALArgumentParser>(
219 139 : "gdalinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
220 :
221 139 : argParser->add_description(_("Raster dataset information utility."));
222 :
223 139 : argParser->add_epilog(
224 139 : _("For more details, consult https://gdal.org/programs/gdalinfo.html"));
225 :
226 139 : argParser->add_argument("-json")
227 139 : .flag()
228 81 : .action([psOptions](const auto &)
229 139 : { psOptions->eFormat = GDALINFO_FORMAT_JSON; })
230 139 : .help(_("Display the output in json format."));
231 :
232 139 : argParser->add_argument("-mm")
233 139 : .store_into(psOptions->bComputeMinMax)
234 : .help(_("Force computation of the actual min/max values for each band "
235 139 : "in the dataset."));
236 :
237 : {
238 139 : auto &group = argParser->add_mutually_exclusive_group();
239 139 : group.add_argument("-stats")
240 139 : .store_into(psOptions->bStats)
241 : .help(_("Read and display image statistics computing exact values "
242 139 : "if required."));
243 :
244 139 : group.add_argument("-approx_stats")
245 139 : .store_into(psOptions->bApproxStats)
246 : .help(
247 : _("Read and display image statistics computing approximated "
248 139 : "values on overviews or a subset of all tiles if required."));
249 : }
250 :
251 139 : argParser->add_argument("-hist")
252 139 : .store_into(psOptions->bReportHistograms)
253 139 : .help(_("Report histogram information for all bands."));
254 :
255 139 : argParser->add_usage_newline();
256 :
257 : argParser->add_inverted_logic_flag(
258 : "-nogcp", &psOptions->bShowGCPs,
259 139 : _("Suppress ground control points list printing."));
260 :
261 : argParser->add_inverted_logic_flag("-nomd", &psOptions->bShowMetadata,
262 139 : _("Suppress metadata printing."));
263 :
264 : argParser->add_inverted_logic_flag(
265 : "-norat", &psOptions->bShowRAT,
266 139 : _("Suppress printing of raster attribute table."));
267 :
268 : argParser->add_inverted_logic_flag("-noct", &psOptions->bShowColorTable,
269 139 : _("Suppress printing of color table."));
270 :
271 : argParser->add_inverted_logic_flag("-nofl", &psOptions->bShowFileList,
272 139 : _("Suppress display of the file list."));
273 :
274 : argParser->add_inverted_logic_flag(
275 : "-nonodata", &psOptions->bShowNodata,
276 139 : _("Suppress nodata printing (implies -nomask)."));
277 :
278 : argParser->add_inverted_logic_flag("-nomask", &psOptions->bShowMask,
279 139 : _("Suppress mask printing."));
280 :
281 139 : argParser->add_usage_newline();
282 :
283 139 : argParser->add_argument("-checksum")
284 139 : .flag()
285 139 : .store_into(psOptions->bComputeChecksum)
286 : .help(_(
287 139 : "Force computation of the checksum for each band in the dataset."));
288 :
289 139 : argParser->add_argument("-listmdd")
290 139 : .flag()
291 139 : .store_into(psOptions->bListMDD)
292 139 : .help(_("List all metadata domains available for the dataset."));
293 :
294 139 : argParser->add_argument("-proj4")
295 139 : .flag()
296 139 : .store_into(psOptions->bReportProj4)
297 : .help(_("Report a PROJ.4 string corresponding to the file's coordinate "
298 139 : "system."));
299 :
300 139 : argParser->add_argument("-wkt_format")
301 278 : .metavar("<WKT1|WKT2|WKT2_2015|WKT2_2018|WKT2_2019>")
302 139 : .choices("WKT1", "WKT2", "WKT2_2015", "WKT2_2018", "WKT2_2019")
303 139 : .store_into(psOptions->osWKTFormat)
304 139 : .help(_("WKT format used for SRS."));
305 :
306 139 : 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 139 : argParser->add_argument("-oo")
317 278 : .metavar("<NAME>=<VALUE>")
318 139 : .append()
319 : .action(
320 2 : [psOptionsForBinary](const std::string &s)
321 : {
322 1 : if (psOptionsForBinary)
323 1 : psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
324 139 : })
325 139 : .help(_("Open option(s) for dataset."));
326 :
327 : argParser->add_input_format_argument(
328 : psOptionsForBinary ? &psOptionsForBinary->aosAllowedInputDrivers
329 139 : : nullptr);
330 :
331 139 : argParser->add_argument("-mdd")
332 278 : .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 139 : })
339 : .help(_("Report metadata for the specified domains. 'all' can be used "
340 139 : "to report metadata in all domains."));
341 :
342 : /* Not documented: used by gdalinfo_bin.cpp only */
343 139 : argParser->add_argument("-stdout").flag().hidden().store_into(
344 139 : psOptions->bStdoutOutput);
345 :
346 139 : 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 139 : 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 134 : char *GDALInfo(GDALDatasetH hDataset, const GDALInfoOptions *psOptions)
402 : {
403 134 : if (hDataset == nullptr)
404 0 : return nullptr;
405 :
406 134 : GDALInfoOptions *psOptionsToFree = nullptr;
407 134 : if (psOptions == nullptr)
408 : {
409 0 : psOptionsToFree = GDALInfoOptionsNew(nullptr, nullptr);
410 0 : psOptions = psOptionsToFree;
411 : }
412 :
413 268 : CPLString osStr;
414 134 : json_object *poJsonObject = nullptr;
415 134 : json_object *poBands = nullptr;
416 134 : json_object *poMetadata = nullptr;
417 134 : json_object *poStac = nullptr;
418 134 : json_object *poStacRasterBands = nullptr;
419 134 : json_object *poStacEOBands = nullptr;
420 :
421 134 : const bool bJson = psOptions->eFormat == GDALINFO_FORMAT_JSON;
422 :
423 : /* -------------------------------------------------------------------- */
424 : /* Report general info. */
425 : /* -------------------------------------------------------------------- */
426 134 : GDALDriverH hDriver = GDALGetDatasetDriver(hDataset);
427 134 : if (bJson)
428 : {
429 : json_object *poDescription =
430 81 : json_object_new_string(GDALGetDescription(hDataset));
431 : json_object *poDriverShortName =
432 81 : json_object_new_string(GDALGetDriverShortName(hDriver));
433 : json_object *poDriverLongName =
434 81 : json_object_new_string(GDALGetDriverLongName(hDriver));
435 81 : poJsonObject = json_object_new_object();
436 81 : poBands = json_object_new_array();
437 81 : poMetadata = json_object_new_object();
438 81 : poStac = json_object_new_object();
439 81 : poStacRasterBands = json_object_new_array();
440 81 : poStacEOBands = json_object_new_array();
441 :
442 81 : json_object_object_add(poJsonObject, "description", poDescription);
443 81 : json_object_object_add(poJsonObject, "driverShortName",
444 : poDriverShortName);
445 81 : json_object_object_add(poJsonObject, "driverLongName",
446 : poDriverLongName);
447 : }
448 : else
449 : {
450 53 : Concat(osStr, psOptions->bStdoutOutput, "Driver: %s/%s\n",
451 : GDALGetDriverShortName(hDriver), GDALGetDriverLongName(hDriver));
452 : }
453 :
454 134 : 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 53 : (!bJson && EQUAL(GDALGetDriverShortName(hDriver), "OpenFileGDB"))
460 132 : ? nullptr
461 132 : : GDALGetFileList(hDataset);
462 :
463 132 : 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 106 : if (bJson)
479 : {
480 59 : json_object *poFiles = json_object_new_array();
481 :
482 135 : 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 59 : json_object_object_add(poJsonObject, "files", poFiles);
491 : }
492 : else
493 : {
494 47 : Concat(osStr, psOptions->bStdoutOutput, "Files: %s\n",
495 : papszFileList[0]);
496 64 : for (int i = 1; papszFileList[i] != nullptr; i++)
497 17 : Concat(osStr, psOptions->bStdoutOutput, " %s\n",
498 17 : papszFileList[i]);
499 : }
500 : }
501 132 : CSLDestroy(papszFileList);
502 : }
503 :
504 134 : if (bJson)
505 : {
506 : {
507 81 : json_object *poSize = json_object_new_array();
508 : json_object *poSizeX =
509 81 : json_object_new_int(GDALGetRasterXSize(hDataset));
510 : json_object *poSizeY =
511 81 : json_object_new_int(GDALGetRasterYSize(hDataset));
512 :
513 : // size is X, Y ordered
514 81 : json_object_array_add(poSize, poSizeX);
515 81 : json_object_array_add(poSize, poSizeY);
516 :
517 81 : json_object_object_add(poJsonObject, "size", poSize);
518 : }
519 :
520 : {
521 81 : json_object *poStacSize = json_object_new_array();
522 : json_object *poSizeX =
523 81 : json_object_new_int(GDALGetRasterXSize(hDataset));
524 : json_object *poSizeY =
525 81 : json_object_new_int(GDALGetRasterYSize(hDataset));
526 :
527 : // ... but ... proj:shape is Y, X ordered.
528 81 : json_object_array_add(poStacSize, poSizeY);
529 81 : json_object_array_add(poStacSize, poSizeX);
530 :
531 81 : json_object_object_add(poStac, "proj:shape", poStacSize);
532 : }
533 : }
534 : else
535 : {
536 53 : Concat(osStr, psOptions->bStdoutOutput, "Size is %d, %d\n",
537 : GDALGetRasterXSize(hDataset), GDALGetRasterYSize(hDataset));
538 : }
539 :
540 268 : CPLString osWKTFormat("FORMAT=");
541 134 : osWKTFormat += psOptions->osWKTFormat;
542 134 : const char *const apszWKTOptions[] = {osWKTFormat.c_str(), "MULTILINE=YES",
543 134 : nullptr};
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* Report projection. */
547 : /* -------------------------------------------------------------------- */
548 134 : auto hSRS = GDALGetSpatialRef(hDataset);
549 134 : if (hSRS != nullptr)
550 : {
551 115 : json_object *poCoordinateSystem = nullptr;
552 :
553 115 : if (bJson)
554 70 : poCoordinateSystem = json_object_new_object();
555 :
556 115 : char *pszPrettyWkt = nullptr;
557 :
558 115 : OSRExportToWktEx(hSRS, &pszPrettyWkt, apszWKTOptions);
559 :
560 115 : int nAxesCount = 0;
561 115 : const int *panAxes = OSRGetDataAxisToSRSAxisMapping(hSRS, &nAxesCount);
562 :
563 115 : const double dfCoordinateEpoch = OSRGetCoordinateEpoch(hSRS);
564 :
565 115 : if (bJson)
566 : {
567 70 : json_object *poWkt = json_object_new_string(pszPrettyWkt);
568 70 : if (psOptions->osWKTFormat == "WKT2")
569 : {
570 65 : json_object *poStacWkt = nullptr;
571 65 : json_object_deep_copy(poWkt, &poStacWkt, nullptr);
572 65 : json_object_object_add(poStac, "proj:wkt2", poStacWkt);
573 : }
574 70 : json_object_object_add(poCoordinateSystem, "wkt", poWkt);
575 :
576 70 : const char *pszAuthCode = OSRGetAuthorityCode(hSRS, nullptr);
577 70 : const char *pszAuthName = OSRGetAuthorityName(hSRS, nullptr);
578 70 : if (pszAuthCode && pszAuthName && EQUAL(pszAuthName, "EPSG"))
579 : {
580 53 : json_object *poEPSG = json_object_new_int64(atoi(pszAuthCode));
581 53 : 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 140 : CPLQuietErrorHandler);
594 70 : char *pszProjJson = nullptr;
595 : OGRErr result =
596 70 : OSRExportToPROJJSON(hSRS, &pszProjJson, nullptr);
597 70 : if (result == OGRERR_NONE)
598 : {
599 : json_object *poStacProjJson =
600 70 : json_tokener_parse(pszProjJson);
601 70 : json_object_object_add(poStac, "proj:projjson",
602 : poStacProjJson);
603 70 : CPLFree(pszProjJson);
604 : }
605 : }
606 :
607 70 : json_object *poAxisMapping = json_object_new_array();
608 212 : for (int i = 0; i < nAxesCount; i++)
609 : {
610 142 : json_object_array_add(poAxisMapping,
611 142 : json_object_new_int(panAxes[i]));
612 : }
613 70 : json_object_object_add(poCoordinateSystem,
614 : "dataAxisToSRSAxisMapping", poAxisMapping);
615 :
616 70 : 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 45 : Concat(osStr, psOptions->bStdoutOutput,
626 : "Coordinate System is:\n%s\n", pszPrettyWkt);
627 :
628 45 : Concat(osStr, psOptions->bStdoutOutput,
629 : "Data axis to CRS axis mapping: ");
630 137 : for (int i = 0; i < nAxesCount; i++)
631 : {
632 92 : if (i > 0)
633 : {
634 47 : Concat(osStr, psOptions->bStdoutOutput, ",");
635 : }
636 92 : Concat(osStr, psOptions->bStdoutOutput, "%d", panAxes[i]);
637 : }
638 45 : Concat(osStr, psOptions->bStdoutOutput, "\n");
639 :
640 45 : 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 115 : CPLFree(pszPrettyWkt);
656 :
657 115 : 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 115 : if (bJson)
674 70 : json_object_object_add(poJsonObject, "coordinateSystem",
675 : poCoordinateSystem);
676 : }
677 :
678 : /* -------------------------------------------------------------------- */
679 : /* Report Geotransform. */
680 : /* -------------------------------------------------------------------- */
681 134 : double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
682 134 : if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
683 : {
684 116 : if (bJson)
685 : {
686 69 : 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 69 : json_object *poStacGeoTransform = json_object_new_array();
690 :
691 483 : for (int i = 0; i < 6; i++)
692 : {
693 : json_object *poGeoTransformCoefficient =
694 414 : json_object_new_double_with_precision(adfGeoTransform[i],
695 : 16);
696 : json_object *poStacGeoTransformCoefficient =
697 414 : json_object_new_double_with_precision(adfGeoTransform[i],
698 : 16);
699 :
700 414 : json_object_array_add(poGeoTransform,
701 : poGeoTransformCoefficient);
702 414 : json_object_array_add(poStacGeoTransform,
703 : poStacGeoTransformCoefficient);
704 : }
705 :
706 69 : json_object_object_add(poJsonObject, "geoTransform",
707 : poGeoTransform);
708 69 : json_object_object_add(poStac, "proj:transform",
709 : poStacGeoTransform);
710 : }
711 : else
712 : {
713 47 : if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
714 : {
715 45 : Concat(osStr, psOptions->bStdoutOutput,
716 : "Origin = (%.15f,%.15f)\n", adfGeoTransform[0],
717 : adfGeoTransform[3]);
718 :
719 45 : 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 134 : 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 134 : GDALInfoReportMetadata(psOptions, hDataset, false, bJson, poMetadata,
850 : osStr);
851 134 : if (bJson)
852 : {
853 81 : if (psOptions->bShowMetadata)
854 78 : 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 81 : GDALGetMetadataItem(hDataset, "CLOUDCOVER", "IMAGERY");
861 81 : json_object *poValue = nullptr;
862 81 : 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 134 : OGRSpatialReferenceH hProj = nullptr;
873 134 : if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
874 116 : hProj = GDALGetSpatialRef(hDataset);
875 :
876 134 : OGRCoordinateTransformationH hTransform = nullptr;
877 134 : bool bTransformToWGS84 = false;
878 :
879 134 : if (hProj)
880 : {
881 114 : OGRSpatialReferenceH hLatLong = nullptr;
882 :
883 114 : 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 138 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
888 69 : OGRErr eErr = OGRERR_NONE;
889 137 : if (fabs(OSRGetSemiMajor(hProj, &eErr) - 6378137.0) < 10000.0 &&
890 68 : eErr == OGRERR_NONE)
891 : {
892 67 : bTransformToWGS84 = true;
893 67 : hLatLong = OSRNewSpatialReference(nullptr);
894 67 : OSRSetWellKnownGeogCS(hLatLong, "WGS84");
895 : }
896 : }
897 : else
898 : {
899 45 : hLatLong = OSRCloneGeogCS(hProj);
900 45 : if (hLatLong)
901 : {
902 : // Override GEOGCS|UNIT child to be sure to output as degrees
903 45 : OSRSetAngularUnits(hLatLong, SRS_UA_DEGREE,
904 : CPLAtof(SRS_UA_DEGREE_CONV));
905 : }
906 : }
907 :
908 114 : if (hLatLong != nullptr)
909 : {
910 112 : OSRSetAxisMappingStrategy(hLatLong, OAMS_TRADITIONAL_GIS_ORDER);
911 112 : CPLPushErrorHandler(CPLQuietErrorHandler);
912 112 : hTransform = OCTNewCoordinateTransformation(hProj, hLatLong);
913 112 : CPLPopErrorHandler();
914 :
915 112 : OSRDestroySpatialReference(hLatLong);
916 : }
917 : }
918 :
919 : /* -------------------------------------------------------------------- */
920 : /* Report corners. */
921 : /* -------------------------------------------------------------------- */
922 134 : if (bJson && GDALGetRasterXSize(hDataset))
923 : {
924 162 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
925 :
926 81 : json_object *poCornerCoordinates = json_object_new_object();
927 81 : json_object *poLongLatExtentCoordinates = json_object_new_array();
928 :
929 81 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
930 : 0.0, bJson, poCornerCoordinates,
931 : poLongLatExtentCoordinates, osStr);
932 162 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "lowerLeft", 0.0,
933 81 : GDALGetRasterYSize(hDataset), bJson,
934 : poCornerCoordinates, poLongLatExtentCoordinates,
935 : osStr);
936 243 : GDALInfoReportCorner(
937 : psOptions, hDataset, hTransform, "lowerRight",
938 81 : GDALGetRasterXSize(hDataset), GDALGetRasterYSize(hDataset), bJson,
939 : poCornerCoordinates, poLongLatExtentCoordinates, osStr);
940 162 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperRight",
941 81 : GDALGetRasterXSize(hDataset), 0.0, bJson,
942 : poCornerCoordinates, poLongLatExtentCoordinates,
943 : osStr);
944 243 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "center",
945 81 : GDALGetRasterXSize(hDataset) / 2.0,
946 81 : GDALGetRasterYSize(hDataset) / 2.0, bJson,
947 : poCornerCoordinates, poLongLatExtentCoordinates,
948 : osStr);
949 81 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
950 : 0.0, bJson, poCornerCoordinates,
951 : poLongLatExtentCoordinates, osStr);
952 :
953 81 : json_object_object_add(poJsonObject, "cornerCoordinates",
954 : poCornerCoordinates);
955 :
956 81 : if (json_object_array_length(poLongLatExtentCoordinates) > 0)
957 : {
958 67 : json_object *poLinearRing = json_object_new_array();
959 67 : json_object *poLongLatExtent = json_object_new_object();
960 : json_object *poLongLatExtentType =
961 67 : json_object_new_string("Polygon");
962 67 : json_object_object_add(poLongLatExtent, "type",
963 : poLongLatExtentType);
964 67 : json_object_array_add(poLinearRing, poLongLatExtentCoordinates);
965 67 : json_object_object_add(poLongLatExtent, "coordinates",
966 : poLinearRing);
967 67 : json_object_object_add(poJsonObject,
968 : bTransformToWGS84 ? "wgs84Extent" : "extent",
969 : poLongLatExtent);
970 : }
971 : else
972 : {
973 14 : json_object_put(poLongLatExtentCoordinates);
974 : }
975 : }
976 53 : else if (GDALGetRasterXSize(hDataset))
977 : {
978 106 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
979 :
980 53 : Concat(osStr, psOptions->bStdoutOutput, "Corner Coordinates:\n");
981 53 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Left", 0.0,
982 : 0.0, bJson, nullptr, nullptr, osStr);
983 106 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Left", 0.0,
984 53 : GDALGetRasterYSize(hDataset), bJson, nullptr,
985 : nullptr, osStr);
986 106 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Right",
987 53 : GDALGetRasterXSize(hDataset), 0.0, bJson, nullptr,
988 : nullptr, osStr);
989 159 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Right",
990 53 : GDALGetRasterXSize(hDataset),
991 53 : GDALGetRasterYSize(hDataset), bJson, nullptr,
992 : nullptr, osStr);
993 159 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Center",
994 53 : GDALGetRasterXSize(hDataset) / 2.0,
995 53 : GDALGetRasterYSize(hDataset) / 2.0, bJson, nullptr,
996 : nullptr, osStr);
997 : }
998 :
999 134 : if (hTransform != nullptr)
1000 : {
1001 112 : OCTDestroyCoordinateTransformation(hTransform);
1002 112 : hTransform = nullptr;
1003 : }
1004 :
1005 : /* ==================================================================== */
1006 : /* Loop over bands. */
1007 : /* ==================================================================== */
1008 297 : for (int iBand = 0; iBand < GDALGetRasterCount(hDataset); iBand++)
1009 : {
1010 163 : json_object *poBand = nullptr;
1011 163 : json_object *poBandMetadata = nullptr;
1012 163 : json_object *poStacRasterBand = nullptr;
1013 163 : json_object *poStacEOBand = nullptr;
1014 :
1015 163 : if (bJson)
1016 : {
1017 106 : poBand = json_object_new_object();
1018 106 : poBandMetadata = json_object_new_object();
1019 106 : poStacRasterBand = json_object_new_object();
1020 106 : poStacEOBand = json_object_new_object();
1021 : }
1022 :
1023 163 : GDALRasterBandH const hBand = GDALGetRasterBand(hDataset, iBand + 1);
1024 163 : const auto eDT = GDALGetRasterDataType(hBand);
1025 :
1026 163 : 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 163 : int nBlockXSize = 0;
1038 163 : int nBlockYSize = 0;
1039 163 : GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
1040 163 : if (bJson)
1041 : {
1042 106 : json_object *poBandNumber = json_object_new_int(iBand + 1);
1043 106 : json_object *poBlock = json_object_new_array();
1044 : json_object *poType =
1045 106 : json_object_new_string(GDALGetDataTypeName(eDT));
1046 : json_object *poColorInterp =
1047 106 : json_object_new_string(GDALGetColorInterpretationName(
1048 : GDALGetRasterColorInterpretation(hBand)));
1049 :
1050 106 : json_object_array_add(poBlock, json_object_new_int(nBlockXSize));
1051 106 : json_object_array_add(poBlock, json_object_new_int(nBlockYSize));
1052 106 : json_object_object_add(poBand, "band", poBandNumber);
1053 106 : json_object_object_add(poBand, "block", poBlock);
1054 106 : json_object_object_add(poBand, "type", poType);
1055 106 : json_object_object_add(poBand, "colorInterpretation",
1056 : poColorInterp);
1057 :
1058 106 : const char *stacDataType = nullptr;
1059 106 : switch (eDT)
1060 : {
1061 89 : case GDT_Byte:
1062 89 : stacDataType = "uint8";
1063 89 : 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 106 : if (stacDataType)
1114 106 : json_object_object_add(poStacRasterBand, "data_type",
1115 : json_object_new_string(stacDataType));
1116 : }
1117 : else
1118 : {
1119 57 : 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 163 : if (bJson)
1127 : {
1128 : json_object *poBandName =
1129 106 : json_object_new_string(CPLSPrintf("b%i", iBand + 1));
1130 106 : json_object_object_add(poStacEOBand, "name", poBandName);
1131 : }
1132 :
1133 163 : const char *pszBandDesc = GDALGetDescription(hBand);
1134 163 : 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 123 : if (bJson)
1153 : {
1154 : json_object *poColorInterp =
1155 72 : json_object_new_string(GDALGetColorInterpretationName(
1156 : GDALGetRasterColorInterpretation(hBand)));
1157 72 : json_object_object_add(poStacEOBand, "description",
1158 : poColorInterp);
1159 : }
1160 : }
1161 :
1162 163 : if (bJson)
1163 : {
1164 106 : const char *pszCommonName = GDALGetSTACCommonNameFromColorInterp(
1165 : GDALGetRasterColorInterpretation(hBand));
1166 106 : if (pszCommonName)
1167 : {
1168 22 : json_object_object_add(poStacEOBand, "common_name",
1169 : json_object_new_string(pszCommonName));
1170 : }
1171 : }
1172 :
1173 : {
1174 163 : int bGotMin = FALSE;
1175 163 : int bGotMax = FALSE;
1176 163 : const double dfMin = GDALGetRasterMinimum(hBand, &bGotMin);
1177 163 : const double dfMax = GDALGetRasterMaximum(hBand, &bGotMax);
1178 163 : if (bGotMin || bGotMax || psOptions->bComputeMinMax)
1179 : {
1180 37 : if (!bJson)
1181 8 : Concat(osStr, psOptions->bStdoutOutput, " ");
1182 37 : if (bGotMin)
1183 : {
1184 34 : if (bJson)
1185 : {
1186 : json_object *poMin =
1187 27 : gdal_json_object_new_double_or_str_for_non_finite(
1188 : dfMin, 3);
1189 27 : json_object_object_add(poBand, "min", poMin);
1190 : }
1191 : else
1192 : {
1193 7 : Concat(osStr, psOptions->bStdoutOutput, "Min=%.3f ",
1194 : dfMin);
1195 : }
1196 : }
1197 37 : if (bGotMax)
1198 : {
1199 34 : if (bJson)
1200 : {
1201 : json_object *poMax =
1202 27 : gdal_json_object_new_double_or_str_for_non_finite(
1203 : dfMax, 3);
1204 27 : json_object_object_add(poBand, "max", poMax);
1205 : }
1206 : else
1207 : {
1208 7 : Concat(osStr, psOptions->bStdoutOutput, "Max=%.3f ",
1209 : dfMax);
1210 : }
1211 : }
1212 :
1213 37 : 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 37 : if (!bJson)
1242 8 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1243 : }
1244 : }
1245 :
1246 163 : double dfMinStat = 0.0;
1247 163 : double dfMaxStat = 0.0;
1248 163 : double dfMean = 0.0;
1249 163 : double dfStdDev = 0.0;
1250 326 : CPLErr eErr = GDALGetRasterStatistics(hBand, psOptions->bApproxStats,
1251 163 : psOptions->bStats, &dfMinStat,
1252 : &dfMaxStat, &dfMean, &dfStdDev);
1253 163 : if (eErr == CE_None)
1254 : {
1255 16 : if (bJson)
1256 : {
1257 7 : json_object *poStacStats = json_object_new_object();
1258 : json_object *poMinimum =
1259 7 : gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
1260 : 3);
1261 7 : json_object_object_add(poBand, "minimum", poMinimum);
1262 : json_object *poStacMinimum =
1263 7 : gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
1264 : 3);
1265 7 : json_object_object_add(poStacStats, "minimum", poStacMinimum);
1266 :
1267 : json_object *poMaximum =
1268 7 : gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
1269 : 3);
1270 7 : json_object_object_add(poBand, "maximum", poMaximum);
1271 : json_object *poStacMaximum =
1272 7 : gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
1273 : 3);
1274 7 : json_object_object_add(poStacStats, "maximum", poStacMaximum);
1275 :
1276 : json_object *poMean =
1277 7 : gdal_json_object_new_double_or_str_for_non_finite(dfMean,
1278 : 3);
1279 7 : json_object_object_add(poBand, "mean", poMean);
1280 : json_object *poStacMean =
1281 7 : gdal_json_object_new_double_or_str_for_non_finite(dfMean,
1282 : 3);
1283 7 : json_object_object_add(poStacStats, "mean", poStacMean);
1284 :
1285 : json_object *poStdDev =
1286 7 : gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
1287 : 3);
1288 7 : json_object_object_add(poBand, "stdDev", poStdDev);
1289 : json_object *poStacStdDev =
1290 7 : gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
1291 : 3);
1292 7 : json_object_object_add(poStacStats, "stddev", poStacStdDev);
1293 :
1294 7 : json_object_object_add(poStacRasterBand, "stats", poStacStats);
1295 : }
1296 : else
1297 : {
1298 9 : Concat(osStr, psOptions->bStdoutOutput,
1299 : " Minimum=%.3f, Maximum=%.3f, Mean=%.3f, StdDev=%.3f\n",
1300 : dfMinStat, dfMaxStat, dfMean, dfStdDev);
1301 : }
1302 : }
1303 :
1304 163 : 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 163 : 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 163 : int bGotNodata = FALSE;
1389 163 : if (!psOptions->bShowNodata)
1390 : {
1391 : // nothing to do
1392 : }
1393 161 : 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 161 : 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 161 : GDALGetRasterNoDataValue(hBand, &bGotNodata);
1462 161 : if (bGotNodata)
1463 : {
1464 26 : const bool bIsNoDataFloat =
1465 42 : eDT == GDT_Float32 &&
1466 16 : static_cast<double>(static_cast<float>(dfNoData)) ==
1467 : dfNoData;
1468 : // Find the most compact decimal representation of the nodata
1469 : // value that can be used to exactly represent the binary value
1470 26 : int nSignificantDigits = bIsNoDataFloat ? 8 : 18;
1471 26 : char szNoData[64] = {0};
1472 286 : while (nSignificantDigits > 0)
1473 : {
1474 : char szCandidateNoData[64];
1475 : char szFormat[16];
1476 265 : snprintf(szFormat, sizeof(szFormat), "%%.%dg",
1477 : nSignificantDigits);
1478 265 : CPLsnprintf(szCandidateNoData, sizeof(szCandidateNoData),
1479 : szFormat, dfNoData);
1480 239 : if (szNoData[0] == '\0' ||
1481 112 : (bIsNoDataFloat &&
1482 112 : static_cast<float>(CPLAtof(szCandidateNoData)) ==
1483 504 : static_cast<float>(dfNoData)) ||
1484 127 : (!bIsNoDataFloat &&
1485 127 : CPLAtof(szCandidateNoData) == dfNoData))
1486 : {
1487 260 : strcpy(szNoData, szCandidateNoData);
1488 260 : nSignificantDigits--;
1489 : }
1490 : else
1491 : {
1492 5 : nSignificantDigits++;
1493 5 : break;
1494 : }
1495 : }
1496 :
1497 26 : if (bJson)
1498 : {
1499 : json_object *poNoDataValue =
1500 19 : (GDALDataTypeIsInteger(eDT) && dfNoData >= INT_MIN &&
1501 3 : dfNoData <= INT_MAX &&
1502 3 : static_cast<int>(dfNoData) == dfNoData)
1503 19 : ? json_object_new_int(static_cast<int>(dfNoData))
1504 13 : : gdal_json_object_new_double_significant_digits(
1505 16 : dfNoData, nSignificantDigits);
1506 16 : json_object *poStacNoDataValue = nullptr;
1507 16 : json_object_deep_copy(poNoDataValue, &poStacNoDataValue,
1508 : nullptr);
1509 16 : json_object_object_add(poStacRasterBand, "nodata",
1510 : poStacNoDataValue);
1511 16 : json_object_object_add(poBand, "noDataValue",
1512 : poNoDataValue);
1513 : }
1514 10 : else if (std::isnan(dfNoData))
1515 : {
1516 0 : Concat(osStr, psOptions->bStdoutOutput,
1517 : " NoData Value=nan\n");
1518 : }
1519 : else
1520 : {
1521 10 : Concat(osStr, psOptions->bStdoutOutput,
1522 : " NoData Value=%s\n", szNoData);
1523 : }
1524 : }
1525 : }
1526 :
1527 163 : if (GDALGetOverviewCount(hBand) > 0)
1528 : {
1529 9 : json_object *poOverviews = nullptr;
1530 :
1531 9 : if (bJson)
1532 8 : poOverviews = json_object_new_array();
1533 : else
1534 1 : Concat(osStr, psOptions->bStdoutOutput, " Overviews: ");
1535 :
1536 35 : for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
1537 : iOverview++)
1538 : {
1539 26 : if (!bJson)
1540 1 : if (iOverview != 0)
1541 0 : Concat(osStr, psOptions->bStdoutOutput, ", ");
1542 :
1543 26 : GDALRasterBandH hOverview = GDALGetOverview(hBand, iOverview);
1544 26 : if (hOverview != nullptr)
1545 : {
1546 26 : if (bJson)
1547 : {
1548 25 : json_object *poOverviewSize = json_object_new_array();
1549 25 : json_object *poOverviewSizeX = json_object_new_int(
1550 : GDALGetRasterBandXSize(hOverview));
1551 25 : json_object *poOverviewSizeY = json_object_new_int(
1552 : GDALGetRasterBandYSize(hOverview));
1553 :
1554 25 : json_object *poOverview = json_object_new_object();
1555 25 : json_object_array_add(poOverviewSize, poOverviewSizeX);
1556 25 : json_object_array_add(poOverviewSize, poOverviewSizeY);
1557 25 : json_object_object_add(poOverview, "size",
1558 : poOverviewSize);
1559 :
1560 25 : if (psOptions->bComputeChecksum)
1561 : {
1562 16 : const int nOverviewChecksum = GDALChecksumImage(
1563 : hOverview, 0, 0,
1564 : GDALGetRasterBandXSize(hOverview),
1565 : GDALGetRasterBandYSize(hOverview));
1566 : json_object *poOverviewChecksum =
1567 16 : json_object_new_int(nOverviewChecksum);
1568 16 : json_object_object_add(poOverview, "checksum",
1569 : poOverviewChecksum);
1570 : }
1571 25 : json_object_array_add(poOverviews, poOverview);
1572 : }
1573 : else
1574 : {
1575 1 : Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
1576 : GDALGetRasterBandXSize(hOverview),
1577 : GDALGetRasterBandYSize(hOverview));
1578 : }
1579 :
1580 : const char *pszResampling =
1581 26 : GDALGetMetadataItem(hOverview, "RESAMPLING", "");
1582 :
1583 26 : if (pszResampling != nullptr && !bJson &&
1584 0 : STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
1585 0 : Concat(osStr, psOptions->bStdoutOutput, "*");
1586 : }
1587 : else
1588 : {
1589 0 : if (!bJson)
1590 0 : Concat(osStr, psOptions->bStdoutOutput, "(null)");
1591 : }
1592 : }
1593 9 : if (bJson)
1594 8 : json_object_object_add(poBand, "overviews", poOverviews);
1595 : else
1596 1 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1597 :
1598 9 : if (psOptions->bComputeChecksum && !bJson)
1599 : {
1600 0 : Concat(osStr, psOptions->bStdoutOutput,
1601 : " Overviews checksum: ");
1602 :
1603 0 : for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
1604 : iOverview++)
1605 : {
1606 : GDALRasterBandH hOverview;
1607 :
1608 0 : if (iOverview != 0)
1609 0 : Concat(osStr, psOptions->bStdoutOutput, ", ");
1610 :
1611 0 : hOverview = GDALGetOverview(hBand, iOverview);
1612 0 : if (hOverview)
1613 : {
1614 0 : Concat(osStr, psOptions->bStdoutOutput, "%d",
1615 : GDALChecksumImage(
1616 : hOverview, 0, 0,
1617 : GDALGetRasterBandXSize(hOverview),
1618 : GDALGetRasterBandYSize(hOverview)));
1619 : }
1620 : else
1621 : {
1622 0 : Concat(osStr, psOptions->bStdoutOutput, "(null)");
1623 : }
1624 : }
1625 0 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1626 : }
1627 : }
1628 :
1629 163 : if (GDALHasArbitraryOverviews(hBand) && !bJson)
1630 : {
1631 0 : Concat(osStr, psOptions->bStdoutOutput, " Overviews: arbitrary\n");
1632 : }
1633 :
1634 : const int nMaskFlags =
1635 163 : psOptions->bShowMask ? GDALGetMaskFlags(hBand) : GMF_ALL_VALID;
1636 163 : if ((nMaskFlags & (GMF_NODATA | GMF_ALL_VALID)) == 0 ||
1637 : nMaskFlags == (GMF_NODATA | GMF_PER_DATASET))
1638 : {
1639 21 : GDALRasterBandH hMaskBand = GDALGetMaskBand(hBand);
1640 21 : json_object *poMask = nullptr;
1641 21 : json_object *poFlags = nullptr;
1642 21 : json_object *poMaskOverviews = nullptr;
1643 :
1644 21 : if (bJson)
1645 : {
1646 17 : poMask = json_object_new_object();
1647 17 : poFlags = json_object_new_array();
1648 : }
1649 : else
1650 4 : Concat(osStr, psOptions->bStdoutOutput, " Mask Flags: ");
1651 21 : if (nMaskFlags & GMF_PER_DATASET)
1652 : {
1653 19 : if (bJson)
1654 : {
1655 16 : json_object *poFlag = json_object_new_string("PER_DATASET");
1656 16 : json_object_array_add(poFlags, poFlag);
1657 : }
1658 : else
1659 3 : Concat(osStr, psOptions->bStdoutOutput, "PER_DATASET ");
1660 : }
1661 21 : if (nMaskFlags & GMF_ALPHA)
1662 : {
1663 15 : if (bJson)
1664 : {
1665 15 : json_object *poFlag = json_object_new_string("ALPHA");
1666 15 : json_object_array_add(poFlags, poFlag);
1667 : }
1668 : else
1669 0 : Concat(osStr, psOptions->bStdoutOutput, "ALPHA ");
1670 : }
1671 21 : if (nMaskFlags & GMF_NODATA)
1672 : {
1673 3 : if (bJson)
1674 : {
1675 0 : json_object *poFlag = json_object_new_string("NODATA");
1676 0 : json_object_array_add(poFlags, poFlag);
1677 : }
1678 : else
1679 : {
1680 3 : Concat(osStr, psOptions->bStdoutOutput, "NODATA ");
1681 : }
1682 : }
1683 :
1684 21 : if (bJson)
1685 17 : json_object_object_add(poMask, "flags", poFlags);
1686 : else
1687 4 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1688 :
1689 21 : if (bJson)
1690 17 : poMaskOverviews = json_object_new_array();
1691 :
1692 21 : if (hMaskBand != nullptr && GDALGetOverviewCount(hMaskBand) > 0)
1693 : {
1694 0 : if (!bJson)
1695 0 : Concat(osStr, psOptions->bStdoutOutput,
1696 : " Overviews of mask band: ");
1697 :
1698 0 : for (int iOverview = 0;
1699 0 : iOverview < GDALGetOverviewCount(hMaskBand); iOverview++)
1700 : {
1701 : GDALRasterBandH hOverview =
1702 0 : GDALGetOverview(hMaskBand, iOverview);
1703 0 : if (!hOverview)
1704 0 : break;
1705 0 : json_object *poMaskOverview = nullptr;
1706 0 : json_object *poMaskOverviewSize = nullptr;
1707 :
1708 0 : if (bJson)
1709 : {
1710 0 : poMaskOverview = json_object_new_object();
1711 0 : poMaskOverviewSize = json_object_new_array();
1712 : }
1713 : else
1714 : {
1715 0 : if (iOverview != 0)
1716 0 : Concat(osStr, psOptions->bStdoutOutput, ", ");
1717 : }
1718 :
1719 0 : if (bJson)
1720 : {
1721 0 : json_object *poMaskOverviewSizeX = json_object_new_int(
1722 : GDALGetRasterBandXSize(hOverview));
1723 0 : json_object *poMaskOverviewSizeY = json_object_new_int(
1724 : GDALGetRasterBandYSize(hOverview));
1725 :
1726 0 : json_object_array_add(poMaskOverviewSize,
1727 : poMaskOverviewSizeX);
1728 0 : json_object_array_add(poMaskOverviewSize,
1729 : poMaskOverviewSizeY);
1730 0 : json_object_object_add(poMaskOverview, "size",
1731 : poMaskOverviewSize);
1732 0 : json_object_array_add(poMaskOverviews, poMaskOverview);
1733 : }
1734 : else
1735 : {
1736 0 : Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
1737 : GDALGetRasterBandXSize(hOverview),
1738 : GDALGetRasterBandYSize(hOverview));
1739 : }
1740 : }
1741 0 : if (!bJson)
1742 0 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1743 : }
1744 21 : if (bJson)
1745 : {
1746 17 : json_object_object_add(poMask, "overviews", poMaskOverviews);
1747 17 : json_object_object_add(poBand, "mask", poMask);
1748 : }
1749 : }
1750 :
1751 163 : if (strlen(GDALGetRasterUnitType(hBand)) > 0)
1752 : {
1753 0 : if (bJson)
1754 : {
1755 : json_object *poUnit =
1756 0 : json_object_new_string(GDALGetRasterUnitType(hBand));
1757 0 : json_object *poStacUnit = nullptr;
1758 0 : json_object_deep_copy(poUnit, &poStacUnit, nullptr);
1759 0 : json_object_object_add(poStacRasterBand, "unit", poStacUnit);
1760 0 : json_object_object_add(poBand, "unit", poUnit);
1761 : }
1762 : else
1763 : {
1764 0 : Concat(osStr, psOptions->bStdoutOutput, " Unit Type: %s\n",
1765 : GDALGetRasterUnitType(hBand));
1766 : }
1767 : }
1768 :
1769 163 : if (GDALGetRasterCategoryNames(hBand) != nullptr)
1770 : {
1771 0 : char **papszCategories = GDALGetRasterCategoryNames(hBand);
1772 0 : json_object *poCategories = nullptr;
1773 :
1774 0 : if (bJson)
1775 0 : poCategories = json_object_new_array();
1776 : else
1777 0 : Concat(osStr, psOptions->bStdoutOutput, " Categories:\n");
1778 :
1779 0 : for (int i = 0; papszCategories[i] != nullptr; i++)
1780 : {
1781 0 : if (bJson)
1782 : {
1783 : json_object *poCategoryName =
1784 0 : json_object_new_string(papszCategories[i]);
1785 0 : json_object_array_add(poCategories, poCategoryName);
1786 : }
1787 : else
1788 0 : Concat(osStr, psOptions->bStdoutOutput, " %3d: %s\n", i,
1789 0 : papszCategories[i]);
1790 : }
1791 0 : if (bJson)
1792 0 : json_object_object_add(poBand, "categories", poCategories);
1793 : }
1794 :
1795 163 : int bSuccess = FALSE;
1796 326 : if (GDALGetRasterScale(hBand, &bSuccess) != 1.0 ||
1797 163 : GDALGetRasterOffset(hBand, &bSuccess) != 0.0)
1798 : {
1799 0 : if (bJson)
1800 : {
1801 0 : json_object *poOffset = json_object_new_double_with_precision(
1802 : GDALGetRasterOffset(hBand, &bSuccess), 15);
1803 0 : json_object *poScale = json_object_new_double_with_precision(
1804 : GDALGetRasterScale(hBand, &bSuccess), 15);
1805 0 : json_object *poStacScale = nullptr;
1806 0 : json_object *poStacOffset = nullptr;
1807 0 : json_object_deep_copy(poScale, &poStacScale, nullptr);
1808 0 : json_object_deep_copy(poOffset, &poStacOffset, nullptr);
1809 0 : json_object_object_add(poStacRasterBand, "scale", poStacScale);
1810 0 : json_object_object_add(poStacRasterBand, "offset",
1811 : poStacOffset);
1812 0 : json_object_object_add(poBand, "offset", poOffset);
1813 0 : json_object_object_add(poBand, "scale", poScale);
1814 : }
1815 : else
1816 : {
1817 0 : Concat(osStr, psOptions->bStdoutOutput,
1818 : " Offset: %.15g, Scale:%.15g\n",
1819 : GDALGetRasterOffset(hBand, &bSuccess),
1820 : GDALGetRasterScale(hBand, &bSuccess));
1821 : }
1822 : }
1823 :
1824 163 : GDALInfoReportMetadata(psOptions, hBand, true, bJson, poBandMetadata,
1825 : osStr);
1826 163 : if (bJson)
1827 : {
1828 106 : if (psOptions->bShowMetadata)
1829 103 : json_object_object_add(poBand, "metadata", poBandMetadata);
1830 : else
1831 3 : json_object_put(poBandMetadata);
1832 : }
1833 :
1834 : GDALColorTableH hTable;
1835 172 : if (GDALGetRasterColorInterpretation(hBand) == GCI_PaletteIndex &&
1836 9 : (hTable = GDALGetRasterColorTable(hBand)) != nullptr)
1837 : {
1838 9 : if (!bJson)
1839 2 : Concat(osStr, psOptions->bStdoutOutput,
1840 : " Color Table (%s with %d entries)\n",
1841 : GDALGetPaletteInterpretationName(
1842 : GDALGetPaletteInterpretation(hTable)),
1843 : GDALGetColorEntryCount(hTable));
1844 :
1845 9 : if (psOptions->bShowColorTable)
1846 : {
1847 7 : json_object *poEntries = nullptr;
1848 :
1849 7 : if (bJson)
1850 : {
1851 : json_object *poPalette =
1852 6 : json_object_new_string(GDALGetPaletteInterpretationName(
1853 : GDALGetPaletteInterpretation(hTable)));
1854 : json_object *poCount =
1855 6 : json_object_new_int(GDALGetColorEntryCount(hTable));
1856 :
1857 6 : json_object *poColorTable = json_object_new_object();
1858 :
1859 6 : json_object_object_add(poColorTable, "palette", poPalette);
1860 6 : json_object_object_add(poColorTable, "count", poCount);
1861 :
1862 6 : poEntries = json_object_new_array();
1863 6 : json_object_object_add(poColorTable, "entries", poEntries);
1864 6 : json_object_object_add(poBand, "colorTable", poColorTable);
1865 : }
1866 :
1867 750 : for (int i = 0; i < GDALGetColorEntryCount(hTable); i++)
1868 : {
1869 : GDALColorEntry sEntry;
1870 :
1871 743 : GDALGetColorEntryAsRGB(hTable, i, &sEntry);
1872 :
1873 743 : if (bJson)
1874 : {
1875 727 : json_object *poEntry = json_object_new_array();
1876 727 : json_object *poC1 = json_object_new_int(sEntry.c1);
1877 727 : json_object *poC2 = json_object_new_int(sEntry.c2);
1878 727 : json_object *poC3 = json_object_new_int(sEntry.c3);
1879 727 : json_object *poC4 = json_object_new_int(sEntry.c4);
1880 :
1881 727 : json_object_array_add(poEntry, poC1);
1882 727 : json_object_array_add(poEntry, poC2);
1883 727 : json_object_array_add(poEntry, poC3);
1884 727 : json_object_array_add(poEntry, poC4);
1885 727 : json_object_array_add(poEntries, poEntry);
1886 : }
1887 : else
1888 : {
1889 16 : Concat(osStr, psOptions->bStdoutOutput,
1890 16 : " %3d: %d,%d,%d,%d\n", i, sEntry.c1, sEntry.c2,
1891 16 : sEntry.c3, sEntry.c4);
1892 : }
1893 : }
1894 : }
1895 : }
1896 :
1897 163 : if (psOptions->bShowRAT && GDALGetDefaultRAT(hBand) != nullptr)
1898 : {
1899 5 : GDALRasterAttributeTableH hRAT = GDALGetDefaultRAT(hBand);
1900 :
1901 5 : if (bJson)
1902 : {
1903 : json_object *poRAT =
1904 4 : static_cast<json_object *>(GDALRATSerializeJSON(hRAT));
1905 4 : json_object_object_add(poBand, "rat", poRAT);
1906 : }
1907 : else
1908 : {
1909 : CPLXMLNode *psTree =
1910 1 : static_cast<GDALRasterAttributeTable *>(hRAT)->Serialize();
1911 1 : char *pszXMLText = CPLSerializeXMLTree(psTree);
1912 1 : CPLDestroyXMLNode(psTree);
1913 1 : Concat(osStr, psOptions->bStdoutOutput, "%s\n", pszXMLText);
1914 1 : CPLFree(pszXMLText);
1915 : }
1916 : }
1917 163 : if (bJson)
1918 : {
1919 106 : json_object_array_add(poBands, poBand);
1920 106 : json_object_array_add(poStacRasterBands, poStacRasterBand);
1921 106 : json_object_array_add(poStacEOBands, poStacEOBand);
1922 : }
1923 : }
1924 :
1925 134 : if (bJson)
1926 : {
1927 81 : json_object_object_add(poJsonObject, "bands", poBands);
1928 81 : json_object_object_add(poStac, "raster:bands", poStacRasterBands);
1929 81 : json_object_object_add(poStac, "eo:bands", poStacEOBands);
1930 81 : json_object_object_add(poJsonObject, "stac", poStac);
1931 81 : Concat(osStr, psOptions->bStdoutOutput, "%s",
1932 : json_object_to_json_string_ext(
1933 : poJsonObject, JSON_C_TO_STRING_PRETTY
1934 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
1935 : | JSON_C_TO_STRING_NOSLASHESCAPE
1936 : #endif
1937 : ));
1938 81 : json_object_put(poJsonObject);
1939 81 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1940 : }
1941 :
1942 134 : if (psOptionsToFree != nullptr)
1943 0 : GDALInfoOptionsFree(psOptionsToFree);
1944 :
1945 134 : return VSI_STRDUP_VERBOSE(osStr);
1946 : }
1947 :
1948 : /************************************************************************/
1949 : /* GDALInfoReportCorner() */
1950 : /************************************************************************/
1951 :
1952 751 : static int GDALInfoReportCorner(const GDALInfoOptions *psOptions,
1953 : GDALDatasetH hDataset,
1954 : OGRCoordinateTransformationH hTransform,
1955 : const char *corner_name, double x, double y,
1956 : bool bJson, json_object *poCornerCoordinates,
1957 : json_object *poLongLatExtentCoordinates,
1958 : CPLString &osStr)
1959 :
1960 : {
1961 751 : if (!bJson)
1962 265 : Concat(osStr, psOptions->bStdoutOutput, "%-11s ", corner_name);
1963 :
1964 : /* -------------------------------------------------------------------- */
1965 : /* Transform the point into georeferenced coordinates. */
1966 : /* -------------------------------------------------------------------- */
1967 751 : double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
1968 751 : double dfGeoX = 0.0;
1969 751 : double dfGeoY = 0.0;
1970 :
1971 751 : if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
1972 : {
1973 649 : dfGeoX = adfGeoTransform[0] + adfGeoTransform[1] * x +
1974 649 : adfGeoTransform[2] * y;
1975 649 : dfGeoY = adfGeoTransform[3] + adfGeoTransform[4] * x +
1976 649 : adfGeoTransform[5] * y;
1977 : }
1978 : else
1979 : {
1980 102 : if (bJson)
1981 : {
1982 72 : json_object *const poCorner = json_object_new_array();
1983 : json_object *const poX =
1984 72 : json_object_new_double_with_precision(x, 1);
1985 : json_object *const poY =
1986 72 : json_object_new_double_with_precision(y, 1);
1987 72 : json_object_array_add(poCorner, poX);
1988 72 : json_object_array_add(poCorner, poY);
1989 72 : json_object_object_add(poCornerCoordinates, corner_name, poCorner);
1990 : }
1991 : else
1992 : {
1993 30 : Concat(osStr, psOptions->bStdoutOutput, "(%7.1f,%7.1f)\n", x, y);
1994 : }
1995 102 : return FALSE;
1996 : }
1997 :
1998 : /* -------------------------------------------------------------------- */
1999 : /* Report the georeferenced coordinates. */
2000 : /* -------------------------------------------------------------------- */
2001 649 : if (std::abs(dfGeoX) < 181 && std::abs(dfGeoY) < 91)
2002 : {
2003 88 : if (bJson)
2004 : {
2005 48 : json_object *const poCorner = json_object_new_array();
2006 : json_object *const poX =
2007 48 : json_object_new_double_with_precision(dfGeoX, 7);
2008 : json_object *const poY =
2009 48 : json_object_new_double_with_precision(dfGeoY, 7);
2010 48 : json_object_array_add(poCorner, poX);
2011 48 : json_object_array_add(poCorner, poY);
2012 48 : json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2013 : }
2014 : else
2015 : {
2016 40 : Concat(osStr, psOptions->bStdoutOutput, "(%12.7f,%12.7f) ", dfGeoX,
2017 : dfGeoY);
2018 : }
2019 : }
2020 : else
2021 : {
2022 561 : if (bJson)
2023 : {
2024 366 : json_object *const poCorner = json_object_new_array();
2025 : json_object *const poX =
2026 366 : json_object_new_double_with_precision(dfGeoX, 3);
2027 : json_object *const poY =
2028 366 : json_object_new_double_with_precision(dfGeoY, 3);
2029 366 : json_object_array_add(poCorner, poX);
2030 366 : json_object_array_add(poCorner, poY);
2031 366 : json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2032 : }
2033 : else
2034 : {
2035 195 : Concat(osStr, psOptions->bStdoutOutput, "(%12.3f,%12.3f) ", dfGeoX,
2036 : dfGeoY);
2037 : }
2038 : }
2039 :
2040 : /* -------------------------------------------------------------------- */
2041 : /* Transform to latlong and report. */
2042 : /* -------------------------------------------------------------------- */
2043 649 : if (bJson)
2044 : {
2045 414 : double dfZ = 0.0;
2046 749 : if (hTransform != nullptr && !EQUAL(corner_name, "center") &&
2047 335 : OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
2048 : {
2049 335 : json_object *const poCorner = json_object_new_array();
2050 : json_object *const poX =
2051 335 : json_object_new_double_with_precision(dfGeoX, 7);
2052 : json_object *const poY =
2053 335 : json_object_new_double_with_precision(dfGeoY, 7);
2054 335 : json_object_array_add(poCorner, poX);
2055 335 : json_object_array_add(poCorner, poY);
2056 335 : json_object_array_add(poLongLatExtentCoordinates, poCorner);
2057 : }
2058 : }
2059 : else
2060 : {
2061 235 : double dfZ = 0.0;
2062 460 : if (hTransform != nullptr &&
2063 225 : OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
2064 : {
2065 225 : Concat(osStr, psOptions->bStdoutOutput, "(%s,",
2066 : GDALDecToDMS(dfGeoX, "Long", 2));
2067 225 : Concat(osStr, psOptions->bStdoutOutput, "%s)",
2068 : GDALDecToDMS(dfGeoY, "Lat", 2));
2069 : }
2070 235 : Concat(osStr, psOptions->bStdoutOutput, "\n");
2071 : }
2072 :
2073 649 : return TRUE;
2074 : }
2075 :
2076 : /************************************************************************/
2077 : /* GDALInfoPrintMetadata() */
2078 : /************************************************************************/
2079 1405 : static void GDALInfoPrintMetadata(const GDALInfoOptions *psOptions,
2080 : GDALMajorObjectH hObject,
2081 : const char *pszDomain,
2082 : const char *pszDisplayedname,
2083 : const char *pszIndent, int bJsonOutput,
2084 : json_object *poMetadata, CPLString &osStr)
2085 : {
2086 1405 : const bool bIsxml =
2087 1405 : pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:");
2088 1405 : const bool bMDIsJson =
2089 1405 : pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:");
2090 :
2091 1405 : char **papszMetadata = GDALGetMetadata(hObject, pszDomain);
2092 1405 : if (papszMetadata != nullptr && *papszMetadata != nullptr)
2093 : {
2094 129 : json_object *poDomain = (bJsonOutput && !bIsxml && !bMDIsJson)
2095 346 : ? json_object_new_object()
2096 217 : : nullptr;
2097 :
2098 217 : if (!bJsonOutput)
2099 88 : Concat(osStr, psOptions->bStdoutOutput, "%s%s:\n", pszIndent,
2100 : pszDisplayedname);
2101 :
2102 217 : json_object *poValue = nullptr;
2103 :
2104 1047 : for (int i = 0; papszMetadata[i] != nullptr; i++)
2105 : {
2106 833 : if (bJsonOutput)
2107 : {
2108 535 : if (bIsxml)
2109 : {
2110 2 : poValue = json_object_new_string(papszMetadata[i]);
2111 2 : break;
2112 : }
2113 533 : else if (bMDIsJson)
2114 : {
2115 1 : OGRJSonParse(papszMetadata[i], &poValue, true);
2116 1 : break;
2117 : }
2118 : else
2119 : {
2120 532 : char *pszKey = nullptr;
2121 : const char *pszValue =
2122 532 : CPLParseNameValue(papszMetadata[i], &pszKey);
2123 532 : if (pszKey)
2124 : {
2125 532 : poValue = json_object_new_string(pszValue);
2126 532 : json_object_object_add(poDomain, pszKey, poValue);
2127 532 : CPLFree(pszKey);
2128 : }
2129 : }
2130 : }
2131 : else
2132 : {
2133 298 : if (bIsxml || bMDIsJson)
2134 2 : Concat(osStr, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
2135 2 : papszMetadata[i]);
2136 : else
2137 296 : Concat(osStr, psOptions->bStdoutOutput, "%s %s\n",
2138 296 : pszIndent, papszMetadata[i]);
2139 : }
2140 : }
2141 217 : if (bJsonOutput)
2142 : {
2143 129 : if (bIsxml || bMDIsJson)
2144 : {
2145 3 : json_object_object_add(poMetadata, pszDomain, poValue);
2146 : }
2147 : else
2148 : {
2149 126 : if (pszDomain == nullptr)
2150 62 : json_object_object_add(poMetadata, "", poDomain);
2151 : else
2152 64 : json_object_object_add(poMetadata, pszDomain, poDomain);
2153 : }
2154 : }
2155 : }
2156 1405 : }
2157 :
2158 : /************************************************************************/
2159 : /* GDALInfoReportMetadata() */
2160 : /************************************************************************/
2161 297 : static void GDALInfoReportMetadata(const GDALInfoOptions *psOptions,
2162 : GDALMajorObjectH hObject, bool bIsBand,
2163 : bool bJson, json_object *poMetadata,
2164 : CPLString &osStr)
2165 : {
2166 297 : const char *const pszIndent = bIsBand ? " " : "";
2167 :
2168 : /* -------------------------------------------------------------------- */
2169 : /* Report list of Metadata domains */
2170 : /* -------------------------------------------------------------------- */
2171 297 : if (psOptions->bListMDD)
2172 : {
2173 16 : const CPLStringList aosDomainList(GDALGetMetadataDomainList(hObject));
2174 8 : json_object *poMDD = nullptr;
2175 : json_object *const poListMDD =
2176 8 : bJson ? json_object_new_array() : nullptr;
2177 :
2178 8 : if (!aosDomainList.empty())
2179 : {
2180 5 : if (!bJson)
2181 1 : Concat(osStr, psOptions->bStdoutOutput, "%sMetadata domains:\n",
2182 : pszIndent);
2183 : }
2184 :
2185 24 : for (const char *pszDomain : aosDomainList)
2186 : {
2187 16 : if (EQUAL(pszDomain, ""))
2188 : {
2189 5 : if (bJson)
2190 4 : poMDD = json_object_new_string(pszDomain);
2191 : else
2192 1 : Concat(osStr, psOptions->bStdoutOutput, "%s (default)\n",
2193 : pszIndent);
2194 : }
2195 : else
2196 : {
2197 11 : if (bJson)
2198 8 : poMDD = json_object_new_string(pszDomain);
2199 : else
2200 3 : Concat(osStr, psOptions->bStdoutOutput, "%s %s\n",
2201 : pszIndent, pszDomain);
2202 : }
2203 16 : if (bJson)
2204 12 : json_object_array_add(poListMDD, poMDD);
2205 : }
2206 8 : if (bJson)
2207 6 : json_object_object_add(poMetadata, "metadataDomains", poListMDD);
2208 : }
2209 :
2210 297 : if (!psOptions->bShowMetadata)
2211 8 : return;
2212 :
2213 : /* -------------------------------------------------------------------- */
2214 : /* Report default Metadata domain. */
2215 : /* -------------------------------------------------------------------- */
2216 289 : GDALInfoPrintMetadata(psOptions, hObject, nullptr, "Metadata", pszIndent,
2217 : bJson, poMetadata, osStr);
2218 :
2219 : /* -------------------------------------------------------------------- */
2220 : /* Report extra Metadata domains */
2221 : /* -------------------------------------------------------------------- */
2222 289 : if (!psOptions->aosExtraMDDomains.empty())
2223 : {
2224 36 : CPLStringList aosExtraMDDomainsExpanded;
2225 :
2226 26 : if (EQUAL(psOptions->aosExtraMDDomains[0], "all") &&
2227 8 : psOptions->aosExtraMDDomains.Count() == 1)
2228 : {
2229 16 : const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
2230 24 : for (const char *pszDomain : aosMDDList)
2231 : {
2232 16 : if (!EQUAL(pszDomain, "") &&
2233 12 : !EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
2234 8 : !EQUAL(pszDomain, "TILING_SCHEME") &&
2235 8 : !EQUAL(pszDomain, "SUBDATASETS") &&
2236 8 : !EQUAL(pszDomain, "GEOLOCATION") &&
2237 8 : !EQUAL(pszDomain, "RPC"))
2238 : {
2239 8 : aosExtraMDDomainsExpanded.AddString(pszDomain);
2240 : }
2241 : }
2242 : }
2243 : else
2244 : {
2245 10 : aosExtraMDDomainsExpanded = psOptions->aosExtraMDDomains;
2246 : }
2247 :
2248 36 : for (const char *pszDomain : aosExtraMDDomainsExpanded)
2249 : {
2250 18 : if (bJson)
2251 : {
2252 12 : GDALInfoPrintMetadata(psOptions, hObject, pszDomain, pszDomain,
2253 : pszIndent, bJson, poMetadata, osStr);
2254 : }
2255 : else
2256 : {
2257 : const std::string osDisplayedName =
2258 18 : std::string("Metadata (").append(pszDomain).append(")");
2259 :
2260 6 : GDALInfoPrintMetadata(psOptions, hObject, pszDomain,
2261 : osDisplayedName.c_str(), pszIndent, bJson,
2262 : poMetadata, osStr);
2263 : }
2264 : }
2265 : }
2266 :
2267 : /* -------------------------------------------------------------------- */
2268 : /* Report various named metadata domains. */
2269 : /* -------------------------------------------------------------------- */
2270 289 : GDALInfoPrintMetadata(psOptions, hObject, "IMAGE_STRUCTURE",
2271 : "Image Structure Metadata", pszIndent, bJson,
2272 : poMetadata, osStr);
2273 :
2274 289 : if (!bIsBand)
2275 : {
2276 130 : GDALInfoPrintMetadata(psOptions, hObject, "TILING_SCHEME",
2277 : "Tiling Scheme", pszIndent, bJson, poMetadata,
2278 : osStr);
2279 130 : GDALInfoPrintMetadata(psOptions, hObject, "SUBDATASETS", "Subdatasets",
2280 : pszIndent, bJson, poMetadata, osStr);
2281 130 : GDALInfoPrintMetadata(psOptions, hObject, "GEOLOCATION", "Geolocation",
2282 : pszIndent, bJson, poMetadata, osStr);
2283 130 : GDALInfoPrintMetadata(psOptions, hObject, "RPC", "RPC Metadata",
2284 : pszIndent, bJson, poMetadata, osStr);
2285 : }
2286 :
2287 289 : GDALInfoPrintMetadata(psOptions, hObject, "IMAGERY", "Imagery", pszIndent,
2288 : bJson, poMetadata, osStr);
2289 : }
2290 :
2291 : /************************************************************************/
2292 : /* GDALInfoOptionsNew() */
2293 : /************************************************************************/
2294 :
2295 : /**
2296 : * Allocates a GDALInfoOptions struct.
2297 : *
2298 : * @param papszArgv NULL terminated list of options (potentially including
2299 : * filename and open options too), or NULL. The accepted options are the ones of
2300 : * the <a href="/programs/gdalinfo.html">gdalinfo</a> utility.
2301 : * @param psOptionsForBinary (output) may be NULL (and should generally be
2302 : * NULL), otherwise (gdalinfo_bin.cpp use case) must be allocated with
2303 : * GDALInfoOptionsForBinaryNew() prior to this
2304 : * function. Will be filled with potentially present filename, open options,
2305 : * subdataset number...
2306 : * @return pointer to the allocated GDALInfoOptions struct. Must be freed with
2307 : * GDALInfoOptionsFree().
2308 : *
2309 : * @since GDAL 2.1
2310 : */
2311 :
2312 : GDALInfoOptions *
2313 139 : GDALInfoOptionsNew(char **papszArgv,
2314 : GDALInfoOptionsForBinary *psOptionsForBinary)
2315 : {
2316 278 : auto psOptions = std::make_unique<GDALInfoOptions>();
2317 :
2318 : /* -------------------------------------------------------------------- */
2319 : /* Parse arguments. */
2320 : /* -------------------------------------------------------------------- */
2321 :
2322 278 : CPLStringList aosArgv;
2323 :
2324 139 : if (papszArgv)
2325 : {
2326 125 : const int nArgc = CSLCount(papszArgv);
2327 432 : for (int i = 0; i < nArgc; i++)
2328 : {
2329 307 : aosArgv.AddString(papszArgv[i]);
2330 : }
2331 : }
2332 :
2333 : try
2334 : {
2335 : auto argParser =
2336 278 : GDALInfoAppOptionsGetParser(psOptions.get(), psOptionsForBinary);
2337 :
2338 139 : argParser->parse_args_without_binary_name(aosArgv.List());
2339 :
2340 139 : if (psOptions->bApproxStats)
2341 2 : psOptions->bStats = true;
2342 : }
2343 0 : catch (const std::exception &error)
2344 : {
2345 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
2346 0 : return nullptr;
2347 : }
2348 :
2349 139 : if (!psOptions->bShowNodata)
2350 2 : psOptions->bShowMask = false;
2351 :
2352 139 : return psOptions.release();
2353 : }
2354 :
2355 : /************************************************************************/
2356 : /* GDALInfoOptionsFree() */
2357 : /************************************************************************/
2358 :
2359 : /**
2360 : * Frees the GDALInfoOptions struct.
2361 : *
2362 : * @param psOptions the options struct for GDALInfo().
2363 : *
2364 : * @since GDAL 2.1
2365 : */
2366 :
2367 80 : void GDALInfoOptionsFree(GDALInfoOptions *psOptions)
2368 : {
2369 80 : delete psOptions;
2370 80 : }
|