summit/backend/venv/lib/python3.12/site-packages/rasterio/__init__.py

459 lines
16 KiB
Python

"""Rasterio"""
from collections import namedtuple
from contextlib import ExitStack
import glob
import logging
from logging import NullHandler
import os
import platform
# On Windows we must explicitly register the directories that contain
# the GDAL and supporting DLLs starting with Python 3.8. Presently, we
# support the rasterio-wheels location or directories on the system's
# executable path.
if platform.system() == "Windows":
_whl_dir = os.path.join(os.path.dirname(__file__), ".libs")
if os.path.exists(_whl_dir):
os.add_dll_directory(_whl_dir)
else:
if "PATH" in os.environ:
for p in os.environ["PATH"].split(os.pathsep):
if p and glob.glob(os.path.join(p, "gdal*.dll")):
os.add_dll_directory(os.path.abspath(p))
from rasterio._base import DatasetBase
from rasterio._io import Statistics
from rasterio._vsiopener import _opener_registration
from rasterio._show_versions import show_versions
from rasterio._version import gdal_version, get_geos_version, get_proj_version
from rasterio.crs import CRS
from rasterio.drivers import driver_from_extension, is_blacklisted
from rasterio.dtypes import (
bool_,
ubyte,
sbyte,
uint8,
int8,
uint16,
int16,
uint32,
int32,
int64,
uint64,
float32,
float64,
complex_,
check_dtype,
complex_int16,
)
from rasterio.env import ensure_env_with_credentials, Env
from rasterio.errors import (
RasterioIOError,
DriverCapabilityError,
RasterioDeprecationWarning,
)
from rasterio.io import (
DatasetReader,
get_writer_for_path,
get_writer_for_driver,
MemoryFile,
)
from rasterio.profiles import default_gtiff_profile
from rasterio.transform import Affine, guard_transform
from rasterio._path import _parse_path, _UnparsedPath
# These modules are imported from the Cython extensions, but are also import
# here to help tools like cx_Freeze find them automatically
import rasterio._err
import rasterio.coords
import rasterio.enums
import rasterio._path
try:
from rasterio.io import FilePath
have_vsi_plugin = True
except ImportError:
class FilePath:
pass
have_vsi_plugin = False
__all__ = ['band', 'open', 'pad', 'Band', 'Env', 'CRS']
__version__ = "1.4.3"
__gdal_version__ = gdal_version()
__proj_version__ = ".".join([str(version) for version in get_proj_version()])
__geos_version__ = ".".join([str(version) for version in get_geos_version()])
# Rasterio attaches NullHandler to the 'rasterio' logger and its
# descendents. See
# https://docs.python.org/2/howto/logging.html#configuring-logging-for-a-library
# Applications must attach their own handlers in order to see messages.
# See rasterio/rio/main.py for an example.
log = logging.getLogger(__name__)
log.addHandler(NullHandler())
@ensure_env_with_credentials
def open(
fp,
mode="r",
driver=None,
width=None,
height=None,
count=None,
crs=None,
transform=None,
dtype=None,
nodata=None,
sharing=False,
opener=None,
**kwargs
):
"""Open a dataset for reading or writing.
The dataset may be located in a local file, in a resource located by
a URL, or contained within a stream of bytes. This function accepts
different types of fp parameters. However, it is almost always best
to pass a string that has a dataset name as its value. These are
passed directly to GDAL protocol and format handlers. A path to
a zipfile is more efficiently used by GDAL than a Python ZipFile
object, for example.
In read ('r') or read/write ('r+') mode, no keyword arguments are
required: these attributes are supplied by the opened dataset.
In write ('w' or 'w+') mode, the driver, width, height, count, and
dtype keywords are strictly required.
Parameters
----------
fp : str, os.PathLike, file-like, or rasterio.io.MemoryFile
A filename or URL, a file object opened in binary ('rb') mode,
a Path object, or one of the rasterio classes that provides the
dataset-opening interface (has an open method that returns
a dataset). Use a string when possible: GDAL can more
efficiently access a dataset if it opens it natively.
mode : str, optional
'r' (read, the default), 'r+' (read/write), 'w' (write), or
'w+' (write/read).
driver : str, optional
A short format driver name (e.g. "GTiff" or "JPEG") or a list of
such names (see GDAL docs at
https://gdal.org/drivers/raster/index.html). In 'w' or 'w+' modes
a single name is required. In 'r' or 'r+' modes the driver can
usually be omitted. Registered drivers will be tried
sequentially until a match is found. When multiple drivers are
available for a format such as JPEG2000, one of them can be
selected by using this keyword argument.
width : int, optional
The number of columns of the raster dataset. Required in 'w' or
'w+' modes, it is ignored in 'r' or 'r+' modes.
height : int, optional
The number of rows of the raster dataset. Required in 'w' or
'w+' modes, it is ignored in 'r' or 'r+' modes.
count : int, optional
The count of dataset bands. Required in 'w' or 'w+' modes, it is
ignored in 'r' or 'r+' modes.
crs : str, dict, or CRS, optional
The coordinate reference system. Required in 'w' or 'w+' modes,
it is ignored in 'r' or 'r+' modes.
transform : affine.Affine, optional
Affine transformation mapping the pixel space to geographic
space. Required in 'w' or 'w+' modes, it is ignored in 'r' or
'r+' modes.
dtype : str or numpy.dtype, optional
The data type for bands. For example: 'uint8' or
`rasterio.uint16`. Required in 'w' or 'w+' modes, it is
ignored in 'r' or 'r+' modes.
nodata : int, float, or nan, optional
Defines the pixel value to be interpreted as not valid data.
Required in 'w' or 'w+' modes, it is ignored in 'r' or 'r+'
modes.
sharing : bool, optional
To reduce overhead and prevent programs from running out of file
descriptors, rasterio maintains a pool of shared low level
dataset handles. If True this function will use a shared
handle if one is available. Multithreaded programs must avoid
sharing and should set *sharing* to `False`.
opener : callable, optional
A custom dataset opener which can serve GDAL's virtual
filesystem machinery via Python file-like objects. The
underlying file-like object is obtained by calling *opener* with
(*fp*, *mode*) or (*fp*, *mode* + "b") depending on the format
driver's native mode. *opener* must return a Python file-like
object that provides read, seek, tell, and close methods. Note:
only one opener at a time per fp, mode pair is allowed.
kwargs : optional
These are passed to format drivers as directives for creating or
interpreting datasets. For example: in 'w' or 'w+' modes
a `tiled=True` keyword argument will direct the GeoTIFF format
driver to create a tiled, rather than striped, TIFF.
Returns
-------
:class:`rasterio.io.DatasetReader`
If `mode` is "r".
:class:`rasterio.io.DatasetWriter`
If `mode` is "r+", "w", or "w+".
Raises
------
:class:`TypeError`
If arguments are of the wrong Python type.
:class:`rasterio.errors.RasterioIOError`
If the dataset can not be opened. Such as when there is no
dataset with the given name.
:class:`rasterio.errors.DriverCapabilityError`
If the detected format driver does not support the requested
opening mode.
Examples
--------
To open a local GeoTIFF dataset for reading using standard driver
discovery and no directives:
>>> import rasterio
>>> with rasterio.open('example.tif') as dataset:
... print(dataset.profile)
To open a local JPEG2000 dataset using only the JP2OpenJPEG driver:
>>> with rasterio.open(
... 'example.jp2', driver='JP2OpenJPEG') as dataset:
... print(dataset.profile)
To create a new 8-band, 16-bit unsigned, tiled, and LZW-compressed
GeoTIFF with a global extent and 0.5 degree resolution:
>>> from rasterio.transform import from_origin
>>> with rasterio.open(
... 'example.tif', 'w', driver='GTiff', dtype='uint16',
... width=720, height=360, count=8, crs='EPSG:4326',
... transform=from_origin(-180.0, 90.0, 0.5, 0.5),
... nodata=0, tiled=True, compress='lzw') as dataset:
... dataset.write(...)
"""
if not isinstance(fp, str):
if not (
hasattr(fp, "open")
or hasattr(fp, "read")
or hasattr(fp, "write")
or isinstance(fp, (os.PathLike, MemoryFile, FilePath))
) or isinstance(fp, DatasetBase):
raise TypeError(f"invalid path or file: {fp!r}")
if not isinstance(mode, str):
raise TypeError(f"invalid mode: {mode!r}")
elif mode[0] not in ("r", "w"):
raise ValueError(f"invalid mode: {mode!r}")
if driver and not isinstance(driver, str):
raise TypeError(f"invalid driver: {driver!r}")
if dtype and not check_dtype(dtype):
raise TypeError(f"invalid dtype: {dtype!r}")
if nodata is not None:
nodata = float(nodata)
if transform:
transform = guard_transform(transform)
# Check driver/mode blacklist.
if driver and is_blacklisted(driver, mode):
raise RasterioIOError(
"Blacklisted: file cannot be opened by "
"driver '{}' in '{}' mode".format(driver, mode)
)
# This check should come first, since MemoryFile has read/write methods
# TODO: test for a shared base class or abstract type.
elif isinstance(fp, (FilePath, MemoryFile)):
if mode.startswith("r"):
dataset = fp.open(driver=driver, sharing=sharing, **kwargs)
# Note: FilePath does not support writing and an exception will
# result from this.
else:
dataset = fp.open(
driver=driver,
width=width,
height=height,
count=count,
crs=crs,
transform=transform,
dtype=dtype,
nodata=nodata,
sharing=sharing,
**kwargs
)
return dataset
# If the fp argument is a file-like object and can be adapted by
# rasterio's FilePath we do so. Otherwise, we use a MemoryFile to
# hold fp's contents and store that in an ExitStack attached to the
# dataset object that we will return. When a dataset's close method
# is called, this ExitStack will be unwound and the MemoryFile's
# storage will be cleaned up.
elif mode == 'r' and hasattr(fp, 'read'):
memfile = MemoryFile(fp.read())
fp.seek(0)
dataset = memfile.open(driver=driver, sharing=sharing, **kwargs)
dataset._env.enter_context(memfile)
return dataset
elif mode in ('w', 'w+') and hasattr(fp, 'write'):
memfile = MemoryFile()
dataset = memfile.open(
driver=driver,
width=width,
height=height,
count=count,
crs=crs,
transform=transform,
dtype=dtype,
nodata=nodata,
sharing=sharing,
**kwargs
)
dataset._env.enter_context(memfile)
# For the writing case we push an extra callback onto the
# ExitStack. It ensures that the MemoryFile's contents are
# copied to the open file object.
def func(*args, **kwds):
memfile.seek(0)
fp.write(memfile.read())
dataset._env.callback(func)
return dataset
# At this point, the fp argument is a string or path-like object
# which can be converted to a string.
else:
stack = ExitStack()
if hasattr(fp, "path") and hasattr(fp, "fs"):
log.debug("Detected fp is an OpenFile: fp=%r", fp)
raw_dataset_path = fp.path
opener = fp.fs.open
else:
raw_dataset_path = os.fspath(fp)
try:
# when opener is a callable that takes a filename or URL and returns
# a file-like object with read, seek, tell, and close methods, we
# can register it with GDAL and use it as the basis for a GDAL
# virtual filesystem. This is generally better than the FilePath
# approach introduced in version 1.3.
if opener:
vsi_path_ctx = _opener_registration(raw_dataset_path, opener)
registered_vsi_path = stack.enter_context(vsi_path_ctx)
path = _UnparsedPath(registered_vsi_path)
else:
path = _parse_path(raw_dataset_path)
if mode == "r":
dataset = DatasetReader(path, driver=driver, sharing=sharing, **kwargs)
elif mode == "r+":
dataset = get_writer_for_path(path, driver=driver)(
path, mode, driver=driver, sharing=sharing, **kwargs
)
elif mode.startswith("w"):
if not driver:
driver = driver_from_extension(path)
writer = get_writer_for_driver(driver)
if writer is not None:
dataset = writer(
path,
mode,
driver=driver,
width=width,
height=height,
count=count,
crs=crs,
transform=transform,
dtype=dtype,
nodata=nodata,
sharing=sharing,
**kwargs
)
else:
raise DriverCapabilityError(
"Writer does not exist for driver: %s" % str(driver)
)
else:
raise DriverCapabilityError(
"mode must be one of 'r', 'r+', or 'w', not %s" % mode)
except Exception:
stack.close()
raise
dataset._env = stack
return dataset
Band = namedtuple('Band', ['ds', 'bidx', 'dtype', 'shape'])
Band.__doc__ = """
Band(s) of a Dataset.
Parameters
----------
ds: dataset object
An opened rasterio dataset object.
bidx: int or sequence of ints
Band number(s), index starting at 1.
dtype: str
rasterio data type of the data.
shape: tuple
Width, height of band.
"""
def band(ds, bidx):
"""A dataset and one or more of its bands
Parameters
----------
ds: dataset object
An opened rasterio dataset object.
bidx: int or sequence of ints
Band number(s), index starting at 1.
Returns
-------
Band
"""
return Band(ds, bidx, set(ds.dtypes).pop(), ds.shape)
def pad(array, transform, pad_width, mode=None, **kwargs):
"""pad array and adjust affine transform matrix.
Parameters
----------
array: numpy.ndarray
Numpy ndarray, for best results a 2D array
transform: Affine transform
transform object mapping pixel space to coordinates
pad_width: int
number of pixels to pad array on all four
mode: str or function
define the method for determining padded values
Returns
-------
(array, transform): tuple
Tuple of new array and affine transform
Notes
-----
See :func:`numpy.pad` for details on mode and other kwargs.
"""
import numpy as np
transform = guard_transform(transform)
padded_array = np.pad(array, pad_width, mode, **kwargs)
padded_trans = list(transform)
padded_trans[2] -= pad_width * padded_trans[0]
padded_trans[5] -= pad_width * padded_trans[4]
return padded_array, Affine(*padded_trans[:6])