From f4766b18a0eb4f53cd67b8983210aab3fd263c45 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 26 Sep 2024 19:20:35 +0200 Subject: [PATCH] Make gdalinfo/ogrinfo --formats -json work Fixes #10878 Note: only available in utilities-as-binaries, not utilities-as-libraries... Returns drivers in the order of their registration. Example ```shell $ gdalinfo --formats -json [ { "short_name":"VRT", "long_name":"Virtual Raster", "scopes":[ "raster", "multidimensional_raster" ], "capabilities":[ "open", "create", "create_copy", "virtual_io" ], "file_extensions":[ "vrt" ] }, ... ] ``` ```shell $ ogrinfo --formats -json [ { "short_name":"FITS", "long_name":"Flexible Image Transport System", "scopes":[ "raster", "vector" ], "capabilities":[ "open", "create" ], "file_extensions":[ "fits" ] }, ... ] ``` --- autotest/utilities/test_gdalinfo.py | 19 ++++ autotest/utilities/test_ogrinfo.py | 20 +++++ doc/source/programs/raster_common_options.rst | 4 + doc/source/programs/vector_common_options.rst | 4 + gcore/gdal_misc.cpp | 87 ++++++++++++++++++- 5 files changed, 132 insertions(+), 2 deletions(-) diff --git a/autotest/utilities/test_gdalinfo.py b/autotest/utilities/test_gdalinfo.py index c3857c716b0f..a740ecdae29a 100755 --- a/autotest/utilities/test_gdalinfo.py +++ b/autotest/utilities/test_gdalinfo.py @@ -343,6 +343,25 @@ def test_gdalinfo_20(gdalinfo_path): assert "GTiff -raster- (rw+vs): GeoTIFF" in ret +############################################################################### +# Test --formats -json + + +@pytest.mark.require_driver("VRT") +def test_gdalinfo_formats_json(gdalinfo_path): + + ret = json.loads( + gdaltest.runexternal(gdalinfo_path + " --formats -json", check_memleak=False) + ) + assert { + "short_name": "VRT", + "long_name": "Virtual Raster", + "scopes": ["raster", "multidimensional_raster"], + "capabilities": ["open", "create", "create_copy", "virtual_io"], + "file_extensions": ["vrt"], + } in ret + + ############################################################################### # Test erroneous use of --format. diff --git a/autotest/utilities/test_ogrinfo.py b/autotest/utilities/test_ogrinfo.py index 1d15129dd13e..82113b6ccf3c 100755 --- a/autotest/utilities/test_ogrinfo.py +++ b/autotest/utilities/test_ogrinfo.py @@ -29,6 +29,7 @@ # DEALINGS IN THE SOFTWARE. ############################################################################### +import json import os import pathlib import stat @@ -324,6 +325,25 @@ def test_ogrinfo_19(ogrinfo_path): assert "ESRI Shapefile -vector- (rw+v): ESRI Shapefile" in ret +############################################################################### +# Test --formats -json + + +@pytest.mark.require_driver("ESRI Shapefile") +def test_ogrinfo_formats_json(ogrinfo_path): + + ret = json.loads( + gdaltest.runexternal(ogrinfo_path + " --formats -json", check_memleak=False) + ) + assert { + "short_name": "ESRI Shapefile", + "long_name": "ESRI Shapefile", + "scopes": ["vector"], + "capabilities": ["open", "create", "virtual_io"], + "file_extensions": ["shp", "dbf", "shz", "shp.zip"], + } in ret + + ############################################################################### # Test --help-general diff --git a/doc/source/programs/raster_common_options.rst b/doc/source/programs/raster_common_options.rst index e976b9a254d6..de42edb5ccf6 100644 --- a/doc/source/programs/raster_common_options.rst +++ b/doc/source/programs/raster_common_options.rst @@ -21,6 +21,10 @@ All GDAL command line programs support the following common options. List detailed information about a single format driver. The format should be the short name reported in the --formats list, such as GTiff. +.. option:: --formats + + List all drivers. Can be combined with ``-json`` switch of :program:`gdalinfo` of since GDAL 3.10 + .. _raster_common_options_optfile: .. option:: --optfile diff --git a/doc/source/programs/vector_common_options.rst b/doc/source/programs/vector_common_options.rst index 8e281e088bbc..152f75c3c5dc 100644 --- a/doc/source/programs/vector_common_options.rst +++ b/doc/source/programs/vector_common_options.rst @@ -22,6 +22,10 @@ All GDAL OGR command line programs support the following common options. The format should be the short name reported in the :option:`--formats` list, such as GML. +.. option:: --formats + + List all drivers. Can be combined with ``-json`` switch of :program:`ogrinfo` of since GDAL 3.10 + .. option:: --optfile Read the named file and substitute the contents into the command line diff --git a/gcore/gdal_misc.cpp b/gcore/gdal_misc.cpp index 50e1c65793db..9d344fea1c16 100644 --- a/gcore/gdal_misc.cpp +++ b/gcore/gdal_misc.cpp @@ -46,6 +46,7 @@ #include "cpl_conv.h" #include "cpl_error.h" +#include "cpl_json.h" #include "cpl_minixml.h" #include "cpl_multiproc.h" #include "cpl_string.h" @@ -3425,7 +3426,7 @@ static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions) * --version: report version of GDAL in use. * --build: report build info about GDAL in use. * --license: report GDAL license info. - * --formats: report all format drivers configured. + * --formats: report all format drivers configured. Can be used with -json since 3.10 * --format [format]: report details of one format driver. * --optfile filename: expand an option file into the argument list. * --config key value: set system configuration option. @@ -3716,6 +3717,88 @@ int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv, if (nOptions == 0) nOptions = GDAL_OF_RASTER; + bool bJSON = false; + for (int i = 1; i < nArgc; i++) + { + if (strcmp(papszArgv[i], "-json") == 0) + { + bJSON = true; + break; + } + } + + if (bJSON) + { + auto poDM = GetGDALDriverManager(); + CPLJSONArray oArray; + const int nDriverCount = poDM->GetDriverCount(); + for (int iDr = 0; iDr < nDriverCount; ++iDr) + { + auto poDriver = poDM->GetDriver(iDr); + CSLConstList papszMD = poDriver->GetMetadata(); + + if (nOptions == GDAL_OF_RASTER && + !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false)) + continue; + if (nOptions == GDAL_OF_VECTOR && + !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false)) + continue; + if (nOptions == GDAL_OF_GNM && + !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false)) + continue; + if (nOptions == GDAL_OF_MULTIDIM_RASTER && + !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, + false)) + continue; + + CPLJSONObject oJDriver; + oJDriver.Set("short_name", poDriver->GetDescription()); + if (const char *pszLongName = + CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME)) + oJDriver.Set("long_name", pszLongName); + CPLJSONArray oJScopes; + if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false)) + oJScopes.Add("raster"); + if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false)) + oJScopes.Add("multidimensional_raster"); + if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false)) + oJScopes.Add("vector"); + oJDriver.Add("scopes", oJScopes); + CPLJSONArray oJCaps; + if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false)) + oJCaps.Add("open"); + if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false)) + oJCaps.Add("create"); + if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false)) + oJCaps.Add("create_copy"); + if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false)) + oJCaps.Add("virtual_io"); + oJDriver.Add("capabilities", oJCaps); + + if (const char *pszExtensions = CSLFetchNameValueDef( + papszMD, GDAL_DMD_EXTENSIONS, + CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION))) + { + const CPLStringList aosExt( + CSLTokenizeString2(pszExtensions, " ", 0)); + CPLJSONArray oJExts; + for (int i = 0; i < aosExt.size(); ++i) + { + oJExts.Add(aosExt[i]); + } + oJDriver.Add("file_extensions", oJExts); + } + + oArray.Add(oJDriver); + } + printf(/*ok*/ + "%s\n", + oArray.Format(CPLJSONObject::PrettyFormat::Pretty) + .c_str()); + + return 0; + } + printf(/*ok*/ "Supported Formats: (ro:read-only, rw:read-write, +:update, " "v:virtual-I/O s:subdatasets)\n"); @@ -3725,7 +3808,7 @@ int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv, const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets; - char **papszMD = GDALGetMetadata(hDriver, nullptr); + CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr); if (nOptions == GDAL_OF_RASTER && !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))