Skip to content
Commits on Source (4)
Changes
=======
1.0.4 (2018-09-17)
------------------
Bug fixes:
- Boundless reads of datasets without a coordinate reference system have been
fixed (#1448).
- A y-directional error in disjoint_bounds (#1459) has been fixed.
- Prevent geometries from being thrown near projection singularities (#1446).
- Missing --aws-no-sign-requests and --aws-requester-pays options added to
the main rio command (#1460).
- Add missing bilinear, cubic spline, lanczos resampling modes for overviews
(#1457).
- Raise ValueError if get_writer_for_driver() is called without a driver
name.
- Windows are now frozen so that they are hashable (#1452).
Refactoring:
- Use of InMemoryRaster eliminates redundant code in the _warp module (#1427,
#816).
- Generalize sessions to support cloud providers other than AWS (#1429).
1.0.3.post1 (2018-09-07)
------------------------
This version corrects errors made in building binary wheels for 1.0.3. There
are no bug fixes or new features in this version.
1.0.3 (2018-08-01)
------------------
......
rasterio (1.0.3-2) UNRELEASED; urgency=medium
rasterio (1.0.4-1) unstable; urgency=medium
* Team upload.
* New upstream release.
* Bump Standards-Version to 4.2.1, no changes.
* Drop autopkgtests to test installability & module import.
* Add lintian override for testsuite-autopkgtest-missing.
* Update watch file to limit matches to archive path.
-- Bas Couwenberg <sebastic@debian.org> Sun, 05 Aug 2018 20:56:12 +0200
-- Bas Couwenberg <sebastic@debian.org> Mon, 17 Sep 2018 18:01:27 +0200
rasterio (1.0.3-1) unstable; urgency=medium
......
......@@ -43,7 +43,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
__version__ = "1.0.3"
__version__ = "1.0.4"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
......
......@@ -65,6 +65,12 @@ class _CRS(UserDict):
cdef int retval
try:
if (
isinstance(other, self.__class__) and
self.data == other.data
):
return True
if not self or not other:
return not self and not other
......
......@@ -29,7 +29,7 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
cdef class InMemoryRaster:
cdef GDALDatasetH _hds
cdef double gdal_transform[6]
cdef int band_ids[1]
cdef int* band_ids
cdef np.ndarray _image
cdef object crs
cdef object transform # this is an Affine object.
......
......@@ -1540,7 +1540,10 @@ cdef class DatasetWriterBase(DatasetReaderBase):
# (no corresponding member in the warp enum).
resampling_map = {
0: 'NEAREST',
1: 'BILINEAR',
2: 'CUBIC',
3: 'CUBICSPLINE',
4: 'LANCZOS',
5: 'AVERAGE',
6: 'MODE',
7: 'GAUSS'}
......@@ -1619,7 +1622,6 @@ cdef class InMemoryRaster:
This class is only intended for internal use within rasterio to support
IO with GDAL. Other memory based operations should use numpy arrays.
"""
def __cinit__(self, image=None, dtype='uint8', count=1, width=None,
height=None, transform=None, gcps=None, crs=None):
"""
......@@ -1634,9 +1636,6 @@ cdef class InMemoryRaster:
(see rasterio.dtypes.dtype_rev)
:param transform: Affine transform object
"""
self._image = image
cdef int i = 0 # avoids Cython warning in for loop below
cdef const char *srcwkt = NULL
cdef OGRSpatialReferenceH osr = NULL
......@@ -1644,9 +1643,9 @@ cdef class InMemoryRaster:
cdef GDAL_GCP *gcplist = NULL
if image is not None:
if len(image.shape) == 3:
if image.ndim == 3:
count, height, width = image.shape
elif len(image.shape) == 2:
elif image.ndim == 2:
count = 1
height, width = image.shape
dtype = image.dtype.name
......@@ -1657,9 +1656,18 @@ cdef class InMemoryRaster:
if width is None or width == 0:
raise ValueError("width must be > 0")
self.band_ids[0] = 1
self.band_ids = <int *>CPLMalloc(count*sizeof(int))
for i in range(1, count + 1):
self.band_ids[i-1] = i
try:
memdriver = exc_wrap_pointer(GDALGetDriverByName("MEM"))
except Exception:
raise DriverRegistrationError(
"'MEM' driver not found. Check that this call is contained "
"in a `with rasterio.Env()` or `with rasterio.open()` "
"block.")
datasetname = str(uuid.uuid4()).encode('utf-8')
self._hds = exc_wrap_pointer(
GDALCreate(memdriver, <const char *>datasetname, width, height,
......@@ -1670,19 +1678,19 @@ cdef class InMemoryRaster:
gdal_transform = transform.to_gdal()
for i in range(6):
self.gdal_transform[i] = gdal_transform[i]
err = GDALSetGeoTransform(self._hds, self.gdal_transform)
if err:
raise ValueError("transform not set: %s" % transform)
exc_wrap_int(GDALSetGeoTransform(self._hds, self.gdal_transform))
if crs:
osr = _osr_from_crs(crs)
OSRExportToWkt(osr, <char**>&srcwkt)
GDALSetProjection(self._hds, srcwkt)
log.debug("Set CRS on temp source dataset: %s", srcwkt)
CPLFree(<void *>srcwkt)
try:
OSRExportToWkt(osr, &srcwkt)
exc_wrap_int(GDALSetProjection(self._hds, srcwkt))
log.debug("Set CRS on temp dataset: %s", srcwkt)
finally:
CPLFree(srcwkt)
_safe_osr_release(osr)
elif gcps and crs:
try:
gcplist = <GDAL_GCP *>CPLMalloc(len(gcps) * sizeof(GDAL_GCP))
for i, obj in enumerate(gcps):
ident = str(i).encode('utf-8')
......@@ -1696,13 +1704,16 @@ cdef class InMemoryRaster:
gcplist[i].dfGCPZ = obj.z or 0.0
osr = _osr_from_crs(crs)
OSRExportToWkt(osr, <char**>&srcwkt)
GDALSetGCPs(self._hds, len(gcps), gcplist, srcwkt)
OSRExportToWkt(osr, &srcwkt)
exc_wrap_int(GDALSetGCPs(self._hds, len(gcps), gcplist, srcwkt))
finally:
CPLFree(gcplist)
CPLFree(<void *>srcwkt)
CPLFree(srcwkt)
_safe_osr_release(osr)
if self._image is not None:
self.write(self._image)
self._image = None
if image is not None:
self.write(image)
def __enter__(self):
return self
......@@ -1710,6 +1721,9 @@ cdef class InMemoryRaster:
def __exit__(self, *args, **kwargs):
self.close()
def __dealloc__(self):
CPLFree(self.band_ids)
cdef GDALDatasetH handle(self) except NULL:
"""Return the object's GDAL dataset handle"""
return self._hds
......@@ -1735,11 +1749,22 @@ cdef class InMemoryRaster:
self._hds = NULL
def read(self):
io_auto(self._image, self.band(1), False)
if self._image is None:
raise IOError("You need to write data before you can read the data.")
if self._image.ndim == 2:
exc_wrap_int(io_auto(self._image, self.band(1), False))
else:
exc_wrap_int(io_auto(self._image, self._hds, False))
return self._image
def write(self, image):
io_auto(image, self.band(1), True)
def write(self, np.ndarray image):
self._image = image
if image.ndim == 2:
exc_wrap_int(io_auto(self._image, self.band(1), True))
else:
exc_wrap_int(io_auto(self._image, self._hds, True))
cdef class BufferedDatasetWriterBase(DatasetWriterBase):
......
......@@ -17,6 +17,7 @@ from rasterio._err import (
from rasterio import dtypes
from rasterio.control import GroundControlPoint
from rasterio.enums import Resampling, MaskFlags, ColorInterp
from rasterio.env import GDALVersion
from rasterio.crs import CRS
from rasterio.errors import (
DriverRegistrationError, CRSError, RasterioIOError,
......@@ -67,11 +68,15 @@ def _transform_geom(
_safe_osr_release(dst)
raise
# Transform options.
if GDALVersion().runtime() < GDALVersion.parse('2.2'):
valb = str(antimeridian_offset).encode('utf-8')
options = CSLSetNameValue(options, "DATELINEOFFSET", <const char *>valb)
if antimeridian_cutting:
options = CSLSetNameValue(options, "WRAPDATELINE", "YES")
else:
# GDAL cuts on the antimeridian by default and using different
# logic in versions >= 2.2.
pass
try:
factory = new OGRGeometryFactory()
......@@ -286,48 +291,17 @@ def _reproject(
out: None
Output is written to destination.
"""
cdef int retval
cdef int rows
cdef int cols
cdef int src_count
cdef GDALDriverH driver = NULL
cdef GDALDatasetH src_dataset = NULL
cdef GDALDatasetH dst_dataset = NULL
cdef GDALAccess GA
cdef double gt[6]
cdef char *srcwkt = NULL
cdef char *dstwkt= NULL
cdef OGRSpatialReferenceH src_osr = NULL
cdef OGRSpatialReferenceH dst_osr = NULL
cdef char **warp_extras = NULL
cdef const char* pszWarpThread = NULL
cdef int i
cdef double tolerance = 0.125
cdef GDAL_GCP *gcplist = NULL
cdef void *hTransformArg = NULL
cdef GDALTransformerFunc pfnTransformer = NULL
cdef GDALWarpOptions *psWOptions = NULL
# If working with identity transform, assume it is crs-less data
# and that translating the matrix very slightly will avoid #674
eps = 1e-100
if src_transform:
src_transform = guard_transform(src_transform)
# if src_transform is like `identity` with positive or negative `e`,
# translate matrix very slightly to avoid #674 and #1272.
if src_transform.almost_equals(identity) or src_transform.almost_equals(Affine(1, 0, 0, 0, -1, 0)):
src_transform = src_transform.translation(eps, eps)
src_transform = src_transform.to_gdal()
if dst_transform:
dst_transform = guard_transform(dst_transform)
if dst_transform.almost_equals(identity) or dst_transform.almost_equals(Affine(1, 0, 0, 0, -1, 0)):
dst_transform = dst_transform.translation(eps, eps)
dst_transform = dst_transform.to_gdal()
# Validate nodata values immediately.
if src_nodata is not None:
if not in_dtype_range(src_nodata, source.dtype):
......@@ -339,90 +313,42 @@ def _reproject(
raise ValueError("dst_nodata must be in valid range for "
"destination dtype")
def format_transform(in_transform):
if not in_transform:
return in_transform
in_transform = guard_transform(in_transform)
# If working with identity transform, assume it is crs-less data
# and that translating the matrix very slightly will avoid #674 and #1272
eps = 1e-100
if in_transform.almost_equals(identity) or in_transform.almost_equals(Affine(1, 0, 0, 0, -1, 0)):
in_transform = in_transform.translation(eps, eps)
return in_transform
# If the source is an ndarray, we copy to a MEM dataset.
# We need a src_transform and src_dst in this case. These will
# be copied to the MEM dataset.
if dtypes.is_ndarray(source):
if not src_crs:
raise CRSError("Missing src_crs.")
if src_nodata is None and hasattr(source, 'fill_value'):
# source is a masked array
src_nodata = source.fill_value
# Convert 2D single-band arrays to 3D multi-band.
if len(source.shape) == 2:
source = source.reshape(1, *source.shape)
src_count = source.shape[0]
src_bidx = range(1, src_count + 1)
rows = source.shape[1]
cols = source.shape[2]
dtype = np.dtype(source.dtype).name
if src_nodata is None and hasattr(source, 'fill_value'):
# source is a masked array
src_nodata = source.fill_value
try:
driver = exc_wrap_pointer(GDALGetDriverByName("MEM"))
except:
raise DriverRegistrationError(
"'MEM' driver not found. Check that this call is contained "
"in a `with rasterio.Env()` or `with rasterio.open()` "
"block.")
datasetname = str(uuid.uuid4()).encode('utf-8')
src_dataset = exc_wrap_pointer(
GDALCreate(driver, <const char *>datasetname, cols, rows,
src_count, dtypes.dtype_rev[dtype], NULL))
GDALSetDescription(
src_dataset, "Temporary source dataset for _reproject()")
log.debug("Created temp source dataset")
try:
src_osr = _osr_from_crs(src_crs)
OSRExportToWkt(src_osr, &srcwkt)
if src_transform:
for i in range(6):
gt[i] = src_transform[i]
exc_wrap_int(GDALSetGeoTransform(src_dataset, gt))
exc_wrap_int(GDALSetProjection(src_dataset, srcwkt))
log.debug("Set CRS on temp source dataset: %s", srcwkt)
elif gcps:
gcplist = <GDAL_GCP *>CPLMalloc(len(gcps) * sizeof(GDAL_GCP))
try:
for i, obj in enumerate(gcps):
ident = str(i).encode('utf-8')
info = "".encode('utf-8')
gcplist[i].pszId = ident
gcplist[i].pszInfo = info
gcplist[i].dfGCPPixel = obj.col
gcplist[i].dfGCPLine = obj.row
gcplist[i].dfGCPX = obj.x
gcplist[i].dfGCPY = obj.y
gcplist[i].dfGCPZ = obj.z or 0.0
exc_wrap_int(GDALSetGCPs(src_dataset, len(gcps), gcplist, srcwkt))
finally:
CPLFree(gcplist)
finally:
CPLFree(srcwkt)
_safe_osr_release(src_osr)
# Copy arrays to the dataset.
exc_wrap_int(io_auto(source, src_dataset, 1))
log.debug("Wrote array to temp source dataset")
src_dataset = InMemoryRaster(image=source,
transform=format_transform(src_transform),
gcps=gcps,
crs=src_crs).handle()
# If the source is a rasterio MultiBand, no copy necessary.
# A MultiBand is a tuple: (dataset, bidx, dtype, shape(2d)).
# A MultiBand is a tuple: (dataset, bidx, dtype, shape(2d))
elif isinstance(source, tuple):
rdr, src_bidx, dtype, shape = source
if isinstance(src_bidx, int):
src_bidx = [src_bidx]
src_count = len(src_bidx)
rows, cols = shape
src_dataset = (<DatasetReaderBase?>rdr).handle()
if src_nodata is None:
src_nodata = rdr.nodata
......@@ -431,6 +357,8 @@ def _reproject(
# Next, do the same for the destination raster.
if dtypes.is_ndarray(destination):
if not dst_crs:
raise CRSError("Missing dst_crs.")
if len(destination.shape) == 2:
destination = destination.reshape(1, *destination.shape)
......@@ -443,21 +371,9 @@ def _reproject(
raise ValueError("Invalid destination shape")
dst_bidx = src_bidx
try:
driver = exc_wrap_pointer(GDALGetDriverByName("MEM"))
except:
raise DriverRegistrationError(
"'MEM' driver not found. Check that this call is contained "
"in a `with rasterio.Env()` or `with rasterio.open()` "
"block.")
count, rows, cols = destination.shape
datasetname = str(uuid.uuid4()).encode('utf-8')
dst_dataset = exc_wrap_pointer(
GDALCreate(driver, <const char *>datasetname, cols, rows, count,
dtypes.dtype_rev[np.dtype(destination.dtype).name], NULL))
dst_dataset = InMemoryRaster(image=destination,
transform=format_transform(dst_transform),
crs=dst_crs).handle()
if dst_alpha:
for i in range(destination.shape[0]):
try:
......@@ -472,26 +388,6 @@ def _reproject(
log.debug("Created temp destination dataset.")
for i in range(6):
gt[i] = dst_transform[i]
exc_wrap_int(GDALSetGeoTransform(dst_dataset, gt))
try:
dst_osr = _osr_from_crs(dst_crs)
OSRExportToWkt(dst_osr, &dstwkt)
log.debug("CRS for temp destination dataset: %s.", dstwkt)
exc_wrap_int(GDALSetProjection(dst_dataset, dstwkt))
finally:
CPLFree(dstwkt)
_safe_osr_release(dst_osr)
exc_wrap_int(io_auto(destination, dst_dataset, 1))
log.debug("Wrote array to temp output dataset")
if dst_nodata is None:
if hasattr(destination, "fill_value"):
# destination is a masked array
......@@ -503,7 +399,6 @@ def _reproject(
udr, dst_bidx, _, _ = destination
if isinstance(dst_bidx, int):
dst_bidx = [dst_bidx]
udr = destination.ds
dst_dataset = (<DatasetReaderBase?>udr).handle()
if dst_nodata is None:
dst_nodata = udr.nodata
......@@ -581,6 +476,8 @@ def _reproject(
# Now that the transformer and warp options are set up, we init
# and run the warper.
cdef GDALWarpOperation oWarper
cdef int rows
cdef int cols
try:
exc_wrap_int(oWarper.Initialize(psWOptions))
rows, cols = destination.shape[-2:]
......
......@@ -52,7 +52,7 @@ def disjoint_bounds(bounds1, bounds2):
if bounds1_north_up:
return (bounds1[0] > bounds2[2] or bounds2[0] > bounds1[2] or
bounds1[1] > bounds2[3] or bounds2[1] > bounds2[3])
bounds1[1] > bounds2[3] or bounds2[1] > bounds1[3])
else:
return (bounds1[0] > bounds2[2] or bounds2[0] > bounds1[2] or
bounds1[3] > bounds2[1] or bounds2[3] > bounds2[1])
bounds1[3] > bounds2[1] or bounds2[3] > bounds1[1])
"""Rasterio's GDAL/AWS environment"""
import attr
from functools import wraps, total_ordering
import logging
import threading
import re
import attr
import threading
import warnings
import rasterio
from rasterio._env import (
GDALEnv, del_gdal_config, get_gdal_config, set_gdal_config)
from rasterio.compat import string_types, getargspec
from rasterio.dtypes import check_dtype
from rasterio.errors import EnvError, GDALVersionError
from rasterio.path import parse_path
from rasterio.errors import (
EnvError, GDALVersionError, RasterioDeprecationWarning)
from rasterio.path import parse_path, UnparsedPath, ParsedPath
from rasterio.session import Session, AWSSession
from rasterio.transform import guard_transform
......@@ -106,7 +108,8 @@ class Env(object):
def __init__(
self, session=None, aws_unsigned=False, aws_access_key_id=None,
aws_secret_access_key=None, aws_session_token=None,
region_name=None, profile_name=None, **options):
region_name=None, profile_name=None, session_class=AWSSession,
**options):
"""Create a new GDAL/AWS environment.
Note: this class is a context manager. GDAL isn't configured
......@@ -115,7 +118,7 @@ class Env(object):
Parameters
----------
session : optional
A boto3 session object.
A Session object.
aws_unsigned : bool, optional (default: False)
If True, requests will be unsigned.
aws_access_key_id : str, optional
......@@ -128,6 +131,8 @@ class Env(object):
A region name, as per boto3.
profile_name : str, optional
A shared credentials profile name, as per boto3.
session_class : Session, optional
A sub-class of Session.
**options : optional
A mapping of GDAL configuration options, e.g.,
`CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.
......@@ -142,25 +147,60 @@ class Env(object):
AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY are given. AWS
credentials are handled exclusively by boto3.
Examples
--------
>>> with Env(CPL_DEBUG=True, CPL_CURL_VERBOSE=True):
... with rasterio.open("https://example.com/a.tif") as src:
... print(src.profile)
For access to secured cloud resources, a Rasterio Session or a
foreign session object may be passed to the constructor.
>>> import boto3
>>> from rasterio.session import AWSSession
>>> boto3_session = boto3.Session(...)
>>> with Env(AWSSession(boto3_session)):
... with rasterio.open("s3://mybucket/a.tif") as src:
... print(src.profile)
"""
if ('AWS_ACCESS_KEY_ID' in options or
'AWS_SECRET_ACCESS_KEY' in options):
raise EnvError(
"GDAL's AWS config options can not be directly set. "
"AWS credentials are handled exclusively by boto3.")
self.aws_unsigned = aws_unsigned
self.aws_access_key_id = aws_access_key_id
self.aws_secret_access_key = aws_secret_access_key
self.aws_session_token = aws_session_token
self.region_name = region_name
self.profile_name = profile_name
if session:
# Passing a session via keyword argument is the canonical
# way to configure access to secured cloud resources.
if not isinstance(session, Session):
warnings.warn(
"Passing a boto3 session is deprecated. Pass a Rasterio "
"AWSSession object instead.",
RasterioDeprecationWarning
)
session = AWSSession(session=session)
self.session = session
else:
# Before 1.0, Rasterio only supported AWS. We will special
# case AWS in 1.0.x. TODO: warn deprecation in 1.1.
warnings.warn(
"Passing abstract session keyword arguments is deprecated. "
"Pass a Rasterio AWSSession object instead.",
RasterioDeprecationWarning
)
self.session = AWSSession(
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
aws_session_token=aws_session_token,
region_name=region_name,
profile_name=profile_name,
aws_unsigned=aws_unsigned)
self.options = options.copy()
self.context_options = {}
self._creds = None
@classmethod
def from_defaults(cls, *args, **kwargs):
"""Create an environment with default config options
......@@ -193,7 +233,7 @@ class Env(object):
-------
bool
"""
return bool(self._creds)
return hascreds() # bool(self.session)
def credentialize(self):
"""Get credentials and configure GDAL
......@@ -204,54 +244,21 @@ class Env(object):
Returns
-------
None
"""
if hascreds():
pass
else:
import boto3
if not self.session and not self.aws_access_key_id and not self.profile_name:
self.session = boto3.Session()
elif not self.session:
self.session = boto3.Session(
aws_access_key_id=self.aws_access_key_id,
aws_secret_access_key=self.aws_secret_access_key,
aws_session_token=self.aws_session_token,
region_name=self.region_name,
profile_name=self.profile_name)
else:
# use self.session
pass
self._creds = self.session._session.get_credentials()
# Pass these credentials to the GDAL environment.
cred_opts = {}
if self.aws_unsigned:
cred_opts['AWS_NO_SIGN_REQUEST'] = 'YES'
else:
if self._creds.access_key: # pragma: no branch
cred_opts['AWS_ACCESS_KEY_ID'] = self._creds.access_key
if self._creds.secret_key: # pragma: no branch
cred_opts['AWS_SECRET_ACCESS_KEY'] = self._creds.secret_key
if self._creds.token:
cred_opts['AWS_SESSION_TOKEN'] = self._creds.token
if self.session.region_name:
cred_opts['AWS_REGION'] = self.session.region_name
cred_opts = self.session.get_credential_options()
self.options.update(**cred_opts)
setenv(**cred_opts)
def can_credentialize_on_enter(self):
return bool(self.session or self.aws_access_key_id or self.profile_name)
def drivers(self):
"""Return a mapping of registered drivers."""
return local._env.drivers()
def __enter__(self):
log.debug("Entering env context: %r", self)
# No parent Rasterio environment exists.
if local._env is None:
log.debug("Starting outermost env")
self._has_parent_env = False
......@@ -274,7 +281,6 @@ class Env(object):
self.context_options = getenv()
setenv(**self.options)
if self.can_credentialize_on_enter():
self.credentialize()
log.debug("Entered env context: %r", self)
......@@ -389,17 +395,14 @@ def ensure_env_credentialled(f):
env_ctor = Env
else:
env_ctor = Env.from_defaults
with env_ctor() as wrapper_env:
if isinstance(args[0], str):
path = parse_path(args[0])
scheme = getattr(path, 'scheme', None)
if scheme == 's3':
wrapper_env.credentialize()
log.debug("Credentialized: {!r}".format(getenv()))
else:
pass
session = Session.from_path(args[0])
else:
pass
session = Session.from_path(None)
with env_ctor(session=session):
log.debug("Credentialized: {!r}".format(getenv()))
return f(*args, **kwds)
return wrapper
......
......@@ -175,6 +175,8 @@ class ZipMemoryFile(MemoryFile):
def get_writer_for_driver(driver):
"""Return the writer class appropriate for the specified driver."""
if not driver:
raise ValueError("'driver' is required to write dataset.")
cls = None
if driver_can_create(driver):
cls = DatasetWriter
......
......@@ -11,7 +11,7 @@ import rasterio
@click.option('--formats', 'key', flag_value='formats', default=True,
help="Enumerate the available formats.")
@click.option('--credentials', 'key', flag_value='credentials', default=False,
help="Print AWS credentials.")
help="Print credentials.")
@click.pass_context
def env(ctx, key):
"""Print information about the Rasterio environment."""
......@@ -20,7 +20,4 @@ def env(ctx, key):
for k, v in sorted(env.drivers().items()):
click.echo("{0}: {1}".format(k, v))
elif key == 'credentials':
click.echo(json.dumps({
'aws_access_key_id': env._creds.access_key,
'aws_secret_access_key': env._creds.secret_key,
'aws_session_token': env._creds.token}))
click.echo(json.dumps(env.session.credentials))
......@@ -41,6 +41,7 @@ import cligj
from . import options
import rasterio
from rasterio.session import AWSSession
def configure_logging(verbosity):
......@@ -51,28 +52,50 @@ def configure_logging(verbosity):
def gdal_version_cb(ctx, param, value):
if not value or ctx.resilient_parsing:
return
click.echo("{0}".format(rasterio.__gdal_version__), color=ctx.color)
ctx.exit()
@with_plugins(ep for ep in list(iter_entry_points('rasterio.rio_commands')) +
list(iter_entry_points('rasterio.rio_plugins')))
@with_plugins(
ep
for ep in list(iter_entry_points("rasterio.rio_commands"))
+ list(iter_entry_points("rasterio.rio_plugins"))
)
@click.group()
@cligj.verbose_opt
@cligj.quiet_opt
@click.option('--aws-profile',
help="Selects a profile from your shared AWS credentials file")
@click.version_option(version=rasterio.__version__, message='%(version)s')
@click.option('--gdal-version', is_eager=True, is_flag=True,
callback=gdal_version_cb)
@click.option(
"--aws-profile", help="Select a profile from the AWS credentials file"
)
@click.option("--aws-no-sign-requests", is_flag=True, help="Make requests anonymously")
@click.option(
"--aws-requester-pays", is_flag=True, help="Requester pays data transfer costs"
)
@click.version_option(version=rasterio.__version__, message="%(version)s")
@click.option("--gdal-version", is_eager=True, is_flag=True, callback=gdal_version_cb)
@click.pass_context
def main_group(ctx, verbose, quiet, aws_profile, gdal_version):
def main_group(
ctx,
verbose,
quiet,
aws_profile,
aws_no_sign_requests,
aws_requester_pays,
gdal_version,
):
"""Rasterio command line interface.
"""
verbosity = verbose - quiet
configure_logging(verbosity)
ctx.obj = {}
ctx.obj['verbosity'] = verbosity
ctx.obj['aws_profile'] = aws_profile
ctx.obj['env'] = rasterio.Env(CPL_DEBUG=(verbosity > 2),
profile_name=aws_profile)
ctx.obj["verbosity"] = verbosity
ctx.obj["aws_profile"] = aws_profile
ctx.obj["env"] = rasterio.Env(
session=AWSSession(
profile_name=aws_profile,
aws_unsigned=aws_no_sign_requests,
requester_pays=aws_requester_pays,
),
CPL_DEBUG=(verbosity > 2)
)
......@@ -38,7 +38,7 @@ def build_handler(ctx, param, value):
is_flag=True, default=False)
@click.option('--resampling', help="Resampling algorithm.",
type=click.Choice(
[it.name for it in Resampling if it.value in [0, 2, 5, 6, 7]]),
[it.name for it in Resampling if it.value in [0, 1, 2, 3, 4, 5, 6, 7]]),
default='nearest', show_default=True)
@click.pass_context
def overview(ctx, input, build, ls, rebuild, resampling):
......
"""Abstraction for sessions in various clouds."""
from rasterio.path import parse_path, UnparsedPath, ParsedPath
class Session(object):
"""Base for classes that configure access to secured resources.
Attributes
----------
credentials : dict
Keys and values for session credentials.
Notes
-----
This class is not intended to be instantiated.
"""
def get_credential_options(self):
"""Get credentials as GDAL configuration options
Returns
-------
dict
"""
return NotImplementedError
@staticmethod
def from_foreign_session(session, cls=None):
"""Create a session object matching the foreign `session`.
Parameters
----------
session : obj
A foreign session object.
cls : Session class, optional
The class to return.
Returns
-------
Session
"""
if not cls:
return DummySession()
else:
return cls(session)
@staticmethod
def from_path(path, *args, **kwargs):
"""Create a session object suited to the data at `path`.
Parameters
----------
path : str
A dataset path or identifier.
args : sequence
Positional arguments for the foreign session constructor.
kwargs : dict
Keyword arguments for the foreign session constructor.
Returns
-------
Session
"""
if not path:
return DummySession()
path = parse_path(path)
if isinstance(path, UnparsedPath) or path.is_local:
return DummySession()
elif path.scheme == "s3" or "amazonaws.com" in path.path:
return AWSSession(*args, **kwargs)
# This factory can be extended to other cloud providers here.
# elif path.scheme == "cumulonimbus": # for example.
# return CumulonimbusSession(*args, **kwargs)
else:
return DummySession()
class DummySession(Session):
"""A dummy session.
Attributes
----------
credentials : dict
The session credentials.
"""
def __init__(self, *args, **kwargs):
self._session = None
self.credentials = {}
def get_credential_options(self):
"""Get credentials as GDAL configuration options
Returns
-------
dict
"""
return {}
class AWSSession(Session):
"""Configures access to secured resources stored in AWS S3.
"""
def __init__(
self, session=None, aws_unsigned=False, aws_access_key_id=None,
aws_secret_access_key=None, aws_session_token=None,
region_name=None, profile_name=None, requester_pays=False):
"""Create a new boto3 session
Parameters
----------
session : optional
A boto3 session object.
aws_unsigned : bool, optional (default: False)
If True, requests will be unsigned.
aws_access_key_id : str, optional
An access key id, as per boto3.
aws_secret_access_key : str, optional
A secret access key, as per boto3.
aws_session_token : str, optional
A session token, as per boto3.
region_name : str, optional
A region name, as per boto3.
profile_name : str, optional
A shared credentials profile name, as per boto3.
requester_pays : bool, optional
True if the requester agrees to pay transfer costs (default:
False)
"""
import boto3
if session:
self._session = session
else:
if not aws_access_key_id and not profile_name:
self._session = boto3.Session()
else:
self._session = boto3.Session(
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
aws_session_token=aws_session_token,
region_name=region_name,
profile_name=profile_name)
self.requester_pays = requester_pays
self.unsigned = aws_unsigned
self._creds = self._session._session.get_credentials()
@property
def credentials(self):
"""The session credentials as a dict"""
creds = {}
if self._creds.access_key: # pragma: no branch
creds['aws_access_key_id'] = self._creds.access_key
if self._creds.secret_key: # pragma: no branch
creds['aws_secret_access_key'] = self._creds.secret_key
if self._creds.token:
creds['aws_session_token'] = self._creds.token
if self._session.region_name:
creds['aws_region'] = self._session.region_name
if self.requester_pays:
creds['aws_request_payer'] = 'requester'
return creds
def get_credential_options(self):
"""Get credentials as GDAL configuration options
Returns
-------
dict
"""
if self.unsigned:
return {'AWS_NO_SIGN_REQUEST': 'YES'}
else:
return {k.upper(): v for k, v in self.credentials.items()}
......@@ -81,7 +81,7 @@ def _boundless_vrt_doc(src_dataset, nodata=None, width=None, height=None, transf
vrtdataset.attrib['rasterYSize'] = str(height)
vrtdataset.attrib['rasterXSize'] = str(width)
srs = ET.SubElement(vrtdataset, 'SRS')
srs.text = src_dataset.crs.wkt
srs.text = src_dataset.crs.wkt if src_dataset.crs else ""
geotransform = ET.SubElement(vrtdataset, 'GeoTransform')
geotransform.text = ','.join([str(v) for v in transform.to_gdal()])
......
......@@ -475,7 +475,8 @@ def validate_length_value(instance, attribute, value):
raise ValueError("Number of columns or rows must be non-negative")
@attr.s(slots=True)
@attr.s(slots=True,
frozen=True)
class Window(object):
"""Windows are rectangular subsets of rasters.
......
{
"type": "FeatureCollection",
"name": "test_lines",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "fid": 183521, "uuid": "{2c66f90e-befb-49d5-8083-c5642194be06}" }, "geometry": { "type": "LineString", "coordinates": [ [ -9.146176034327512, 2.172296541854189 ], [ -9.146450423923667, 2.172488614571499 ], [ -9.146649356380882, 2.172845321046502 ], [ -9.146697374560208, 2.172989375584485 ], [ -9.146834569358287, 2.173099131422947 ], [ -9.146944325196749, 2.173380380759008 ], [ -9.146951184936654, 2.17349013659747 ], [ -9.146923745977038, 2.173757666453723 ], [ -9.146717953779921, 2.174196689807574 ], [ -9.146621917421266, 2.174395622264787 ], [ -9.146553320022226, 2.174553396282577 ], [ -9.146436704443861, 2.174704310560464 ], [ -9.146004540829914, 2.175074736515274 ], [ -9.145572377215966, 2.17541772351047 ], [ -9.145270548660195, 2.175644094927299 ] ] } },
{ "type": "Feature", "properties": { "fid": 183518, "uuid": "{a84f6b24-082c-4e78-a39d-46f2d96582e4}" }, "geometry": { "type": "LineString", "coordinates": [ [ -9.148784450425975, 2.175074736515274 ], [ -9.148949084183668, 2.174889523537869 ], [ -9.149120577681266, 2.174807206659022 ], [ -9.149381247797615, 2.174642572901328 ], [ -9.149614478954348, 2.174457359923922 ], [ -9.149827130891369, 2.174299585906132 ], [ -9.150026063348584, 2.173936019691225 ], [ -9.150190697106277, 2.17349013659747 ], [ -9.150348471124067, 2.172996235324388 ], [ -9.150417068523106, 2.172783583387367 ], [ -9.150458226962529, 2.172399437952748 ], [ -9.15054740358128, 2.171994713298417 ], [ -9.150595421760608, 2.17175462240178 ], [ -9.150588562020706, 2.171617427603702 ], [ -9.150691458119262, 2.171349897747449 ], [ -9.150753195778398, 2.17112352633062 ] ] } },
{ "type": "Feature", "properties": { "fid": 183519, "uuid": "{be89730e-95de-4744-a18c-406730800b73}" }, "geometry": { "type": "LineString", "coordinates": [ [ -9.147330185566345, 2.175877326084033 ], [ -9.147583995942789, 2.17557549752826 ], [ -9.147782928400003, 2.175355985851335 ], [ -9.147940702417793, 2.175198211833545 ], [ -9.148180793314429, 2.175019858596043 ], [ -9.148324847852413, 2.174834645618637 ], [ -9.148448323170683, 2.174670011860944 ], [ -9.148647255627896, 2.174532817062865 ], [ -9.148784450425975, 2.174340744345556 ], [ -9.148866767304822, 2.174100653448919 ], [ -9.148921645224053, 2.173977178130648 ], [ -9.149072559501938, 2.173750806713819 ], [ -9.14912743742117, 2.173558733996509 ], [ -9.149182315340401, 2.173195167781602 ], [ -9.149168595860594, 2.172982515844581 ], [ -9.149141156900978, 2.172824741826791 ], [ -9.14928521143896, 2.172673827548905 ], [ -9.149394967277424, 2.172612089889769 ], [ -9.149504723115886, 2.17257779119025 ] ] } },
{ "type": "Feature", "properties": { "fid": 183517, "uuid": "{e02bf433-920a-4edd-a869-8bfabc832149}" }, "geometry": { "type": "LineString", "coordinates": [ [ -9.150869811356765, 2.174711170300367 ], [ -9.151041304854362, 2.174594554722001 ], [ -9.15117849965244, 2.174464219663826 ], [ -9.151267676271191, 2.174347604085459 ], [ -9.151329413930327, 2.174182970327766 ], [ -9.151349993150038, 2.174011476830168 ], [ -9.151281395750999, 2.173689069054684 ], [ -9.151226517831768, 2.173476417117663 ], [ -9.151130481473112, 2.173222606741218 ], [ -9.151048164594268, 2.173009954804196 ], [ -9.151034445114458, 2.17270126650852 ], [ -9.15105502433417, 2.172433736652267 ], [ -9.151164780172632, 2.172269102894574 ], [ -9.151205938612057, 2.172166206796015 ], [ -9.151233377571671, 2.171898676939762 ], [ -9.151247097051479, 2.171672305522933 ], [ -9.151315694450519, 2.171473373065719 ], [ -9.151391151589461, 2.171240141908986 ], [ -9.151446029508692, 2.171075508151293 ] ] } },
{ "type": "Feature", "properties": { "fid": 183522, "uuid": "{662d526c-0ef9-4aab-b533-7ac0a64ee014}" }, "geometry": { "type": "LineString", "coordinates": [ [ -9.146133160953113, 2.172913918445542 ], [ -9.146194898612247, 2.173037393763812 ], [ -9.146332093410326, 2.173154009342179 ], [ -9.146421270029077, 2.17324318596093 ], [ -9.146441849248788, 2.17340095997872 ], [ -9.146359532369942, 2.173558733996509 ], [ -9.146284075230998, 2.173689069054684 ], [ -9.146105721993496, 2.174073214489303 ], [ -9.146043984334362, 2.174230988507093 ], [ -9.146043984334362, 2.174272146946516 ] ] } },
{ "type": "Feature", "properties": { "fid": 183523, "uuid": "{d54434ff-2013-4897-bb9c-856468743b13}" }, "geometry": { "type": "LineString", "coordinates": [ [ -9.14416441560069, 2.172234804195054 ], [ -9.144377067537711, 2.172529773010922 ], [ -9.144438805196845, 2.172708126248424 ], [ -9.144589719474732, 2.172955076884965 ], [ -9.144651457133866, 2.173133430122467 ], [ -9.144802371411753, 2.173133430122467 ], [ -9.145008163608871, 2.173119710642659 ], [ -9.145138498667045, 2.17308541194314 ] ] } },
{ "type": "Feature", "properties": { "fid": 183520, "uuid": "{8bfc07f0-8550-479b-9682-741a3c3b1c75}" }, "geometry": { "type": "LineString", "coordinates": [ [ -9.148954228988597, 2.171761482141684 ], [ -9.148981667948213, 2.172090749657072 ], [ -9.148967948468405, 2.172344560033517 ], [ -9.148871912109751, 2.172625809369577 ], [ -9.148576943293881, 2.172797302867175 ], [ -9.148350571877053, 2.173044253503716 ], [ -9.148336852397245, 2.17317458856189 ], [ -9.148398590056379, 2.173298063880161 ], [ -9.148357431616956, 2.17352443529699 ], [ -9.148206517339069, 2.173572453476317 ], [ -9.147987005662147, 2.174025196309976 ], [ -9.147808652424644, 2.174340744345556 ], [ -9.147630299187142, 2.174615133941712 ], [ -9.147417647250119, 2.174923822237389 ], [ -9.147150117393867, 2.1752942481922 ], [ -9.146868868057807, 2.175589217008068 ] ] } }
]
}
import rasterio
import numpy as np
import rasterio
from rasterio.coords import BoundingBox, disjoint_bounds
def test_bounds():
with rasterio.open('tests/data/RGB.byte.tif') as src:
assert src.bounds == (101985.0, 2611485.0, 339315.0, 2826915.0)
def test_ul():
with rasterio.open('tests/data/RGB.byte.tif') as src:
assert src.xy(0, 0, offset='ul') == (101985.0, 2826915.0)
assert src.xy(1, 0, offset='ul') == (101985.0, 2826614.95821727)
assert src.xy(src.height, src.width, offset='ul') == (339315.0, 2611485.0)
def test_res():
with rasterio.open('tests/data/RGB.byte.tif') as src:
assert tuple(round(v, 6) for v in src.res) == (300.037927, 300.041783)
def test_rotated_bounds():
with rasterio.open('tests/data/rotated.tif') as src:
assert src.res == (20.0, 10.0)
np.testing.assert_almost_equal(
src.bounds,
(100.0, 70.0961894323342, 348.20508075688775, 300.0))
def test_disjoint_bounds_issue1459():
a = BoundingBox(left=478038, bottom=57155, right=703888, top=266344)
b = BoundingBox(left=584184, bottom=469629, right=740727, top=626172)
assert disjoint_bounds(a, b)
def test_disjoint_bounds_issue1459_south_up():
a = BoundingBox(left=0.0, bottom=1.0, right=1.0, top=0.0)
b = BoundingBox(left=0.0, bottom=2.0, right=1.0, top=1.01)
assert disjoint_bounds(a, b)
......@@ -167,10 +167,10 @@ def test_aws_session(gdalenv):
aws_access_key_id='id', aws_secret_access_key='key',
aws_session_token='token', region_name='null-island-1')
with rasterio.env.Env(session=aws_session) as s:
assert s._creds.access_key == 'id'
assert s._creds.secret_key == 'key'
assert s._creds.token == 'token'
assert s.session.region_name == 'null-island-1'
assert s.session._creds.access_key == 'id'
assert s.session._creds.secret_key == 'key'
assert s.session._creds.token == 'token'
assert s.session._session.region_name == 'null-island-1'
def test_aws_session_credentials(gdalenv):
......