Commit 6a96aed5 authored by Bas Couwenberg's avatar Bas Couwenberg

New upstream version 1.0.9

parent 5e9e72db
Changes
=======
1.0.9 (2019-10-25)
------------------
Bug fixes:
- Delegate test of the environment for existing session credentials to the
session class to generalize credentialization of GDAL to cloud providers
other than AWS (#1520). The env.hascreds function is no longer used in
Rasterio and has been marked as deprecated.
- Switch to use of botocore Credentials.get_frozen_credentials (#1521).
- Numpy masked arrays with the normal Numpy mask sense (True == invalid) are
now supported as input for feature.shapes(). The mask keyword argument of the
function keeps to the GDAL sense of masks (nonzero == invalid) and the
documentation has been improved to communicate this (#1517).
- The defaults for WarpedVRT's `src_nodata` and `nodata` keyword arguments are
no longer None, but are markers for the default behavior: inheritance of
nodata values from the VRT's source dataset. Values of `None` for these
keyword arguments means that the VRT does not inherit from the source and has
no nodata value at all (#1503).
- WEBP has been added to the Compression enum. Exploitation of this new
compression mode will require GDAL 2.4.
- Rasterio now check for two AWS environment variables before creating a
session (#1494).
- The ensure_env_with_credentials decorator which wraps rasterio.open will no
longer clobber the credentials in a surrounding environment or seek
credentials when they already exist (#1507).
- The comparison of CRS objects to dicts and strs that we pledged to remove at
1.0 has at last been removed (#1492).
1.0.8 (2018-10-02)
------------------
......@@ -81,10 +110,12 @@ 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)
------------------
Bug fixes:
- A regression in GeoJSON source handling in rio-rasterize (#1425) has been
fixed.
- Rasterization of linear rings (#1431) is now supported.
- The input file handler of rio-rasterize has been changed so that it does not
reject GeoJSON files (#1425).
- Linear rings have been added as acceptable input for rio-rasterize and other
......
......@@ -230,6 +230,13 @@ this from the downloads folder:
$ pip install GDAL-2.0.2-cp27-none-win32.whl
$ pip install rasterio-0.34.0-cp27-cp27m-win32.whl
You can also install rasterio with conda using Anaconda's conda-forge channel.
.. code-block:: console
$ conda install -c conda-forge rasterio
Source Distributions
--------------------
......
......@@ -452,6 +452,15 @@ Information about existing overviews can be printed using the --ls option.
$ rio overview --ls
The block size (tile width and height) used for overviews (internal
or external) can be specified by setting the ``GDAL_TIFF_OVR_BLOCKSIZE``
environment variable to a power-of-two value between 64 and 4096. The
default value is 128.
.. code-block:: console
$ GDAL_TIFF_OVR_BLOCKSIZE=256 rio overview --build 2^1..4
rasterize
---------
......
......@@ -25,7 +25,7 @@ from rasterio.drivers import is_blacklisted
from rasterio.dtypes import (
bool_, ubyte, uint8, uint16, int16, uint32, int32, float32, float64,
complex_, check_dtype)
from rasterio.env import ensure_env_credentialled, Env
from rasterio.env import ensure_env_with_credentials, Env
from rasterio.errors import RasterioIOError
from rasterio.compat import string_types
from rasterio.io import (
......@@ -43,7 +43,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
__version__ = "1.0.8"
__version__ = "1.0.9"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
......@@ -55,7 +55,7 @@ log = logging.getLogger(__name__)
log.addHandler(NullHandler())
@ensure_env_credentialled
@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=True,
**kwargs):
......@@ -119,7 +119,7 @@ def open(fp, mode='r', driver=None, width=None, height=None, count=None,
Returns
-------
A ``DatasetReader`` or ``DatasetUpdater`` object.
A ``DatasetReader`` or ``DatasetWriter`` object.
Examples
--------
......
......@@ -197,7 +197,7 @@ cdef class DatasetBase(object):
try:
self._hds = open_dataset(filename, flags, driver, kwargs, None)
except CPLE_OpenFailedError as err:
except CPLE_BaseError as err:
raise RasterioIOError(str(err))
self.name = path.name
......@@ -993,7 +993,7 @@ cdef class DatasetBase(object):
else:
band = self._hds
if ovr:
if ovr is not None:
obj = GDALGetOverview(band, ovr)
if obj == NULL:
raise BandOverviewError(
......
......@@ -5,12 +5,11 @@ include "gdal.pxi"
import json
import logging
import warnings
from rasterio._err import CPLE_BaseError
from rasterio.compat import UserDict
from rasterio.compat import string_types
from rasterio.errors import CRSError, RasterioDeprecationWarning
from rasterio.errors import CRSError
from rasterio._base cimport _osr_from_crs as osr_from_crs
from rasterio._base cimport _safe_osr_release
......@@ -74,25 +73,10 @@ class _CRS(UserDict):
if not self or not other:
return not self and not other
# use dictionary equality rules first
elif isinstance(other, dict):
warnings.warn(
"Comparison to dict will be removed in 1.0",
RasterioDeprecationWarning
)
if UserDict(self.data) == UserDict(other):
return True
else:
if isinstance(other, str):
warnings.warn(
"Comparison to str will be removed in 1.0",
RasterioDeprecationWarning
)
osr_crs1 = osr_from_crs(self)
osr_crs2 = osr_from_crs(other)
retval = OSRIsSame(osr_crs1, osr_crs2)
return bool(retval == 1)
osr_crs1 = osr_from_crs(self)
osr_crs2 = osr_from_crs(other)
retval = OSRIsSame(osr_crs1, osr_crs2)
return bool(retval == 1)
finally:
_safe_osr_release(osr_crs1)
......
......@@ -55,7 +55,7 @@ cdef bint is_64bit = sys.maxsize > 2 ** 32
cdef void log_error(CPLErr err_class, int err_no, const char* msg) with gil:
"""Send CPL debug messages and warnings to Python's logger."""
log = logging.getLogger('rasterio._gdal')
log = logging.getLogger(__name__)
if err_class < 3:
if err_no in code_map:
log.log(level_map[err_class], "%s in %s", code_map[err_no], msg)
......
......@@ -99,6 +99,10 @@ class CPLE_AWSSignatureDoesNotMatchError(CPLE_BaseError):
pass
class CPLE_AWSError(CPLE_BaseError):
pass
# Map of GDAL error numbers to the Python exceptions.
exception_map = {
1: CPLE_AppDefinedError,
......@@ -119,7 +123,9 @@ exception_map = {
13: CPLE_AWSObjectNotFoundError,
14: CPLE_AWSAccessDeniedError,
15: CPLE_AWSInvalidCredentialsError,
16: CPLE_AWSSignatureDoesNotMatchError}
16: CPLE_AWSSignatureDoesNotMatchError,
17: CPLE_AWSError
}
# CPL Error types as an enum.
......
......@@ -592,11 +592,14 @@ def _calculate_default_transform(src_crs, dst_crs, width, height,
return dst_affine, dst_width, dst_height
DEFAULT_NODATA_FLAG = object()
cdef class WarpedVRTReaderBase(DatasetReaderBase):
def __init__(self, src_dataset, src_crs=None, dst_crs=None, crs=None,
resampling=Resampling.nearest, tolerance=0.125,
src_nodata=None, dst_nodata=None, nodata=None,
src_nodata=DEFAULT_NODATA_FLAG, dst_nodata=None, nodata=DEFAULT_NODATA_FLAG,
dst_width=None, width=None, dst_height=None, height=None,
src_transform=None, dst_transform=None, transform=None,
init_dest_nodata=True, src_alpha=0, add_alpha=False,
......@@ -612,7 +615,8 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
src_transfrom : Affine, optional
Overrides the transform of `src_dataset`.
src_nodata : float, optional
Overrides the nodata value of `src_dataset`.
Overrides the nodata value of `src_dataset`, which is the
default.
crs : CRS or str, optional
The coordinate reference system at the end of the warp
operation. Default: the crs of `src_dataset`. dst_crs is
......@@ -727,8 +731,8 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
self.resampling = resampling
self.tolerance = tolerance
self.src_nodata = self.src_dataset.nodata if src_nodata is None else src_nodata
self.dst_nodata = self.src_nodata if nodata is None else nodata
self.src_nodata = self.src_dataset.nodata if src_nodata is DEFAULT_NODATA_FLAG else src_nodata
self.dst_nodata = self.src_nodata if nodata is DEFAULT_NODATA_FLAG else nodata
self.dst_width = width
self.dst_height = height
self.dst_transform = transform
......
......@@ -28,13 +28,14 @@ class ColorInterp(IntEnum):
class Resampling(IntEnum):
"""Available warp resampling algorithms.
The subset of 'nearest', 'cubic', 'average', 'mode', and 'gauss'
are available in making dataset overviews.
The first 8, 'nearest', 'bilinear', 'cubic', 'cubic_spline',
'lanczos', 'average', 'mode', and 'gauss', are available for making
dataset overviews.
'max', 'min', 'med', 'q1', 'q3' are only supported in GDAL >= 2.0.0.
'nearest', 'bilinear', 'cubic', 'cubic_spline', 'lanczos', 'average', 'mode'
are always available (GDAL >= 1.10).
'nearest', 'bilinear', 'cubic', 'cubic_spline', 'lanczos',
'average', 'mode' are always available (GDAL >= 1.10).
Note: 'gauss' is not available to the functions in rio.warp.
"""
......@@ -66,6 +67,7 @@ class Compression(Enum):
none = 'NONE'
zstd = 'ZSTD'
lerc = 'LERC'
webp = 'WEBP'
class Interleaving(Enum):
......
......@@ -8,16 +8,11 @@ import re
import threading
import warnings
import rasterio
from rasterio._env import (
GDALEnv, del_gdal_config, get_gdal_config, set_gdal_config)
from rasterio._env import GDALEnv, 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, RasterioDeprecationWarning)
from rasterio.path import parse_path, UnparsedPath, ParsedPath
from rasterio.session import Session, AWSSession, DummySession
from rasterio.transform import guard_transform
class ThreadEnv(threading.local):
......@@ -194,7 +189,7 @@ class Env(object):
region_name=region_name,
profile_name=profile_name,
aws_unsigned=aws_unsigned)
elif 'AWS_ACCESS_KEY_ID' in os.environ:
elif 'AWS_ACCESS_KEY_ID' in os.environ and 'AWS_SECRET_ACCESS_KEY' in os.environ:
self.session = AWSSession()
else:
self.session = DummySession()
......@@ -226,16 +221,6 @@ class Env(object):
options.update(**kwargs)
return Env(*args, **options)
@property
def is_credentialized(self):
"""Test for existence of cloud credentials
Returns
-------
bool
"""
return hascreds() # bool(self.session)
def credentialize(self):
"""Get credentials and configure GDAL
......@@ -247,7 +232,7 @@ class Env(object):
None
"""
if hascreds():
if self.session.hascreds(getenv()):
pass
else:
cred_opts = self.session.get_credential_options()
......@@ -338,9 +323,8 @@ def setenv(**options):
def hascreds():
gdal_config = local._env.get_config_options()
return bool('AWS_ACCESS_KEY_ID' in gdal_config and
'AWS_SECRET_ACCESS_KEY' in gdal_config)
warnings.warn("Please use Env.session.hascreds() instead", RasterioDeprecationWarning)
return local._env is not None and all(key in local._env.get_config_options() for key in ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY'])
def delenv():
......@@ -368,6 +352,12 @@ def ensure_env(f):
def ensure_env_credentialled(f):
"""DEPRECATED alias for ensure_env_with_credentials"""
warnings.warn("Please use ensure_env_with_credentials instead", RasterioDeprecationWarning)
return ensure_env_with_credentials(f)
def ensure_env_with_credentials(f):
"""Ensures a config environment exists and is credentialized
Parameters
......@@ -394,9 +384,15 @@ def ensure_env_credentialled(f):
env_ctor = Env.from_defaults
if isinstance(args[0], str):
session = Session.from_path(args[0])
session_cls = Session.cls_from_path(args[0])
if local._env and session_cls.hascreds(getenv()):
session_cls = DummySession
session = session_cls()
else:
session = Session.from_path(None)
session = DummySession()
with env_ctor(session=session):
return f(*args, **kwds)
......@@ -449,7 +445,7 @@ class GDALVersion(object):
elif isinstance(input, string_types):
# Extract major and minor version components.
# alpha, beta, rc suffixes ignored
match = re.search('^\d+\.\d+', input)
match = re.search(r'^\d+\.\d+', input)
if not match:
raise ValueError(
"value does not appear to be a valid GDAL version "
......
......@@ -78,8 +78,12 @@ def shapes(source, mask=None, connectivity=4, transform=IDENTITY):
Data type must be one of rasterio.int16, rasterio.int32,
rasterio.uint8, rasterio.uint16, or rasterio.float32.
mask : numpy ndarray or rasterio Band object, optional
Values of False or 0 will be excluded from feature generation
Must evaluate to bool (rasterio.bool_ or rasterio.uint8)
Must evaluate to bool (rasterio.bool_ or rasterio.uint8). Values
of False or 0 will be excluded from feature generation. Note
well that this is the inverse sense from Numpy's, where a mask
value of True indicates invalid data in an array. If `source` is
a Numpy masked array and `mask` is None, the source's mask will
be inverted and used in place of `mask`.
connectivity : int, optional
Use 4 or 8 pixel connectivity for grouping pixels into features
transform : Affine transformation, optional
......@@ -104,6 +108,10 @@ def shapes(source, mask=None, connectivity=4, transform=IDENTITY):
memory.
"""
if hasattr(source, 'mask') and mask is None:
mask = ~source.mask
source = source.data
transform = guard_transform(transform)
for s, v in _shapes(source, mask, connectivity, transform):
yield s, v
......
"""Abstraction for sessions in various clouds."""
from rasterio.path import parse_path, UnparsedPath, ParsedPath
from rasterio.path import parse_path, UnparsedPath
class Session(object):
......@@ -18,6 +18,24 @@ class Session(object):
"""
@classmethod
def hascreds(cls, config):
"""Determine if the given configuration has proper credentials
Parameters
----------
cls : class
A Session class.
config : dict
GDAL configuration as a dict.
Returns
-------
bool
"""
return NotImplementedError
def get_credential_options(self):
"""Get credentials as GDAL configuration options
......@@ -50,40 +68,56 @@ class Session(object):
return cls(session)
@staticmethod
def from_path(path, *args, **kwargs):
"""Create a session object suited to the data at `path`.
def cls_from_path(path):
"""Find the session class 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
class
"""
if not path:
return DummySession()
return DummySession
path = parse_path(path)
if isinstance(path, UnparsedPath) or path.is_local:
return DummySession()
return DummySession
elif path.scheme == "s3" or "amazonaws.com" in path.path:
return AWSSession(*args, **kwargs)
return AWSSession
# This factory can be extended to other cloud providers here.
# elif path.scheme == "cumulonimbus": # for example.
# return CumulonimbusSession(*args, **kwargs)
else:
return DummySession()
return DummySession
@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
"""
return Session.cls_from_path(path)(*args, **kwargs)
class DummySession(Session):
......@@ -100,6 +134,24 @@ class DummySession(Session):
self._session = None
self.credentials = {}
@classmethod
def hascreds(cls, config):
"""Determine if the given configuration has proper credentials
Parameters
----------
cls : class
A Session class.
config : dict
GDAL configuration as a dict.
Returns
-------
bool
"""
return True
def get_credential_options(self):
"""Get credentials as GDAL configuration options
......@@ -157,22 +209,41 @@ class AWSSession(Session):
self.unsigned = aws_unsigned
self._creds = self._session._session.get_credentials()
@classmethod
def hascreds(cls, config):
"""Determine if the given configuration has proper credentials
Parameters
----------
cls : class
A Session class.
config : dict
GDAL configuration as a dict.
Returns
-------
bool
"""
return 'AWS_ACCESS_KEY_ID' in config and 'AWS_SECRET_ACCESS_KEY' in config
@property
def credentials(self):
"""The session credentials as a dict"""
creds = {}
res = {}
if self._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
frozen_creds = self._creds.get_frozen_credentials()
if frozen_creds.access_key: # pragma: no branch
res['aws_access_key_id'] = frozen_creds.access_key
if frozen_creds.secret_key: # pragma: no branch
res['aws_secret_access_key'] = frozen_creds.secret_key
if frozen_creds.token:
res['aws_session_token'] = frozen_creds.token
if self._session.region_name:
creds['aws_region'] = self._session.region_name
res['aws_region'] = self._session.region_name
if self.requester_pays:
creds['aws_request_payer'] = 'requester'
return creds
res['aws_request_payer'] = 'requester'
return res
def get_credential_options(self):
"""Get credentials as GDAL configuration options
......
......@@ -7,7 +7,7 @@ import logging
from rasterio._io cimport DatasetReaderBase
from rasterio._err cimport exc_wrap_int, exc_wrap_pointer
from rasterio.env import ensure_env_credentialled
from rasterio.env import ensure_env_with_credentials
from rasterio._err import CPLE_OpenFailedError
from rasterio.errors import DriverRegistrationError, RasterioIOError
from rasterio.path import parse_path, vsi_path
......@@ -16,7 +16,7 @@ from rasterio.path import parse_path, vsi_path
log = logging.getLogger(__name__)
@ensure_env_credentialled
@ensure_env_with_credentials
def exists(path):
"""Determine if a dataset exists by attempting to open it.
......@@ -47,7 +47,7 @@ def exists(path):
GDALClose(h_dataset)
@ensure_env_credentialled
@ensure_env_with_credentials
def copy(src, dst, driver='GTiff', strict=True, **creation_options):
"""Copy a raster from a path or open dataset handle to a new destination
......@@ -120,7 +120,7 @@ def copy(src, dst, driver='GTiff', strict=True, **creation_options):
GDALClose(src_dataset)
@ensure_env_credentialled
@ensure_env_with_credentials
def copyfiles(src, dst):
"""Copy files associated with a dataset from one location to another.
......@@ -162,7 +162,7 @@ def copyfiles(src, dst):
GDALClose(h_dataset)
@ensure_env_credentialled
@ensure_env_with_credentials
def delete(path, driver=None):
"""Delete a GDAL dataset
......
......@@ -20,7 +20,7 @@ def test_drivers_bwd_compat():
def test_cpl_debug_true(tmpdir):
"""Setting CPL_DEBUG=True results in GDAL debug messages."""
log = logging.getLogger('rasterio._gdal')
log = logging.getLogger('rasterio._env')
log.setLevel(logging.DEBUG)
logfile = str(tmpdir.join('test.log'))
fh = logging.FileHandler(logfile)
......
......@@ -13,6 +13,7 @@ from rasterio.env import Env, defenv, delenv, getenv, setenv, ensure_env, ensure
from rasterio.env import GDALVersion, require_gdal_version
from rasterio.errors import EnvError, RasterioIOError, GDALVersionError
from rasterio.rio.main import main_group
from rasterio.session import AWSSession
from .conftest import requires_gdal21
......@@ -23,8 +24,6 @@ credentials = pytest.mark.skipif(
reason="S3 raster access requires credentials")
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
L8TIF = "s3://landsat-pds/L8/139/045/LC81390452014295LGN00/LC81390452014295LGN00_B1.TIF"
L8TIFB2 = "s3://landsat-pds/L8/139/045/LC81390452014295LGN00/LC81390452014295LGN00_B2.TIF"
httpstif = "https://landsat-pds.s3.amazonaws.com/L8/139/045/LC81390452014295LGN00/LC81390452014295LGN00_B1.TIF"
......@@ -167,9 +166,9 @@ 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.session._creds.access_key == 'id'
assert s.session._creds.secret_key == 'key'
assert s.session._creds.token == 'token'
assert s.session._session.get_credentials().get_frozen_credentials().access_key == 'id'
assert s.session._session.get_credentials().get_frozen_credentials().secret_key == 'key'
assert s.session._session.get_credentials().get_frozen_credentials().token == 'token'
assert s.session._session.region_name == 'null-island-1'
......@@ -739,3 +738,22 @@ def test_rio_env_no_credentials(tmpdir, monkeypatch, runner):
with rasterio.Env() as env:
assert env.drivers()
def test_nested_credentials(monkeypatch):
"""Check that rasterio.open() doesn't wipe out surrounding credentials"""
@ensure_env_credentialled
def fake_opener(path):
return getenv()
with rasterio.Env(session=AWSSession(aws_access_key_id='foo', aws_secret_access_key='bar')):
assert getenv()['AWS_ACCESS_KEY_ID'] == 'foo'
assert getenv()['AWS_SECRET_ACCESS_KEY'] == 'bar'
monkeypatch.setenv('AWS_ACCESS_KEY_ID', 'lol')
monkeypatch.setenv('AWS_SECRET_ACCESS_KEY', 'wut')
gdalenv = fake_opener('s3://foo/bar')
assert gdalenv['AWS_ACCESS_KEY_ID'] == 'foo'
assert gdalenv['AWS_SECRET_ACCESS_KEY'] == 'bar'
......@@ -845,6 +845,25 @@ def test_shapes_mask(basic_image):
assert value == 1
def test_shapes_masked_array(basic_image):
"""Only pixels not masked out should be converted to features."""
mask = np.full(basic_image.shape, False, dtype=rasterio.bool_)
mask[4:5, 4:5] = True
results = list(shapes(np.ma.masked_array(basic_image, mask=mask)))
assert len(results) == 2
shape, value = results[0]
assert shape == {
'coordinates': [
[(2, 2), (2, 5), (4, 5), (4, 4), (5, 4), (5, 2), (2, 2)]
],
'type': 'Polygon'
}
assert value == 1
def test_shapes_blank_mask(basic_image):
"""Mask is blank so results should mask shapes without mask."""
assert np.array_equal(
......
"""
Rasterio exposes GDAL's resampling/decimation on I/O. These are the tests
that it does this correctly.
"""
import numpy as np
import rasterio
......@@ -7,9 +12,6 @@ from rasterio.windows import Window
from .conftest import requires_gdal2
# Rasterio exposes GDAL's resampling/decimation on I/O. These are the tests
# that it does this correctly.
#
# Rasterio's test dataset is 718 rows by 791 columns.
def test_read_out_shape_resample_down():
......@@ -38,6 +40,7 @@ def test_read_out_shape_resample_up():
assert data.mean() == s.read(1, masked=True).mean()
# TODO: justify or remove this test.
def test_read_downsample_alpha():
with rasterio.Env(GTIFF_IMPLICIT_JPEG_OVR=False):
with rasterio.open('tests/data/alpha.tif') as src:
......@@ -51,15 +54,31 @@ def test_read_downsample_alpha():
@requires_gdal2
def test_resample_alg():
def test_resample_alg_effect_1():
"""default (nearest) and cubic produce different results"""
with rasterio.open('tests/data/RGB.byte.tif') as s:
# Existence of overviews can upset our expectations, so we
# guard against that here.
assert not any([s.overviews(bidx) for bidx in s.indexes])
out_shape = (s.height // 2, s.width // 2)
nearest = s.read(1, out_shape=out_shape)
cubic = s.read(1, out_shape=out_shape, resampling=Resampling.cubic)
assert np.any(nearest != cubic)
@requires_gdal2
def test_resample_alg_effect_2():
"""Average and bilinear produce different results"""
with rasterio.open('tests/data/RGB.byte.tif') as s:
# Existence of overviews can upset our expectations, so we
# guard against that here.
assert not any([s.overviews(bidx) for bidx in s.indexes])
out_shape = (s.height // 2, s.width // 2)
avg = s.read(1, out_shape=out_shape, resampling=Resampling.average)
bilin = s.read(1, out_shape=out_shape, resampling=Resampling.bilinear)
assert np.any(avg != bilin)