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