Skip to content
Commits on Source (4)
dist: trusty
language: python
dist: trusty
env:
global:
- PIP_WHEEL_DIR=$HOME/.cache/pip/wheels
......@@ -17,28 +17,16 @@ jobs:
env: GDALVERSION="1.11.5" PROJVERSION="4.8.0"
- python: "2.7"
env: GDALVERSION="2.2.4" PROJVERSION="4.9.3"
- python: "2.7"
env: GDALVERSION="2.3.3" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="2.2.4" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="2.3.3" PROJVERSION="4.9.3"
- python: "2.7"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.7"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.8"
env: GDALVERSION="2.4.3" PROJVERSION="4.9.3"
- python: "3.6"
env: GDALVERSION="3.0.1" PROJVERSION="6.1.1"
- python: "3.7"
env: GDALVERSION="3.0.1" PROJVERSION="6.1.1"
- python: "3.8"
env: GDALVERSION="3.0.1" PROJVERSION="6.1.1"
- python: "3.8"
env: GDALVERSION="master" PROJVERSION="6.1.1"
allow_failures:
- env: GDALVERSION="master" PROJVERSION="6.1.1"
addons:
apt:
......@@ -49,6 +37,11 @@ addons:
- libatlas-base-dev
- gfortran
before_script:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- "sleep 3"
before_install:
- python -m pip install -U pip
- python -m pip install wheel
......@@ -69,11 +62,6 @@ install:
- "rio --gdal-version"
- "python -m pip list"
before_script:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- "sleep 3"
script:
- "if [[ $TRAVIS_PYTHON_VERSION == 3.5 && $GDALVERSION == 2.1.0 ]]; then python -m pytest --doctest-ignore-import-errors --doctest-glob='*.rst' docs/*.rst -k 'not index and not quickstart and not switch' ; fi"
- python -m pytest -v -m "not wheel" -rxXs --cov rasterio --cov-report term-missing
......
Changes
=======
1.1.2 (2019-12-18)
------------------
Bug fixes:
- Sampling of WarpedVRT datasets was broken in version in 1.1.1 (#1833) and has
been fixed.
- The missing out_dtype keyword argument has been added to
WarpedVRTReaderBase.read() (#1849).
- The missing --format option has been added to rio-calc (#1846).
- Reduce all the band masks when computing the dataset mask. Previously we had
missed the last band.
- PR #1842 makes sure that rio-calc's cleanup doesn't fail due to unbound
variables.
- The conflict between the --bbox/--feature/--collection and
--sequence/--collection option of rio-bounds (#1807) has been fixed by
removing "collection" from the possible JSON type options.
- Increase default precision for the merge tool from 7 to 10 (#1837).
- Allow rio-clip and rio-convert to overwrite output files (#1836).
- Allow src_crs parameter to fully override the source dataset's CRS (#1808).
Packaging notes:
- The wheels on PyPI now include the base (version 1.8) PROJ datum grids and
are thus a few MB larger in size.
1.1.1 (2019-11-13)
------------------
......
rasterio (1.1.1-2) UNRELEASED; urgency=medium
rasterio (1.1.2-1) unstable; urgency=medium
* Team upload.
* New upstream release.
* Drop Name field from upstream metadata.
-- Bas Couwenberg <sebastic@debian.org> Mon, 09 Dec 2019 09:42:10 +0100
-- Bas Couwenberg <sebastic@debian.org> Thu, 19 Dec 2019 05:25:17 +0100
rasterio (1.1.1-1) unstable; urgency=medium
......
......@@ -42,7 +42,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
__version__ = "1.1.1"
__version__ = "1.1.2"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
......
......@@ -713,31 +713,73 @@ cdef class DatasetReaderBase(DatasetBase):
def dataset_mask(self, out=None, out_shape=None, window=None,
boundless=False, resampling=Resampling.nearest):
"""Calculate the dataset's 2D mask. Derived from the individual band masks
provided by read_masks().
"""Get the dataset's 2D valid data mask.
Parameters
----------
out, out_shape, window, boundless and resampling are passed directly to read_masks()
out : numpy ndarray, optional
As with Numpy ufuncs, this is an optional reference to an
output array with the same dimensions and shape into which
data will be placed.
*Note*: the method's return value may be a view on this
array. In other words, `out` is likely to be an
incomplete representation of the method's results.
Cannot be combined with `out_shape`.
out_shape : tuple, optional
A tuple describing the output array's shape. Allows for decimated
reads without constructing an output Numpy array.
Cannot be combined with `out`.
window : a pair (tuple) of pairs of ints or Window, optional
The optional `window` argument is a 2 item tuple. The first
item is a tuple containing the indexes of the rows at which
the window starts and stops and the second is a tuple
containing the indexes of the columns at which the window
starts and stops. For example, ((0, 2), (0, 2)) defines
a 2x2 window at the upper left of the raster dataset.
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
resampling algorithms may be specified. Resampled pixels
are not cached.
Returns
-------
ndarray, shape=(self.height, self.width), dtype='uint8'
0 = nodata, 255 = valid data
The dataset mask is calculate based on the individual band masks according to
the following logic, in order of precedence:
1. If a .msk file, dataset-wide alpha or internal mask exists,
it will be used as the dataset mask.
2. If an 4-band RGBA with a shadow nodata value,
band 4 will be used as the dataset mask.
3. If a nodata value exists, use the binary OR (|) of the band masks
4. If no nodata value exists, return a mask filled with 255
Note that this differs from read_masks and GDAL RFC15
in that it applies per-dataset, not per-band
(see https://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask)
Numpy ndarray or a view on a Numpy ndarray
The dtype of this array is uint8. 0 = nodata, 255 = valid
data.
Notes
-----
Note: as with Numpy ufuncs, an object is returned even if you
use the optional `out` argument and the return value shall be
preferentially used by callers.
The dataset mask is calculated based on the individual band
masks according to the following logic, in order of precedence:
1. If a .msk file, dataset-wide alpha, or internal mask exists
it will be used for the dataset mask.
2. Else if the dataset is a 4-band with a shadow nodata value, band 4 will be
used as the dataset mask.
3. If a nodata value exists, use the binary OR (|) of the band
masks 4. If no nodata value exists, return a mask filled with
255.
Note that this differs from read_masks and GDAL RFC15 in that it
applies per-dataset, not per-band (see
https://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask)
"""
kwargs = {
'out': out,
......@@ -746,21 +788,23 @@ cdef class DatasetReaderBase(DatasetBase):
'boundless': boundless,
'resampling': resampling}
# GDAL found dataset-wide alpha band or mask
# All band masks are equal so we can return the first
if MaskFlags.per_dataset in self.mask_flag_enums[0]:
return self.read_masks(1, **kwargs)
# use Alpha mask if available and looks like RGB, even if nodata is shadowing
elif self.count == 4 and self.colorinterp[0] == ColorInterp.red:
return self.read_masks(4, **kwargs)
# Or use the binary OR intersection of all GDALGetMaskBands
else:
mask = self.read_masks(1, **kwargs)
for i in range(1, self.count):
mask = mask | self.read_masks(i, **kwargs)
return mask
elif out is not None:
kwargs.pop("out", None)
kwargs["out_shape"] = (self.count, out.shape[-2], out.shape[-1])
out = 255 * np.logical_or.reduce(self.read_masks(**kwargs))
return out
elif out_shape is not None:
kwargs["out_shape"] = (self.count, out_shape[-2], out_shape[-1])
return 255 * np.logical_or.reduce(self.read_masks(**kwargs))
def sample(self, xy, indexes=None, masked=False):
"""Get the values of a dataset at certain positions
......
......@@ -888,13 +888,16 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
psWOptions.hSrcDS = hds
try:
if self.dst_width and self.dst_height and self.dst_transform:
# set up transform args (otherwise handled in
# GDALAutoCreateWarpedVRT)
try:
hTransformArg = exc_wrap_pointer(
GDALCreateGenImgProjTransformer3(
src_crs_wkt, src_gt, dst_crs_wkt, dst_gt))
if c_tolerance > 0.0:
hTransformArg = exc_wrap_pointer(
GDALCreateApproxTransformer(
......@@ -909,6 +912,7 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
log.debug("Created transformer and options.")
psWOptions.pTransformerArg = hTransformArg
except Exception:
GDALDestroyApproxTransformer(hTransformArg)
raise
......@@ -917,16 +921,20 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
hds_warped = GDALCreateWarpedVRT(
hds, c_width, c_height, dst_gt, psWOptions)
GDALSetProjection(hds_warped, dst_crs_wkt)
self._hds = exc_wrap_pointer(hds_warped)
else:
with nogil:
hds_warped = GDALAutoCreateWarpedVRT(
hds, NULL, dst_crs_wkt, c_resampling,
hds, src_crs_wkt, dst_crs_wkt, c_resampling,
c_tolerance, psWOptions)
self._hds = exc_wrap_pointer(hds_warped)
except CPLE_OpenFailedError as err:
raise RasterioIOError(err.errmsg)
finally:
CPLFree(dst_crs_wkt)
CSLDestroy(c_warp_extras)
......@@ -972,12 +980,84 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
def read(self, indexes=None, out=None, window=None, masked=False,
out_shape=None, boundless=False, resampling=Resampling.nearest,
fill_value=None):
"""Read a dataset's raw pixels as an N-d array"""
fill_value=None, out_dtype=None):
"""Read a dataset's raw pixels as an N-d array
This data is read from the dataset's band cache, which means
that repeated reads of the same windows may avoid I/O.
Parameters
----------
indexes : list of ints or a single int, optional
If `indexes` is a list, the result is a 3D array, but is
a 2D array if it is a band index number.
out : numpy ndarray, optional
As with Numpy ufuncs, this is an optional reference to an
output array into which data will be placed. If the height
and width of `out` differ from that of the specified
window (see below), the raster image will be decimated or
replicated using the specified resampling method (also see
below).
*Note*: the method's return value may be a view on this
array. In other words, `out` is likely to be an
incomplete representation of the method's results.
This parameter cannot be combined with `out_shape`.
out_dtype : str or numpy dtype
The desired output data type. For example: 'uint8' or
rasterio.uint16.
out_shape : tuple, optional
A tuple describing the shape of a new output array. See
`out` (above) for notes on image decimation and
replication.
Cannot combined with `out`.
window : a pair (tuple) of pairs of ints or Window, optional
The optional `window` argument is a 2 item tuple. The first
item is a tuple containing the indexes of the rows at which
the window starts and stops and the second is a tuple
containing the indexes of the columns at which the window
starts and stops. For example, ((0, 2), (0, 2)) defines
a 2x2 window at the upper left of the raster dataset.
masked : bool, optional
If `masked` is `True` the return value will be a masked
array. Otherwise (the default) the return value will be a
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
resampling algorithms may be specified. Resampled pixels
are not cached.
fill_value : scalar
Fill value applied in the `boundless=True` case only.
Returns
-------
Numpy ndarray or a view on a Numpy ndarray
Note: as with Numpy ufuncs, an object is returned even if you
use the optional `out` argument and the return value shall be
preferentially used by callers.
"""
if boundless:
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)
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):
......
......@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
MERGE_METHODS = ('first', 'last', 'min', 'max')
def merge(datasets, bounds=None, res=None, nodata=None, precision=7, indexes=None,
def merge(datasets, bounds=None, res=None, nodata=None, precision=10, indexes=None,
method='first'):
"""Copy valid pixels from input files to an output file.
......
......@@ -33,8 +33,7 @@ logger = logging.getLogger(__name__)
help="Output in specified coordinates.")
@options.sequence_opt
@use_rs_opt
@geojson_type_collection_opt(True)
@geojson_type_feature_opt(False)
@geojson_type_feature_opt(True)
@geojson_type_bbox_opt(False)
@click.pass_context
def bounds(ctx, input, precision, indent, compact, projection, dst_crs,
......
......@@ -7,6 +7,7 @@ from distutils.version import LooseVersion
import math
import click
from cligj import format_opt
import snuggs
import rasterio
......@@ -80,6 +81,7 @@ def _chunk_output(width, height, count, itemsize, mem_limit=1):
@click.argument('command')
@options.files_inout_arg
@options.output_opt
@format_opt
@click.option('--name', multiple=True,
help='Specify an input file with a unique short (alphas only) '
'name for use in commands like '
......@@ -90,7 +92,7 @@ def _chunk_output(width, height, count, itemsize, mem_limit=1):
@click.option("--mem-limit", type=int, default=64, help="Limit on memory used to perform calculations, in MB.")
@options.creation_options
@click.pass_context
def calc(ctx, command, files, output, name, dtype, masked, overwrite, mem_limit, creation_options):
def calc(ctx, command, files, output, driver, name, dtype, masked, overwrite, mem_limit, creation_options):
"""A raster data calculator
Evaluates an expression using input datasets and writes the result
......@@ -135,6 +137,9 @@ def calc(ctx, command, files, output, name, dtype, masked, overwrite, mem_limit,
"""
import numpy as np
dst = None
sources = []
try:
with ctx.obj['env']:
output, files = resolve_inout(files=files, output=output,
......@@ -149,6 +154,9 @@ def calc(ctx, command, files, output, name, dtype, masked, overwrite, mem_limit,
dtype = dtype or first.meta['dtype']
kwargs['dtype'] = dtype
if driver:
kwargs['driver'] = driver
# Extend snuggs.
snuggs.func_map['read'] = _read_array
snuggs.func_map['band'] = lambda d, i: _get_bands(inputs, sources, d, i)
......@@ -156,8 +164,6 @@ def calc(ctx, command, files, output, name, dtype, masked, overwrite, mem_limit,
snuggs.func_map['fillnodata'] = lambda *args: fillnodata(*args)
snuggs.func_map['sieve'] = lambda *args: sieve(*args)
dst = None
# The windows iterator is initialized with a single sample.
# The actual work windows will be added in the second
# iteration of the loop.
......@@ -205,8 +211,8 @@ def calc(ctx, command, files, output, name, dtype, masked, overwrite, mem_limit,
except snuggs.ExpressionError as err:
click.echo("Expression Error:")
click.echo(' %s' % err.text)
click.echo(' ' + ' ' * err.offset + "^")
click.echo(" {}".format(err.text))
click.echo(" {}^".format(" " * err.offset))
click.echo(err)
raise click.Abort()
......
......@@ -48,10 +48,11 @@ projection_projected_opt = click.option(
@format_opt
@projection_geographic_opt
@projection_projected_opt
@options.overwrite_opt
@options.creation_options
@click.pass_context
def clip(ctx, files, output, bounds, like, driver, projection,
creation_options):
overwrite, creation_options):
"""Clips a raster using projected or geographic bounds.
\b
......@@ -78,7 +79,7 @@ def clip(ctx, files, output, bounds, like, driver, projection,
with ctx.obj['env']:
output, files = resolve_inout(files=files, output=output)
output, files = resolve_inout(files=files, output=output, overwrite=overwrite)
input = files[0]
with rasterio.open(input) as src:
......
......@@ -20,11 +20,12 @@ from rasterio.rio.helpers import resolve_inout
@click.option('--scale-offset', type=float, default=None,
help="Source to destination scaling offset.")
@options.rgb_opt
@options.overwrite_opt
@options.creation_options
@click.pass_context
def convert(
ctx, files, output, driver, dtype, scale_ratio, scale_offset,
photometric, creation_options):
photometric, overwrite, creation_options):
"""Copy and convert raster datasets to other data types and formats.
Data values may be linearly scaled when copying by using the
......@@ -50,7 +51,7 @@ def convert(
"""
with ctx.obj['env']:
outputfile, files = resolve_inout(files=files, output=output)
outputfile, files = resolve_inout(files=files, output=output, overwrite=overwrite)
inputfile = files[0]
with rasterio.open(inputfile) as src:
......
......@@ -38,25 +38,18 @@ def write_features(
bbox = (min(xs), min(ys), max(xs), max(ys))
if use_rs:
fobj.write(u'\u001e')
if geojson_type == 'feature':
fobj.write(json.dumps(feat, **dump_kwds))
elif geojson_type == 'bbox':
if geojson_type == 'bbox':
fobj.write(json.dumps(bbox, **dump_kwds))
else:
fobj.write(
json.dumps({
'type': 'FeatureCollection',
'bbox': bbox,
'features': [feat]}, **dump_kwds))
fobj.write(json.dumps(feat, **dump_kwds))
fobj.write('\n')
# Aggregate all features into a single object expressed as
# bbox or collection.
else:
features = list(collection())
if geojson_type == 'bbox':
fobj.write(json.dumps(collection.bbox, **dump_kwds))
elif geojson_type == 'feature':
fobj.write(json.dumps(features[0], **dump_kwds))
else:
fobj.write(json.dumps({
'bbox': collection.bbox,
......
......@@ -18,7 +18,7 @@ from rasterio.rio.helpers import resolve_inout
@options.nodata_opt
@options.bidx_mult_opt
@options.overwrite_opt
@click.option('--precision', type=int, default=7,
@click.option('--precision', type=int, default=10,
help="Number of decimal places of precision in alignment of "
"pixels")
@options.creation_options
......
......@@ -2,6 +2,7 @@
import numpy
from rasterio.enums import MaskFlags
from rasterio.windows import Window
......@@ -31,14 +32,24 @@ def sample_gen(dataset, xy, indexes=None, masked=False):
index = dataset.index
read = dataset.read
if isinstance(indexes, int):
if indexes is None:
indexes = dataset.indexes
elif isinstance(indexes, int):
indexes = [indexes]
for x, y in xy:
row_off, col_off = index(x, y)
# if row_off < 0 or col_off < 0:
# yield numpy.ones((dataset.count,), dtype=dataset.dtypes[0]) * dataset.nodata
# else:
window = Window(col_off, row_off, 1, 1)
data = read(indexes, window=window, masked=masked, boundless=True)
yield data[:, 0, 0]
if row_off < 0 or col_off < 0 or row_off >= dataset.height or col_off >= dataset.width:
data = numpy.ones((len(indexes),), dtype=dataset.dtypes[0]) * (dataset.nodata or 0)
if masked:
mask = [False if MaskFlags.all_valid in dataset.mask_flag_enums[i - 1] else True for i in indexes]
yield numpy.ma.array(data, mask=mask)
else:
yield data
else:
window = Window(col_off, row_off, 1, 1)
data = read(indexes, window=window, masked=masked)
yield data[:, 0, 0]
......@@ -188,20 +188,23 @@ def test_rgba_msk(tiffs):
# mask takes precendent over alpha
assert np.array_equal(src.dataset_mask(), msk)
def test_kwargs(tiffs):
with rasterio.open(str(tiffs.join('rgb_ndv.tif'))) as src:
# window and boundless are passed along
other = src.dataset_mask(window=((1, 4), (1, 4)), boundless=True)
assert np.array_equal(alp_shift_lr, other)
other = src.dataset_mask(out_shape=(1, 5, 5))
assert np.array_equal(resampmask, other)
out = np.zeros((1, 5, 5), dtype=np.uint8)
other = src.dataset_mask(out=out)
assert np.array_equal(resampmask, other)
# band indexes are not supported
@pytest.mark.parametrize("kwds,expected", [(dict(window=((1, 4), (1, 4)), boundless=True), alp_shift_lr), (dict(out_shape=(1, 5, 5)), resampmask), (dict(out=np.zeros((1, 5, 5), dtype=np.uint8)), resampmask)])
def test_kwargs(tiffs, kwds, expected):
with rasterio.open(str(tiffs.join('rgb_ndv.tif'))) as src:
result = src.dataset_mask(**kwds)
assert np.array_equal(expected, result)
#
# other = src.dataset_mask(out_shape=(1, 5, 5))
# assert np.array_equal(resampmask, other)
#
# out = np.zeros((1, 5, 5), dtype=np.uint8)
# other = src.dataset_mask(out=out)
# assert np.array_equal(resampmask, other)
def test_indexes_not_supported(tiffs):
with rasterio.open(str(tiffs.join('rgb_ndv.tif'))) as src:
with pytest.raises(TypeError):
src.dataset_mask(indexes=1)
......
import pytest
import rasterio
from rasterio.rio.main import main_group
def test_bounds_sequence_single(runner, basic_image_file):
"""
--sequence option should produce a feature collection for a single image.
"""
result = runner.invoke(main_group, ["bounds", "--sequence", basic_image_file])
assert result.output.count('"FeatureCollection"') == 0
assert result.output.count('"Feature"') == 1
def tests_bounds_sequence_multiple(runner, basic_image_file):
"""
--sequence option should produce a feature collection for each image passed as argument.
"""
result = runner.invoke(
main_group, ["bounds", "--sequence", basic_image_file, basic_image_file]
)
assert result.output.count('"FeatureCollection"') == 0
assert result.output.count('"Feature"') == 2
def test_bounds_no_sequence_multiple(runner, basic_image_file):
"""
--no-sequence option should produce a single feature collection
"""
result = runner.invoke(
main_group, ["bounds", "--collection", basic_image_file, basic_image_file]
)
assert result.output.count('"FeatureCollection"') == 1
assert result.output.count('"Feature"') == 2
......@@ -84,6 +84,34 @@ def test_clip_like_disjunct(runner, tmpdir):
assert '--like' in result.output
def test_clip_overwrite_without_option(runner, tmpdir):
output = str(tmpdir.join('test.tif'))
result = runner.invoke(
main_group,
['clip', 'tests/data/shade.tif', output, '--bounds', bbox(*TEST_BBOX)])
assert result.exit_code == 0
result = runner.invoke(
main_group,
['clip', 'tests/data/shade.tif', output, '--bounds', bbox(*TEST_BBOX)])
assert result.exit_code == 1
assert '--overwrite' in result.output
def test_clip_overwrite_with_option(runner, tmpdir):
output = str(tmpdir.join('test.tif'))
result = runner.invoke(
main_group,
['clip', 'tests/data/shade.tif', output, '--bounds', bbox(*TEST_BBOX)])
assert result.exit_code == 0
result = runner.invoke(
main_group, [
'clip', 'tests/data/shade.tif', output, '--bounds', bbox(*TEST_BBOX),
'--overwrite'])
assert result.exit_code == 0
# Tests: format and type conversion, --format and --dtype
def test_format(tmpdir):
......@@ -202,3 +230,31 @@ def test_rgb(tmpdir):
assert result.exit_code == 0
with rasterio.open(outputname) as src:
assert src.colorinterp[0] == rasterio.enums.ColorInterp.red
def test_convert_overwrite_without_option(runner, tmpdir):
outputname = str(tmpdir.join('test.tif'))
result = runner.invoke(
main_group,
['convert', 'tests/data/RGB.byte.tif', '-o', outputname, '-f', 'JPEG'])
assert result.exit_code == 0
result = runner.invoke(
main_group,
['convert', 'tests/data/RGB.byte.tif', '-o', outputname, '-f', 'JPEG'])
assert result.exit_code == 1
assert '--overwrite' in result.output
def test_convert_overwrite_with_option(runner, tmpdir):
outputname = str(tmpdir.join('test.tif'))
result = runner.invoke(
main_group,
['convert', 'tests/data/RGB.byte.tif', '-o', outputname, '-f', 'JPEG'])
assert result.exit_code == 0
result = runner.invoke(
main_group, [
'convert', 'tests/data/RGB.byte.tif', '-o', outputname, '-f', 'JPEG',
'--overwrite'])
assert result.exit_code == 0
......@@ -266,7 +266,7 @@ def test_bounds_defaults():
'tests/data/RGB.byte.tif'
])
assert result.exit_code == 0
assert 'FeatureCollection' in result.output
assert 'Feature' in result.output
def test_bounds_err():
......
"""Unittests for $ rio merge"""
import sys
import os
import logging
import sys
import textwrap
import affine
from click.testing import CliRunner
......@@ -18,6 +18,7 @@ from rasterio.transform import Affine
from .conftest import requires_gdal22
# Fixture to create test datasets within temporary directory
@fixture(scope='function')
def test_data_dir_1(tmpdir):
......@@ -455,7 +456,7 @@ def test_merge_rgb(tmpdir):
assert result.exit_code == 0
with rasterio.open(outputname) as src:
assert [src.checksum(i) for i in src.indexes] == [25420, 29131, 37860]
assert [src.checksum(i) for i in src.indexes] == [33219, 35315, 45188]
def test_merge_tiny_intres(tiffs):
......@@ -463,3 +464,52 @@ def test_merge_tiny_intres(tiffs):
inputs.sort()
datasets = [rasterio.open(x) for x in inputs]
merge(datasets, res=2)
@pytest.mark.parametrize("precision", [[], ["--precision", "9"]])
def test_merge_precision(tmpdir, precision):
"""See https://github.com/mapbox/rasterio/issues/1837"""
# TDOD move ascii grids to a fixture?
expected = """\
ncols 8
nrows 8
xllcorner 0.000000000000
yllcorner 0.000000000000
cellsize 1.000000000000
1 2 3 4 1 2 3 4
3 4 5 6 3 4 5 6
4 5 6 8 4 5 6 8
7 9 5 4 7 9 5 4
1 2 3 4 1 2 3 4
3 4 5 6 3 4 5 6
4 5 6 8 4 5 6 8
7 9 5 4 7 9 5 4
"""
template = """\
ncols 4
nrows 4
xllcorner {:f}
yllcorner {:f}
cellsize 1.0
1 2 3 4
3 4 5 6
4 5 6 8
7 9 5 4
"""
names = ["sw.asc", "se.asc", "nw.asc", "ne.asc"]
corners = [(0.0, 0.0), (4.0, 0.0), (0.0, 4.0), (4.0, 4.0)]
for name, (minx, miny) in zip(names, corners):
content = textwrap.dedent(template.format(minx, miny))
tmpdir.join(name).write(content)
inputs = [str(tmpdir.join(name)) for name in names]
outputname = str(tmpdir.join("merged.asc"))
runner = CliRunner()
result = runner.invoke(main_group, ["merge", "-f", "AAIGrid"] + precision + inputs + [outputname])
assert result.exit_code == 0
assert open(outputname).read() == textwrap.dedent(expected)
......@@ -496,6 +496,35 @@ def test_warp_warp(dsrec, path_rgb_byte_tif):
assert "1 N GTiff" in records[0]
def test_out_dtype(red_green):
"""Read as float"""
with rasterio.Env():
with rasterio.open(str(red_green.join("red.tif"))) as src, WarpedVRT(
src,
transform=affine.Affine.translation(-src.width / 4, src.height / 4) * src.transform,
width=2 * src.width,
height=2 * src.height
) as vrt:
data = vrt.read(out_dtype="float32")
image = numpy.moveaxis(data, 0, -1)
assert image[31, 31, 0] == 0.0
assert image[32, 32, 0] == 204.0
assert image[32, 32, 1] == 17.0
def test_sample(red_green):
"""See https://github.com/mapbox/rasterio/issues/1833."""
with rasterio.Env():
with rasterio.open(str(red_green.join("red.tif"))) as src, WarpedVRT(
src,
transform=affine.Affine.translation(-src.width / 4, src.height / 4) * src.transform,
width=2 * src.width,
height=2 * src.height
) as vrt:
sample = next(vrt.sample([(-20, -50)]))
assert not sample.any()
@pytest.fixture
def dsrec(capfd):
"""GDAL's open dataset records as a pytest fixture"""
......