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