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 3055 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
156 : ...)
157 : {
158 : va_list args;
159 3055 : va_start(args, pszFormat);
160 :
161 3055 : if (bStdoutOutput)
162 : {
163 2022 : vfprintf(stdout, pszFormat, args);
164 : }
165 : else
166 : {
167 : try
168 : {
169 2066 : CPLString osTarget;
170 1033 : osTarget.vPrintf(pszFormat, args);
171 :
172 1033 : 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 3055 : va_end(args);
181 3055 : }
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 157 : GDALInfoAppOptionsGetParser(GDALInfoOptions *psOptions,
222 : GDALInfoOptionsForBinary *psOptionsForBinary)
223 : {
224 : auto argParser = std::make_unique<GDALArgumentParser>(
225 157 : "gdalinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
226 :
227 157 : argParser->add_description(_("Raster dataset information utility."));
228 :
229 157 : argParser->add_epilog(
230 157 : _("For more details, consult https://gdal.org/programs/gdalinfo.html"));
231 :
232 : // Hidden: only for gdal raster info
233 157 : argParser->add_argument("--invoked-from")
234 157 : .store_into(psOptions->osInvokedFrom)
235 157 : .hidden();
236 :
237 : // Hidden: only for gdal raster info
238 157 : argParser->add_argument("--crs-format")
239 157 : .choices("AUTO", "WKT2", "PROJJSON")
240 157 : .store_into(psOptions->osCRSFormat)
241 157 : .hidden();
242 :
243 157 : argParser->add_argument("-json")
244 157 : .flag()
245 95 : .action([psOptions](const auto &)
246 157 : { psOptions->eFormat = GDALINFO_FORMAT_JSON; })
247 157 : .help(_("Display the output in json format."));
248 :
249 157 : argParser->add_argument("-mm")
250 157 : .store_into(psOptions->bComputeMinMax)
251 : .help(_("Force computation of the actual min/max values for each band "
252 157 : "in the dataset."));
253 :
254 : {
255 157 : auto &group = argParser->add_mutually_exclusive_group();
256 157 : group.add_argument("-stats")
257 157 : .store_into(psOptions->bStats)
258 : .help(_("Read and display image statistics computing exact values "
259 157 : "if required."));
260 :
261 157 : group.add_argument("-approx_stats")
262 157 : .store_into(psOptions->bApproxStats)
263 : .help(
264 : _("Read and display image statistics computing approximated "
265 157 : "values on overviews or a subset of all tiles if required."));
266 : }
267 :
268 157 : argParser->add_argument("-hist")
269 157 : .store_into(psOptions->bReportHistograms)
270 157 : .help(_("Report histogram information for all bands."));
271 :
272 157 : argParser->add_usage_newline();
273 :
274 : argParser->add_inverted_logic_flag(
275 : "-nogcp", &psOptions->bShowGCPs,
276 157 : _("Suppress ground control points list printing."));
277 :
278 : argParser->add_inverted_logic_flag("-nomd", &psOptions->bShowMetadata,
279 157 : _("Suppress metadata printing."));
280 :
281 : argParser->add_inverted_logic_flag(
282 : "-norat", &psOptions->bShowRAT,
283 157 : _("Suppress printing of raster attribute table."));
284 :
285 : argParser->add_inverted_logic_flag("-noct", &psOptions->bShowColorTable,
286 157 : _("Suppress printing of color table."));
287 :
288 : argParser->add_inverted_logic_flag("-nofl", &psOptions->bShowFileList,
289 157 : _("Suppress display of the file list."));
290 :
291 : argParser->add_inverted_logic_flag(
292 : "-nonodata", &psOptions->bShowNodata,
293 157 : _("Suppress nodata printing (implies -nomask)."));
294 :
295 : argParser->add_inverted_logic_flag("-nomask", &psOptions->bShowMask,
296 157 : _("Suppress mask printing."));
297 :
298 157 : argParser->add_usage_newline();
299 :
300 157 : argParser->add_argument("-checksum")
301 157 : .flag()
302 157 : .store_into(psOptions->bComputeChecksum)
303 : .help(_(
304 157 : "Force computation of the checksum for each band in the dataset."));
305 :
306 157 : argParser->add_argument("-listmdd")
307 157 : .flag()
308 157 : .store_into(psOptions->bListMDD)
309 157 : .help(_("List all metadata domains available for the dataset."));
310 :
311 157 : argParser->add_argument("-proj4")
312 157 : .flag()
313 157 : .store_into(psOptions->bReportProj4)
314 : .help(_("Report a PROJ.4 string corresponding to the file's coordinate "
315 157 : "system."));
316 :
317 157 : argParser->add_argument("-wkt_format")
318 314 : .metavar("<WKT1|WKT1_ESRI|WKT2|WKT2_2015|WKT2_2018|WKT2_2019>")
319 : .choices("WKT1", "WKT1_ESRI", "WKT2", "WKT2_2015", "WKT2_2018",
320 157 : "WKT2_2019")
321 157 : .store_into(psOptions->osWKTFormat)
322 157 : .help(_("WKT format used for SRS."));
323 :
324 157 : 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 157 : argParser->add_argument("-oo")
335 314 : .metavar("<NAME>=<VALUE>")
336 157 : .append()
337 : .action(
338 2 : [psOptionsForBinary](const std::string &s)
339 : {
340 1 : if (psOptionsForBinary)
341 1 : psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
342 157 : })
343 157 : .help(_("Open option(s) for dataset."));
344 :
345 : argParser->add_input_format_argument(
346 : psOptionsForBinary ? &psOptionsForBinary->aosAllowedInputDrivers
347 157 : : nullptr);
348 :
349 157 : argParser->add_argument("-mdd")
350 314 : .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 157 : })
357 : .help(_("Report metadata for the specified domains. 'all' can be used "
358 157 : "to report metadata in all domains."));
359 :
360 : /* Not documented: used by gdalinfo_bin.cpp only */
361 157 : argParser->add_argument("-stdout").flag().hidden().store_into(
362 157 : psOptions->bStdoutOutput);
363 :
364 157 : 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 157 : 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 : static void
454 27 : EmitSimplifiedOutput(const OGRSpatialReference *poSRS, const char *pszIndent,
455 : const char *pszAuthName, const char *pszAuthCode,
456 : 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 152 : char *GDALInfo(GDALDatasetH hDataset, const GDALInfoOptions *psOptions)
684 : {
685 152 : if (hDataset == nullptr)
686 0 : return nullptr;
687 :
688 152 : GDALInfoOptions *psOptionsToFree = nullptr;
689 152 : if (psOptions == nullptr)
690 : {
691 0 : psOptionsToFree = GDALInfoOptionsNew(nullptr, nullptr);
692 0 : psOptions = psOptionsToFree;
693 : }
694 :
695 304 : CPLString osStr;
696 152 : json_object *poJsonObject = nullptr;
697 152 : json_object *poBands = nullptr;
698 152 : json_object *poMetadata = nullptr;
699 152 : json_object *poStac = nullptr;
700 152 : json_object *poStacRasterBands = nullptr;
701 152 : json_object *poStacEOBands = nullptr;
702 :
703 152 : const bool bJson = psOptions->eFormat == GDALINFO_FORMAT_JSON;
704 :
705 : /* -------------------------------------------------------------------- */
706 : /* Report general info. */
707 : /* -------------------------------------------------------------------- */
708 152 : GDALDriverH hDriver = GDALGetDatasetDriver(hDataset);
709 152 : if (bJson)
710 : {
711 : json_object *poDescription =
712 95 : json_object_new_string(GDALGetDescription(hDataset));
713 95 : poJsonObject = json_object_new_object();
714 95 : poBands = json_object_new_array();
715 95 : poMetadata = json_object_new_object();
716 95 : poStac = json_object_new_object();
717 95 : poStacRasterBands = json_object_new_array();
718 95 : poStacEOBands = json_object_new_array();
719 :
720 95 : json_object_object_add(poJsonObject, "description", poDescription);
721 95 : if (hDriver)
722 : {
723 : json_object *poDriverShortName =
724 94 : json_object_new_string(GDALGetDriverShortName(hDriver));
725 : json_object *poDriverLongName =
726 94 : json_object_new_string(GDALGetDriverLongName(hDriver));
727 94 : json_object_object_add(poJsonObject, "driverShortName",
728 : poDriverShortName);
729 94 : 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 152 : 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 150 : ? nullptr
747 150 : : GDALGetFileList(hDataset);
748 :
749 150 : if (!papszFileList || *papszFileList == nullptr)
750 : {
751 28 : if (bJson)
752 : {
753 22 : json_object *poFiles = json_object_new_array();
754 22 : json_object_object_add(poJsonObject, "files", poFiles);
755 : }
756 : else
757 : {
758 6 : Concat(osStr, psOptions->bStdoutOutput,
759 : "Files: none associated\n");
760 28 : }
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 150 : CSLDestroy(papszFileList);
788 : }
789 :
790 152 : if (bJson)
791 : {
792 : {
793 95 : json_object *poSize = json_object_new_array();
794 : json_object *poSizeX =
795 95 : json_object_new_int(GDALGetRasterXSize(hDataset));
796 : json_object *poSizeY =
797 95 : json_object_new_int(GDALGetRasterYSize(hDataset));
798 :
799 : // size is X, Y ordered
800 95 : json_object_array_add(poSize, poSizeX);
801 95 : json_object_array_add(poSize, poSizeY);
802 :
803 95 : json_object_object_add(poJsonObject, "size", poSize);
804 : }
805 :
806 : {
807 95 : json_object *poStacSize = json_object_new_array();
808 : json_object *poSizeX =
809 95 : json_object_new_int(GDALGetRasterXSize(hDataset));
810 : json_object *poSizeY =
811 95 : json_object_new_int(GDALGetRasterYSize(hDataset));
812 :
813 : // ... but ... proj:shape is Y, X ordered.
814 95 : json_object_array_add(poStacSize, poSizeY);
815 95 : json_object_array_add(poStacSize, poSizeX);
816 :
817 95 : 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 304 : CPLString osWKTFormat("FORMAT=");
827 152 : osWKTFormat += psOptions->osWKTFormat;
828 152 : const char *const apszWKTOptions[] = {osWKTFormat.c_str(), "MULTILINE=YES",
829 152 : nullptr};
830 :
831 : /* -------------------------------------------------------------------- */
832 : /* Report projection. */
833 : /* -------------------------------------------------------------------- */
834 152 : auto hSRS = GDALGetSpatialRef(hDataset);
835 152 : if (hSRS != nullptr)
836 : {
837 : const OGRSpatialReference *poSRS =
838 129 : OGRSpatialReference::FromHandle(hSRS);
839 :
840 129 : json_object *poCoordinateSystem = nullptr;
841 :
842 129 : if (bJson)
843 80 : poCoordinateSystem = json_object_new_object();
844 :
845 258 : const std::string osWkt = poSRS->exportToWkt(apszWKTOptions);
846 :
847 258 : const std::vector<int> anAxes = poSRS->GetDataAxisToSRSAxisMapping();
848 :
849 129 : const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
850 :
851 129 : const char *pszAuthCode = poSRS->GetAuthorityCode();
852 129 : const char *pszAuthName = poSRS->GetAuthorityName();
853 129 : if (bJson)
854 : {
855 80 : json_object *poWkt = json_object_new_string(osWkt.c_str());
856 80 : if (psOptions->osWKTFormat == "WKT2")
857 : {
858 73 : json_object *poStacWkt = nullptr;
859 73 : json_object_deep_copy(poWkt, &poStacWkt, nullptr);
860 73 : json_object_object_add(poStac, "proj:wkt2", poStacWkt);
861 : }
862 80 : json_object_object_add(poCoordinateSystem, "wkt", poWkt);
863 :
864 80 : 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 17 : json_object_object_add(poStac, "proj:epsg", nullptr);
875 : }
876 : {
877 80 : char *pszProjJson = nullptr;
878 80 : OGRErr result = poSRS->exportToPROJJSON(&pszProjJson, nullptr);
879 80 : if (result == OGRERR_NONE)
880 : {
881 : json_object *poStacProjJson =
882 80 : json_tokener_parse(pszProjJson);
883 80 : json_object_object_add(poStac, "proj:projjson",
884 : poStacProjJson);
885 80 : CPLFree(pszProjJson);
886 : }
887 : }
888 :
889 80 : json_object *poAxisMapping = json_object_new_array();
890 242 : for (int nMapping : anAxes)
891 : {
892 162 : json_object_array_add(poAxisMapping,
893 : json_object_new_int(nMapping));
894 : }
895 80 : json_object_object_add(poCoordinateSystem,
896 : "dataAxisToSRSAxisMapping", poAxisMapping);
897 :
898 80 : 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 129 : 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 129 : if (bJson)
968 80 : json_object_object_add(poJsonObject, "coordinateSystem",
969 : poCoordinateSystem);
970 : }
971 :
972 : /* -------------------------------------------------------------------- */
973 : /* Report Geotransform. */
974 : /* -------------------------------------------------------------------- */
975 152 : double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
976 152 : if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
977 : {
978 131 : if (bJson)
979 : {
980 80 : json_object *poGeoTransform = json_object_new_array();
981 :
982 560 : for (int i = 0; i < 6; i++)
983 : {
984 : json_object *poGeoTransformCoefficient =
985 480 : json_object_new_double_with_precision(adfGeoTransform[i],
986 : 16);
987 480 : json_object_array_add(poGeoTransform,
988 : poGeoTransformCoefficient);
989 : }
990 :
991 80 : json_object_object_add(poJsonObject, "geoTransform",
992 : poGeoTransform);
993 :
994 80 : json_object *poStacGeoTransform = json_object_new_array();
995 80 : json_object_array_add(
996 : poStacGeoTransform,
997 : json_object_new_double_with_precision(adfGeoTransform[1], 16));
998 80 : json_object_array_add(
999 : poStacGeoTransform,
1000 : json_object_new_double_with_precision(adfGeoTransform[2], 16));
1001 80 : json_object_array_add(
1002 : poStacGeoTransform,
1003 : json_object_new_double_with_precision(adfGeoTransform[0], 16));
1004 80 : json_object_array_add(
1005 : poStacGeoTransform,
1006 : json_object_new_double_with_precision(adfGeoTransform[4], 16));
1007 80 : json_object_array_add(
1008 : poStacGeoTransform,
1009 : json_object_new_double_with_precision(adfGeoTransform[5], 16));
1010 80 : json_object_array_add(
1011 : poStacGeoTransform,
1012 : json_object_new_double_with_precision(adfGeoTransform[3], 16));
1013 80 : 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 152 : 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 152 : GDALInfoReportMetadata(psOptions, hDataset, false, bJson, poMetadata,
1155 : osStr);
1156 152 : if (bJson)
1157 : {
1158 95 : if (psOptions->bShowMetadata)
1159 92 : 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 95 : GDALGetMetadataItem(hDataset, "CLOUDCOVER", GDAL_MDD_IMAGERY);
1166 95 : json_object *poValue = nullptr;
1167 95 : 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 152 : OGRSpatialReferenceH hProj = nullptr;
1178 152 : if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
1179 131 : hProj = GDALGetSpatialRef(hDataset);
1180 :
1181 152 : OGRCoordinateTransformationH hTransform = nullptr;
1182 152 : bool bTransformToWGS84 = false;
1183 :
1184 152 : if (hProj)
1185 : {
1186 128 : OGRSpatialReferenceH hLatLong = nullptr;
1187 :
1188 128 : 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 158 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1193 79 : OGRErr eErr = OGRERR_NONE;
1194 157 : 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 : }
1202 : else
1203 : {
1204 49 : hLatLong = OSRCloneGeogCS(hProj);
1205 49 : if (hLatLong)
1206 : {
1207 : // Override GEOGCS|UNIT child to be sure to output as degrees
1208 49 : OSRSetAngularUnits(hLatLong, SRS_UA_DEGREE,
1209 : CPLAtof(SRS_UA_DEGREE_CONV));
1210 : }
1211 : }
1212 :
1213 128 : if (hLatLong != nullptr)
1214 : {
1215 126 : OSRSetAxisMappingStrategy(hLatLong, OAMS_TRADITIONAL_GIS_ORDER);
1216 126 : CPLPushErrorHandler(CPLQuietErrorHandler);
1217 126 : hTransform = OCTNewCoordinateTransformation(hProj, hLatLong);
1218 126 : CPLPopErrorHandler();
1219 :
1220 126 : OSRDestroySpatialReference(hLatLong);
1221 : }
1222 : }
1223 :
1224 : /* -------------------------------------------------------------------- */
1225 : /* Report corners. */
1226 : /* -------------------------------------------------------------------- */
1227 152 : if (bJson && GDALGetRasterXSize(hDataset))
1228 : {
1229 190 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1230 :
1231 95 : json_object *poCornerCoordinates = json_object_new_object();
1232 95 : json_object *poLongLatExtentCoordinates = json_object_new_array();
1233 :
1234 95 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
1235 : 0.0, bJson, poCornerCoordinates,
1236 : poLongLatExtentCoordinates, osStr);
1237 190 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "lowerLeft", 0.0,
1238 95 : GDALGetRasterYSize(hDataset), bJson,
1239 : poCornerCoordinates, poLongLatExtentCoordinates,
1240 : osStr);
1241 285 : GDALInfoReportCorner(
1242 : psOptions, hDataset, hTransform, "lowerRight",
1243 95 : GDALGetRasterXSize(hDataset), GDALGetRasterYSize(hDataset), bJson,
1244 : poCornerCoordinates, poLongLatExtentCoordinates, osStr);
1245 190 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperRight",
1246 95 : GDALGetRasterXSize(hDataset), 0.0, bJson,
1247 : poCornerCoordinates, poLongLatExtentCoordinates,
1248 : osStr);
1249 285 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "center",
1250 95 : GDALGetRasterXSize(hDataset) / 2.0,
1251 95 : GDALGetRasterYSize(hDataset) / 2.0, bJson,
1252 : poCornerCoordinates, poLongLatExtentCoordinates,
1253 : osStr);
1254 95 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "upperLeft", 0.0,
1255 : 0.0, bJson, poCornerCoordinates,
1256 : poLongLatExtentCoordinates, osStr);
1257 :
1258 95 : json_object_object_add(poJsonObject, "cornerCoordinates",
1259 : poCornerCoordinates);
1260 :
1261 95 : if (json_object_array_length(poLongLatExtentCoordinates) > 0)
1262 : {
1263 77 : json_object *poLinearRing = json_object_new_array();
1264 77 : json_object *poLongLatExtent = json_object_new_object();
1265 : json_object *poLongLatExtentType =
1266 77 : json_object_new_string("Polygon");
1267 77 : json_object_object_add(poLongLatExtent, "type",
1268 : poLongLatExtentType);
1269 77 : json_object_array_add(poLinearRing, poLongLatExtentCoordinates);
1270 77 : json_object_object_add(poLongLatExtent, "coordinates",
1271 : poLinearRing);
1272 77 : json_object_object_add(poJsonObject,
1273 : bTransformToWGS84 ? "wgs84Extent" : "extent",
1274 : poLongLatExtent);
1275 : }
1276 : else
1277 : {
1278 18 : json_object_put(poLongLatExtentCoordinates);
1279 : }
1280 : }
1281 57 : else if (GDALGetRasterXSize(hDataset))
1282 : {
1283 114 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1284 :
1285 57 : Concat(osStr, psOptions->bStdoutOutput, "Corner Coordinates:\n");
1286 57 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Left", 0.0,
1287 : 0.0, bJson, nullptr, nullptr, osStr);
1288 114 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Left", 0.0,
1289 57 : GDALGetRasterYSize(hDataset), bJson, nullptr,
1290 : nullptr, osStr);
1291 114 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Upper Right",
1292 57 : GDALGetRasterXSize(hDataset), 0.0, bJson, nullptr,
1293 : nullptr, osStr);
1294 171 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Lower Right",
1295 57 : GDALGetRasterXSize(hDataset),
1296 57 : GDALGetRasterYSize(hDataset), bJson, nullptr,
1297 : nullptr, osStr);
1298 171 : GDALInfoReportCorner(psOptions, hDataset, hTransform, "Center",
1299 57 : GDALGetRasterXSize(hDataset) / 2.0,
1300 57 : GDALGetRasterYSize(hDataset) / 2.0, bJson, nullptr,
1301 : nullptr, osStr);
1302 : }
1303 :
1304 152 : if (hTransform != nullptr)
1305 : {
1306 126 : OCTDestroyCoordinateTransformation(hTransform);
1307 126 : hTransform = nullptr;
1308 : }
1309 :
1310 : /* ==================================================================== */
1311 : /* Loop over bands. */
1312 : /* ==================================================================== */
1313 335 : for (int iBand = 0; iBand < GDALGetRasterCount(hDataset); iBand++)
1314 : {
1315 183 : json_object *poBand = nullptr;
1316 183 : json_object *poBandMetadata = nullptr;
1317 183 : json_object *poStacRasterBand = nullptr;
1318 183 : json_object *poStacEOBand = nullptr;
1319 :
1320 183 : if (bJson)
1321 : {
1322 122 : poBand = json_object_new_object();
1323 122 : poBandMetadata = json_object_new_object();
1324 122 : poStacRasterBand = json_object_new_object();
1325 122 : poStacEOBand = json_object_new_object();
1326 : }
1327 :
1328 183 : GDALRasterBandH const hBand = GDALGetRasterBand(hDataset, iBand + 1);
1329 183 : const auto eDT = GDALGetRasterDataType(hBand);
1330 :
1331 183 : if (psOptions->bSample)
1332 : {
1333 0 : vector<float> ofSample(10000, 0);
1334 0 : float *const pafSample = &ofSample[0];
1335 : const int nCount =
1336 0 : GDALGetRandomRasterSample(hBand, 10000, pafSample);
1337 0 : if (!bJson)
1338 0 : Concat(osStr, psOptions->bStdoutOutput, "Got %d samples.\n",
1339 : nCount);
1340 : }
1341 :
1342 183 : int nBlockXSize = 0;
1343 183 : int nBlockYSize = 0;
1344 183 : GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
1345 183 : if (bJson)
1346 : {
1347 122 : json_object *poBandNumber = json_object_new_int(iBand + 1);
1348 122 : json_object *poBlock = json_object_new_array();
1349 : json_object *poType =
1350 122 : json_object_new_string(GDALGetDataTypeName(eDT));
1351 : json_object *poColorInterp =
1352 122 : json_object_new_string(GDALGetColorInterpretationName(
1353 : GDALGetRasterColorInterpretation(hBand)));
1354 :
1355 122 : json_object_array_add(poBlock, json_object_new_int(nBlockXSize));
1356 122 : json_object_array_add(poBlock, json_object_new_int(nBlockYSize));
1357 122 : json_object_object_add(poBand, "band", poBandNumber);
1358 122 : json_object_object_add(poBand, "block", poBlock);
1359 122 : json_object_object_add(poBand, "type", poType);
1360 122 : json_object_object_add(poBand, "colorInterpretation",
1361 : poColorInterp);
1362 :
1363 122 : const char *stacDataType = nullptr;
1364 122 : switch (eDT)
1365 : {
1366 103 : case GDT_UInt8:
1367 103 : stacDataType = "uint8";
1368 103 : break;
1369 0 : case GDT_Int8:
1370 0 : stacDataType = "int8";
1371 0 : break;
1372 3 : case GDT_UInt16:
1373 3 : stacDataType = "uint16";
1374 3 : break;
1375 0 : case GDT_Int16:
1376 0 : stacDataType = "int16";
1377 0 : break;
1378 2 : case GDT_UInt32:
1379 2 : stacDataType = "uint32";
1380 2 : break;
1381 1 : case GDT_Int32:
1382 1 : stacDataType = "int32";
1383 1 : break;
1384 0 : case GDT_UInt64:
1385 0 : stacDataType = "uint64";
1386 0 : break;
1387 0 : case GDT_Int64:
1388 0 : stacDataType = "int64";
1389 0 : break;
1390 0 : case GDT_Float16:
1391 0 : stacDataType = "float16";
1392 0 : break;
1393 11 : case GDT_Float32:
1394 11 : stacDataType = "float32";
1395 11 : break;
1396 2 : case GDT_Float64:
1397 2 : stacDataType = "float64";
1398 2 : break;
1399 0 : case GDT_CInt16:
1400 0 : stacDataType = "cint16";
1401 0 : break;
1402 0 : case GDT_CInt32:
1403 0 : stacDataType = "cint32";
1404 0 : break;
1405 0 : case GDT_CFloat16:
1406 0 : stacDataType = "cfloat16";
1407 0 : break;
1408 0 : case GDT_CFloat32:
1409 0 : stacDataType = "cfloat32";
1410 0 : break;
1411 0 : case GDT_CFloat64:
1412 0 : stacDataType = "cfloat64";
1413 0 : break;
1414 0 : case GDT_Unknown:
1415 : case GDT_TypeCount:
1416 0 : stacDataType = nullptr;
1417 : }
1418 122 : if (stacDataType)
1419 122 : json_object_object_add(poStacRasterBand, "data_type",
1420 : json_object_new_string(stacDataType));
1421 : }
1422 : else
1423 : {
1424 61 : Concat(osStr, psOptions->bStdoutOutput,
1425 : "Band %d Block=%dx%d Type=%s, ColorInterp=%s\n", iBand + 1,
1426 : nBlockXSize, nBlockYSize, GDALGetDataTypeName(eDT),
1427 : GDALGetColorInterpretationName(
1428 : GDALGetRasterColorInterpretation(hBand)));
1429 : }
1430 :
1431 183 : if (bJson)
1432 : {
1433 : json_object *poBandName =
1434 122 : json_object_new_string(CPLSPrintf("b%i", iBand + 1));
1435 122 : json_object_object_add(poStacEOBand, "name", poBandName);
1436 : }
1437 :
1438 183 : const char *pszBandDesc = GDALGetDescription(hBand);
1439 183 : if (pszBandDesc != nullptr && strlen(pszBandDesc) > 0)
1440 : {
1441 41 : if (bJson)
1442 : {
1443 35 : json_object_object_add(poBand, "description",
1444 : json_object_new_string(pszBandDesc));
1445 :
1446 35 : json_object_object_add(poStacEOBand, "description",
1447 : json_object_new_string(pszBandDesc));
1448 : }
1449 : else
1450 : {
1451 6 : Concat(osStr, psOptions->bStdoutOutput, " Description = %s\n",
1452 : pszBandDesc);
1453 : }
1454 : }
1455 : else
1456 : {
1457 142 : if (bJson)
1458 : {
1459 : json_object *poColorInterp =
1460 87 : json_object_new_string(GDALGetColorInterpretationName(
1461 : GDALGetRasterColorInterpretation(hBand)));
1462 87 : json_object_object_add(poStacEOBand, "description",
1463 : poColorInterp);
1464 : }
1465 : }
1466 :
1467 183 : if (bJson)
1468 : {
1469 122 : const char *pszCommonName = GDALGetSTACCommonNameFromColorInterp(
1470 : GDALGetRasterColorInterpretation(hBand));
1471 122 : if (pszCommonName)
1472 : {
1473 25 : json_object_object_add(poStacEOBand, "common_name",
1474 : json_object_new_string(pszCommonName));
1475 : }
1476 : }
1477 :
1478 : {
1479 183 : int bGotMin = FALSE;
1480 183 : int bGotMax = FALSE;
1481 183 : const double dfMin = GDALGetRasterMinimum(hBand, &bGotMin);
1482 183 : const double dfMax = GDALGetRasterMaximum(hBand, &bGotMax);
1483 183 : if (bGotMin || bGotMax || psOptions->bComputeMinMax)
1484 : {
1485 38 : if (!bJson)
1486 8 : Concat(osStr, psOptions->bStdoutOutput, " ");
1487 38 : if (bGotMin)
1488 : {
1489 35 : if (bJson)
1490 : {
1491 : json_object *poMin =
1492 28 : gdal_json_object_new_double_or_str_for_non_finite(
1493 : dfMin, 3);
1494 28 : json_object_object_add(poBand, "min", poMin);
1495 : }
1496 : else
1497 : {
1498 7 : Concat(osStr, psOptions->bStdoutOutput, "Min=%.3f ",
1499 : dfMin);
1500 : }
1501 : }
1502 38 : if (bGotMax)
1503 : {
1504 35 : if (bJson)
1505 : {
1506 : json_object *poMax =
1507 28 : gdal_json_object_new_double_or_str_for_non_finite(
1508 : dfMax, 3);
1509 28 : json_object_object_add(poBand, "max", poMax);
1510 : }
1511 : else
1512 : {
1513 7 : Concat(osStr, psOptions->bStdoutOutput, "Max=%.3f ",
1514 : dfMax);
1515 : }
1516 : }
1517 :
1518 38 : if (psOptions->bComputeMinMax)
1519 : {
1520 4 : CPLErrorReset();
1521 4 : double adfCMinMax[2] = {0.0, 0.0};
1522 4 : GDALComputeRasterMinMax(hBand, FALSE, adfCMinMax);
1523 4 : if (CPLGetLastErrorType() == CE_None)
1524 : {
1525 4 : if (bJson)
1526 : {
1527 : json_object *poComputedMin =
1528 2 : gdal_json_object_new_double_or_str_for_non_finite(
1529 : adfCMinMax[0], 3);
1530 : json_object *poComputedMax =
1531 2 : gdal_json_object_new_double_or_str_for_non_finite(
1532 : adfCMinMax[1], 3);
1533 2 : json_object_object_add(poBand, "computedMin",
1534 : poComputedMin);
1535 2 : json_object_object_add(poBand, "computedMax",
1536 : poComputedMax);
1537 : }
1538 : else
1539 : {
1540 2 : Concat(osStr, psOptions->bStdoutOutput,
1541 : " Computed Min/Max=%.3f,%.3f",
1542 : adfCMinMax[0], adfCMinMax[1]);
1543 : }
1544 : }
1545 : }
1546 38 : if (!bJson)
1547 8 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1548 : }
1549 : }
1550 :
1551 183 : double dfMinStat = 0.0;
1552 183 : double dfMaxStat = 0.0;
1553 183 : double dfMean = 0.0;
1554 183 : double dfStdDev = 0.0;
1555 366 : CPLErr eErr = GDALGetRasterStatistics(hBand, psOptions->bApproxStats,
1556 183 : psOptions->bStats, &dfMinStat,
1557 : &dfMaxStat, &dfMean, &dfStdDev);
1558 183 : if (eErr == CE_None)
1559 : {
1560 17 : if (bJson)
1561 : {
1562 8 : json_object *poStacStats = json_object_new_object();
1563 : json_object *poMinimum =
1564 8 : gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
1565 : 3);
1566 8 : json_object_object_add(poBand, "minimum", poMinimum);
1567 : json_object *poStacMinimum =
1568 8 : gdal_json_object_new_double_or_str_for_non_finite(dfMinStat,
1569 : 3);
1570 8 : json_object_object_add(poStacStats, "minimum", poStacMinimum);
1571 :
1572 : json_object *poMaximum =
1573 8 : gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
1574 : 3);
1575 8 : json_object_object_add(poBand, "maximum", poMaximum);
1576 : json_object *poStacMaximum =
1577 8 : gdal_json_object_new_double_or_str_for_non_finite(dfMaxStat,
1578 : 3);
1579 8 : json_object_object_add(poStacStats, "maximum", poStacMaximum);
1580 :
1581 : json_object *poMean =
1582 8 : gdal_json_object_new_double_or_str_for_non_finite(dfMean,
1583 : 3);
1584 8 : json_object_object_add(poBand, "mean", poMean);
1585 : json_object *poStacMean =
1586 8 : gdal_json_object_new_double_or_str_for_non_finite(dfMean,
1587 : 3);
1588 8 : json_object_object_add(poStacStats, "mean", poStacMean);
1589 :
1590 : json_object *poStdDev =
1591 8 : gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
1592 : 3);
1593 8 : json_object_object_add(poBand, "stdDev", poStdDev);
1594 : json_object *poStacStdDev =
1595 8 : gdal_json_object_new_double_or_str_for_non_finite(dfStdDev,
1596 : 3);
1597 8 : json_object_object_add(poStacStats, "stddev", poStacStdDev);
1598 :
1599 8 : json_object_object_add(poStacRasterBand, "stats", poStacStats);
1600 : }
1601 : else
1602 : {
1603 9 : Concat(osStr, psOptions->bStdoutOutput,
1604 : " Minimum=%.3f, Maximum=%.3f, Mean=%.3f, StdDev=%.3f\n",
1605 : dfMinStat, dfMaxStat, dfMean, dfStdDev);
1606 : }
1607 : }
1608 :
1609 183 : if (psOptions->bReportHistograms)
1610 : {
1611 5 : int nBucketCount = 0;
1612 5 : GUIntBig *panHistogram = nullptr;
1613 :
1614 5 : if (bJson)
1615 4 : eErr = GDALGetDefaultHistogramEx(
1616 : hBand, &dfMinStat, &dfMaxStat, &nBucketCount, &panHistogram,
1617 : TRUE, GDALDummyProgress, nullptr);
1618 : else
1619 1 : eErr = GDALGetDefaultHistogramEx(
1620 : hBand, &dfMinStat, &dfMaxStat, &nBucketCount, &panHistogram,
1621 : TRUE, GDALTermProgress, nullptr);
1622 5 : if (eErr == CE_None)
1623 : {
1624 5 : json_object *poHistogram = nullptr;
1625 5 : json_object *poBuckets = nullptr;
1626 :
1627 5 : if (bJson)
1628 : {
1629 4 : json_object *poCount = json_object_new_int(nBucketCount);
1630 4 : json_object *poMin = json_object_new_double(dfMinStat);
1631 4 : json_object *poMax = json_object_new_double(dfMaxStat);
1632 :
1633 4 : poBuckets = json_object_new_array();
1634 4 : poHistogram = json_object_new_object();
1635 4 : json_object_object_add(poHistogram, "count", poCount);
1636 4 : json_object_object_add(poHistogram, "min", poMin);
1637 4 : json_object_object_add(poHistogram, "max", poMax);
1638 : }
1639 : else
1640 : {
1641 1 : Concat(osStr, psOptions->bStdoutOutput,
1642 : " %d buckets from %g to %g:\n ", nBucketCount,
1643 : dfMinStat, dfMaxStat);
1644 : }
1645 :
1646 1285 : for (int iBucket = 0; iBucket < nBucketCount; iBucket++)
1647 : {
1648 1280 : if (bJson)
1649 : {
1650 : json_object *poBucket =
1651 1024 : json_object_new_int64(panHistogram[iBucket]);
1652 1024 : json_object_array_add(poBuckets, poBucket);
1653 : }
1654 : else
1655 256 : Concat(osStr, psOptions->bStdoutOutput,
1656 256 : CPL_FRMT_GUIB " ", panHistogram[iBucket]);
1657 : }
1658 5 : if (bJson)
1659 : {
1660 4 : json_object_object_add(poHistogram, "buckets", poBuckets);
1661 4 : json_object *poStacHistogram = nullptr;
1662 4 : json_object_deep_copy(poHistogram, &poStacHistogram,
1663 : nullptr);
1664 4 : json_object_object_add(poBand, "histogram", poHistogram);
1665 4 : json_object_object_add(poStacRasterBand, "histogram",
1666 : poStacHistogram);
1667 : }
1668 : else
1669 : {
1670 1 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1671 : }
1672 5 : CPLFree(panHistogram);
1673 : }
1674 : }
1675 :
1676 183 : if (psOptions->bComputeChecksum)
1677 : {
1678 : const int nBandChecksum =
1679 42 : GDALChecksumImage(hBand, 0, 0, GDALGetRasterXSize(hDataset),
1680 : GDALGetRasterYSize(hDataset));
1681 42 : if (bJson)
1682 : {
1683 32 : json_object *poChecksum = json_object_new_int(nBandChecksum);
1684 32 : json_object_object_add(poBand, "checksum", poChecksum);
1685 : }
1686 : else
1687 : {
1688 10 : Concat(osStr, psOptions->bStdoutOutput, " Checksum=%d\n",
1689 : nBandChecksum);
1690 : }
1691 : }
1692 :
1693 183 : int bGotNodata = FALSE;
1694 183 : if (!psOptions->bShowNodata)
1695 : {
1696 : // nothing to do
1697 : }
1698 181 : else if (eDT == GDT_Int64)
1699 : {
1700 : const auto nNoData =
1701 0 : GDALGetRasterNoDataValueAsInt64(hBand, &bGotNodata);
1702 0 : if (bGotNodata)
1703 : {
1704 0 : if (bJson)
1705 : {
1706 0 : json_object *poNoDataValue = json_object_new_int64(nNoData);
1707 0 : json_object *poStacNoDataValue = nullptr;
1708 0 : json_object_deep_copy(poNoDataValue, &poStacNoDataValue,
1709 : nullptr);
1710 0 : json_object_object_add(poStacRasterBand, "nodata",
1711 : poStacNoDataValue);
1712 0 : json_object_object_add(poBand, "noDataValue",
1713 : poNoDataValue);
1714 : }
1715 : else
1716 : {
1717 0 : Concat(osStr, psOptions->bStdoutOutput,
1718 : " NoData Value=" CPL_FRMT_GIB "\n",
1719 : static_cast<GIntBig>(nNoData));
1720 : }
1721 : }
1722 : }
1723 181 : else if (eDT == GDT_UInt64)
1724 : {
1725 : const auto nNoData =
1726 0 : GDALGetRasterNoDataValueAsUInt64(hBand, &bGotNodata);
1727 0 : if (bGotNodata)
1728 : {
1729 0 : if (bJson)
1730 : {
1731 0 : if (nNoData < static_cast<uint64_t>(
1732 0 : std::numeric_limits<int64_t>::max()))
1733 : {
1734 0 : json_object *poNoDataValue = json_object_new_int64(
1735 : static_cast<int64_t>(nNoData));
1736 0 : json_object *poStacNoDataValue = nullptr;
1737 0 : json_object_deep_copy(poNoDataValue, &poStacNoDataValue,
1738 : nullptr);
1739 0 : json_object_object_add(poStacRasterBand, "nodata",
1740 : poStacNoDataValue);
1741 0 : json_object_object_add(poBand, "noDataValue",
1742 : poNoDataValue);
1743 : }
1744 : else
1745 : {
1746 : // not pretty to serialize as a string but there's no
1747 : // way to serialize a uint64_t with libjson-c
1748 : json_object *poNoDataValue =
1749 0 : json_object_new_string(CPLSPrintf(
1750 : CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
1751 0 : json_object_object_add(poBand, "noDataValue",
1752 : poNoDataValue);
1753 : }
1754 : }
1755 : else
1756 : {
1757 0 : Concat(osStr, psOptions->bStdoutOutput,
1758 : " NoData Value=" CPL_FRMT_GUIB "\n",
1759 : static_cast<GUIntBig>(nNoData));
1760 : }
1761 : }
1762 : }
1763 : else
1764 : {
1765 : const double dfNoData =
1766 181 : GDALGetRasterNoDataValue(hBand, &bGotNodata);
1767 181 : if (bGotNodata)
1768 : {
1769 28 : const bool bIsNoDataFloat =
1770 44 : eDT == GDT_Float32 &&
1771 16 : static_cast<double>(static_cast<float>(dfNoData)) ==
1772 : dfNoData;
1773 : // Find the most compact decimal representation of the nodata
1774 : // value that can be used to exactly represent the binary value
1775 28 : int nSignificantDigits = bIsNoDataFloat ? 8 : 18;
1776 28 : char szNoData[64] = {0};
1777 324 : while (nSignificantDigits > 0)
1778 : {
1779 : char szCandidateNoData[64];
1780 : char szFormat[16];
1781 301 : snprintf(szFormat, sizeof(szFormat), "%%.%dg",
1782 : nSignificantDigits);
1783 301 : CPLsnprintf(szCandidateNoData, sizeof(szCandidateNoData),
1784 : szFormat, dfNoData);
1785 273 : if (szNoData[0] == '\0' ||
1786 112 : (bIsNoDataFloat &&
1787 112 : static_cast<float>(CPLAtof(szCandidateNoData)) ==
1788 574 : static_cast<float>(dfNoData)) ||
1789 161 : (!bIsNoDataFloat &&
1790 161 : CPLAtof(szCandidateNoData) == dfNoData))
1791 : {
1792 296 : strcpy(szNoData, szCandidateNoData);
1793 296 : nSignificantDigits--;
1794 : }
1795 : else
1796 : {
1797 5 : nSignificantDigits++;
1798 5 : break;
1799 : }
1800 : }
1801 :
1802 28 : if (bJson)
1803 : {
1804 : json_object *poNoDataValue =
1805 23 : (GDALDataTypeIsInteger(eDT) && dfNoData >= INT_MIN &&
1806 5 : dfNoData <= INT_MAX &&
1807 5 : static_cast<int>(dfNoData) == dfNoData)
1808 23 : ? json_object_new_int(static_cast<int>(dfNoData))
1809 13 : : gdal_json_object_new_double_significant_digits(
1810 18 : dfNoData, nSignificantDigits);
1811 : json_object *poStacNoDataValue =
1812 23 : (GDALDataTypeIsInteger(eDT) && dfNoData >= INT_MIN &&
1813 5 : dfNoData <= INT_MAX &&
1814 5 : static_cast<int>(dfNoData) == dfNoData)
1815 23 : ? json_object_new_int(static_cast<int>(dfNoData))
1816 13 : : gdal_json_object_new_double_significant_digits(
1817 18 : dfNoData, nSignificantDigits);
1818 18 : json_object_object_add(poStacRasterBand, "nodata",
1819 : poStacNoDataValue);
1820 18 : json_object_object_add(poBand, "noDataValue",
1821 : poNoDataValue);
1822 : }
1823 10 : else if (std::isnan(dfNoData))
1824 : {
1825 0 : Concat(osStr, psOptions->bStdoutOutput,
1826 : " NoData Value=nan\n");
1827 : }
1828 : else
1829 : {
1830 10 : Concat(osStr, psOptions->bStdoutOutput,
1831 : " NoData Value=%s\n", szNoData);
1832 : }
1833 : }
1834 : }
1835 :
1836 183 : if (GDALGetOverviewCount(hBand) > 0)
1837 : {
1838 9 : json_object *poOverviews = nullptr;
1839 :
1840 9 : if (bJson)
1841 8 : poOverviews = json_object_new_array();
1842 : else
1843 1 : Concat(osStr, psOptions->bStdoutOutput, " Overviews: ");
1844 :
1845 35 : for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
1846 : iOverview++)
1847 : {
1848 26 : if (!bJson)
1849 1 : if (iOverview != 0)
1850 0 : Concat(osStr, psOptions->bStdoutOutput, ", ");
1851 :
1852 26 : GDALRasterBandH hOverview = GDALGetOverview(hBand, iOverview);
1853 26 : if (hOverview != nullptr)
1854 : {
1855 26 : if (bJson)
1856 : {
1857 25 : json_object *poOverviewSize = json_object_new_array();
1858 25 : json_object *poOverviewSizeX = json_object_new_int(
1859 : GDALGetRasterBandXSize(hOverview));
1860 25 : json_object *poOverviewSizeY = json_object_new_int(
1861 : GDALGetRasterBandYSize(hOverview));
1862 :
1863 25 : json_object *poOverview = json_object_new_object();
1864 25 : json_object_array_add(poOverviewSize, poOverviewSizeX);
1865 25 : json_object_array_add(poOverviewSize, poOverviewSizeY);
1866 25 : json_object_object_add(poOverview, "size",
1867 : poOverviewSize);
1868 :
1869 25 : if (psOptions->bComputeChecksum)
1870 : {
1871 16 : const int nOverviewChecksum = GDALChecksumImage(
1872 : hOverview, 0, 0,
1873 : GDALGetRasterBandXSize(hOverview),
1874 : GDALGetRasterBandYSize(hOverview));
1875 : json_object *poOverviewChecksum =
1876 16 : json_object_new_int(nOverviewChecksum);
1877 16 : json_object_object_add(poOverview, "checksum",
1878 : poOverviewChecksum);
1879 : }
1880 25 : json_object_array_add(poOverviews, poOverview);
1881 : }
1882 : else
1883 : {
1884 1 : Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
1885 : GDALGetRasterBandXSize(hOverview),
1886 : GDALGetRasterBandYSize(hOverview));
1887 : }
1888 :
1889 : const char *pszResampling =
1890 26 : GDALGetMetadataItem(hOverview, "RESAMPLING", "");
1891 :
1892 26 : if (pszResampling != nullptr && !bJson &&
1893 0 : STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2"))
1894 0 : Concat(osStr, psOptions->bStdoutOutput, "*");
1895 : }
1896 : else
1897 : {
1898 0 : if (!bJson)
1899 0 : Concat(osStr, psOptions->bStdoutOutput, "(null)");
1900 : }
1901 : }
1902 9 : if (bJson)
1903 8 : json_object_object_add(poBand, "overviews", poOverviews);
1904 : else
1905 1 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1906 :
1907 9 : if (psOptions->bComputeChecksum && !bJson)
1908 : {
1909 0 : Concat(osStr, psOptions->bStdoutOutput,
1910 : " Overviews checksum: ");
1911 :
1912 0 : for (int iOverview = 0; iOverview < GDALGetOverviewCount(hBand);
1913 : iOverview++)
1914 : {
1915 : GDALRasterBandH hOverview;
1916 :
1917 0 : if (iOverview != 0)
1918 0 : Concat(osStr, psOptions->bStdoutOutput, ", ");
1919 :
1920 0 : hOverview = GDALGetOverview(hBand, iOverview);
1921 0 : if (hOverview)
1922 : {
1923 0 : Concat(osStr, psOptions->bStdoutOutput, "%d",
1924 : GDALChecksumImage(
1925 : hOverview, 0, 0,
1926 : GDALGetRasterBandXSize(hOverview),
1927 : GDALGetRasterBandYSize(hOverview)));
1928 : }
1929 : else
1930 : {
1931 0 : Concat(osStr, psOptions->bStdoutOutput, "(null)");
1932 : }
1933 : }
1934 0 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1935 : }
1936 : }
1937 :
1938 183 : if (GDALHasArbitraryOverviews(hBand) && !bJson)
1939 : {
1940 0 : Concat(osStr, psOptions->bStdoutOutput, " Overviews: arbitrary\n");
1941 : }
1942 :
1943 : const int nMaskFlags =
1944 183 : psOptions->bShowMask ? GDALGetMaskFlags(hBand) : GMF_ALL_VALID;
1945 183 : if ((nMaskFlags & (GMF_NODATA | GMF_ALL_VALID)) == 0 ||
1946 : nMaskFlags == (GMF_NODATA | GMF_PER_DATASET))
1947 : {
1948 21 : GDALRasterBandH hMaskBand = GDALGetMaskBand(hBand);
1949 21 : json_object *poMask = nullptr;
1950 21 : json_object *poFlags = nullptr;
1951 21 : json_object *poMaskOverviews = nullptr;
1952 :
1953 21 : if (bJson)
1954 : {
1955 17 : poMask = json_object_new_object();
1956 17 : poFlags = json_object_new_array();
1957 : }
1958 : else
1959 4 : Concat(osStr, psOptions->bStdoutOutput, " Mask Flags: ");
1960 21 : if (nMaskFlags & GMF_PER_DATASET)
1961 : {
1962 19 : if (bJson)
1963 : {
1964 16 : json_object *poFlag = json_object_new_string("PER_DATASET");
1965 16 : json_object_array_add(poFlags, poFlag);
1966 : }
1967 : else
1968 3 : Concat(osStr, psOptions->bStdoutOutput, "PER_DATASET ");
1969 : }
1970 21 : if (nMaskFlags & GMF_ALPHA)
1971 : {
1972 15 : if (bJson)
1973 : {
1974 15 : json_object *poFlag = json_object_new_string("ALPHA");
1975 15 : json_object_array_add(poFlags, poFlag);
1976 : }
1977 : else
1978 0 : Concat(osStr, psOptions->bStdoutOutput, "ALPHA ");
1979 : }
1980 21 : if (nMaskFlags & GMF_NODATA)
1981 : {
1982 3 : if (bJson)
1983 : {
1984 0 : json_object *poFlag = json_object_new_string("NODATA");
1985 0 : json_object_array_add(poFlags, poFlag);
1986 : }
1987 : else
1988 : {
1989 3 : Concat(osStr, psOptions->bStdoutOutput, "NODATA ");
1990 : }
1991 : }
1992 :
1993 21 : if (bJson)
1994 17 : json_object_object_add(poMask, "flags", poFlags);
1995 : else
1996 4 : Concat(osStr, psOptions->bStdoutOutput, "\n");
1997 :
1998 21 : if (bJson)
1999 17 : poMaskOverviews = json_object_new_array();
2000 :
2001 21 : if (hMaskBand != nullptr && GDALGetOverviewCount(hMaskBand) > 0)
2002 : {
2003 0 : if (!bJson)
2004 0 : Concat(osStr, psOptions->bStdoutOutput,
2005 : " Overviews of mask band: ");
2006 :
2007 0 : for (int iOverview = 0;
2008 0 : iOverview < GDALGetOverviewCount(hMaskBand); iOverview++)
2009 : {
2010 : GDALRasterBandH hOverview =
2011 0 : GDALGetOverview(hMaskBand, iOverview);
2012 0 : if (!hOverview)
2013 0 : break;
2014 0 : json_object *poMaskOverview = nullptr;
2015 0 : json_object *poMaskOverviewSize = nullptr;
2016 :
2017 0 : if (bJson)
2018 : {
2019 0 : poMaskOverview = json_object_new_object();
2020 0 : poMaskOverviewSize = json_object_new_array();
2021 : }
2022 : else
2023 : {
2024 0 : if (iOverview != 0)
2025 0 : Concat(osStr, psOptions->bStdoutOutput, ", ");
2026 : }
2027 :
2028 0 : if (bJson)
2029 : {
2030 0 : json_object *poMaskOverviewSizeX = json_object_new_int(
2031 : GDALGetRasterBandXSize(hOverview));
2032 0 : json_object *poMaskOverviewSizeY = json_object_new_int(
2033 : GDALGetRasterBandYSize(hOverview));
2034 :
2035 0 : json_object_array_add(poMaskOverviewSize,
2036 : poMaskOverviewSizeX);
2037 0 : json_object_array_add(poMaskOverviewSize,
2038 : poMaskOverviewSizeY);
2039 0 : json_object_object_add(poMaskOverview, "size",
2040 : poMaskOverviewSize);
2041 0 : json_object_array_add(poMaskOverviews, poMaskOverview);
2042 : }
2043 : else
2044 : {
2045 0 : Concat(osStr, psOptions->bStdoutOutput, "%dx%d",
2046 : GDALGetRasterBandXSize(hOverview),
2047 : GDALGetRasterBandYSize(hOverview));
2048 : }
2049 : }
2050 0 : if (!bJson)
2051 0 : Concat(osStr, psOptions->bStdoutOutput, "\n");
2052 : }
2053 21 : if (bJson)
2054 : {
2055 17 : json_object_object_add(poMask, "overviews", poMaskOverviews);
2056 17 : json_object_object_add(poBand, "mask", poMask);
2057 : }
2058 : }
2059 :
2060 183 : if (strlen(GDALGetRasterUnitType(hBand)) > 0)
2061 : {
2062 0 : if (bJson)
2063 : {
2064 : json_object *poUnit =
2065 0 : json_object_new_string(GDALGetRasterUnitType(hBand));
2066 0 : json_object *poStacUnit = nullptr;
2067 0 : json_object_deep_copy(poUnit, &poStacUnit, nullptr);
2068 0 : json_object_object_add(poStacRasterBand, "unit", poStacUnit);
2069 0 : json_object_object_add(poBand, "unit", poUnit);
2070 : }
2071 : else
2072 : {
2073 0 : Concat(osStr, psOptions->bStdoutOutput, " Unit Type: %s\n",
2074 : GDALGetRasterUnitType(hBand));
2075 : }
2076 : }
2077 :
2078 183 : if (GDALGetRasterCategoryNames(hBand) != nullptr)
2079 : {
2080 0 : char **papszCategories = GDALGetRasterCategoryNames(hBand);
2081 0 : json_object *poCategories = nullptr;
2082 :
2083 0 : if (bJson)
2084 0 : poCategories = json_object_new_array();
2085 : else
2086 0 : Concat(osStr, psOptions->bStdoutOutput, " Categories:\n");
2087 :
2088 0 : for (int i = 0; papszCategories[i] != nullptr; i++)
2089 : {
2090 0 : if (bJson)
2091 : {
2092 : json_object *poCategoryName =
2093 0 : json_object_new_string(papszCategories[i]);
2094 0 : json_object_array_add(poCategories, poCategoryName);
2095 : }
2096 : else
2097 0 : Concat(osStr, psOptions->bStdoutOutput, " %3d: %s\n", i,
2098 0 : papszCategories[i]);
2099 : }
2100 0 : if (bJson)
2101 0 : json_object_object_add(poBand, "categories", poCategories);
2102 : }
2103 :
2104 183 : int bSuccess = FALSE;
2105 366 : if (GDALGetRasterScale(hBand, &bSuccess) != 1.0 ||
2106 183 : GDALGetRasterOffset(hBand, &bSuccess) != 0.0)
2107 : {
2108 0 : if (bJson)
2109 : {
2110 0 : json_object *poOffset = json_object_new_double_with_precision(
2111 : GDALGetRasterOffset(hBand, &bSuccess), 15);
2112 0 : json_object *poScale = json_object_new_double_with_precision(
2113 : GDALGetRasterScale(hBand, &bSuccess), 15);
2114 0 : json_object *poStacScale = nullptr;
2115 0 : json_object *poStacOffset = nullptr;
2116 0 : json_object_deep_copy(poScale, &poStacScale, nullptr);
2117 0 : json_object_deep_copy(poOffset, &poStacOffset, nullptr);
2118 0 : json_object_object_add(poStacRasterBand, "scale", poStacScale);
2119 0 : json_object_object_add(poStacRasterBand, "offset",
2120 : poStacOffset);
2121 0 : json_object_object_add(poBand, "offset", poOffset);
2122 0 : json_object_object_add(poBand, "scale", poScale);
2123 : }
2124 : else
2125 : {
2126 0 : Concat(osStr, psOptions->bStdoutOutput,
2127 : " Offset: %.15g, Scale:%.15g\n",
2128 : GDALGetRasterOffset(hBand, &bSuccess),
2129 : GDALGetRasterScale(hBand, &bSuccess));
2130 : }
2131 : }
2132 :
2133 183 : GDALInfoReportMetadata(psOptions, hBand, true, bJson, poBandMetadata,
2134 : osStr);
2135 183 : if (bJson)
2136 : {
2137 122 : if (psOptions->bShowMetadata)
2138 119 : json_object_object_add(poBand, "metadata", poBandMetadata);
2139 : else
2140 3 : json_object_put(poBandMetadata);
2141 : }
2142 :
2143 : GDALColorTableH hTable;
2144 192 : if (GDALGetRasterColorInterpretation(hBand) == GCI_PaletteIndex &&
2145 9 : (hTable = GDALGetRasterColorTable(hBand)) != nullptr)
2146 : {
2147 9 : if (!bJson)
2148 2 : Concat(osStr, psOptions->bStdoutOutput,
2149 : " Color Table (%s with %d entries)\n",
2150 : GDALGetPaletteInterpretationName(
2151 : GDALGetPaletteInterpretation(hTable)),
2152 : GDALGetColorEntryCount(hTable));
2153 :
2154 9 : if (psOptions->bShowColorTable)
2155 : {
2156 7 : json_object *poEntries = nullptr;
2157 :
2158 7 : if (bJson)
2159 : {
2160 : json_object *poPalette =
2161 6 : json_object_new_string(GDALGetPaletteInterpretationName(
2162 : GDALGetPaletteInterpretation(hTable)));
2163 : json_object *poCount =
2164 6 : json_object_new_int(GDALGetColorEntryCount(hTable));
2165 :
2166 6 : json_object *poColorTable = json_object_new_object();
2167 :
2168 6 : json_object_object_add(poColorTable, "palette", poPalette);
2169 6 : json_object_object_add(poColorTable, "count", poCount);
2170 :
2171 6 : poEntries = json_object_new_array();
2172 6 : json_object_object_add(poColorTable, "entries", poEntries);
2173 6 : json_object_object_add(poBand, "colorTable", poColorTable);
2174 : }
2175 :
2176 750 : for (int i = 0; i < GDALGetColorEntryCount(hTable); i++)
2177 : {
2178 : GDALColorEntry sEntry;
2179 :
2180 743 : GDALGetColorEntryAsRGB(hTable, i, &sEntry);
2181 :
2182 743 : if (bJson)
2183 : {
2184 727 : json_object *poEntry = json_object_new_array();
2185 727 : json_object *poC1 = json_object_new_int(sEntry.c1);
2186 727 : json_object *poC2 = json_object_new_int(sEntry.c2);
2187 727 : json_object *poC3 = json_object_new_int(sEntry.c3);
2188 727 : json_object *poC4 = json_object_new_int(sEntry.c4);
2189 :
2190 727 : json_object_array_add(poEntry, poC1);
2191 727 : json_object_array_add(poEntry, poC2);
2192 727 : json_object_array_add(poEntry, poC3);
2193 727 : json_object_array_add(poEntry, poC4);
2194 727 : json_object_array_add(poEntries, poEntry);
2195 : }
2196 : else
2197 : {
2198 16 : Concat(osStr, psOptions->bStdoutOutput,
2199 16 : " %3d: %d,%d,%d,%d\n", i, sEntry.c1, sEntry.c2,
2200 16 : sEntry.c3, sEntry.c4);
2201 : }
2202 : }
2203 : }
2204 : }
2205 :
2206 183 : if (psOptions->bShowRAT && GDALGetDefaultRAT(hBand) != nullptr)
2207 : {
2208 10 : GDALRasterAttributeTableH hRAT = GDALGetDefaultRAT(hBand);
2209 :
2210 10 : if (bJson)
2211 : {
2212 : json_object *poRAT =
2213 9 : static_cast<json_object *>(GDALRATSerializeJSON(hRAT));
2214 9 : json_object_object_add(poBand, "rat", poRAT);
2215 : }
2216 : else
2217 : {
2218 : CPLXMLNode *psTree =
2219 1 : static_cast<GDALRasterAttributeTable *>(hRAT)->Serialize();
2220 1 : char *pszXMLText = CPLSerializeXMLTree(psTree);
2221 1 : CPLDestroyXMLNode(psTree);
2222 1 : Concat(osStr, psOptions->bStdoutOutput, "%s\n", pszXMLText);
2223 1 : CPLFree(pszXMLText);
2224 : }
2225 : }
2226 183 : if (bJson)
2227 : {
2228 122 : json_object_array_add(poBands, poBand);
2229 122 : json_object_array_add(poStacRasterBands, poStacRasterBand);
2230 122 : json_object_array_add(poStacEOBands, poStacEOBand);
2231 : }
2232 : }
2233 :
2234 152 : if (bJson)
2235 : {
2236 95 : json_object_object_add(poJsonObject, "bands", poBands);
2237 95 : json_object_object_add(poStac, "raster:bands", poStacRasterBands);
2238 95 : json_object_object_add(poStac, "eo:bands", poStacEOBands);
2239 95 : json_object_object_add(poJsonObject, "stac", poStac);
2240 95 : Concat(osStr, psOptions->bStdoutOutput, "%s",
2241 : json_object_to_json_string_ext(
2242 : poJsonObject, JSON_C_TO_STRING_PRETTY
2243 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
2244 : | JSON_C_TO_STRING_NOSLASHESCAPE
2245 : #endif
2246 : ));
2247 95 : json_object_put(poJsonObject);
2248 95 : Concat(osStr, psOptions->bStdoutOutput, "\n");
2249 : }
2250 :
2251 152 : if (psOptionsToFree != nullptr)
2252 0 : GDALInfoOptionsFree(psOptionsToFree);
2253 :
2254 152 : return VSI_STRDUP_VERBOSE(osStr);
2255 : }
2256 :
2257 : /************************************************************************/
2258 : /* GDALInfoReportCorner() */
2259 : /************************************************************************/
2260 :
2261 855 : static int GDALInfoReportCorner(const GDALInfoOptions *psOptions,
2262 : GDALDatasetH hDataset,
2263 : OGRCoordinateTransformationH hTransform,
2264 : const char *corner_name, double x, double y,
2265 : bool bJson, json_object *poCornerCoordinates,
2266 : json_object *poLongLatExtentCoordinates,
2267 : CPLString &osStr)
2268 :
2269 : {
2270 855 : if (!bJson)
2271 285 : Concat(osStr, psOptions->bStdoutOutput, "%-11s ", corner_name);
2272 :
2273 : /* -------------------------------------------------------------------- */
2274 : /* Transform the point into georeferenced coordinates. */
2275 : /* -------------------------------------------------------------------- */
2276 855 : double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
2277 855 : double dfGeoX = 0.0;
2278 855 : double dfGeoY = 0.0;
2279 :
2280 855 : if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
2281 : {
2282 735 : dfGeoX = adfGeoTransform[0] + adfGeoTransform[1] * x +
2283 735 : adfGeoTransform[2] * y;
2284 735 : dfGeoY = adfGeoTransform[3] + adfGeoTransform[4] * x +
2285 735 : adfGeoTransform[5] * y;
2286 : }
2287 : else
2288 : {
2289 120 : if (bJson)
2290 : {
2291 90 : json_object *const poCorner = json_object_new_array();
2292 : json_object *const poX =
2293 90 : json_object_new_double_with_precision(x, 1);
2294 : json_object *const poY =
2295 90 : json_object_new_double_with_precision(y, 1);
2296 90 : json_object_array_add(poCorner, poX);
2297 90 : json_object_array_add(poCorner, poY);
2298 90 : json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2299 : }
2300 : else
2301 : {
2302 30 : Concat(osStr, psOptions->bStdoutOutput, "(%7.1f,%7.1f)\n", x, y);
2303 : }
2304 120 : return FALSE;
2305 : }
2306 :
2307 : /* -------------------------------------------------------------------- */
2308 : /* Report the georeferenced coordinates. */
2309 : /* -------------------------------------------------------------------- */
2310 735 : if (std::abs(dfGeoX) < 181 && std::abs(dfGeoY) < 91)
2311 : {
2312 100 : if (bJson)
2313 : {
2314 60 : json_object *const poCorner = json_object_new_array();
2315 : json_object *const poX =
2316 60 : json_object_new_double_with_precision(dfGeoX, 7);
2317 : json_object *const poY =
2318 60 : json_object_new_double_with_precision(dfGeoY, 7);
2319 60 : json_object_array_add(poCorner, poX);
2320 60 : json_object_array_add(poCorner, poY);
2321 60 : json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2322 : }
2323 : else
2324 : {
2325 40 : Concat(osStr, psOptions->bStdoutOutput, "(%12.7f,%12.7f) ", dfGeoX,
2326 : dfGeoY);
2327 : }
2328 : }
2329 : else
2330 : {
2331 635 : if (bJson)
2332 : {
2333 420 : json_object *const poCorner = json_object_new_array();
2334 : json_object *const poX =
2335 420 : json_object_new_double_with_precision(dfGeoX, 3);
2336 : json_object *const poY =
2337 420 : json_object_new_double_with_precision(dfGeoY, 3);
2338 420 : json_object_array_add(poCorner, poX);
2339 420 : json_object_array_add(poCorner, poY);
2340 420 : json_object_object_add(poCornerCoordinates, corner_name, poCorner);
2341 : }
2342 : else
2343 : {
2344 215 : Concat(osStr, psOptions->bStdoutOutput, "(%12.3f,%12.3f) ", dfGeoX,
2345 : dfGeoY);
2346 : }
2347 : }
2348 :
2349 : /* -------------------------------------------------------------------- */
2350 : /* Transform to latlong and report. */
2351 : /* -------------------------------------------------------------------- */
2352 735 : if (bJson)
2353 : {
2354 480 : double dfZ = 0.0;
2355 865 : if (hTransform != nullptr && !EQUAL(corner_name, "center") &&
2356 385 : OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
2357 : {
2358 385 : json_object *const poCorner = json_object_new_array();
2359 : json_object *const poX =
2360 385 : json_object_new_double_with_precision(dfGeoX, 7);
2361 : json_object *const poY =
2362 385 : json_object_new_double_with_precision(dfGeoY, 7);
2363 385 : json_object_array_add(poCorner, poX);
2364 385 : json_object_array_add(poCorner, poY);
2365 385 : json_object_array_add(poLongLatExtentCoordinates, poCorner);
2366 : }
2367 : }
2368 : else
2369 : {
2370 255 : double dfZ = 0.0;
2371 500 : if (hTransform != nullptr &&
2372 245 : OCTTransform(hTransform, 1, &dfGeoX, &dfGeoY, &dfZ))
2373 : {
2374 245 : Concat(osStr, psOptions->bStdoutOutput, "(%s,",
2375 : GDALDecToDMS(dfGeoX, "Long", 2));
2376 245 : Concat(osStr, psOptions->bStdoutOutput, "%s)",
2377 : GDALDecToDMS(dfGeoY, "Lat", 2));
2378 : }
2379 255 : Concat(osStr, psOptions->bStdoutOutput, "\n");
2380 : }
2381 :
2382 735 : return TRUE;
2383 : }
2384 :
2385 : /************************************************************************/
2386 : /* GDALInfoPrintMetadata() */
2387 : /************************************************************************/
2388 1591 : static void GDALInfoPrintMetadata(const GDALInfoOptions *psOptions,
2389 : GDALMajorObjectH hObject,
2390 : const char *pszDomain,
2391 : const char *pszDisplayedname,
2392 : const char *pszIndent, int bJsonOutput,
2393 : json_object *poMetadata, CPLString &osStr)
2394 : {
2395 1591 : const bool bIsxml =
2396 1591 : pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:");
2397 1591 : const bool bMDIsJson =
2398 1591 : pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:");
2399 :
2400 1591 : CSLConstList papszMetadata = GDALGetMetadata(hObject, pszDomain);
2401 1591 : if (papszMetadata != nullptr && *papszMetadata != nullptr)
2402 : {
2403 149 : json_object *poDomain = (bJsonOutput && !bIsxml && !bMDIsJson)
2404 392 : ? json_object_new_object()
2405 243 : : nullptr;
2406 :
2407 243 : if (!bJsonOutput)
2408 94 : Concat(osStr, psOptions->bStdoutOutput, "%s%s:\n", pszIndent,
2409 : pszDisplayedname);
2410 :
2411 243 : json_object *poValue = nullptr;
2412 :
2413 1109 : for (int i = 0; papszMetadata[i] != nullptr; i++)
2414 : {
2415 869 : if (bJsonOutput)
2416 : {
2417 565 : if (bIsxml)
2418 : {
2419 2 : poValue = json_object_new_string(papszMetadata[i]);
2420 2 : break;
2421 : }
2422 563 : else if (bMDIsJson)
2423 : {
2424 1 : OGRJSonParse(papszMetadata[i], &poValue, true);
2425 1 : break;
2426 : }
2427 : else
2428 : {
2429 562 : char *pszKey = nullptr;
2430 : const char *pszValue =
2431 562 : CPLParseNameValue(papszMetadata[i], &pszKey);
2432 562 : if (pszKey)
2433 : {
2434 562 : poValue = json_object_new_string(pszValue);
2435 562 : json_object_object_add(poDomain, pszKey, poValue);
2436 562 : CPLFree(pszKey);
2437 : }
2438 : }
2439 : }
2440 : else
2441 : {
2442 304 : if (bIsxml || bMDIsJson)
2443 2 : Concat(osStr, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
2444 2 : papszMetadata[i]);
2445 : else
2446 302 : Concat(osStr, psOptions->bStdoutOutput, "%s %s\n",
2447 302 : pszIndent, papszMetadata[i]);
2448 : }
2449 : }
2450 243 : if (bJsonOutput)
2451 : {
2452 149 : if (bIsxml || bMDIsJson)
2453 : {
2454 3 : json_object_object_add(poMetadata, pszDomain, poValue);
2455 : }
2456 : else
2457 : {
2458 146 : if (pszDomain == nullptr)
2459 73 : json_object_object_add(poMetadata, "", poDomain);
2460 : else
2461 73 : json_object_object_add(poMetadata, pszDomain, poDomain);
2462 : }
2463 : }
2464 : }
2465 1591 : }
2466 :
2467 : /************************************************************************/
2468 : /* GDALInfoReportMetadata() */
2469 : /************************************************************************/
2470 335 : static void GDALInfoReportMetadata(const GDALInfoOptions *psOptions,
2471 : GDALMajorObjectH hObject, bool bIsBand,
2472 : bool bJson, json_object *poMetadata,
2473 : CPLString &osStr)
2474 : {
2475 335 : const char *const pszIndent = bIsBand ? " " : "";
2476 :
2477 : /* -------------------------------------------------------------------- */
2478 : /* Report list of Metadata domains */
2479 : /* -------------------------------------------------------------------- */
2480 335 : if (psOptions->bListMDD)
2481 : {
2482 16 : const CPLStringList aosDomainList(GDALGetMetadataDomainList(hObject));
2483 8 : json_object *poMDD = nullptr;
2484 : json_object *const poListMDD =
2485 8 : bJson ? json_object_new_array() : nullptr;
2486 :
2487 8 : if (!aosDomainList.empty())
2488 : {
2489 5 : if (!bJson)
2490 1 : Concat(osStr, psOptions->bStdoutOutput, "%sMetadata domains:\n",
2491 : pszIndent);
2492 : }
2493 :
2494 24 : for (const char *pszDomain : aosDomainList)
2495 : {
2496 16 : if (EQUAL(pszDomain, ""))
2497 : {
2498 5 : if (bJson)
2499 4 : poMDD = json_object_new_string(pszDomain);
2500 : else
2501 1 : Concat(osStr, psOptions->bStdoutOutput, "%s (default)\n",
2502 : pszIndent);
2503 : }
2504 : else
2505 : {
2506 11 : if (bJson)
2507 8 : poMDD = json_object_new_string(pszDomain);
2508 : else
2509 3 : Concat(osStr, psOptions->bStdoutOutput, "%s %s\n",
2510 : pszIndent, pszDomain);
2511 : }
2512 16 : if (bJson)
2513 12 : json_object_array_add(poListMDD, poMDD);
2514 : }
2515 8 : if (bJson)
2516 6 : json_object_object_add(poMetadata, "metadataDomains", poListMDD);
2517 : }
2518 :
2519 335 : if (!psOptions->bShowMetadata)
2520 8 : return;
2521 :
2522 : /* -------------------------------------------------------------------- */
2523 : /* Report default Metadata domain. */
2524 : /* -------------------------------------------------------------------- */
2525 327 : GDALInfoPrintMetadata(psOptions, hObject, nullptr, "Metadata", pszIndent,
2526 : bJson, poMetadata, osStr);
2527 :
2528 : /* -------------------------------------------------------------------- */
2529 : /* Report extra Metadata domains */
2530 : /* -------------------------------------------------------------------- */
2531 327 : if (!psOptions->aosExtraMDDomains.empty())
2532 : {
2533 36 : CPLStringList aosExtraMDDomainsExpanded;
2534 :
2535 26 : if (EQUAL(psOptions->aosExtraMDDomains[0], "all") &&
2536 8 : psOptions->aosExtraMDDomains.Count() == 1)
2537 : {
2538 16 : const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
2539 24 : for (const char *pszDomain : aosMDDList)
2540 : {
2541 16 : if (!EQUAL(pszDomain, "") &&
2542 12 : !EQUAL(pszDomain, GDAL_MDD_IMAGE_STRUCTURE) &&
2543 8 : !EQUAL(pszDomain, "TILING_SCHEME") &&
2544 8 : !EQUAL(pszDomain, GDAL_MDD_SUBDATASETS) &&
2545 8 : !EQUAL(pszDomain, GDAL_MDD_GEOLOCATION) &&
2546 8 : !EQUAL(pszDomain, GDAL_MDD_RPC))
2547 : {
2548 8 : aosExtraMDDomainsExpanded.AddString(pszDomain);
2549 : }
2550 : }
2551 : }
2552 : else
2553 : {
2554 10 : aosExtraMDDomainsExpanded = psOptions->aosExtraMDDomains;
2555 : }
2556 :
2557 36 : for (const char *pszDomain : aosExtraMDDomainsExpanded)
2558 : {
2559 18 : if (bJson)
2560 : {
2561 12 : GDALInfoPrintMetadata(psOptions, hObject, pszDomain, pszDomain,
2562 : pszIndent, bJson, poMetadata, osStr);
2563 : }
2564 : else
2565 : {
2566 : const std::string osDisplayedName =
2567 18 : std::string("Metadata (").append(pszDomain).append(")");
2568 :
2569 6 : GDALInfoPrintMetadata(psOptions, hObject, pszDomain,
2570 : osDisplayedName.c_str(), pszIndent, bJson,
2571 : poMetadata, osStr);
2572 : }
2573 : }
2574 : }
2575 :
2576 : /* -------------------------------------------------------------------- */
2577 : /* Report various named metadata domains. */
2578 : /* -------------------------------------------------------------------- */
2579 327 : GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_IMAGE_STRUCTURE,
2580 : "Image Structure Metadata", pszIndent, bJson,
2581 : poMetadata, osStr);
2582 :
2583 327 : if (!bIsBand)
2584 : {
2585 148 : GDALInfoPrintMetadata(psOptions, hObject, "TILING_SCHEME",
2586 : "Tiling Scheme", pszIndent, bJson, poMetadata,
2587 : osStr);
2588 148 : GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_SUBDATASETS,
2589 : "Subdatasets", pszIndent, bJson, poMetadata,
2590 : osStr);
2591 148 : GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_GEOLOCATION,
2592 : "Geolocation", pszIndent, bJson, poMetadata,
2593 : osStr);
2594 148 : GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_RPC, "RPC Metadata",
2595 : pszIndent, bJson, poMetadata, osStr);
2596 : }
2597 :
2598 327 : GDALInfoPrintMetadata(psOptions, hObject, GDAL_MDD_IMAGERY, "Imagery",
2599 : pszIndent, bJson, poMetadata, osStr);
2600 : }
2601 :
2602 : /************************************************************************/
2603 : /* GDALInfoOptionsNew() */
2604 : /************************************************************************/
2605 :
2606 : /**
2607 : * Allocates a GDALInfoOptions struct.
2608 : *
2609 : * @param papszArgv NULL terminated list of options (potentially including
2610 : * filename and open options too), or NULL. The accepted options are the ones of
2611 : * the <a href="/programs/gdalinfo.html">gdalinfo</a> utility.
2612 : * @param psOptionsForBinary (output) may be NULL (and should generally be
2613 : * NULL), otherwise (gdalinfo_bin.cpp use case) must be allocated with
2614 : * GDALInfoOptionsForBinaryNew() prior to this
2615 : * function. Will be filled with potentially present filename, open options,
2616 : * subdataset number...
2617 : * @return pointer to the allocated GDALInfoOptions struct. Must be freed with
2618 : * GDALInfoOptionsFree().
2619 : *
2620 : * @since GDAL 2.1
2621 : */
2622 :
2623 : GDALInfoOptions *
2624 157 : GDALInfoOptionsNew(char **papszArgv,
2625 : GDALInfoOptionsForBinary *psOptionsForBinary)
2626 : {
2627 314 : auto psOptions = std::make_unique<GDALInfoOptions>();
2628 :
2629 : /* -------------------------------------------------------------------- */
2630 : /* Parse arguments. */
2631 : /* -------------------------------------------------------------------- */
2632 :
2633 314 : CPLStringList aosArgv;
2634 :
2635 157 : if (papszArgv)
2636 : {
2637 144 : const int nArgc = CSLCount(papszArgv);
2638 541 : for (int i = 0; i < nArgc; i++)
2639 : {
2640 397 : aosArgv.AddString(papszArgv[i]);
2641 : }
2642 : }
2643 :
2644 : try
2645 : {
2646 : auto argParser =
2647 314 : GDALInfoAppOptionsGetParser(psOptions.get(), psOptionsForBinary);
2648 :
2649 157 : argParser->parse_args_without_binary_name(aosArgv.List());
2650 :
2651 157 : if (psOptions->bApproxStats)
2652 2 : psOptions->bStats = true;
2653 : }
2654 0 : catch (const std::exception &error)
2655 : {
2656 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
2657 0 : return nullptr;
2658 : }
2659 :
2660 157 : if (!psOptions->bShowNodata)
2661 2 : psOptions->bShowMask = false;
2662 :
2663 157 : return psOptions.release();
2664 : }
2665 :
2666 : /************************************************************************/
2667 : /* GDALInfoOptionsFree() */
2668 : /************************************************************************/
2669 :
2670 : /**
2671 : * Frees the GDALInfoOptions struct.
2672 : *
2673 : * @param psOptions the options struct for GDALInfo().
2674 : *
2675 : * @since GDAL 2.1
2676 : */
2677 :
2678 98 : void GDALInfoOptionsFree(GDALInfoOptions *psOptions)
2679 : {
2680 98 : delete psOptions;
2681 98 : }
|