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