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