[Git][debian-gis-team/rasterio][experimental] 4 commits: New upstream version 1.2.2
Bas Couwenberg
gitlab at salsa.debian.org
Wed Apr 7 14:59:48 BST 2021
Bas Couwenberg pushed to branch experimental at Debian GIS Project / rasterio
Commits:
11360c97 by Bas Couwenberg at 2021-04-07T15:32:21+02:00
New upstream version 1.2.2
- - - - -
165b119c by Bas Couwenberg at 2021-04-07T15:32:54+02:00
Update upstream source from tag 'upstream/1.2.2'
Update to upstream version '1.2.2'
with Debian dir 2f84c02103c9ae09c077a0c9590828f5ac5f6b45
- - - - -
a0035050 by Bas Couwenberg at 2021-04-07T15:39:22+02:00
New upstream release.
- - - - -
55e5da9f by Bas Couwenberg at 2021-04-07T15:40:30+02:00
Set distribution to experimental.
- - - - -
17 changed files:
- CHANGES.txt
- debian/changelog
- pyproject.toml
- rasterio/__init__.py
- rasterio/_warp.pyx
- rasterio/enums.py
- rasterio/env.py
- rasterio/merge.py
- rasterio/rio/clip.py
- rasterio/session.py
- rasterio/warp.py
- rasterio/windows.py
- + tests/test_merge.py
- tests/test_rio_clip.py
- tests/test_session.py
- tests/test_warp.py
- tests/test_windows.py
Changes:
=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,23 @@
Changes
=======
+1.2.2 (2021-04-06)
+------------------
+
+- Add a --nodata option to rio-clip to help support the relative complement use
+ case (#2087).
+- Remove boundless keyword argument from WarpedVRT method signatures (#2084).
+- Implement the advertised intent of warp.reproject's src_crs parameter. It can
+ override the source's crs (#2036).
+- A regression in the merge tool's min and max methods has been fixed (#2145).
+- windows.from_bounds raises WindowError when the given bounds and transform are
+ inconsistent with each other (#2138).
+- Support for GDAL's "sum" resampling algorith has been added (#2137).
+- Cython and numpy versions are pinned in pyproject.toml. Numpy versions
+ correspond to scipy's oldest supported versions for Python versions 3.6-3.10.
+- GDAL data path is configured in env.py with set_gdal_config (#2139).
+- AWS sessions are not created for pre-signed URLs (#2133).
+
1.2.1 (2021-03-03)
------------------
=====================================
debian/changelog
=====================================
@@ -1,8 +1,10 @@
-rasterio (1.2.1-1~exp2) UNRELEASED; urgency=medium
+rasterio (1.2.2-1~exp1) experimental; urgency=medium
+ * Team upload.
+ * New upstream release.
* Update watch file for GitHub URL changes.
- -- Bas Couwenberg <sebastic at debian.org> Sat, 20 Mar 2021 09:14:41 +0100
+ -- Bas Couwenberg <sebastic at debian.org> Wed, 07 Apr 2021 15:40:17 +0200
rasterio (1.2.1-1~exp1) experimental; urgency=medium
=====================================
pyproject.toml
=====================================
@@ -1,3 +1,2 @@
[build-system]
-# Minimum requirements for the build system to execute.
-requires = ["setuptools", "wheel", "cython", "numpy"]
+requires = ["setuptools", "wheel", "cython==0.29.21", "oldest-supported-numpy"]
=====================================
rasterio/__init__.py
=====================================
@@ -27,7 +27,7 @@ import rasterio.enums
import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
-__version__ = "1.2.1"
+__version__ = "1.2.2"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
=====================================
rasterio/_warp.pyx
=====================================
@@ -477,7 +477,7 @@ def _reproject(
if key == b"RPC_DEM":
# don't .upper() since might be a path
val = str(val).encode('utf-8')
-
+
if rpcs:
bUseApproxTransformer = False
else:
@@ -1077,9 +1077,7 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
"""The dataset's coordinate reference system"""
return self.dst_crs
- def read(self, indexes=None, out=None, window=None, masked=False,
- out_shape=None, boundless=False, resampling=Resampling.nearest,
- fill_value=None, out_dtype=None):
+ def read(self, indexes=None, out=None, window=None, masked=False, out_shape=None, resampling=Resampling.nearest, fill_value=None, out_dtype=None, **kwargs):
"""Read a dataset's raw pixels as an N-d array
This data is read from the dataset's band cache, which means
@@ -1130,11 +1128,6 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
regular array. Masks will be exactly the inverse of the
GDAL RFC 15 conforming arrays returned by read_masks().
- boundless : bool, optional (default `False`)
- If `True`, windows that extend beyond the dataset's extent
- are permitted and partially or completely filled arrays will
- be returned as appropriate.
-
resampling : Resampling
By default, pixel values are read raw or interpolated using
a nearest neighbor algorithm from the band cache. Other
@@ -1144,6 +1137,10 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
fill_value : scalar
Fill value applied in the `boundless=True` case only.
+ kwargs : dict
+ This is only for backwards compatibility. No keyword arguments
+ are supported other than the ones named above.
+
Returns
-------
Numpy ndarray or a view on a Numpy ndarray
@@ -1153,15 +1150,14 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
preferentially used by callers.
"""
- if boundless:
+ if kwargs.get("boundless", False):
raise ValueError("WarpedVRT does not permit boundless reads")
else:
return super(WarpedVRTReaderBase, self).read(indexes=indexes, out=out, window=window, masked=masked, out_shape=out_shape, resampling=resampling, fill_value=fill_value, out_dtype=out_dtype)
- def read_masks(self, indexes=None, out=None, out_shape=None, window=None,
- boundless=False, resampling=Resampling.nearest):
+ def read_masks(self, indexes=None, out=None, out_shape=None, window=None, resampling=Resampling.nearest, **kwargs):
"""Read raster band masks as a multidimensional array"""
- if boundless:
+ if kwargs.get("boundless", False):
raise ValueError("WarpedVRT does not permit boundless reads")
else:
return super(WarpedVRTReaderBase, self).read_masks(indexes=indexes, out=out, window=window, out_shape=out_shape, resampling=resampling)
=====================================
rasterio/enums.py
=====================================
@@ -37,6 +37,8 @@ class Resampling(IntEnum):
'nearest', 'bilinear', 'cubic', 'cubic_spline', 'lanczos',
'average', 'mode' are always available (GDAL >= 1.10).
+ 'sum' is only supported in GDAL >= 3.1.
+
'rms' is only supported in GDAL >= 3.3.
Note: 'gauss' is not available to the functions in rio.warp.
@@ -54,6 +56,7 @@ class Resampling(IntEnum):
med = 10
q1 = 11
q3 = 12
+ sum = 13
rms = 14
=====================================
rasterio/env.py
=====================================
@@ -621,7 +621,7 @@ if 'GDAL_DATA' not in os.environ:
path = GDALDataFinder().search_wheel()
if path:
- os.environ['GDAL_DATA'] = path
+ set_gdal_config("GDAL_DATA", path)
log.debug("GDAL data found in package, GDAL_DATA set to %r.", path)
# See https://github.com/mapbox/rasterio/issues/1631.
@@ -632,7 +632,7 @@ if 'GDAL_DATA' not in os.environ:
path = GDALDataFinder().search()
if path:
- os.environ['GDAL_DATA'] = path
+ set_gdal_config("GDAL_DATA", path)
log.debug("GDAL_DATA not found in environment, set to %r.", path)
if "PROJ_LIB" in os.environ:
=====================================
rasterio/merge.py
=====================================
@@ -17,41 +17,37 @@ from rasterio.transform import Affine
logger = logging.getLogger(__name__)
-def copy_first(old_data, new_data, old_nodata, new_nodata, **kwargs):
- mask = np.empty_like(old_data, dtype='bool')
- np.logical_not(new_nodata, out=mask)
- np.logical_and(old_nodata, mask, out=mask)
- np.copyto(old_data, new_data, where=mask)
+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)
-def copy_last(old_data, new_data, old_nodata, new_nodata, **kwargs):
- mask = np.empty_like(old_data, dtype='bool')
- np.logical_not(new_nodata, out=mask)
- np.copyto(old_data, new_data, where=mask)
+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)
-def copy_min(old_data, new_data, old_nodata, new_nodata, **kwargs):
- mask = np.empty_like(old_data, dtype='bool')
- np.logical_or(old_nodata, new_nodata, out=mask)
+def copy_min(merged_data, new_data, merged_mask, new_mask, **kwargs):
+ mask = np.empty_like(merged_mask, dtype="bool")
+ np.logical_or(merged_mask, new_mask, out=mask)
np.logical_not(mask, out=mask)
+ 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.minimum(old_data, new_data, out=old_data, where=mask)
- np.logical_not(new_nodata, out=mask)
- np.logical_and(old_data, mask, out=mask)
- np.copyto(old_data, new_data, where=mask)
-
-
-def copy_max(old_data, new_data, old_nodata, new_nodata, **kwargs):
- mask = np.empty_like(old_data, dtype='bool')
- np.logical_or(old_nodata, new_nodata, out=mask)
+def copy_max(merged_data, new_data, merged_mask, new_mask, **kwargs):
+ mask = np.empty_like(merged_mask, dtype="bool")
+ np.logical_or(merged_mask, new_mask, out=mask)
np.logical_not(mask, out=mask)
-
- np.maximum(old_data, new_data, out=old_data, where=mask)
-
- np.logical_not(new_nodata, out=mask)
- np.logical_and(old_data, mask, out=mask)
- np.copyto(old_data, new_data, where=mask)
+ 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)
MERGE_METHODS = {
@@ -124,18 +120,18 @@ def merge(
max: pixel-wise max of existing and new
or custom callable with signature:
- def function(old_data, new_data, old_nodata, new_nodata, index=None, roff=None, coff=None):
+ def function(merged_data, new_data, merged_mask, new_mask, index=None, roff=None, coff=None):
Parameters
----------
- old_data : array_like
+ merged_data : array_like
array to update with new_data
new_data : array_like
data to merge
- same shape as old_data
- old_nodata, new_data : array_like
- boolean masks where old/new data is nodata
- same shape as old_data
+ same shape as merged_data
+ merged_mask, new_mask : array_like
+ boolean masks where merged/new data pixels are invalid
+ same shape as merged_data
index: int
index of the current dataset within the merged dataset collection
roff: int
@@ -333,13 +329,13 @@ def merge(
region = dest[:, roff:roff + trows, coff:coff + tcols]
if math.isnan(nodataval):
- region_nodata = np.isnan(region)
+ region_mask = np.isnan(region)
else:
- region_nodata = region == nodataval
- temp_nodata = np.ma.getmaskarray(temp)
+ region_mask = region == nodataval
+
+ temp_mask = np.ma.getmask(temp)
- copyto(region, temp, region_nodata, temp_nodata,
- index=idx, roff=roff, coff=coff)
+ copyto(region, temp, region_mask, temp_mask, index=idx, roff=roff, coff=coff)
if dst_path is None:
return dest, output_transform
=====================================
rasterio/rio/clip.py
=====================================
@@ -43,6 +43,7 @@ projection_projected_opt = click.option(
type=click.Path(exists=True),
help='Raster dataset to use as a template for bounds')
@options.format_opt
+ at options.nodata_opt
@projection_geographic_opt
@projection_projected_opt
@options.overwrite_opt
@@ -60,6 +61,7 @@ def clip(
bounds,
like,
driver,
+ nodata,
projection,
overwrite,
creation_options,
@@ -140,13 +142,18 @@ def clip(
width = int(out_window.width)
out_kwargs = src.profile
- out_kwargs.pop("driver", None)
+
if driver:
out_kwargs["driver"] = driver
+
+ if nodata is not None:
+ out_kwargs["nodata"] = nodata
+
out_kwargs.update({
'height': height,
'width': width,
'transform': src.window_transform(out_window)})
+
out_kwargs.update(**creation_options)
if "blockxsize" in out_kwargs and int(out_kwargs["blockxsize"]) > width:
@@ -166,5 +173,6 @@ def clip(
window=out_window,
out_shape=(src.count, height, width),
boundless=True,
+ masked=True,
)
)
=====================================
rasterio/session.py
=====================================
@@ -100,7 +100,9 @@ class Session(object):
if isinstance(path, UnparsedPath) or path.is_local:
return DummySession
- elif path.scheme == "s3" or "amazonaws.com" in path.path:
+ elif (
+ path.scheme == "s3" or "amazonaws.com" in path.path
+ ) and not "X-Amz-Signature" in path.path:
if boto3 is not None:
return AWSSession
else:
=====================================
rasterio/warp.py
=====================================
@@ -18,6 +18,9 @@ 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)
@@ -319,7 +322,7 @@ def reproject(source, destination=None, src_transform=None, gcps=None, rpcs=None
else:
src_count = 1
src_height, src_width = source.shape
-
+
# try to compute src_bounds if we don't have gcps
if not (gcps or rpcs):
src_bounds = array_bounds(src_height, src_width, src_transform)
@@ -331,9 +334,11 @@ def reproject(source, destination=None, src_transform=None, gcps=None, rpcs=None
if not (src_rdr.transform.is_identity and src_rdr.crs is None):
src_bounds = src_rdr.bounds
- src_crs = src_rdr.crs
+ src_crs = src_crs or src_rdr.crs
+
if isinstance(src_bidx, int):
src_bidx = [src_bidx]
+
src_count = len(src_bidx)
src_height, src_width = src_shape
gcps = src_rdr.gcps[0] if src_rdr.gcps[0] else None
=====================================
rasterio/windows.py
=====================================
@@ -285,6 +285,12 @@ def from_bounds(
if not isinstance(transform, Affine): # TODO: RPCs?
raise WindowError("A transform object is required to calculate the window")
+ if (right - left) / transform.a <= 0:
+ raise WindowError("Bounds and transform are inconsistent")
+
+ if (bottom - top) / transform.e <= 0:
+ raise WindowError("Bounds and transform are inconsistent")
+
rows, cols = rowcol(
transform,
[left, right, right, left],
=====================================
tests/test_merge.py
=====================================
@@ -0,0 +1,53 @@
+"""Tests of rasterio.merge"""
+
+import numpy
+import pytest
+
+import affine
+import rasterio
+from rasterio.merge import merge
+
+# Non-coincident datasets test fixture.
+# Three overlapping GeoTIFFs, two to the NW and one to the SE.
+ at pytest.fixture(scope="function")
+def test_data_dir_overlapping(tmp_path):
+ kwargs = {
+ "crs": "EPSG:4326",
+ "transform": affine.Affine(0.2, 0, -114, 0, -0.2, 46),
+ "count": 1,
+ "dtype": rasterio.uint8,
+ "driver": "GTiff",
+ "width": 10,
+ "height": 10,
+ "nodata": 0,
+ }
+
+ with rasterio.open(tmp_path.joinpath("nw1.tif"), "w", **kwargs) as dst:
+ data = numpy.ones((10, 10), dtype=rasterio.uint8)
+ dst.write(data, indexes=1)
+
+ with rasterio.open(tmp_path.joinpath("nw3.tif"), "w", **kwargs) as dst:
+ data = numpy.ones((10, 10), dtype=rasterio.uint8) * 3
+ dst.write(data, indexes=1)
+
+ kwargs["transform"] = affine.Affine(0.2, 0, -113, 0, -0.2, 45)
+
+ with rasterio.open(tmp_path.joinpath("se.tif"), "w", **kwargs) as dst:
+ data = numpy.ones((10, 10), dtype=rasterio.uint8) * 2
+ dst.write(data, indexes=1)
+
+ return tmp_path
+
+
+ at pytest.mark.parametrize(
+ "method,value", [("first", 1), ("last", 2), ("min", 1), ("max", 3)]
+)
+def test_merge_method(test_data_dir_overlapping, method, value):
+ """Merge method produces expected values in intersection"""
+ inputs = sorted(list(test_data_dir_overlapping.iterdir())) # nw is first.
+ datasets = [rasterio.open(x) for x in inputs]
+ output_count = 1
+ arr, _ = merge(
+ datasets, output_count=output_count, method=method, dtype=numpy.uint64
+ )
+ numpy.testing.assert_array_equal(arr[:, 5:10, 5:10], value)
=====================================
tests/test_rio_clip.py
=====================================
@@ -145,3 +145,31 @@ def test_clip_rotated(runner, tmpdir):
main_group, ['clip', 'tests/data/rotated.tif', output])
assert result.exit_code == 2
assert 'Non-rectilinear' in result.output
+
+
+ at pytest.mark.parametrize("bounds", [bbox(31.0, -1.0, 33.0, 1.0)])
+def test_clip_bounds_with_complement_nodata(runner, tmpdir, bounds):
+ """Show fix of #2084"""
+ output = str(tmpdir.join("test.tif"))
+ result = runner.invoke(
+ main_group,
+ [
+ "clip",
+ "tests/data/green.tif",
+ output,
+ "--bounds",
+ bounds,
+ "--with-complement",
+ "--nodata",
+ "0",
+ ],
+ )
+ assert result.exit_code == 0
+ assert os.path.exists(output)
+
+ with rasterio.open(output) as out:
+ assert out.shape == (4, 4)
+ data = out.read(masked=True)
+ assert not data.mask[:, 2:, :2].any()
+ assert data.mask[:, :2, :].all()
+ assert data.mask[:, :, 2:].all()
=====================================
tests/test_session.py
=====================================
@@ -116,6 +116,12 @@ def test_session_factory_s3():
assert isinstance(sesh, AWSSession)
+def test_session_factory_s3_presigned_url():
+ """Get a DummySession for presigned URLs"""
+ sesh = Session.from_path("https://fancy-cloud.com/lolwut?X-Amz-Signature=foo")
+ assert isinstance(sesh, DummySession)
+
+
def test_session_factory_s3_no_boto3(monkeypatch):
"""Get an AWSSession for s3:// paths"""
pytest.importorskip("boto3")
=====================================
tests/test_warp.py
=====================================
@@ -1116,6 +1116,7 @@ def test_reproject_resampling(path_rgb_byte_tif, method):
Resampling.med: [437194],
Resampling.q1: [436397],
Resampling.q3: [438948],
+ Resampling.sum: [439118],
Resampling.rms: [439385],
}
@@ -1207,6 +1208,7 @@ def test_reproject_resampling_alpha(method):
Resampling.med: [437194],
Resampling.q1: [436397],
Resampling.q3: [438948],
+ Resampling.sum: [439118],
Resampling.rms: [439385],
}
=====================================
tests/test_windows.py
=====================================
@@ -606,3 +606,12 @@ def test_from_bounds_rotation():
assert win.row_off == pytest.approx(-2.0)
assert win.width == pytest.approx(2.0 * width)
assert win.height == pytest.approx(2.0 * height)
+
+
+def test_issue_2138():
+ """WindowError is raised if bounds and transform are inconsistent"""
+ w, s, e, n = 1.0, 45.7, 1.2, 45.9
+ a = 0.001
+ transform = Affine.translation(w, n) * Affine.scale(a, -a)
+ with pytest.raises(WindowError):
+ from_bounds(w, n, e, s, transform)
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/compare/96b70e016215d1a8db747c976a79e520163afb17...55e5da9f10986f76959a12d4e6dde8fa5e44d18d
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/compare/96b70e016215d1a8db747c976a79e520163afb17...55e5da9f10986f76959a12d4e6dde8fa5e44d18d
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/20210407/b91038f7/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list