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