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