diff --git a/.github/workflows/doc_checks.yml b/.github/workflows/doc_checks.yml index 26502afd0062..545cb4eb5375 100644 --- a/.github/workflows/doc_checks.yml +++ b/.github/workflows/doc_checks.yml @@ -39,7 +39,7 @@ jobs: cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ - -DBUILD_APPS=OFF \ + -DBUILD_APPS=ON \ -DBUILD_TESTING=OFF \ -DGDAL_BUILD_OPTIONAL_DRIVERS=OFF \ -DOGR_BUILD_OPTIONAL_DRIVERS=OFF diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 058d13cf2877..aeba53308bb2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,7 @@ repos: (?x)^( swig/python/osgeo/__init__.py| swig/python/osgeo/gdalnumeric.py| + doc/source/_extensions/sphinxcontrib_programoutput_gdal.py| autotest/ogr/data/ ) - repo: https://github.com/timothycrosley/isort @@ -17,6 +18,7 @@ repos: (?x)^( swig/python/osgeo/__init__.py| swig/python/osgeo/gdalnumeric.py| + doc/source/_extensions/sphinxcontrib_programoutput_gdal.py| autotest/ogr/data/ ) - repo: https://github.com/pycqa/flake8 @@ -27,6 +29,7 @@ repos: (?x)^( swig/python/osgeo/__init__.py| swig/python/osgeo/gdalnumeric.py| + doc/source/_extensions/sphinxcontrib_programoutput_gdal.py| examples/| autotest/ogr/data/ ) diff --git a/doc/requirements.txt b/doc/requirements.txt index 8e566338af66..da22d12e3326 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,14 +1,16 @@ # This file may be used to create an environment using: # $ pip install --upgrade -r +breathe fsspec numpy sphinx -breathe sphinx_bootstrap_theme -sphinxcontrib-bibtex -sphinx_rtd_theme -recommonmark sphinx-markdown-tables -sphinxcontrib-spelling +sphinx_rtd_theme +sphinxcontrib-bibtex sphinxcontrib-jquery +# We use a slightly forked version +# sphinxcontrib-programoutput +sphinxcontrib-spelling myst_nb +recommonmark diff --git a/doc/rtd/pre_build.sh b/doc/rtd/pre_build.sh index 56d8d8060346..3faeba2ffb56 100755 --- a/doc/rtd/pre_build.sh +++ b/doc/rtd/pre_build.sh @@ -13,7 +13,7 @@ cmake \ -DGDAL_PYTHON_INSTALL_PREFIX=${PREFIX} \ -DGDAL_BUILD_OPTIONAL_DRIVERS=OFF \ -DOGR_BUILD_OPTIONAL_DRIVERS=OFF \ - -DBUILD_APPS=OFF \ + -DBUILD_APPS=ON \ -DBUILD_PYTHON_BINDINGS=ON \ -DBUILD_JAVA_BINDINGS=ON \ -DBUILD_TESTING=OFF \ diff --git a/doc/source/_extensions/sphinxcontrib_programoutput_gdal.py b/doc/source/_extensions/sphinxcontrib_programoutput_gdal.py new file mode 100644 index 000000000000..66db1be0a7d3 --- /dev/null +++ b/doc/source/_extensions/sphinxcontrib_programoutput_gdal.py @@ -0,0 +1,347 @@ +# Slightly customized version with a new 'language' option, submitted as +# https://github.com/OpenNTI/sphinxcontrib-programoutput/pull/62 + +# -*- coding: utf-8 -*- +# Copyright (c) 2010, 2011, 2012, Sebastian Wiesner +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +""" + sphinxcontrib.programoutput + =========================== + + This extension provides a directive to include the output of commands as + literal block while building the docs. + + .. moduleauthor:: Sebastian Wiesner +""" + +from __future__ import print_function, division, absolute_import + +import sys +import os +import shlex +from subprocess import Popen, PIPE, STDOUT +from collections import defaultdict, namedtuple + +from docutils import nodes +from docutils.parsers import rst +from docutils.parsers.rst.directives import flag, unchanged, nonnegative_int +from docutils.statemachine import StringList + +from sphinx.util import logging as sphinx_logging + +__version__ = '0.18.dev0' + +logger = sphinx_logging.getLogger('contrib.programoutput') + +class program_output(nodes.Element): + pass + + +def _container_wrapper(directive, literal_node, caption): + container_node = nodes.container('', literal_block=True, + classes=['literal-block-wrapper']) + parsed = nodes.Element() + directive.state.nested_parse(StringList([caption], source=''), + directive.content_offset, parsed) + if isinstance(parsed[0], nodes.system_message): # pragma: no cover + # TODO: Figure out if this is really possible and how to produce + # it in a test case. + msg = 'Invalid caption: %s' % parsed[0].astext() + raise ValueError(msg) + assert isinstance(parsed[0], nodes.Element) + caption_node = nodes.caption(parsed[0].rawsource, '', + *parsed[0].children) + caption_node.source = literal_node.source + caption_node.line = literal_node.line + container_node += caption_node + container_node += literal_node + return container_node + + +def _slice(value): + parts = [int(v.strip()) for v in value.split(',')] + if len(parts) > 2: + raise ValueError('too many slice parts') + return tuple((parts + [None] * 2)[:2]) + + +class ProgramOutputDirective(rst.Directive): + has_content = False + final_argument_whitespace = True + required_arguments = 1 + + option_spec = dict(shell=flag, prompt=flag, nostderr=flag, + ellipsis=_slice, extraargs=unchanged, + returncode=nonnegative_int, cwd=unchanged, + caption=unchanged, name=unchanged, + language=unchanged) + + def run(self): + env = self.state.document.settings.env + + node = program_output() + node.line = self.lineno + node['command'] = self.arguments[0] + + if self.name == 'command-output': + node['show_prompt'] = True + else: + node['show_prompt'] = 'prompt' in self.options + + node['hide_standard_error'] = 'nostderr' in self.options + node['extraargs'] = self.options.get('extraargs', '') + _, cwd = env.relfn2path(self.options.get('cwd', '/')) + node['working_directory'] = cwd + node['use_shell'] = 'shell' in self.options + node['returncode'] = self.options.get('returncode', 0) + node['language'] = self.options.get('language', 'text') + if 'ellipsis' in self.options: + node['strip_lines'] = self.options['ellipsis'] + if 'caption' in self.options: + caption = self.options['caption'] or self.arguments[0] + node = _container_wrapper(self, node, caption) + + self.add_name(node) + return [node] + + +_Command = namedtuple( + 'Command', 'command shell hide_standard_error working_directory') + + +class Command(_Command): + """ + A command to be executed. + """ + + def __new__(cls, command, shell=False, hide_standard_error=False, + working_directory='/'): + # `chdir()` resolves symlinks, so we need to resolve them too for + # caching to make sure that different symlinks to the same directory + # don't result in different cache keys. Also normalize paths to make + # sure that identical paths are also equal as strings. + working_directory = os.path.normpath(os.path.realpath( + working_directory)) + # Likewise, normalize the command now for better caching, and so + # that we can present *exactly* what we run to the user. + command = cls.__normalize_command(command, shell) + return _Command.__new__(cls, command, shell, hide_standard_error, + working_directory) + + @staticmethod + def __normalize_command(command, shell): # pylint:disable=unused-private-member + # Returns either a native string, to a tuple. + if (bytes is str + and not isinstance(command, str) + and hasattr(command, 'encode')): + # Python 2, given a unicode string + command = command.encode(sys.getfilesystemencoding()) + assert isinstance(command, str) + + if not shell and isinstance(command, str): + command = shlex.split(command) + + if isinstance(command, list): + command = tuple(command) + + assert isinstance(command, (str, tuple)), command + + return command + + @classmethod + def from_program_output_node(cls, node): + """ + Create a command from a :class:`program_output` node. + """ + extraargs = node.get('extraargs', '') + command = (node['command'] + ' ' + extraargs).strip() + return cls(command, node['use_shell'], + node['hide_standard_error'], node['working_directory']) + + def execute(self): + """ + Execute this command. + + Return the :class:`~subprocess.Popen` object representing the running + command. + """ + command = self.command + + # Popen is a context manager only in Python 3, and we'd have to restructure + # the code to work with it anyway. + # pylint:disable=consider-using-with + return Popen(command, shell=self.shell, stdout=PIPE, + stderr=PIPE if self.hide_standard_error else STDOUT, + cwd=self.working_directory) + + def get_output(self): + """ + Get the output of this command. + + Return a tuple ``(returncode, output)``. ``returncode`` is the + integral return code of the process, ``output`` is the output as + unicode string, with final trailing spaces and new lines stripped. + """ + process = self.execute() + output = process.communicate()[0].decode( + sys.getfilesystemencoding(), 'replace').rstrip() + return process.returncode, output + + def __str__(self): + command = self.command + command = list(command) if isinstance(command, tuple) else command + return repr(command) + + +class ProgramOutputCache(defaultdict): + """ + Execute command and cache their output. + + This class is a mapping. Its keys are :class:`Command` objects represeting + command invocations. Its values are tuples of the form ``(returncode, + output)``, where ``returncode`` is the integral return code of the command, + and ``output`` is the output as unicode string. + + The first time, a key is retrieved from this object, the command is + invoked, and its result is cached. Subsequent access to the same key + returns the cached value. + """ + + def __missing__(self, command): + """ + Called, if a command was not found in the cache. + + ``command`` is an instance of :class:`Command`. + """ + result = command.get_output() + self[command] = result + return result + + +def _prompt_template_as_unicode(app): + tmpl = app.config.programoutput_prompt_template + if isinstance(tmpl, bytes): + for enc in 'utf-8', sys.getfilesystemencoding(): + try: + tmpl = tmpl.decode(enc) + except UnicodeError: # pragma: no cover + pass + else: + app.config.programoutput_prompt_template = tmpl + break + return tmpl + + +def run_programs(app, doctree): + """ + Execute all programs represented by ``program_output`` nodes in + ``doctree``. Each ``program_output`` node in ``doctree`` is then + replaced with a node, that represents the output of this program. + + The program output is retrieved from the cache in + ``app.env.programoutput_cache``. + """ + # The node_class used to be switchable to `sphinxcontrib.ansi.ansi_literal_block` + # if `app.config.programoutput_use_ansi` was set. But sphinxcontrib.ansi + # is no longer available on PyPI, so we can't test that. And if we can't test it, + # we can't support it. + node_class = nodes.literal_block + + cache = app.env.programoutput_cache + + for node in doctree.traverse(program_output): + command = Command.from_program_output_node(node) + try: + returncode, output = cache[command] + except EnvironmentError as error: + error_message = 'Command {0} failed: {1}'.format(command, error) + error_node = doctree.reporter.error(error_message, base_node=node) + # Sphinx 1.8.0b1 started dropping all system_message nodes with a + # level less than 5 by default (or 2 if `keep_warnings` is set to true). + # This appears to be undocumented. Reporting failures is an important + # part of what this extension does, so we raise the default level. + error_node['level'] = 6 + node.replace_self(error_node) + else: + if returncode != node['returncode']: + logger.warning( + 'Unexpected return code %s from command %r (output=%r)', + returncode, command, output + ) + + # replace lines with ..., if ellipsis is specified + + # Recall that `output` is guaranteed to be a unicode string on + # all versions of Python. + if 'strip_lines' in node: + start, stop = node['strip_lines'] + lines = output.splitlines() + lines[start:stop] = [u'...'] + output = u'\n'.join(lines) + + if node['show_prompt']: + # The command in the node is also guaranteed to be + # unicode, but the prompt template might not be. This + # could be a native string on Python 2, or one with an + # explicit b prefix on 2 or 3 (for some reason). + # Attempt to decode it using UTF-8, preferentially, or + # fallback to sys.getfilesystemencoding(). If all that fails, fall back + # to the default encoding (which may have often worked before). + prompt_template = _prompt_template_as_unicode(app) + output = prompt_template.format( + command=node['command'], + output=output, + returncode=returncode + ) + + new_node = node_class(output, output) + new_node['language'] = node['language'] + node.replace_self(new_node) + + + +def init_cache(app): + """ + Initialize the cache for program output at + ``app.env.programoutput_cache``, if not already present (e.g. being + loaded from a pickled environment). + + The cache is of type :class:`ProgramOutputCache`. + """ + if not hasattr(app.env, 'programoutput_cache'): + app.env.programoutput_cache = ProgramOutputCache() + + +def setup(app): + app.add_config_value('programoutput_prompt_template', + '$ {command}\n{output}', 'env') + app.add_directive('program-output', ProgramOutputDirective) + app.add_directive('command-output', ProgramOutputDirective) + app.connect('builder-inited', init_cache) + app.connect('doctree-read', run_programs) + metadata = { + 'parallel_read_safe': True + } + return metadata diff --git a/doc/source/conf.py b/doc/source/conf.py index 44dc7ae0f5d0..f3e323d38f13 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -71,6 +71,7 @@ "source_file", "sphinx.ext.napoleon", "sphinxcontrib.jquery", + "sphinxcontrib_programoutput_gdal", "sphinxcontrib.spelling", "myst_nb", ] diff --git a/doc/source/programs/gdalinfo.rst b/doc/source/programs/gdalinfo.rst index 01ef40ad0d97..405801a4d2d2 100644 --- a/doc/source/programs/gdalinfo.rst +++ b/doc/source/programs/gdalinfo.rst @@ -184,286 +184,13 @@ This utility is also callable from C with :cpp:func:`GDALInfo`. Example ------- -.. code-block:: - - gdalinfo ~/openev/utm.tif - Driver: GTiff/GeoTIFF - Size is 512, 512 - Coordinate System is: - PROJCS["NAD27 / UTM zone 11N", - GEOGCS["NAD27", - DATUM["North_American_Datum_1927", - SPHEROID["Clarke 1866",6378206.4,294.978698213901]], - PRIMEM["Greenwich",0], - UNIT["degree",0.0174532925199433]], - PROJECTION["Transverse_Mercator"], - PARAMETER["latitude_of_origin",0], - PARAMETER["central_meridian",-117], - PARAMETER["scale_factor",0.9996], - PARAMETER["false_easting",500000], - PARAMETER["false_northing",0], - UNIT["metre",1]] - Origin = (440720.000000,3751320.000000) - Pixel Size = (60.000000,-60.000000) - Corner Coordinates: - Upper Left ( 440720.000, 3751320.000) (117d38'28.21"W, 33d54'8.47"N) - Lower Left ( 440720.000, 3720600.000) (117d38'20.79"W, 33d37'31.04"N) - Upper Right ( 471440.000, 3751320.000) (117d18'32.07"W, 33d54'13.08"N) - Lower Right ( 471440.000, 3720600.000) (117d18'28.50"W, 33d37'35.61"N) - Center ( 456080.000, 3735960.000) (117d28'27.39"W, 33d45'52.46"N) - Band 1 Block=512x16 Type=Byte, ColorInterp=Gray +.. command-output:: gdalinfo utmsmall.tif + :cwd: ../../../autotest/gcore/data For corner coordinates formatted as decimal degree instead of the above degree, minute, second, inspect the ``wgs84Extent`` member of gdalinfo -json: -Example of JSON output with ``gdalinfo -json byte.tif`` - -.. code-block:: json - - { - "description":"byte.tif", - "driverShortName":"GTiff", - "driverLongName":"GeoTIFF", - "files":[ - "byte.tif" - ], - "size":[ - 20, - 20 - ], - "coordinateSystem":{ - "wkt":"PROJCRS[\"NAD27 / UTM zone 11N\",\n BASEGEOGCRS[\"NAD27\",\n DATUM[\"North American Datum 1927\",\n ELLIPSOID[\"Clarke 1866\",6378206.4,294.978698213898,\n LENGTHUNIT[\"metre\",1]]],\n PRIMEM[\"Greenwich\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433]],\n ID[\"EPSG\",4267]],\n CONVERSION[\"UTM zone 11N\",\n METHOD[\"Transverse Mercator\",\n ID[\"EPSG\",9807]],\n PARAMETER[\"Latitude of natural origin\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8801]],\n PARAMETER[\"Longitude of natural origin\",-117,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8802]],\n PARAMETER[\"Scale factor at natural origin\",0.9996,\n SCALEUNIT[\"unity\",1],\n ID[\"EPSG\",8805]],\n PARAMETER[\"False easting\",500000,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8806]],\n PARAMETER[\"False northing\",0,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8807]]],\n CS[Cartesian,2],\n AXIS[\"(E)\",east,\n ORDER[1],\n LENGTHUNIT[\"metre\",1]],\n AXIS[\"(N)\",north,\n ORDER[2],\n LENGTHUNIT[\"metre\",1]],\n USAGE[\n SCOPE[\"Engineering survey, topographic mapping.\"],\n AREA[\"North America - between 120°W and 114°W - onshore. Canada - Alberta; British Columbia; Northwest Territories; Nunavut. Mexico. United States (USA) - California; Idaho; Nevada; Oregon; Washington.\"],\n BBOX[26.93,-120,78.13,-114]],\n ID[\"EPSG\",26711]]", - "dataAxisToSRSAxisMapping":[ - 1, - 2 - ] - }, - "geoTransform":[ - 440720.0, - 60.0, - 0.0, - 3751320.0, - 0.0, - -60.0 - ], - "metadata":{ - "":{ - "AREA_OR_POINT":"Area" - }, - "IMAGE_STRUCTURE":{ - "INTERLEAVE":"BAND" - } - }, - "cornerCoordinates":{ - "upperLeft":[ - 440720.0, - 3751320.0 - ], - "lowerLeft":[ - 440720.0, - 3750120.0 - ], - "lowerRight":[ - 441920.0, - 3750120.0 - ], - "upperRight":[ - 441920.0, - 3751320.0 - ], - "center":[ - 441320.0, - 3750720.0 - ] - }, - "wgs84Extent":{ - "type":"Polygon", - "coordinates":[ - [ - [ - -117.6420428, - 33.9023684 - ], - [ - -117.6419617, - 33.8915461 - ], - [ - -117.6289846, - 33.8916131 - ], - [ - -117.629064, - 33.9024353 - ], - [ - -117.6420428, - 33.9023684 - ] - ] - ] - }, - "bands":[ - { - "band":1, - "block":[ - 20, - 20 - ], - "type":"Byte", - "colorInterpretation":"Gray", - "metadata":{ - } - } - ], - "stac":{ - "proj:shape":[ - 20, - 20 - ], - "proj:wkt2":"PROJCRS[\"NAD27 / UTM zone 11N\",\n BASEGEOGCRS[\"NAD27\",\n DATUM[\"North American Datum 1927\",\n ELLIPSOID[\"Clarke 1866\",6378206.4,294.978698213898,\n LENGTHUNIT[\"metre\",1]]],\n PRIMEM[\"Greenwich\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433]],\n ID[\"EPSG\",4267]],\n CONVERSION[\"UTM zone 11N\",\n METHOD[\"Transverse Mercator\",\n ID[\"EPSG\",9807]],\n PARAMETER[\"Latitude of natural origin\",0,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8801]],\n PARAMETER[\"Longitude of natural origin\",-117,\n ANGLEUNIT[\"degree\",0.0174532925199433],\n ID[\"EPSG\",8802]],\n PARAMETER[\"Scale factor at natural origin\",0.9996,\n SCALEUNIT[\"unity\",1],\n ID[\"EPSG\",8805]],\n PARAMETER[\"False easting\",500000,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8806]],\n PARAMETER[\"False northing\",0,\n LENGTHUNIT[\"metre\",1],\n ID[\"EPSG\",8807]]],\n CS[Cartesian,2],\n AXIS[\"(E)\",east,\n ORDER[1],\n LENGTHUNIT[\"metre\",1]],\n AXIS[\"(N)\",north,\n ORDER[2],\n LENGTHUNIT[\"metre\",1]],\n USAGE[\n SCOPE[\"Engineering survey, topographic mapping.\"],\n AREA[\"North America - between 120°W and 114°W - onshore. Canada - Alberta; British Columbia; Northwest Territories; Nunavut. Mexico. United States (USA) - California; Idaho; Nevada; Oregon; Washington.\"],\n BBOX[26.93,-120,78.13,-114]],\n ID[\"EPSG\",26711]]", - "proj:epsg":26711, - "proj:projjson":{ - "$schema":"https://proj.org/schemas/v0.6/projjson.schema.json", - "type":"ProjectedCRS", - "name":"NAD27 / UTM zone 11N", - "base_crs":{ - "name":"NAD27", - "datum":{ - "type":"GeodeticReferenceFrame", - "name":"North American Datum 1927", - "ellipsoid":{ - "name":"Clarke 1866", - "semi_major_axis":6378206.4, - "semi_minor_axis":6356583.8 - } - }, - "coordinate_system":{ - "subtype":"ellipsoidal", - "axis":[ - { - "name":"Geodetic latitude", - "abbreviation":"Lat", - "direction":"north", - "unit":"degree" - }, - { - "name":"Geodetic longitude", - "abbreviation":"Lon", - "direction":"east", - "unit":"degree" - } - ] - }, - "id":{ - "authority":"EPSG", - "code":4267 - } - }, - "conversion":{ - "name":"UTM zone 11N", - "method":{ - "name":"Transverse Mercator", - "id":{ - "authority":"EPSG", - "code":9807 - } - }, - "parameters":[ - { - "name":"Latitude of natural origin", - "value":0, - "unit":"degree", - "id":{ - "authority":"EPSG", - "code":8801 - } - }, - { - "name":"Longitude of natural origin", - "value":-117, - "unit":"degree", - "id":{ - "authority":"EPSG", - "code":8802 - } - }, - { - "name":"Scale factor at natural origin", - "value":0.9996, - "unit":"unity", - "id":{ - "authority":"EPSG", - "code":8805 - } - }, - { - "name":"False easting", - "value":500000, - "unit":"metre", - "id":{ - "authority":"EPSG", - "code":8806 - } - }, - { - "name":"False northing", - "value":0, - "unit":"metre", - "id":{ - "authority":"EPSG", - "code":8807 - } - } - ] - }, - "coordinate_system":{ - "subtype":"Cartesian", - "axis":[ - { - "name":"Easting", - "abbreviation":"E", - "direction":"east", - "unit":"metre" - }, - { - "name":"Northing", - "abbreviation":"N", - "direction":"north", - "unit":"metre" - } - ] - }, - "scope":"Engineering survey, topographic mapping.", - "area":"North America - between 120°W and 114°W - onshore. Canada - Alberta; British Columbia; Northwest Territories; Nunavut. Mexico. United States (USA) - California; Idaho; Nevada; Oregon; Washington.", - "bbox":{ - "south_latitude":26.93, - "west_longitude":-120, - "north_latitude":78.13, - "east_longitude":-114 - }, - "id":{ - "authority":"EPSG", - "code":26711 - } - }, - "proj:transform":[ - 440720.0, - 60.0, - 0.0, - 3751320.0, - 0.0, - -60.0 - ], - "raster:bands":[ - { - "data_type":"uint8" - } - ], - "eo:bands":[ - { - "name":"b1", - "description":"Gray" - } - ] - } - } +Example of JSON output with ``gdalinfo -json utmsmall.tif``: + +.. program-output:: gdalinfo -json utmsmall.tif + :cwd: ../../../autotest/gcore/data + :language: json