[Git][debian-gis-team/rasterio][upstream] New upstream version 1.2.4
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Tue Jun 1 04:38:10 BST 2021
Bas Couwenberg pushed to branch upstream at Debian GIS Project / rasterio
Commits:
c6ca2f01 by Bas Couwenberg at 2021-06-01T05:20:13+02:00
New upstream version 1.2.4
- - - - -
28 changed files:
- .travis.yml
- CHANGES.txt
- rasterio/__init__.py
- rasterio/_base.pyx
- rasterio/_features.pyx
- rasterio/_io.pyx
- rasterio/_shim1.pyx
- rasterio/_warp.pyx
- rasterio/coords.py
- rasterio/dtypes.py
- rasterio/errors.py
- rasterio/features.py
- rasterio/merge.py
- rasterio/rio/edit_info.py
- rasterio/rio/options.py
- rasterio/shim_rasterioex.pxi
- rasterio/windows.py
- setup.py
- tests/test_complex_dtypes.py
- tests/test_dtypes.py
- tests/test_merge.py
- tests/test_read.py
- tests/test_rio_edit_info.py
- tests/test_rio_options.py
- tests/test_warp.py
- tests/test_warpedvrt.py
- tests/test_windows.py
- tests/test_write.py
Changes:
=====================================
.travis.yml
=====================================
@@ -48,11 +48,11 @@ jobs:
- python: "3.8"
env:
GDALVERSION="master"
- PROJVERSION="7.2.1"
+ PROJVERSION="8.0.1"
allow_failures:
- env:
GDALVERSION="master"
- PROJVERSION="7.2.1"
+ PROJVERSION="8.0.1"
addons:
apt:
=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,27 @@
Changes
=======
+1.2.4 (2021-05-31)
+------------------
+
+- Eliminate unneeded marker for CLI nodata options to be ignored. We will stick
+ with None (#2191).
+- Guard against use of gauss resampling when creating a WarpedVRT (#2190).
+- Prevent segfaults when buffer and band indexes are mismatched in
+ io_multi_band and io_multi_mask (#2189).
+- Change comparisons of nodata to IgnoreOption to accomodate changes in click
+ 8.0.
+- Use Window constructor instead of from_slices in windows.union to allow a
+ proper union to be formed from windows extending outside a dataset (#2186).
+- Read GDAL CInt16 data as np.complex64 and allow saving complex data to CInt16
+ (#2185).
+- Skip merge sources which do not overlap the destination.
+- Allow unsafe casting in the merge tool, restoring behavior of version 1.2.0
+ (#2179).
+- Remove a workaround for an old Python 3.4 bug from the BoundingBox
+ implementation (#2172).
+- Add setuptools as an explicit installation requirement.
+
1.2.3 (2021-04-26)
------------------
=====================================
rasterio/__init__.py
=====================================
@@ -9,8 +9,21 @@ from pathlib import Path
from rasterio._base import gdal_version
from rasterio.drivers import driver_from_extension, is_blacklisted
from rasterio.dtypes import (
- bool_, ubyte, sbyte, uint8, int8, uint16, int16, uint32, int32, float32, float64,
- complex_, check_dtype)
+ bool_,
+ ubyte,
+ sbyte,
+ uint8,
+ int8,
+ uint16,
+ int16,
+ uint32,
+ int32,
+ float32,
+ float64,
+ complex_,
+ complex_int16,
+ check_dtype,
+)
from rasterio.env import ensure_env_with_credentials, Env
from rasterio.errors import RasterioIOError, DriverCapabilityError
from rasterio.io import (
@@ -27,7 +40,7 @@ import rasterio.enums
import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
-__version__ = "1.2.3"
+__version__ = "1.2.4"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
=====================================
rasterio/_base.pyx
=====================================
@@ -172,6 +172,7 @@ cdef _band_dtype(GDALRasterBandH band):
else:
return 'uint8'
+
return dtypes.dtype_fwd[gdal_dtype]
@@ -501,7 +502,9 @@ cdef class DatasetBase(object):
# to check that the return value is within the range of the
# data type. If so, the band has a nodata value. If not,
# there's no nodata value.
- if (success == 0 or
+ if dtype not in dtypes.dtype_ranges:
+ pass
+ elif (success == 0 or
val < dtypes.dtype_ranges[dtype][0] or
val > dtypes.dtype_ranges[dtype][1]):
val = None
=====================================
rasterio/_features.pyx
=====================================
@@ -9,6 +9,7 @@ import logging
import numpy as np
from rasterio import dtypes
+from rasterio.dtypes import _getnpdtype
from rasterio.enums import MergeAlg
from rasterio._err cimport exc_wrap_int, exc_wrap_pointer
@@ -62,12 +63,12 @@ def _shapes(image, mask, connectivity, transform):
cdef ShapeIterator shape_iter = None
cdef int fieldtp
- is_float = np.dtype(image.dtype).kind == "f"
+ is_float = _getnpdtype(image.dtype).kind == "f"
fieldtp = 2 if is_float else 0
valid_dtypes = ('int16', 'int32', 'uint8', 'uint16', 'float32')
- if np.dtype(image.dtype).name not in valid_dtypes:
+ if _getnpdtype(image.dtype).name not in valid_dtypes:
raise ValueError("image dtype must be one of: {0}".format(
', '.join(valid_dtypes)))
@@ -89,7 +90,7 @@ def _shapes(image, mask, connectivity, transform):
if mask.shape != image.shape:
raise ValueError("Mask must have same shape as image")
- if np.dtype(mask.dtype).name not in ('bool', 'uint8'):
+ if _getnpdtype(mask.dtype).name not in ('bool', 'uint8'):
raise ValueError("Mask must be dtype rasterio.bool_ or "
"rasterio.uint8")
@@ -184,7 +185,7 @@ def _sieve(image, size, out, mask, connectivity):
valid_dtypes = ('int16', 'int32', 'uint8', 'uint16')
- if np.dtype(image.dtype).name not in valid_dtypes:
+ if _getnpdtype(image.dtype).name not in valid_dtypes:
valid_types_str = ', '.join(('rasterio.{0}'.format(t) for t
in valid_dtypes))
raise ValueError(
@@ -203,7 +204,7 @@ def _sieve(image, size, out, mask, connectivity):
if out.shape != image.shape:
raise ValueError('out raster shape must be same as image shape')
- if np.dtype(image.dtype).name != np.dtype(out.dtype).name:
+ if _getnpdtype(image.dtype).name != _getnpdtype(out.dtype).name:
raise ValueError('out raster must match dtype of image')
try:
@@ -231,7 +232,7 @@ def _sieve(image, size, out, mask, connectivity):
if mask.shape != image.shape:
raise ValueError("Mask must have same shape as image")
- if np.dtype(mask.dtype) not in ('bool', 'uint8'):
+ if _getnpdtype(mask.dtype) not in ('bool', 'uint8'):
raise ValueError("Mask must be dtype rasterio.bool_ or "
"rasterio.uint8")
=====================================
rasterio/_io.pyx
=====================================
@@ -26,7 +26,7 @@ from rasterio.errors import (
NotGeoreferencedWarning, NodataShadowWarning, WindowError,
UnsupportedOperation, OverviewCreationError, RasterBlockError, InvalidArrayError
)
-from rasterio.dtypes import is_ndarray
+from rasterio.dtypes import is_ndarray, _is_complex_int, _getnpdtype
from rasterio.sample import sample_gen
from rasterio.transform import Affine
from rasterio.path import parse_path, UnparsedPath
@@ -104,7 +104,7 @@ cdef bint in_dtype_range(value, dtype):
105: np.iinfo,
117: np.iinfo}
- key = np.dtype(dtype).kind
+ key = _getnpdtype(dtype).kind
if np.isnan(value):
return key in ('c', 'f', 99, 102)
@@ -241,12 +241,14 @@ cdef class DatasetReaderBase(DatasetBase):
check_dtypes = set()
nodatavals = []
+
# Check each index before processing 3D array
for bidx in indexes:
+
if bidx not in self.indexes:
raise IndexError("band index {} out of range (not in {})".format(bidx, self.indexes))
- idx = self.indexes.index(bidx)
+ idx = self.indexes.index(bidx)
dtype = self.dtypes[idx]
check_dtypes.add(dtype)
@@ -254,16 +256,18 @@ cdef class DatasetReaderBase(DatasetBase):
log.debug("Output nodata value read from file: %r", ndv)
- if ndv is not None:
- kind = np.dtype(dtype).kind
- if chr(kind) in "iu":
+ if ndv is not None and not _is_complex_int(dtype):
+ kind = _getnpdtype(dtype).kind
+
+ if kind in "iu":
info = np.iinfo(dtype)
dt_min, dt_max = info.min, info.max
- elif chr(kind) in "cf":
+ elif kind in "cf":
info = np.finfo(dtype)
dt_min, dt_max = info.min, info.max
else:
dt_min, dt_max = False, True
+
if ndv < dt_min:
ndv = dt_min
elif ndv > dt_max:
@@ -284,6 +288,9 @@ cdef class DatasetReaderBase(DatasetBase):
if out_dtype is not None:
dtype = out_dtype
+ # Ensure we have a numpy dtype.
+ dtype = _getnpdtype(dtype)
+
# Get the natural shape of the read window, boundless or not.
# The window can have float values. In this case, we round up
# when computing the shape.
@@ -357,8 +364,7 @@ cdef class DatasetReaderBase(DatasetBase):
log.debug("Jump straight to _read()")
log.debug("Window: %r", window)
- out = self._read(indexes, out, window, dtype,
- resampling=resampling)
+ out = self._read(indexes, out, window, dtype, resampling=resampling)
if masked or fill_value is not None:
if all_valid:
@@ -555,7 +561,7 @@ cdef class DatasetReaderBase(DatasetBase):
out = np.zeros(out_shape, 'uint8')
if out is not None:
- if out.dtype != np.dtype(dtype):
+ if out.dtype != _getnpdtype(dtype):
raise ValueError(
"the out array's dtype '%s' does not match '%s'"
% (out.dtype, dtype))
@@ -624,8 +630,7 @@ cdef class DatasetReaderBase(DatasetBase):
return out
- def _read(self, indexes, out, window, dtype, masks=False,
- resampling=Resampling.nearest):
+ def _read(self, indexes, out, window, dtype, masks=False, resampling=Resampling.nearest):
"""Read raster bands as a multidimensional array
If `indexes` is a list, the result is a 3D array, but
@@ -1056,19 +1061,21 @@ cdef class DatasetWriterBase(DatasetReaderBase):
try:
width = int(width)
height = int(height)
- except:
+ except TypeError:
raise TypeError("Integer width and height are required.")
try:
count = int(count)
- except:
+ except TypeError:
raise TypeError("Integer band count is required.")
- try:
- assert dtype is not None
- _ = np.dtype(dtype)
- except:
- raise TypeError("A valid dtype is required.")
- self._init_dtype = np.dtype(dtype).name
+ if _is_complex_int(dtype):
+ self._init_dtype = dtype
+ else:
+ try:
+ assert dtype is not None
+ self._init_dtype = _getnpdtype(dtype).name
+ except Exception:
+ raise TypeError("A valid dtype is required.")
# Make and store a GDAL dataset handle.
filename = path.name
@@ -1147,7 +1154,9 @@ cdef class DatasetWriterBase(DatasetReaderBase):
if nodata is not None:
- if not in_dtype_range(nodata, dtype):
+ if _is_complex_int(dtype):
+ pass
+ elif not in_dtype_range(nodata, dtype):
raise ValueError(
"Given nodata value, %s, is beyond the valid "
"range of its data type, %s." % (
@@ -1365,10 +1374,7 @@ cdef class DatasetWriterBase(DatasetReaderBase):
else: # unique dtype; normal case
dtype = check_dtypes.pop()
- if arr is not None and arr.dtype != dtype:
- raise ValueError(
- "the array's dtype '%s' does not match "
- "the file's dtype '%s'" % (arr.dtype, dtype))
+ dtype = _getnpdtype(dtype)
# Require C-continguous arrays (see #108).
arr = np.require(arr, dtype=dtype, requirements='C')
@@ -1573,7 +1579,7 @@ cdef class DatasetWriterBase(DatasetReaderBase):
GDALFillRaster(mask, 255, 0)
elif mask_array is False:
GDALFillRaster(mask, 0, 0)
- elif mask_array.dtype == np.bool:
+ elif mask_array.dtype == bool:
array = 255 * mask_array.astype(np.uint8)
io_band(mask, 1, xoff, yoff, width, height, array)
else:
@@ -1968,13 +1974,14 @@ cdef class BufferedDatasetWriterBase(DatasetWriterBase):
count = int(count)
except:
raise TypeError("Integer band count is required.")
+
try:
assert dtype is not None
- _ = np.dtype(dtype)
- except:
+ _ = _getnpdtype(dtype)
+ except Exception:
raise TypeError("A valid dtype is required.")
- self._init_dtype = np.dtype(dtype).name
+ self._init_dtype = _getnpdtype(dtype).name
self.name = path.name
self.mode = mode
=====================================
rasterio/_shim1.pyx
=====================================
@@ -16,7 +16,7 @@ from rasterio.enums import Resampling
cimport numpy as np
from rasterio._err cimport exc_wrap_int, exc_wrap_pointer
-from rasterio.errors import GDALOptionNotImplementedError
+from rasterio.errors import GDALOptionNotImplementedError, DatasetIOShapeError
cdef GDALDatasetH open_dataset(
@@ -114,6 +114,9 @@ cdef int io_multi_band(
cdef int xsize = <int>width
cdef int ysize = <int>height
+ if len(indexes) != data.shape[0]:
+ raise DatasetIOShapeError("Dataset indexes and destination buffer are mismatched")
+
bandmap = <int *>CPLMalloc(count*sizeof(int))
for i in range(count):
bandmap[i] = <int>indexes[i]
@@ -162,6 +165,9 @@ cdef int io_multi_mask(
cdef int xsize = <int>width
cdef int ysize = <int>height
+ if len(indexes) != data.shape[0]:
+ raise DatasetIOShapeError("Dataset indexes and destination buffer are mismatched")
+
for i in range(count):
j = <int>indexes[i]
band = GDALGetRasterBand(hds, j)
=====================================
rasterio/_warp.pyx
=====================================
@@ -43,6 +43,17 @@ from rasterio._shim cimport delete_nodata_value, open_dataset
log = logging.getLogger(__name__)
+# Gauss (7) is not supported for warp
+SUPPORTED_RESAMPLING = [r for r in Resampling if r.value < 7]
+GDAL2_RESAMPLING = [r for r in Resampling if r.value > 7 and r.value <= 12]
+if GDALVersion.runtime().at_least('2.0'):
+ SUPPORTED_RESAMPLING.extend(GDAL2_RESAMPLING)
+# sum supported since GDAL 3.1
+if GDALVersion.runtime().at_least('3.1'):
+ SUPPORTED_RESAMPLING.append(Resampling.sum)
+# rms supported since GDAL 3.3
+if GDALVersion.runtime().at_least('3.3'):
+ SUPPORTED_RESAMPLING.append(Resampling.rms)
def recursive_round(val, precision):
"""Recursively round coordinates."""
@@ -777,6 +788,16 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
if src_dataset.mode != "r":
warnings.warn("Source dataset should be opened in read-only mode. Use of datasets opened in modes other than 'r' will be disallowed in a future version.", RasterioDeprecationWarning, stacklevel=2)
+ # Guard against invalid or unsupported resampling algorithms.
+ try:
+ if resampling == 7:
+ raise ValueError("Gauss resampling is not supported")
+ Resampling(resampling)
+
+ except ValueError:
+ raise ValueError(
+ "resampling must be one of: {0}".format(", ".join(['Resampling.{0}'.format(r.name) for r in SUPPORTED_RESAMPLING])))
+
self.mode = 'r'
self.options = {}
self._count = 0
=====================================
rasterio/coords.py
=====================================
@@ -1,11 +1,9 @@
"""Bounding box tuple, and disjoint operator."""
-from collections import namedtuple, OrderedDict
+from collections import namedtuple
-_BoundingBox = namedtuple('BoundingBox', ('left', 'bottom', 'right', 'top'))
-
-
-class BoundingBox(_BoundingBox):
+BoundingBox = namedtuple('BoundingBox', ('left', 'bottom', 'right', 'top'))
+BoundingBox.__doc__ = \
"""Bounding box named tuple, defining extent in cartesian coordinates.
.. code::
@@ -24,10 +22,6 @@ class BoundingBox(_BoundingBox):
Top coordinate
"""
- def _asdict(self):
- return OrderedDict(zip(self._fields, self))
-
-
def disjoint_bounds(bounds1, bounds2):
"""Compare two bounds and determine if they are disjoint.
@@ -43,6 +37,7 @@ def disjoint_bounds(bounds1, bounds2):
boolean
``True`` if bounds are disjoint,
``False`` if bounds overlap
+
"""
bounds1_north_up = bounds1[3] > bounds1[1]
bounds2_north_up = bounds2[3] > bounds2[1]
=====================================
rasterio/dtypes.py
=====================================
@@ -4,10 +4,6 @@ Since 0.13 we are not importing numpy here and data types are strings.
Happily strings can be used throughout Numpy and so existing code will
not break.
-Within Rasterio, to test data types, we use Numpy's dtype() factory to
-do something like this:
-
- if np.dtype(destination.dtype) == np.dtype(rasterio.uint8): ...
"""
bool_ = 'bool'
@@ -23,26 +19,29 @@ complex_ = 'complex'
complex64 = 'complex64'
complex128 = 'complex128'
-# Not supported:
-# GDT_CInt16 = 8, GDT_CInt32 = 9, GDT_CFloat32 = 10, GDT_CFloat64 = 11
+complex_int16 = "complex_int16"
dtype_fwd = {
- 0: None, # GDT_Unknown
- 1: ubyte, # GDT_Byte
- 2: uint16, # GDT_UInt16
- 3: int16, # GDT_Int16
- 4: uint32, # GDT_UInt32
- 5: int32, # GDT_Int32
- 6: float32, # GDT_Float32
- 7: float64, # GDT_Float64
- 8: complex_, # GDT_CInt16
- 9: complex_, # GDT_CInt32
- 10: complex64, # GDT_CFloat32
- 11: complex128} # GDT_CFloat64
+ 0: None, # GDT_Unknown
+ 1: ubyte, # GDT_Byte
+ 2: uint16, # GDT_UInt16
+ 3: int16, # GDT_Int16
+ 4: uint32, # GDT_UInt32
+ 5: int32, # GDT_Int32
+ 6: float32, # GDT_Float32
+ 7: float64, # GDT_Float64
+ 8: complex_int16, # GDT_CInt16
+ 9: complex64, # GDT_CInt32
+ 10: complex64, # GDT_CFloat32
+ 11: complex128, # GDT_CFloat64
+}
dtype_rev = dict((v, k) for k, v in dtype_fwd.items())
-dtype_rev['uint8'] = 1
-dtype_rev['int8'] = 1
+
+dtype_rev["uint8"] = 1
+dtype_rev["int8"] = 1
+dtype_rev["complex"] = 11
+dtype_rev["complex_int16"] = 8
typename_fwd = {
0: 'Unknown',
@@ -154,7 +153,7 @@ def can_cast_dtype(values, dtype):
if not is_ndarray(values):
values = np.array(values)
- if values.dtype.name == np.dtype(dtype).name:
+ if values.dtype.name == _getnpdtype(dtype).name:
return True
elif values.dtype.kind == 'f':
@@ -185,3 +184,15 @@ def validate_dtype(values, valid_dtypes):
return (values.dtype.name in valid_dtypes or
get_minimum_dtype(values) in valid_dtypes)
+
+
+def _is_complex_int(dtype):
+ return isinstance(dtype, str) and dtype.startswith("complex_int")
+
+
+def _getnpdtype(dtype):
+ import numpy as np
+ if _is_complex_int(dtype):
+ return np.dtype("complex64")
+ else:
+ return np.dtype(dtype)
=====================================
rasterio/errors.py
=====================================
@@ -87,9 +87,9 @@ class GDALOptionNotImplementedError(RasterioError):
by GDAL 1.x.
"""
+
class GDALVersionError(RasterioError):
- """Raised if the runtime version of GDAL does not meet the required
- version of GDAL."""
+ """Raised if the runtime version of GDAL does not meet the required version of GDAL."""
class WindowEvaluationError(ValueError):
@@ -142,3 +142,7 @@ class TransformError(RasterioError):
class WarpedVRTError(RasterioError):
"""Raised when WarpedVRT can't be initialized"""
+
+
+class DatasetIOShapeError(RasterioError):
+ """Raised when data buffer shape is a mismatch when reading and writing"""
=====================================
rasterio/features.py
=====================================
@@ -9,7 +9,7 @@ import warnings
import numpy as np
import rasterio
-from rasterio.dtypes import validate_dtype, can_cast_dtype, get_minimum_dtype
+from rasterio.dtypes import validate_dtype, can_cast_dtype, get_minimum_dtype, _getnpdtype
from rasterio.enums import MergeAlg
from rasterio.env import ensure_env
from rasterio.errors import ShapeSkipWarning
@@ -276,7 +276,7 @@ def rasterize(
if dtype is not None and not can_cast_dtype(default_value_array, dtype):
raise ValueError(format_cast_error('default_vaue', dtype))
- if dtype is not None and np.dtype(dtype).name not in valid_dtypes:
+ if dtype is not None and _getnpdtype(dtype).name not in valid_dtypes:
raise ValueError(format_invalid_dtype('dtype'))
valid_shapes = []
@@ -332,7 +332,7 @@ def rasterize(
raise ValueError(format_cast_error('shape values', dtype))
if out is not None:
- if np.dtype(out.dtype).name not in valid_dtypes:
+ if _getnpdtype(out.dtype).name not in valid_dtypes:
raise ValueError(format_invalid_dtype('out'))
if not can_cast_dtype(shape_values, out.dtype):
=====================================
rasterio/merge.py
=====================================
@@ -9,6 +9,7 @@ import warnings
import numpy as np
import rasterio
+from rasterio.coords import disjoint_bounds
from rasterio.enums import Resampling
from rasterio import windows
from rasterio.transform import Affine
@@ -21,13 +22,13 @@ def copy_first(merged_data, new_data, merged_mask, new_mask, **kwargs):
mask = np.empty_like(merged_mask, dtype="bool")
np.logical_not(new_mask, out=mask)
np.logical_and(merged_mask, mask, out=mask)
- np.copyto(merged_data, new_data, where=mask)
+ np.copyto(merged_data, new_data, where=mask, casting="unsafe")
def copy_last(merged_data, new_data, merged_mask, new_mask, **kwargs):
mask = np.empty_like(merged_mask, dtype="bool")
np.logical_not(new_mask, out=mask)
- np.copyto(merged_data, new_data, where=mask)
+ np.copyto(merged_data, new_data, where=mask, casting="unsafe")
def copy_min(merged_data, new_data, merged_mask, new_mask, **kwargs):
@@ -37,7 +38,7 @@ def copy_min(merged_data, new_data, merged_mask, new_mask, **kwargs):
np.minimum(merged_data, new_data, out=merged_data, where=mask)
np.logical_not(new_mask, out=mask)
np.logical_and(merged_mask, mask, out=mask)
- np.copyto(merged_data, new_data, where=mask)
+ np.copyto(merged_data, new_data, where=mask, casting="unsafe")
def copy_max(merged_data, new_data, merged_mask, new_mask, **kwargs):
@@ -47,7 +48,7 @@ def copy_max(merged_data, new_data, merged_mask, new_mask, **kwargs):
np.maximum(merged_data, new_data, out=merged_data, where=mask)
np.logical_not(new_mask, out=mask)
np.logical_and(merged_mask, mask, out=mask)
- np.copyto(merged_data, new_data, where=mask)
+ np.copyto(merged_data, new_data, where=mask, casting="unsafe")
MERGE_METHODS = {
@@ -290,6 +291,10 @@ def merge(
# This approach uses the maximum amount of memory to solve the
# problem. Making it more efficient is a TODO.
+ if disjoint_bounds((dst_w, dst_s, dst_e, dst_n), src.bounds):
+ logger.debug("Skipping source: src=%r, window=%r", src)
+ continue
+
# 1. Compute spatial intersection of destination and source
src_w, src_s, src_e, src_n = src.bounds
@@ -302,7 +307,6 @@ def merge(
src_window = windows.from_bounds(
int_w, int_s, int_e, int_n, src.transform, precision=precision
)
- logger.debug("Src %s window: %r", src.name, src_window)
# 3. Compute the destination window
dst_window = windows.from_bounds(
=====================================
rasterio/rio/edit_info.py
=====================================
@@ -190,13 +190,13 @@ def edit(ctx, input, bidx, nodata, unset_nodata, crs, unset_crs, transform,
tags = allmd['tags']
colorinterp = allmd['colorinterp']
- if unset_nodata and nodata is not options.IgnoreOption:
+ if unset_nodata and nodata is not None:
raise click.BadParameter(
- "--unset-nodata and --nodata cannot be used together.")
+ "--unset-nodata and --nodata cannot be used together."
+ )
if unset_crs and crs:
- raise click.BadParameter(
- "--unset-crs and --crs cannot be used together.")
+ raise click.BadParameter("--unset-crs and --crs cannot be used together.")
if unset_nodata:
# Setting nodata to None will raise NotImplementedError
@@ -207,17 +207,18 @@ def edit(ctx, input, bidx, nodata, unset_nodata, crs, unset_crs, transform,
except NotImplementedError as exc: # pragma: no cover
raise click.ClickException(str(exc))
- elif nodata is not options.IgnoreOption:
+ elif nodata is not None:
dtype = dst.dtypes[0]
if nodata is not None and not in_dtype_range(nodata, dtype):
raise click.BadParameter(
- "outside the range of the file's "
- "data type (%s)." % dtype,
- param=nodata, param_hint='nodata')
+ "outside the range of the file's data type (%s)." % dtype,
+ param=nodata,
+ param_hint="nodata",
+ )
dst.nodata = nodata
if unset_crs:
- dst.crs = None # CRS()
+ dst.crs = None
elif crs:
dst.crs = crs
=====================================
rasterio/rio/options.py
=====================================
@@ -57,20 +57,6 @@ from rasterio.path import parse_path, ParsedPath, UnparsedPath
logger = logging.getLogger(__name__)
-class IgnoreOptionMarker(object):
- """A marker for an option that is to be ignored.
-
- For use in the case where `None` is a meaningful option value,
- such as for nodata where `None` means "unset the nodata value."
- """
-
- def __repr__(self):
- return 'IgnoreOption()'
-
-
-IgnoreOption = IgnoreOptionMarker()
-
-
def _cb_key_val(ctx, param, value):
"""
@@ -180,29 +166,36 @@ def like_handler(ctx, param, value):
def nodata_handler(ctx, param, value):
"""Return a float or None"""
- if value is None or value is IgnoreOption:
- return value
- elif value.lower() in ['null', 'nil', 'none', 'nada']:
+ if value is None or value.lower() in ["null", "nil", "none", "nada"]:
return None
else:
try:
return float(value)
except (TypeError, ValueError):
raise click.BadParameter(
- "{!r} is not a number".format(value),
- param=param, param_hint='nodata')
+ "{!r} is not a number".format(value), param=param, param_hint="nodata"
+ )
def edit_nodata_handler(ctx, param, value):
"""Get nodata value from a template file or command line.
- Expected values are 'like', 'null', a numeric value, 'nan', or
- IgnoreOption. Anything else should raise BadParameter.
+ Expected values are 'like', 'null', a numeric value, 'nan', or None.
+
+ Returns
+ -------
+ float or None
+
+ Raises
+ ------
+ click.BadParameter
+
"""
- if value == 'like' or value is IgnoreOption:
+ if value == "like" or value is None:
retval = from_like_context(ctx, param, value)
if retval is not None:
return retval
+
return nodata_handler(ctx, param, value)
@@ -335,12 +328,20 @@ overwrite_opt = click.option(
help="Always overwrite an existing output file.")
nodata_opt = click.option(
- '--nodata', callback=nodata_handler, default=None,
- metavar='NUMBER|nan', help="Set a Nodata value.")
+ "--nodata",
+ callback=nodata_handler,
+ default=None,
+ metavar="NUMBER|nan",
+ help="Set a Nodata value.",
+)
edit_nodata_opt = click.option(
- '--nodata', callback=edit_nodata_handler, default=IgnoreOption,
- metavar='NUMBER|nan|null', help="Modify the Nodata value.")
+ "--nodata",
+ callback=edit_nodata_handler,
+ default=None,
+ metavar="NUMBER|nan|null",
+ help="Modify the Nodata value.",
+)
like_opt = click.option(
'--like',
=====================================
rasterio/shim_rasterioex.pxi
=====================================
@@ -4,7 +4,7 @@
from rasterio import dtypes
from rasterio.enums import Resampling
from rasterio.env import GDALVersion
-from rasterio.errors import ResamplingAlgorithmError
+from rasterio.errors import ResamplingAlgorithmError, DatasetIOShapeError
cimport numpy as np
@@ -145,6 +145,9 @@ cdef int io_multi_band(GDALDatasetH hds, int mode, double x0, double y0,
extras.pfnProgress = NULL
extras.pProgressData = NULL
+ if len(indexes) != data.shape[0]:
+ raise DatasetIOShapeError("Dataset indexes and destination buffer are mismatched")
+
bandmap = <int *>CPLMalloc(count*sizeof(int))
for i in range(count):
bandmap[i] = <int>indexes[i]
@@ -207,6 +210,9 @@ cdef int io_multi_mask(GDALDatasetH hds, int mode, double x0, double y0,
extras.pfnProgress = NULL
extras.pProgressData = NULL
+ if len(indexes) != data.shape[0]:
+ raise DatasetIOShapeError("Dataset indexes and destination buffer are mismatched")
+
for i in range(count):
j = <int>indexes[i]
band = GDALGetRasterBand(hds, j)
=====================================
rasterio/windows.py
=====================================
@@ -187,9 +187,14 @@ def union(*windows):
Window
"""
stacked = np.dstack([toranges(w) for w in windows])
- return Window.from_slices(
- (stacked[0, 0].min(), stacked[0, 1].max()),
- (stacked[1, 0].min(), stacked[1, 1]. max()))
+ row_start, row_stop = stacked[0, 0].min(), stacked[0, 1].max()
+ col_start, col_stop = stacked[1, 0].min(), stacked[1, 1].max()
+ return Window(
+ col_off=col_start,
+ row_off=row_start,
+ width=col_stop - col_start,
+ height=row_stop - row_start,
+ )
@iter_args
@@ -211,9 +216,14 @@ def intersection(*windows):
raise WindowError("windows do not intersect")
stacked = np.dstack([toranges(w) for w in windows])
- return Window.from_slices(
- (stacked[0, 0].max(), stacked[0, 1].min()),
- (stacked[1, 0].max(), stacked[1, 1]. min()))
+ row_start, row_stop = stacked[0, 0].max(), stacked[0, 1].min()
+ col_start, col_stop = stacked[1, 0].max(), stacked[1, 1].min()
+ return Window(
+ col_off=col_start,
+ row_off=row_start,
+ width=col_stop - col_start,
+ height=row_stop - row_start,
+ )
@iter_args
=====================================
setup.py
=====================================
@@ -334,11 +334,12 @@ inst_reqs = [
"affine",
"attrs",
"certifi",
- "click>=4.0,<8",
+ "click>=4.0",
"cligj>=0.5",
"numpy",
"snuggs>=1.4.1",
"click-plugins",
+ "setuptools",
]
extra_reqs = {
=====================================
tests/test_complex_dtypes.py
=====================================
@@ -1,4 +1,5 @@
import logging
+import subprocess
import sys
import uuid
@@ -8,9 +9,6 @@ import pytest
import rasterio
-logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
-
-
@pytest.fixture(scope='function')
def tempfile():
"""A temporary filename in the GDAL '/vsimem' filesystem"""
@@ -23,10 +21,8 @@ def complex_image(height, width, dtype):
[complex(x, x) for x in range(height * width)],
dtype=dtype).reshape(height, width)
-dtypes = ['complex', 'complex64', 'complex128']
-
- at pytest.mark.parametrize("dtype", dtypes)
+ at pytest.mark.parametrize("dtype", ["complex", "complex64", "complex128"])
@pytest.mark.parametrize("height,width", [(20, 20)])
def test_read_array(tempfile, dtype, height, width):
"""_io functions read and write arrays correctly"""
@@ -58,3 +54,45 @@ def test_complex_nodata(tmpdir):
with rasterio.open(tempfile) as dst:
assert dst.nodata == 0
+
+
+ at pytest.mark.gdalbin
+def test_complex_int16(tmpdir):
+ """A cint16 dataset can be created"""
+ import numpy as np
+ import rasterio
+ from rasterio.transform import Affine
+
+ x = np.linspace(-4.0, 4.0, 240)
+ y = np.linspace(-3.0, 3.0, 180)
+ X, Y = np.meshgrid(x, y)
+ Z1 = np.ones_like(X) + 1j
+
+ res = (x[-1] - x[0]) / 240.0
+ transform1 = Affine.translation(x[0] - res / 2, y[-1] - res / 2) * Affine.scale(
+ res, -res
+ )
+
+ tempfile = str(tmpdir.join("test.tif"))
+
+ with rasterio.open(
+ tempfile,
+ "w",
+ driver="GTiff",
+ height=Z1.shape[0],
+ width=Z1.shape[1],
+ nodata=0,
+ count=1,
+ dtype="complex_int16",
+ crs="+proj=latlong",
+ transform=transform1,
+ ) as dst:
+ dst.write(Z1, 1)
+
+ assert "Type=CInt16" in subprocess.check_output(["gdalinfo", tempfile]).decode(
+ "utf-8"
+ )
+
+ with rasterio.open(tempfile) as dst:
+ data = dst.read()
+ assert data.dtype == np.complex64
=====================================
tests/test_dtypes.py
=====================================
@@ -3,10 +3,26 @@ import pytest
import rasterio
from rasterio import (
- ubyte, uint8, uint16, uint32, int16, int32, float32, float64, complex_)
+ ubyte,
+ uint8,
+ uint16,
+ uint32,
+ int16,
+ int32,
+ float32,
+ float64,
+ complex_,
+ complex_int16,
+)
from rasterio.dtypes import (
- _gdal_typename, is_ndarray, check_dtype, get_minimum_dtype, can_cast_dtype,
- validate_dtype
+ _gdal_typename,
+ is_ndarray,
+ check_dtype,
+ get_minimum_dtype,
+ can_cast_dtype,
+ validate_dtype,
+ _is_complex_int,
+ _getnpdtype,
)
@@ -28,10 +44,19 @@ def test_check_dtype_invalid():
assert not check_dtype('foo')
-def test_gdal_name():
- assert _gdal_typename(ubyte) == 'Byte'
- assert _gdal_typename(np.uint8) == 'Byte'
- assert _gdal_typename(np.uint16) == 'UInt16'
+ at pytest.mark.parametrize(
+ ("dtype", "name"),
+ [
+ (ubyte, "Byte"),
+ (np.uint8, "Byte"),
+ (np.uint16, "UInt16"),
+ ("uint8", "Byte"),
+ ("complex_int16", "CInt16"),
+ (complex_int16, "CInt16"),
+ ],
+)
+def test_gdal_name(dtype, name):
+ assert _gdal_typename(dtype) == name
def test_get_minimum_dtype():
@@ -46,8 +71,8 @@ def test_get_minimum_dtype():
assert get_minimum_dtype(np.array([0, 1], dtype=np.uint)) == uint8
assert get_minimum_dtype(np.array([0, 1000], dtype=np.uint)) == uint16
assert get_minimum_dtype(np.array([0, 100000], dtype=np.uint)) == uint32
- assert get_minimum_dtype(np.array([-1, 0, 1], dtype=np.int)) == int16
- assert get_minimum_dtype(np.array([-1, 0, 100000], dtype=np.int)) == int32
+ assert get_minimum_dtype(np.array([-1, 0, 1], dtype=int)) == int16
+ assert get_minimum_dtype(np.array([-1, 0, 100000], dtype=int)) == int32
assert get_minimum_dtype(np.array([-1.5, 0, 1.5], dtype=np.float64)) == float32
@@ -89,3 +114,17 @@ def test_complex(tmpdir):
arr2 = src.read(1)
assert np.array_equal(arr1, arr2)
+
+
+def test_is_complex_int():
+ assert _is_complex_int("complex_int16")
+
+
+def test_not_is_complex_int():
+ assert not _is_complex_int("complex")
+
+
+def test_get_npdtype():
+ npdtype = _getnpdtype("complex_int16")
+ assert npdtype == np.complex64
+ assert npdtype.kind == "c"
=====================================
tests/test_merge.py
=====================================
@@ -59,3 +59,10 @@ def test_issue2163():
data = src.read()
result, transform = merge([src])
assert numpy.allclose(data, result)
+
+
+def test_unsafe_casting():
+ """Demonstrate fix for issue 2179"""
+ with rasterio.open("tests/data/float_raster_with_nodata.tif") as src:
+ result, transform = merge([src], dtype="uint8", nodata=0.0)
+ assert not result.any() # this is why it's called "unsafe".
=====================================
tests/test_read.py
=====================================
@@ -1,23 +1,18 @@
from hashlib import md5
-import logging
-import sys
import unittest
import numpy as np
import pytest
import rasterio
-
-
-logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
-
+from rasterio.errors import DatasetIOShapeError
# Find out if we've got HDF support (needed below).
try:
with rasterio.open('tests/data/no_band.h5') as s:
pass
has_hdf = True
-except:
+except Exception:
has_hdf = False
@@ -93,13 +88,7 @@ class ReaderContextTest(unittest.TestCase):
def test_read_out_dtype_fail(self):
with rasterio.open('tests/data/RGB.byte.tif') as s:
a = np.zeros((718, 791), dtype=rasterio.float32)
- try:
- s.read(1, a)
- except ValueError as e:
- assert ("the array's dtype 'float32' does not match the "
- "file's dtype") in str(e)
- except:
- assert "failed to catch exception" is False
+ s.read(1, a)
def test_read_basic(self):
with rasterio.open('tests/data/shade.tif') as s:
@@ -315,7 +304,7 @@ def test_out_shape_exceptions(path_rgb_byte_tif):
out_shape = (src.count, src.height, src.width)
reader(out=out, out_shape=out_shape)
- with pytest.raises(ValueError):
+ with pytest.raises(Exception):
out_shape = (5, src.height, src.width)
reader(1, out_shape=out_shape)
@@ -325,3 +314,10 @@ def test_out_shape_implicit(path_rgb_byte_tif):
with rasterio.open(path_rgb_byte_tif) as src:
out = src.read(indexes=(1, 2), out_shape=src.shape)
assert out.shape == (2,) + src.shape
+
+
+def test_out_shape_no_segfault(path_rgb_byte_tif):
+ """Prevent segfault as reported in 2189"""
+ with rasterio.open(path_rgb_byte_tif) as src:
+ with pytest.raises(DatasetIOShapeError):
+ src.read(out_shape=(2, src.height, src.width))
=====================================
tests/test_rio_edit_info.py
=====================================
@@ -65,7 +65,7 @@ def test_delete_nodata(data, runner):
"""Delete a dataset's nodata value"""
inputfile = str(data.join('RGB.byte.tif'))
result = runner.invoke(
- main_group, ['edit-info', inputfile, '--unset-nodata'])
+ main_group, ['edit-info', inputfile, '--unset-nodata'], catch_exceptions=False)
assert result.exit_code == 0
=====================================
tests/test_rio_options.py
=====================================
@@ -9,8 +9,13 @@ import pytest
from rasterio.enums import ColorInterp
from rasterio.rio.options import (
- IgnoreOption, bounds_handler, file_in_handler, like_handler,
- edit_nodata_handler, nodata_handler, _cb_key_val)
+ bounds_handler,
+ file_in_handler,
+ like_handler,
+ edit_nodata_handler,
+ nodata_handler,
+ _cb_key_val,
+)
class MockContext:
@@ -186,14 +191,14 @@ def test_edit_nodata_callback_like(data):
def test_edit_nodata_callback_all_like(data):
ctx = MockContext()
- ctx.obj['like'] = {'nodata': 0.0}
- ctx.obj['all_like'] = True
- assert edit_nodata_handler(ctx, MockOption('nodata'), IgnoreOption) == 0.0
+ ctx.obj["like"] = {"nodata": 0.0}
+ ctx.obj["all_like"] = True
+ assert edit_nodata_handler(ctx, MockOption("nodata"), None) == 0.0
def test_edit_nodata_callback_ignore(data):
ctx = MockContext()
- assert edit_nodata_handler(ctx, MockOption('nodata'), IgnoreOption) is IgnoreOption
+ assert edit_nodata_handler(ctx, MockOption("nodata"), None) is None
def test_edit_nodata_callback_none(data):
=====================================
tests/test_warp.py
=====================================
@@ -1,7 +1,6 @@
"""rasterio.warp module tests"""
import json
-import sys
import logging
from affine import Affine
@@ -15,7 +14,6 @@ from rasterio.crs import CRS
from rasterio.enums import Resampling
from rasterio.env import GDALVersion
from rasterio.errors import (
- GDALBehaviorChangeException,
CRSError,
GDALVersionError,
TransformError,
@@ -1136,6 +1134,7 @@ def test_reproject_resampling(path_rgb_byte_tif, method):
assert np.count_nonzero(out) in expected[method]
+
@pytest.mark.parametrize("test3d,count_nonzero", [(True, 1309625), (False, 437686)])
def test_reproject_array_interface(test3d, count_nonzero, path_rgb_byte_tif):
class DataArray:
@@ -1333,11 +1332,12 @@ def test_resample_default_invert_proj(method):
assert out.mean() > 0
+ at pytest.mark.xfail(reason="Projection extents have changed with PROJ 8")
def test_target_aligned_pixels():
"""Issue 853 has been resolved"""
with rasterio.open("tests/data/world.rgb.tif") as src:
source = src.read(1)
- profile = src.profile.copy()
+ profile = src.profile
dst_crs = "EPSG:3857"
@@ -1348,7 +1348,7 @@ def test_target_aligned_pixels():
)
dst_affine, dst_width, dst_height = aligned_target(
- dst_affine, dst_width, dst_height, 100000.0
+ dst_affine, dst_width, dst_height, 10000.0
)
profile["height"] = dst_height
@@ -1366,7 +1366,7 @@ def test_target_aligned_pixels():
resampling=Resampling.nearest,
)
- # Check that there is no black borders
+ # Check that there are no black borders
assert out[:, 0].all()
assert out[:, -1].all()
assert out[0, :].all()
@@ -1694,26 +1694,6 @@ def test_issue_1446_b():
assert all([-350 < x < -150 for x, y in transformed_geoms[183519]["coordinates"]])
-def test_issue_1076():
- """Confirm fix of #1076"""
- arr = (np.random.random((20, 30)) * 100).astype('int32')
- fill_value = 42
- newarr = np.full((200, 300), fill_value=fill_value, dtype='int32')
-
- src_crs = CRS.from_epsg(32632)
- src_transform = Affine(600.0, 0.0, 399960.0, 0.0, -600.0, 6100020.0)
- dst_transform = Affine(60.0, 0.0, 399960.0, 0.0, -60.0, 6100020.0)
-
- reproject(arr, newarr,
- src_transform=src_transform,
- dst_transform=dst_transform,
- src_crs=src_crs,
- dst_crs=src_crs,
- resample=Resampling.nearest)
-
- assert not (newarr == fill_value).all()
-
-
def test_reproject_init_dest_nodata():
"""No pixels should transfer over"""
crs = CRS.from_epsg(4326)
@@ -1785,16 +1765,16 @@ def test_reproject_rpcs_with_transformer_options(caplog):
transform = Affine(0.001953396267361111, 0.0, -124.00013888888888, 0.0, -0.001953396267361111, 50.000138888888884)
with mem.open(
driver="GTiff",
- width=1024,
- height=1024,
+ width=1024,
+ height=1024,
count=1,
transform=transform,
- dtype='int16',
- crs=crs
+ dtype="int16",
+ crs=crs,
) as dem:
# we flush dem dataset before letting GDAL read from vsimem
dem.write_band(1, 500 * np.ones((1024, 1024), dtype='int16'))
-
+
out = np.zeros(
(3, src.profile["width"], src.profile["height"]), dtype=np.uint8
)
@@ -1810,7 +1790,7 @@ def test_reproject_rpcs_with_transformer_options(caplog):
resampling=Resampling.nearest,
RPC_DEM=dem.name,
- )
+ )
caplog.set_level(logging.INFO)
reproject(
rasterio.band(src, src.indexes),
@@ -1820,8 +1800,8 @@ def test_reproject_rpcs_with_transformer_options(caplog):
dst_crs="EPSG:3857",
resampling=Resampling.nearest,
- )
-
+ )
+
assert not out.all()
assert not out2.all()
assert "RPC_DEM" in caplog.text
@@ -1853,10 +1833,10 @@ def test_warp_gcps_compute_dst_transform_automatically_array():
assert not out[:, -1, -1].any()
assert not out[:, -1, 0].any()
+
def test_warp_gcps_compute_dst_transform_automatically_reader(tmpdir):
"""Ensure we don't raise an exception when gcps passed without dst_transform, for a source dataset"""
tiffname = str(tmpdir.join('test.tif'))
- arr = np.ones((3, 800, 800), dtype=np.uint8) * 255
src_gcps = [
GroundControlPoint(row=0, col=0, x=156113, y=2818720, z=0),
GroundControlPoint(row=0, col=800, x=338353, y=2785790, z=0),
@@ -1867,7 +1847,7 @@ def test_warp_gcps_compute_dst_transform_automatically_reader(tmpdir):
with rasterio.open(tiffname, mode='w', height=800, width=800, count=3, dtype=np.uint8) as source:
source.gcps = (src_gcps, CRS.from_epsg(32618))
-
+
with rasterio.open(tiffname) as source:
reproject(
rasterio.band(source, source.indexes),
@@ -1875,13 +1855,14 @@ def test_warp_gcps_compute_dst_transform_automatically_reader(tmpdir):
dst_crs="EPSG:32618",
resampling=Resampling.nearest
)
-
+
assert not out.all()
assert not out[:, 0, 0].any()
assert not out[:, 0, -1].any()
assert not out[:, -1, -1].any()
assert not out[:, -1, 0].any()
+
def test_reproject_rpcs_exact_transformer(caplog):
"""Reproject using rational polynomial coefficients and DEM, requiring that
we don't try to make an approximate transformer.
@@ -1892,16 +1873,16 @@ def test_reproject_rpcs_exact_transformer(caplog):
transform = Affine(0.001953396267361111, 0.0, -124.00013888888888, 0.0, -0.001953396267361111, 50.000138888888884)
with mem.open(
driver="GTiff",
- width=1024,
- height=1024,
+ width=1024,
+ height=1024,
count=1,
transform=transform,
- dtype='int16',
- crs=crs
+ dtype="int16",
+ crs=crs,
) as dem:
# we flush dem dataset before letting GDAL read from vsimem
dem.write_band(1, 500 * np.ones((1024, 1024), dtype='int16'))
-
+
out = np.zeros(
(3, src.profile["width"], src.profile["height"]), dtype=np.uint8
)
@@ -1915,10 +1896,10 @@ def test_reproject_rpcs_exact_transformer(caplog):
dst_crs="EPSG:3857",
resampling=Resampling.nearest,
RPC_DEM=dem.name,
- )
+ )
assert "Created exact transformer" in caplog.text
-
+
def test_reproject_rpcs_approx_transformer(caplog):
"""Reproject using rational polynomial coefficients without a DEM, for which it's
@@ -1937,6 +1918,6 @@ def test_reproject_rpcs_approx_transformer(caplog):
rpcs=src_rpcs,
dst_crs="EPSG:3857",
resampling=Resampling.nearest,
- )
+ )
- assert "Created approximate transformer" in caplog.text
\ No newline at end of file
+ assert "Created approximate transformer" in caplog.text
=====================================
tests/test_warpedvrt.py
=====================================
@@ -608,3 +608,10 @@ def test_issue2086():
with rasterio.open("tests/data/white-gemini-iv.vrt") as src:
with WarpedVRT(src, crs=DST_CRS) as vrt:
assert vrt.shape == (1031, 1146)
+
+
+def test_gauss_no(path_rgb_byte_tif):
+ """Guard against the issue reported in #2190"""
+ with rasterio.open(path_rgb_byte_tif) as src:
+ with pytest.raises(Exception):
+ WarpedVRT(src, resampling=Resampling.gauss)
=====================================
tests/test_windows.py
=====================================
@@ -1,4 +1,3 @@
-from collections import namedtuple
import logging
import math
import sys
@@ -10,7 +9,7 @@ from hypothesis import given, assume, settings, HealthCheck
from hypothesis.strategies import floats, integers
import rasterio
-from rasterio.errors import RasterioDeprecationWarning, WindowError
+from rasterio.errors import WindowError
from rasterio.windows import (
crop, from_bounds, bounds, transform, evaluate, window_index, shape,
Window, intersect, intersection, get_data_window, union,
@@ -631,3 +630,27 @@ def test_zero_height(sy):
"""Permit a zero height window"""
transform = Affine.translation(0, 45.0) * Affine.scale(1.0, sy)
assert from_bounds(0.0, 44.0, 1.0, 44.0, transform).height == 0
+
+
+def test_union_boundless_left():
+ """Windows entirely to the left of a dataset form a proper union"""
+ uw = union(
+ Window(col_off=-10, row_off=0, width=2, height=2),
+ Window(col_off=-8.5, row_off=0, width=2.5, height=2),
+ )
+ assert uw.col_off == -10
+ assert uw.width == 4
+ assert uw.height == 2
+ assert uw.row_off == 0
+
+
+def test_union_boundless_above():
+ """Windows entirely above a dataset form a proper union"""
+ uw = union(
+ Window(col_off=0, row_off=-10, width=2, height=2),
+ Window(col_off=0, row_off=-8.5, width=2, height=2.5),
+ )
+ assert uw.row_off == -10
+ assert uw.height == 4
+ assert uw.width == 2
+ assert uw.col_off == 0
=====================================
tests/test_write.py
=====================================
@@ -107,8 +107,12 @@ def test_write_sbyte(tmpdir):
with rasterio.open(
name, 'w',
driver='GTiff', width=100, height=100, count=1,
- dtype=a.dtype) as s:
- s.write(a, indexes=1)
+ dtype=a.dtype) as dst:
+ dst.write(a, indexes=1)
+
+ with rasterio.open(name) as dst:
+ assert (dst.read() == -33).all()
+
info = subprocess.check_output(["gdalinfo", "-stats", name]).decode('utf-8')
assert "Minimum=-33.000, Maximum=-33.000, Mean=-33.000, StdDev=0.000" in info
assert 'SIGNEDBYTE' in info
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/commit/c6ca2f01db20e899ff73176f54c144088e32b34f
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/commit/c6ca2f01db20e899ff73176f54c144088e32b34f
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20210601/2d83d917/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list