Skip to content

Instantly share code, notes, and snippets.

@JeffJacobson
Last active October 14, 2026 16:39
Show Gist options
  • Select an option

  • Save JeffJacobson/6bd40e60c47465db75ba7db83f3f97a5 to your computer and use it in GitHub Desktop.

Select an option

Save JeffJacobson/6bd40e60c47465db75ba7db83f3f97a5 to your computer and use it in GitHub Desktop.
Useful GDAL commands

Useful GDAL commmands

List Drivers

List vector drivers with create and virtual_io capabilities

  1. List all of the installed GDAL drivers in JSON format.
  2. Convert the JSON output into PowerShell objects.
  3. Filter to include only
    • Vector drivers
    • Drivers with capabilities:
      • create
      • virtual_io
  4. Exclude the scopes property from the output
  5. Sort the output by the short_name property
  6. Format the output as a table
gdal info --formats --json | 
ConvertFrom-Json |
Where-Object { $_.scopes -icontains 'vector' -and $_.capabilities -icontains 'create' -and $_.capabilities -icontains 'virtual_io' }  |
Select-Object -ExcludeProperty scopes |
Sort-Object -Property short_name |
Format-Table -AutoSize
Sample output

short_name     long_name                                           capabilities                            file_extensions
----------     ---------                                           ------------                            ---------------
Arrow          (Geo)Arrow IPC File Format / Stream                 {open, create, virtual_io}              {arrow, feather, arrows, ipc}
BAG            Bathymetry Attributed Grid                          {open, create, create_copy, virtual_io} {bag}
CSV            Comma Separated Value (.csv)                        {open, create, update, virtual_io}      {csv, tsv, psv}
DGN            Microstation DGN                                    {open, create, virtual_io}              {dgn}
DXF            AutoCAD DXF                                         {open, create, virtual_io}              {dxf}
ESRI Shapefile ESRI Shapefile                                      {open, create, update, virtual_io}      {shp, dbf, shz, shp.zip}
FlatGeobuf     FlatGeobuf                                          {open, create, virtual_io}              {fgb}
GeoJSON        GeoJSON                                             {open, create, update, virtual_io}      {json, geojson}
GeoJSONSeq     GeoJSON Sequence                                    {open, create, virtual_io}              {geojsonl, geojsons}
GeoRSS         GeoRSS                                              {open, create, virtual_io}              
GML            Geography Markup Language (GML)                     {open, create, virtual_io}              {gml, xml}
GPKG           GeoPackage                                          {open, create, create_copy, update…}    {gpkg, gpkg.zip}
GPX            GPX                                                 {open, create, virtual_io}              {gpx}
JML            OpenJUMP JML                                        {open, create, virtual_io}              {jml}
JSONFG         OGC Features and Geometries JSON                    {open, create, virtual_io}              {json}
KML            Keyhole Markup Language (KML)                       {open, create, virtual_io}              {kml}
LIBKML         Keyhole Markup Language (LIBKML)                    {open, create, update, virtual_io}      {kml, kmz}
MapInfo File   MapInfo File                                        {open, create, update, virtual_io}      {tab, mif, mid}
MapML          MapML                                               {open, create, virtual_io}              
MBTiles        MBTiles                                             {open, create, create_copy, virtual_io} {mbtiles}
MiraMonVector  MiraMon Vectors (.pol, .arc, .pnt)                  {open, create, virtual_io}              {pol, arc, pnt}
MVT            Mapbox Vector Tiles                                 {open, create, virtual_io}              {mvt, mvt.gz, pbf}
ODS            Open Document/ LibreOffice / OpenOffice Spreadsheet {open, create, update, virtual_io}      {ods}
OGR_GMT        GMT ASCII Vectors (.gmt)                            {open, create, virtual_io}              {gmt}
OpenFileGDB    ESRI FileGeodatabase (using OpenFileGDB)            {open, create, update, virtual_io}      {gdb}
Parquet        (Geo)Parquet                                        {open, create, update, virtual_io}      {parquet}
PCIDSK         PCIDSK Database File                                {open, create, update, virtual_io}      {pix}
PDF            Geospatial PDF                                      {open, create, create_copy, update…}    {pdf}
PDS4           NASA Planetary Data System 4                        {open, create, create_copy, update…}    {xml}
PGDUMP         PostgreSQL SQL dump                                 {create, virtual_io}                    {sql}
PMTiles        ProtoMap Tiles                                      {open, create, virtual_io}              {pmtiles}
S57            IHO S-57 (ENC)                                      {open, create, virtual_io}              {000}
Selafin        Selafin                                             {open, create, virtual_io}              
SQLite         SQLite / Spatialite                                 {open, create, update, virtual_io}      {sqlite, db}
TileDB         TileDB                                              {open, create, create_copy, update…}    
VDV            VDV-451/VDV-452/INTREST Data Format                 {open, create, virtual_io}              {txt, x10}
VICAR          MIPL VICAR file                                     {open, create, create_copy, virtual_io} 
WAsP           WAsP .map format                                    {open, create, virtual_io}              {map}
XLSX           MS Office Open XML spreadsheet                      {open, create, update, virtual_io}      {xlsx, xlsm}

List drivers with short_names that contain json (case insensitive)

gdal info --formats --json | 
ConvertFrom-Json |
Where-Object -Property short_name -ILike '*json*' |
Sort-Object -Property short_name |
Format-Table
short_name long_name                        scopes   capabilities                       file_extensions
---------- ---------                        ------   ------------                       ---------------
ESRIJSON   ESRIJSON                         {vector} {open, virtual_io}                 {json}
GeoJSON    GeoJSON                          {vector} {open, create, update, virtual_io} {json, geojson}
GeoJSONSeq GeoJSON Sequence                 {vector} {open, create, virtual_io}         {geojsonl, geojsons}
JSONFG     OGC Features and Geometries JSON {vector} {open, create, virtual_io}         {json}
TopoJSON   TopoJSON                         {vector} {open, virtual_io}                 {json, topojson}

List drivers with names containing esri (case insensitive)

gdal info --formats --json | 
ConvertFrom-Json |
Where-Object -Property short_name -ILike '*esri*' |
Sort-Object -Property short_name |
Format-Table
short_name     long_name          scopes   capabilities                       file_extensions
----------     ---------          ------   ------------                       ---------------
ESRI Shapefile ESRI Shapefile     {vector} {open, create, update, virtual_io} {shp, dbf, shz, shp.zip}
ESRIC          Esri Compact Cache {raster} {open, virtual_io}                 {json, tpkx}
ESRIJSON       ESRIJSON           {vector} {open, virtual_io}                 {json}
import enum
import json
from collections.abc import Generator, Iterable, MutableSequence
from typing import Annotated, TypedDict, cast
import pyogrio
import typer
from rich.console import Console
from rich.table import Table
_APP = typer.Typer()
class DriverDict(TypedDict):
"""Represents a GDAL driver"""
name: str
long_name: str
read: bool
append: bool
write: bool
supports_vsi: bool
help_topic_url: str | None
extensions: list[str] | None
type DriversDict = dict[str, DriverDict]
"""Output of pyogrio.list_drivers_details()
"""
class OptionsDict(TypedDict, total=False):
read: bool
append: bool
write: bool
supports_vsi: bool
help_topic_url: str
extensions: Iterable[str]
def meets_criteria(
d: DriverDict,
read: bool | None = None,
append: bool | None = None,
write: bool | None = None,
supports_vsi: bool | None = None,
extension: str | None = None,
):
"""Determines if a driver has specified capabilities.
Only capabilities parameters that are not None will be checked.
Args:
d: A dict representing a GDAL driver.
read Check to see if a driver supports reading. Defaults to None.
append: Check to see if a driver supports appending. Defaults to None.
write: Check to see if a driver supports writing. Defaults to None.
supports_vsi: Set to True to check for VSI support. Defaults to None.
extension: Specify an extension to check for.
Comparison of extensions is case-insensitive. Defaults to None.
Returns:
True if the criteria are met, false otherwise.
"""
def is_valid(a: bool | None, b: bool):
"""Returns True if:
* A is None or False
* A is non-None and b is True
Args:
a (bool | None): A boolean value or None
b (bool): A boolean value
Returns:
bool: True or False
"""
if not a:
return True
return b
# Get bool values for the given parameters
# and those of the corresponding values of in the dict
booleans = [
(read, d["read"]),
(append, d["append"]),
(write, d["write"]),
(supports_vsi, d["supports_vsi"]),
]
# Test all of the capabilities for which the
# corresponding parameter was True.
tests = (is_valid(*n) for n in booleans)
# Return False if any of the tests were False
if not all(tests):
return False
# If there was an extension provided, return True only
# if the driver supports that extension.
if extension:
driver_extentions = d["extensions"]
# Return false if the user provided an extension
# but the driver does not have any extensions
if not driver_extentions:
return False
# Add a leading period if the extension does not already have one.
if not extension.startswith("."):
extension = f".{extension}"
# Return True if the driver is in the list of drivers, false otherwise.
return extension.casefold() in (d.casefold() for d in driver_extentions)
# Return True if nothing else has failed or returned a value.
return True
def get_flattened_drivers_list(
read: bool | None = None,
append: bool | None = None,
write: bool | None = None,
supports_vsi: bool | None = None,
extension: str | None = None,
):
"""Get a list of supported GDAL drivers
Args:
read: Supports reading. Defaults to None.
append: Support appending. Defaults to None.
write: Supports writing. Defaults to None.
supports_vsi: Supports VSI. Defaults to None.
extension: Supports the given extension. Defaults to None.
Returns:
A list of drivers that meet the given criteria
"""
# pyogrio.list_drivers_details returns a
# dict[str, dict] keyed by the driver's name.
#
# 1. Add the driver name to each child dict.
# 2. Return a list of these child dicts, now with a "name".
output = ({**{"name": k}, **v} for k, v in (pyogrio.list_drivers_details()).items())
output = cast(Generator[DriverDict], output)
# Filter out the drivers that do not support the features
# specified by the function parameters.
output = [
d
for d in output
if meets_criteria(d, read, append, write, supports_vsi, extension)
]
return output
# We don't know ahead of time what drivers will be available, so we have to create the enum dynamically.
VectorDriverNames = enum.Enum(
"DriversEnum",
((d["name"], d["name"]) for d in get_flattened_drivers_list() if d["write"]),
)
"""An enumeration of valid vector driver names
"""
def has_extension(d: DriverDict, ext: str):
extension_list = d["extensions"]
if not extension_list:
return False
extension_list = [x.casefold() == ext.casefold() for x in extension_list]
return any(extension_list)
@_APP.command()
def main(
read: Annotated[bool, typer.Option("--read", "-r")] = False,
write: Annotated[bool, typer.Option("--write", "-w")] = False,
append: Annotated[bool, typer.Option("--append", "-a")] = False,
supports_vsi: Annotated[
bool, typer.Option("--supports-vsi", "--vsi", "-v")
] = False,
as_json: Annotated[bool, typer.Option()] = False,
extension: Annotated[str | None, typer.Option(case_sensitive=False)] = None,
):
"""Returns all drivers that match the given criteria"""
with Console() as c:
driver_dicts = get_flattened_drivers_list(
read, write, append, supports_vsi, extension
)
# Sort by "name"
driver_dicts.sort(key=lambda d: d["name"])
if as_json:
c.print_json(json.dumps(driver_dicts))
else:
def format_heading(s: str):
parts = s.split("_")
if parts[0] == "help":
return parts[0]
if parts[-1] == "vsi":
return parts[-1]
return " ".join(parts)
table_headings = (format_heading(k) for k in driver_dicts[0].keys())
t = Table(*table_headings, title="Drivers")
for d in driver_dicts:
# row_dict: dict[str, str] = {}
row = []
for name, value in d.items():
if value is None:
value = ""
elif isinstance(value, MutableSequence):
value = ",".join(value)
elif isinstance(value, bool):
value = "✅" if value else ":x:"
elif isinstance(value, str) and name.endswith("url"):
value = f"[link={value}]link[/link]"
else:
value = str(value)
row.append(value)
t.add_row(*row)
c.print(t)
if __name__ == "__main__":
_APP()
import enum
import json
import re
from itertools import groupby
from typing import Annotated
import rich
import rich.table
from rich.console import Console
import typer
from osgeo import gdalconst
_APP = typer.Typer()
class OutputFormat(enum.StrEnum):
"""Output Formats
Value | Description
------|-----------------------
json | Output JSON to console
table | Output as rich table
"""
JSON = "json"
TABLE = "table"
def create_formats_help():
formats_table = rich.table.Table(
"Format Name",
"Description",
title="Output Formats",
)
for k, v in (
("json", "Output JSON to console"),
("table", "Output a table to the console"),
):
formats_table.add_row(f"[green]{k}[/green]", v)
with Console() as c:
with c.capture() as capture:
c.print(formats_table)
output = capture.get()
return output
def iterate_constants():
all_caps = re.compile(r"^[A-Z_]+$")
for name in dir(gdalconst):
if name.startswith("_") or (not all_caps.match(name)):
continue
value = getattr(gdalconst, name)
if isinstance(value, str | int):
yield name, value
@_APP.command(epilog=create_formats_help())
def list_constants(
output_format: Annotated[
OutputFormat, typer.Argument(case_sensitive=False)
] = OutputFormat.JSON,
paging: Annotated[bool, typer.Option()] = False,
):
"""Lists constants in `osgeo.gdalconst`"""
constants = [*[*iterate_constants()]]
def get_key(t: tuple[str, str | int]):
"""For groupby, return the part of the variable name before the "_"."""
return t[0].split("_")[0]
# Group by fist part of name, before "_"
grouped = groupby(constants, get_key)
# Convert `grouped` into a dict
output_dict: dict[str, dict[str, str | int]] = {}
for group_name, group in grouped:
inner_dict: dict[str, str | int] = {}
for key, value in group:
inner_dict[key] = value
output_dict[group_name] = inner_dict
if output_format is OutputFormat.TABLE:
c = Console()
table = rich.table.Table("Name", "Value", title="GDAL Constants")
for group_name, group in output_dict.items():
for prop_name, prop_value in group.items():
table.add_row(
prop_name,
repr(prop_value),
)
table.add_section()
with c:
if paging:
with c.pager(styles=True):
c.print(table)
else:
c.print(table)
return
# json.dump(output_dict, stdout)
json_str = json.dumps(output_dict)
with rich.console.Console() as c:
if paging:
with c.pager(styles=True):
c.print_json(json_str)
else:
c.print_json(json_str)
if __name__ == "__main__":
_APP()
from collections.abc import Generator, Iterable
import enum
from pathlib import Path
from typing import cast
from osgeo import gdal, ogr
def driver_supports_ext(
driver: gdal.Driver, path_or_ext: Path | str, capabilities: Iterable[str] = []
) -> bool:
"""Detects if a GDAL Driver supports the given extension
Parameters:
driver: GDAL driver
path_or_ext:
* str will be assumed to be just an extension
* Paths will use its suffix propety's value
>>> d = gdal.GetDriverByName("OpenFileGDB")
>>> bool(d)
True
>>> print(d.GetMetadataItem(gdal.DMD_EXTENSIONS))
gdb
>>> driver_supports_ext(d, "gdb")
True
>>> driver_supports_ext(d, "bad_ext")
False
>>> driver_supports_ext(d, Path("file.other.gdb"))
True
>>> driver_supports_ext(d, "other.gdb")
False
"""
# Each item is a space-separated list of extensions
supported_exts = driver.GetMetadataItem(gdal.DMD_EXTENSIONS)
supported_exts = cast(str, supported_exts)
if not supported_exts:
return False
# Test for capabilities if provided.
if capabilities:
for c in capabilities:
if not driver.TestCapability(c):
return False
# Get the path's suffix if it's a Path. Otherwise use as-is.
suffix = path_or_ext.suffix[1:] if isinstance(path_or_ext, Path) else path_or_ext
suffix = suffix.casefold()
supported_exts = supported_exts.casefold().split(" ")
for x in supported_exts:
if x == suffix:
return True
return False
def test_capabilities(
driver: gdal.Driver,
capabilities: list[str] | None = None,
metadata_names: list[str] | None = None,
raise_if_no_tests: bool = False,
):
"""
Tests a driver for capabilities a/o presence of metadata.
Parameters:
driver: GDAL driver
capabilities: Capabilities to test for using `Driver.TestCapability`
metadata_names: Metadata items to test for existence on the driver.
raise_if_no_tests:
If user provided nothing to test, neither metadata nor
capabilities, this value determines whether to raise an exception
or simply return `True`.
Returns:
`True` if all test conditions are met, `False` otherwise.
"""
if not capabilities or not metadata_names:
if raise_if_no_tests:
raise TypeError(
"You must provide one or more of either `capabilities` a/o `metadata_names`. You cannot have both be `None`."
)
else:
return True
if capabilities:
for c in capabilities:
if not driver.TestCapability(c):
return False
if metadata_names:
for m in metadata_names:
if not driver.GetMetadataItem(m):
return False
return True
def get_drivers(
capabilities: list[str] | None = None, metadata_names: list[str] | None = None
) -> Generator[gdal.Driver]:
"""Iterates through GDAL's drivers
>>> drivers = [*get_drivers()]
>>> len(drivers) > 1
True
"""
for i in range(gdal.GetDriverCount()):
driver = gdal.GetDriver(i)
driver = cast(gdal.Driver, driver)
if test_capabilities(
driver, capabilities=capabilities, metadata_names=metadata_names
):
yield driver
def get_drivers_supporting_ext(path_or_ext: Path | str) -> Generator[gdal.Driver]:
"""Iterates all of the drivers that supports the given path or extension
>>> drivers = [*get_drivers_supporting_ext("gdb")]
>>> len(drivers) > 1
True
>>> drivers = [
... *get_drivers_supporting_ext(
... Path("file.gdb")
... )
... ]
>>> len(drivers) > 1
True
"""
drivers = get_drivers()
for driver in drivers:
if driver_supports_ext(driver, path_or_ext):
yield driver
def create_driver_enum():
"""Dynamically creates an Enum of the supported driver names.
We need to do this because we don't know the supported drivers at runtime.
"""
_writeable_vector_drivers = [*get_drivers(
capabilities=[ogr.ODrCCreateDataSource], metadata_names=[gdal.DCAP_VECTOR]
)]
names_and_descriptions = [(d.GetName(), d.GetDescription()) for d in _writeable_vector_drivers]
names_and_descriptions = cast(list[tuple[str, str]], names_and_descriptions)
names_and_descriptions.sort(key=lambda item: item[0])
names_and_descriptions = dict(names_and_descriptions)
WriteableVectorDriverEnum = enum.Enum("DriverEnum", names_and_descriptions)
return WriteableVectorDriverEnum
WriteableVectorDriverEnum = create_driver_enum()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment