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