Skip to content
Commits on Source (4)
Changes
=======
1.0.2 (2018-07-27)
------------------
Bug fixes:
- The output of calculate_default_transform() can be fixed to output dimensions
as well as to output resolution (#1409).
- In using rio-warp, the --src-bounds option can now override the bounds of the
source dataset when --dimensions is used (#1419).
- Bounds of rotated rasters are now calculated correctly (#1422).
- A band indexing bug in reproject() (#1350) has been fixed (#1424).
1.0.1 (2018-07-23)
------------------
......
rasterio (1.0.2-1) unstable; urgency=medium
* Team upload.
* New upstream release.
-- Bas Couwenberg <sebastic@debian.org> Sat, 28 Jul 2018 08:36:14 +0200
rasterio (1.0.1-1) unstable; urgency=medium
* Team upload.
......
......@@ -43,7 +43,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
__version__ = "1.0.1"
__version__ = "1.0.2"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
......
......@@ -788,7 +788,18 @@ cdef class DatasetBase(object):
"""
def __get__(self):
a, b, c, d, e, f, _, _, _ = self.transform
return BoundingBox(c, f + e * self.height, c + a * self.width, f)
width = self.width
height = self.height
if b == d == 0:
return BoundingBox(c, f + e * height, c + a * width, f)
else:
c0x, c0y = c, f
c1x, c1y = self.transform * (0, height)
c2x, c2y = self.transform * (width, height)
c3x, c3y = self.transform * (width, 0)
xs = (c0x, c1x, c2x, c3x)
ys = (c0y, c1y, c2y, c3y)
return BoundingBox(min(xs), min(ys), max(xs), max(ys))
property res:
"""Returns the (width, height) of pixels in the units of its
......
......@@ -433,8 +433,14 @@ def _reproject(
if dtypes.is_ndarray(destination):
if len(destination.shape) == 2:
destination = destination.reshape(1, *destination.shape)
dst_bidx = [1]
if destination.shape[0] == src_count:
# Output shape matches number of bands being extracted
dst_bidx = [i + 1 for i in range(src_count)]
else:
# Assume src and dst are the same shape
if max(src_bidx) > destination.shape[0]:
raise ValueError("Invalid destination shape")
dst_bidx = src_bidx
try:
......@@ -565,6 +571,11 @@ def _reproject(
psWOptions.hSrcDS = src_dataset
psWOptions.hDstDS = dst_dataset
for idx, (s, d) in enumerate(zip(src_bidx, dst_bidx)):
psWOptions.panSrcBands[idx] = s
psWOptions.panDstBands[idx] = d
log.debug('Configured to warp src band %d to destination band %d' % (s, d))
log.debug("Set transformer options")
# Now that the transformer and warp options are set up, we init
......
......@@ -231,6 +231,7 @@ def warp(ctx, files, output, driver, like, dst_crs, dimensions, src_bounds,
# Same projection, different dimensions, calculate resolution.
dst_crs = src.crs
dst_width, dst_height = dimensions
l, b, r, t = src_bounds or (l, b, r, t)
dst_transform = Affine(
(r - l) / float(dst_width),
0, l, 0,
......
......@@ -342,7 +342,7 @@ def aligned_target(transform, width, height, resolution):
@ensure_env
def calculate_default_transform(
src_crs, dst_crs, width, height, left=None, bottom=None, right=None,
top=None, gcps=None, resolution=None):
top=None, gcps=None, resolution=None, dst_width=None, dst_height=None):
"""Output dimensions and transform for a reprojection.
Source and destination coordinate reference systems and output
......@@ -374,6 +374,9 @@ def calculate_default_transform(
resolution: tuple (x resolution, y resolution) or float, optional
Target resolution, in units of target coordinate reference
system.
dst_width, dst_height: int, optional
Output file size in pixels and lines. Cannot be used together
with resolution.
Returns
-------
......@@ -399,6 +402,18 @@ def calculate_default_transform(
raise ValueError("Either four bounding values or ground control points"
"must be specified")
if (dst_width is None) != (dst_height is None):
raise ValueError("Either dst_width and dst_height must be specified "
"or none of them.")
if all(x is not None for x in (dst_width, dst_height)):
dimensions = (dst_width, dst_height)
else:
dimensions = None
if resolution and dimensions:
raise ValueError("Resolution cannot be used with dst_width and dst_height.")
dst_affine, dst_width, dst_height = _calculate_default_transform(
src_crs, dst_crs, width, height, left, bottom, right, top, gcps)
......@@ -427,4 +442,14 @@ def calculate_default_transform(
dst_width = ceil(dst_width * xratio)
dst_height = ceil(dst_height * yratio)
if dimensions:
xratio = dst_width / dimensions[0]
yratio = dst_height / dimensions[1]
dst_width = dimensions[0]
dst_height = dimensions[1]
dst_affine = Affine(dst_affine.a * xratio, dst_affine.b, dst_affine.c,
dst_affine.d, dst_affine.e * yratio, dst_affine.f)
return dst_affine, dst_width, dst_height
import rasterio
import numpy as np
def test_bounds():
with rasterio.open('tests/data/RGB.byte.tif') as src:
......@@ -13,3 +14,10 @@ def test_ul():
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))
......@@ -212,6 +212,28 @@ def test_warp_no_reproject_bounds_res(runner, tmpdir):
assert np.allclose(output.bounds, out_bounds)
def test_warp_no_reproject_src_bounds_dimensions(runner, tmpdir):
"""--src-bounds option works with dimensions"""
srcname = 'tests/data/shade.tif'
outputname = str(tmpdir.join('test.tif'))
out_bounds = [-11850000, 4810000, -11849000, 4812000]
result = runner.invoke(
main_group, [
'warp', srcname, outputname, '--dimensions', 9, 14,
'--src-bounds'] + out_bounds)
assert result.exit_code == 0
assert os.path.exists(outputname)
with rasterio.open(srcname) as src:
with rasterio.open(outputname) as output:
assert output.crs == src.crs
assert np.allclose(output.bounds, out_bounds)
assert np.allclose([111.111111, 142.857142],
[output.transform.a, -output.transform.e])
assert output.width == 9
assert output.height == 14
def test_warp_reproject_dst_crs(runner, tmpdir):
srcname = 'tests/data/RGB.byte.tif'
outputname = str(tmpdir.join('test.tif'))
......
......@@ -253,6 +253,24 @@ def test_calculate_default_transform_multiple_resolutions():
assert height == 20
def test_calculate_default_transform_dimensions():
with rasterio.open('tests/data/RGB.byte.tif') as src:
dst_width, dst_height = (113, 103)
target_transform = Affine(
0.02108612597535966, 0.0, -78.95864996545055,
0.0, -0.0192823863230055, 25.550873767433984
)
dst_transform, width, height = calculate_default_transform(
src.crs, {'init': 'EPSG:4326'}, src.width, src.height,
*src.bounds, dst_width=dst_width, dst_height=dst_height
)
assert dst_transform.almost_equals(target_transform)
assert width == dst_width
assert height == dst_height
def test_reproject_ndarray():
with rasterio.open('tests/data/RGB.byte.tif') as src:
source = src.read(1)
......@@ -1268,3 +1286,31 @@ def test_reproject_dst_alpha(path_rgb_msk_byte_tif):
dst_alpha=4)
assert dst_arr[3].any()
@pytest.mark.xfail(
rasterio.__gdal_version__ in ['2.2.0', '2.2.1', '2.2.2', '2.2.3'],
reason=("GDAL had regression in 2.2.X series, fixed in 2.2.4,"
" reproject used dst index instead of src index when destination was single band"))
def test_issue1350():
"""Warp bands other than 1 or All"""
with rasterio.open('tests/data/RGB.byte.tif') as src:
dst_crs = {'init': 'EPSG:3857'}
reprojected = []
for dtype, idx in zip(src.dtypes, src.indexes):
out = np.zeros((1,) + src.shape, dtype=dtype)
reproject(
rasterio.band(src, idx),
out,
resampling=Resampling.nearest,
dst_transform=DST_TRANSFORM,
dst_crs=dst_crs)
reprojected.append(out)
for i in range(1, len(reprojected)):
assert not (reprojected[0] == reprojected[i]).all()
......@@ -16,6 +16,27 @@ def test_gcps_bounds_exclusivity():
'epsg:4326', 'epsg:3857', width=1, height=1, left=1.0, gcps=[1])
def test_resolution_dimensions_exclusivity():
"""resolution and dimensions parameters are mutually exclusive"""
with pytest.raises(ValueError):
calculate_default_transform(
'epsg:4326', 'epsg:3857', width=1, height=1, gcps=[1],
resolution=1, dst_width=1, dst_height=1)
def test_dimensions_missing_params():
"""dst_width and dst_height must be specified together"""
with pytest.raises(ValueError):
calculate_default_transform(
'epsg:4326', 'epsg:3857', width=1, height=1, gcps=[1],
resolution=1, dst_width=1, dst_height=None)
with pytest.raises(ValueError):
calculate_default_transform(
'epsg:4326', 'epsg:3857', width=1, height=1, gcps=[1],
resolution=1, dst_width=None, dst_height=1)
def test_one_of_gcps_bounds():
"""at least one of gcps or bounds parameters must be provided"""
with pytest.raises(ValueError):
......