mirror of
https://github.com/rasterio/rasterio.git
synced 2025-12-08 17:36:12 +00:00
Merge branch 'master' into vfs
Conflicts: rasterio/__init__.py
This commit is contained in:
commit
dd15b4ae53
13
.coveragerc
Normal file
13
.coveragerc
Normal file
@ -0,0 +1,13 @@
|
||||
[run]
|
||||
plugins = Cython.Coverage
|
||||
source = rasterio
|
||||
omit = *.pxd
|
||||
|
||||
[report]
|
||||
show_missing = True
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
def __repr__
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
if __name__ == .__main__.:
|
||||
@ -10,10 +10,11 @@ env:
|
||||
- PIP_FIND_LINKS=file://$HOME/.cache/pip/wheels
|
||||
- GDALINST=$HOME/gdalinstall
|
||||
- GDALBUILD=$HOME/gdalbuild
|
||||
- CYTHON_COVERAGE=1
|
||||
matrix:
|
||||
- GDALVERSION = "1.9.2"
|
||||
- GDALVERSION = "1.11.2"
|
||||
#- GDALVERSION = "2.0.1"
|
||||
- GDALVERSION = "2.0.1"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@ -37,9 +38,9 @@ install:
|
||||
- "pip wheel -r requirements-dev.txt"
|
||||
- "pip install -r requirements-dev.txt"
|
||||
- "pip install --upgrade --force-reinstall --global-option=build_ext --global-option='-I$GDALINST/gdal-$GDALVERSION/include' --global-option='-L$GDALINST/gdal-$GDALVERSION/lib' --global-option='-R$GDALINST/gdal-$GDALVERSION/lib' -e ."
|
||||
- "pip install coveralls"
|
||||
- "pip install coveralls>=1.1"
|
||||
- "pip install -e ."
|
||||
script:
|
||||
script:
|
||||
- py.test --cov rasterio --cov-report term-missing
|
||||
after_success:
|
||||
- coveralls
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
Changes
|
||||
=======
|
||||
|
||||
0.29.0 (2015-10-22)
|
||||
-------------------
|
||||
- Fill masked arrays in rio-calc when using Numpy 1.10.x as well as with 1.8.x
|
||||
(#500).
|
||||
- When a raster dataset is not tiled, blockxszie and blockysize items are no
|
||||
longer included in its `profile` property. This prevents meaningless block
|
||||
size parameters from stripped, not tiled, datasets from being used when
|
||||
creating new datasets (#503).
|
||||
|
||||
0.28.0 (2015-10-06)
|
||||
-------------------
|
||||
- Ensure that tools module is packaged (#489, #490). The rio-merge command was
|
||||
|
||||
@ -161,6 +161,55 @@ This example also demonstrates decimation.
|
||||
:width: 500
|
||||
:height: 300
|
||||
|
||||
|
||||
Data windows
|
||||
------------
|
||||
|
||||
Sometimes it is desirable to crop off an outer boundary of NODATA values around
|
||||
a dataset:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from rasterio import get_data_window
|
||||
|
||||
with rasterio.open('tests/data/RGB.byte.tif') as src:
|
||||
window = get_data_window(src.read(1, masked=True))
|
||||
# window = ((3, 714), (13, 770))
|
||||
|
||||
kwargs = src.meta.copy()
|
||||
del kwargs['transform']
|
||||
kwargs.update({
|
||||
'height': window[0][1] - window[0][0],
|
||||
'width': window[1][1] - window[1][0],
|
||||
'affine': src.window_transform(window)
|
||||
})
|
||||
|
||||
with rasterio.open('/tmp/cropped.tif', 'w', **kwargs) as dst:
|
||||
dst.write(src.read(window=window))
|
||||
|
||||
|
||||
Window utilities
|
||||
----------------
|
||||
|
||||
Basic union and intersection operations are available for windows, to streamline
|
||||
operations across dynamically created windows for a series of bands or datasets
|
||||
with the same full extent.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from rasterio import window_union, window_intersection
|
||||
|
||||
# Full window is ((0, 1000), (0, 500))
|
||||
window1 = ((100, 500), (10, 500))
|
||||
window2 = ((10, 150), (50, 250))
|
||||
|
||||
outer = window_union([window1, window2])
|
||||
# outer = ((10, 500), (10, 500))
|
||||
|
||||
inner = window_intersection([window1, window2])
|
||||
# inner = ((100, 150), (50, 250))
|
||||
|
||||
|
||||
Blocks
|
||||
------
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ from rasterio import _err, coords, enums
|
||||
|
||||
__all__ = [
|
||||
'band', 'open', 'drivers', 'copy', 'pad']
|
||||
__version__ = "0.28.0"
|
||||
__version__ = "0.29.0"
|
||||
|
||||
log = logging.getLogger('rasterio')
|
||||
class NullHandler(logging.Handler):
|
||||
@ -40,7 +40,6 @@ def open(
|
||||
crs=None, transform=None,
|
||||
dtype=None,
|
||||
nodata=None,
|
||||
vfs=None,
|
||||
**kwargs):
|
||||
"""Open file at ``path`` in ``mode`` "r" (read), "r+" (read/write),
|
||||
or "w" (write) and return a ``Reader`` or ``Updater`` object.
|
||||
@ -95,11 +94,6 @@ def open(
|
||||
raise TypeError("invalid mode: %r" % mode)
|
||||
if driver and not isinstance(driver, string_types):
|
||||
raise TypeError("invalid driver: %r" % driver)
|
||||
if vfs and not isinstance(vfs, string_types):
|
||||
raise TypeError("invalid vfs: %r" % vfs)
|
||||
|
||||
path, vsi, archive = parse_paths(path, vfs)
|
||||
path = vsi_path(path, vsi=vsi, archive=archive)
|
||||
|
||||
if transform:
|
||||
transform = guard_transform(transform)
|
||||
@ -180,28 +174,81 @@ def pad(array, transform, pad_width, mode=None, **kwargs):
|
||||
return padded_array, Affine(*padded_trans[:6])
|
||||
|
||||
|
||||
def parse_paths(path, vfs=None):
|
||||
archive = vsi = None
|
||||
if vfs:
|
||||
parts = vfs.split("://")
|
||||
vsi = parts.pop(0) if parts else None
|
||||
archive = parts.pop(0) if parts else None
|
||||
else:
|
||||
parts = path.split("://")
|
||||
path = parts.pop() if parts else None
|
||||
vsi = parts.pop() if parts else None
|
||||
return path, vsi, archive
|
||||
def get_data_window(arr, nodata=None):
|
||||
"""
|
||||
Returns a window for the non-nodata pixels within the input array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arr: numpy ndarray, <= 3 dimensions
|
||||
nodata: number
|
||||
If None, will either return a full window if arr is not a masked
|
||||
array, or will use the mask to determine non-nodata pixels.
|
||||
If provided, it must be a number within the valid range of the dtype
|
||||
of the input array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
"""
|
||||
|
||||
from rasterio._io import get_data_window
|
||||
return get_data_window(arr, nodata)
|
||||
|
||||
|
||||
def vsi_path(path, vsi=None, archive=None):
|
||||
# If a VSF and archive file are specified, we convert the path to
|
||||
# a GDAL VSI path (see cpl_vsi.h).
|
||||
if vsi:
|
||||
path = path.strip(os.path.sep)
|
||||
if archive:
|
||||
result = os.path.sep.join(['/vsi{0}'.format(vsi), archive, path])
|
||||
else:
|
||||
result = os.path.sep.join(['/vsi{0}'.format(vsi), path])
|
||||
else:
|
||||
result = path
|
||||
return result
|
||||
def window_union(windows):
|
||||
"""
|
||||
Union windows and return the outermost extent they cover.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
windows: list-like of window objects
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
Returns
|
||||
-------
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
"""
|
||||
|
||||
from rasterio._io import window_union
|
||||
return window_union(windows)
|
||||
|
||||
|
||||
def window_intersection(windows):
|
||||
"""
|
||||
Intersect windows and return the innermost extent they cover.
|
||||
|
||||
Will raise ValueError if windows do not intersect.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
windows: list-like of window objects
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
Returns
|
||||
-------
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
"""
|
||||
|
||||
from rasterio._io import window_intersection
|
||||
return window_intersection(windows)
|
||||
|
||||
|
||||
def windows_intersect(windows):
|
||||
"""
|
||||
Test if windows intersect.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
windows: list-like of window objects
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean:
|
||||
True if all windows intersect.
|
||||
"""
|
||||
|
||||
from rasterio._io import windows_intersect
|
||||
return windows_intersect(windows)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
@ -30,6 +31,38 @@ else:
|
||||
log.addHandler(NullHandler())
|
||||
|
||||
|
||||
def parse_paths(url, vfs=None):
|
||||
"""Parse a file path or Apache VFS URL into its parts."""
|
||||
archive = scheme = None
|
||||
if vfs:
|
||||
parts = vfs.split("://")
|
||||
vsi = parts.pop(0) if parts else None
|
||||
archive = parts.pop(0) if parts else None
|
||||
else:
|
||||
parts = url.split("://")
|
||||
path = parts.pop() if parts else None
|
||||
scheme = parts.pop() if parts else None
|
||||
if scheme in ('gzip', 'zip', 'tar'):
|
||||
parts = path.split('!')
|
||||
path = parts.pop() if parts else None
|
||||
archive = parts.pop() if parts else None
|
||||
return path, scheme, archive
|
||||
|
||||
|
||||
def vsi_path(path, vsi=None, archive=None):
|
||||
# If a VSF and archive file are specified, we convert the path to
|
||||
# a GDAL VSI path (see cpl_vsi.h).
|
||||
if vsi and vsi != 'file':
|
||||
path = path.strip(os.path.sep)
|
||||
if archive:
|
||||
result = os.path.sep.join(['/vsi{0}'.format(vsi), archive, path])
|
||||
else:
|
||||
result = os.path.sep.join(['/vsi{0}'.format(vsi), path])
|
||||
else:
|
||||
result = path
|
||||
return result
|
||||
|
||||
|
||||
cdef class DatasetReader(object):
|
||||
|
||||
def __init__(self, path):
|
||||
@ -62,7 +95,10 @@ cdef class DatasetReader(object):
|
||||
self.env = GDALEnv(False)
|
||||
self.env.start()
|
||||
|
||||
name_b = self.name.encode('utf-8')
|
||||
path, vsi, archive = parse_paths(self.name)
|
||||
path = vsi_path(path, vsi=vsi, archive=archive)
|
||||
|
||||
name_b = path.encode('utf-8')
|
||||
cdef const char *fname = name_b
|
||||
with cpl_errs:
|
||||
self._hds = _gdal.GDALOpen(fname, 0)
|
||||
@ -448,6 +484,10 @@ cdef class DatasetReader(object):
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_tiled(self):
|
||||
return self.block_shapes[0][1] != self.width
|
||||
|
||||
property profile:
|
||||
"""Basic metadata and creation options of this dataset.
|
||||
|
||||
@ -457,10 +497,13 @@ cdef class DatasetReader(object):
|
||||
def __get__(self):
|
||||
m = self.meta
|
||||
m.update(self.tags(ns='rio_creation_kwds'))
|
||||
m.update(
|
||||
blockxsize=self.block_shapes[0][1],
|
||||
blockysize=self.block_shapes[0][0],
|
||||
tiled=self.block_shapes[0][1] != self.width)
|
||||
if self.is_tiled:
|
||||
m.update(
|
||||
blockxsize=self.block_shapes[0][1],
|
||||
blockysize=self.block_shapes[0][0],
|
||||
tiled=True)
|
||||
else:
|
||||
m.update(tiled=False)
|
||||
if self.compression:
|
||||
m['compress'] = self.compression.name
|
||||
if self.interleaving:
|
||||
|
||||
153
rasterio/_io.pyx
153
rasterio/_io.pyx
@ -13,7 +13,8 @@ cimport numpy as np
|
||||
|
||||
from rasterio cimport _base, _gdal, _ogr, _io
|
||||
from rasterio._base import (
|
||||
crop_window, eval_window, window_shape, window_index, tastes_like_gdal)
|
||||
crop_window, eval_window, window_shape, window_index, tastes_like_gdal,
|
||||
parse_paths)
|
||||
from rasterio._drivers import driver_count, GDALEnv
|
||||
from rasterio._err import cpl_errs
|
||||
from rasterio import dtypes
|
||||
@ -1249,8 +1250,7 @@ cdef class RasterUpdater(RasterReader):
|
||||
cdef void *drv = NULL
|
||||
cdef void *hband = NULL
|
||||
cdef int success
|
||||
name_b = self.name.encode('utf-8')
|
||||
cdef const char *fname = name_b
|
||||
|
||||
|
||||
# Is there not a driver manager already?
|
||||
if driver_count() == 0 and not self.env:
|
||||
@ -1259,7 +1259,16 @@ cdef class RasterUpdater(RasterReader):
|
||||
else:
|
||||
self.env = GDALEnv(False)
|
||||
self.env.start()
|
||||
|
||||
|
||||
path, scheme, archive = parse_paths(self.name)
|
||||
if scheme and scheme != 'file':
|
||||
raise TypeError(
|
||||
"VFS '{0}' datasets can not be created or updated.".format(
|
||||
scheme))
|
||||
|
||||
name_b = self.name.encode('utf-8')
|
||||
cdef const char *fname = name_b
|
||||
|
||||
kwds = []
|
||||
|
||||
if self.mode == 'w':
|
||||
@ -2002,6 +2011,12 @@ def writer(path, mode, **kwargs):
|
||||
cdef const char *drv_name = NULL
|
||||
cdef const char *fname = NULL
|
||||
|
||||
path, scheme, archive = parse_paths(path)
|
||||
if scheme and scheme != 'file':
|
||||
raise TypeError(
|
||||
"VFS '{0}' datasets can not be created or updated.".format(
|
||||
scheme))
|
||||
|
||||
if mode == 'w' and 'driver' in kwargs:
|
||||
if kwargs['driver'] == 'GTiff':
|
||||
return RasterUpdater(path, mode, **kwargs)
|
||||
@ -2041,3 +2056,133 @@ def virtual_file_to_buffer(filename):
|
||||
log.debug("Buffer length: %d bytes", n)
|
||||
cdef np.uint8_t[:] buff_view = <np.uint8_t[:n]>buff
|
||||
return buff_view
|
||||
|
||||
|
||||
def get_data_window(arr, nodata=None):
|
||||
"""
|
||||
Returns a window for the non-nodata pixels within the input array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arr: numpy ndarray, <= 3 dimensions
|
||||
nodata: number
|
||||
If None, will either return a full window if arr is not a masked
|
||||
array, or will use the mask to determine non-nodata pixels.
|
||||
If provided, it must be a number within the valid range of the dtype
|
||||
of the input array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
"""
|
||||
|
||||
num_dims = len(arr.shape)
|
||||
if num_dims > 3:
|
||||
raise ValueError('get_data_window input array must have no more than '
|
||||
'3 dimensions')
|
||||
|
||||
if nodata is None:
|
||||
if not hasattr(arr, 'mask'):
|
||||
return ((0, arr.shape[-2]), (0, arr.shape[-1]))
|
||||
else:
|
||||
arr = np.ma.masked_array(arr, arr == nodata)
|
||||
|
||||
if num_dims == 2:
|
||||
data_rows, data_cols = np.where(arr.mask == False)
|
||||
else:
|
||||
data_rows, data_cols = np.where(
|
||||
np.any(np.rollaxis(arr.mask, 0, 3) == False, axis=2)
|
||||
)
|
||||
|
||||
if data_rows.size:
|
||||
row_range = (data_rows.min(), data_rows.max() + 1)
|
||||
else:
|
||||
row_range = (0, 0)
|
||||
|
||||
if data_cols.size:
|
||||
col_range = (data_cols.min(), data_cols.max() + 1)
|
||||
else:
|
||||
col_range = (0, 0)
|
||||
|
||||
return (row_range, col_range)
|
||||
|
||||
|
||||
def window_union(windows):
|
||||
"""
|
||||
Union windows and return the outermost extent they cover.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
windows: list-like of window objects
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
Returns
|
||||
-------
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
"""
|
||||
|
||||
|
||||
stacked = np.dstack(windows)
|
||||
return (
|
||||
(stacked[0, 0].min(), stacked[0, 1].max()),
|
||||
(stacked[1, 0].min(), stacked[1, 1]. max())
|
||||
)
|
||||
|
||||
|
||||
def window_intersection(windows):
|
||||
"""
|
||||
Intersect windows and return the innermost extent they cover.
|
||||
|
||||
Will raise ValueError if windows do not intersect.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
windows: list-like of window objects
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
Returns
|
||||
-------
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
"""
|
||||
|
||||
if not windows_intersect(windows):
|
||||
raise ValueError('windows do not intersect')
|
||||
|
||||
stacked = np.dstack(windows)
|
||||
return (
|
||||
(stacked[0, 0].max(), stacked[0, 1].min()),
|
||||
(stacked[1, 0].max(), stacked[1, 1]. min())
|
||||
)
|
||||
|
||||
|
||||
def windows_intersect(windows):
|
||||
"""
|
||||
Test if windows intersect.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
windows: list-like of window objects
|
||||
((row_start, row_stop), (col_start, col_stop))
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean:
|
||||
True if all windows intersect.
|
||||
"""
|
||||
|
||||
from itertools import combinations
|
||||
|
||||
def intersects(range1, range2):
|
||||
return not (
|
||||
range1[0] > range2[1] or range1[1] < range2[0]
|
||||
)
|
||||
|
||||
windows = np.array(windows)
|
||||
|
||||
for i in (0, 1):
|
||||
for c in combinations(windows[:, i], 2):
|
||||
if not intersects(*c):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@ -123,8 +123,9 @@ def calc(ctx, command, files, output, name, dtype, masked, creation_options):
|
||||
|
||||
res = snuggs.eval(command, **ctxkwds)
|
||||
|
||||
if (isinstance(res, np.ma.core.MaskedArray) and
|
||||
tuple(LooseVersion(np.__version__).version) < (1, 9, 0)):
|
||||
if (isinstance(res, np.ma.core.MaskedArray) and (
|
||||
tuple(LooseVersion(np.__version__).version) < (1, 9) or
|
||||
tuple(LooseVersion(np.__version__).version) > (1, 10))):
|
||||
res = res.filled(kwargs['nodata'])
|
||||
|
||||
if len(res.shape) == 3:
|
||||
|
||||
@ -26,9 +26,8 @@ def configure_logging(verbosity):
|
||||
@cligj.verbose_opt
|
||||
@cligj.quiet_opt
|
||||
@click.version_option(version=rasterio.__version__, message='%(version)s')
|
||||
@options.vfs_opt
|
||||
@click.pass_context
|
||||
def main_group(ctx, verbose, quiet, vfs):
|
||||
def main_group(ctx, verbose, quiet):
|
||||
|
||||
"""
|
||||
Rasterio command line interface.
|
||||
@ -38,4 +37,3 @@ def main_group(ctx, verbose, quiet, vfs):
|
||||
configure_logging(verbosity)
|
||||
ctx.obj = {}
|
||||
ctx.obj['verbosity'] = verbosity
|
||||
ctx.obj['vfs'] = vfs
|
||||
|
||||
@ -73,6 +73,7 @@ def merge(ctx, files, output, driver, bounds, res, nodata, force_overwrite,
|
||||
profile['height'] = dest.shape[1]
|
||||
profile['width'] = dest.shape[2]
|
||||
profile['driver'] = driver
|
||||
|
||||
profile.update(**creation_options)
|
||||
|
||||
with rasterio.open(output, 'w', **profile) as dst:
|
||||
|
||||
@ -50,7 +50,7 @@ import os.path
|
||||
|
||||
import click
|
||||
|
||||
from rasterio import parse_paths, vsi_path
|
||||
from rasterio._base import parse_paths
|
||||
|
||||
|
||||
def _cb_key_val(ctx, param, value):
|
||||
@ -77,34 +77,27 @@ def _cb_key_val(ctx, param, value):
|
||||
raise click.BadParameter("Invalid syntax for KEY=VAL arg: {}".format(pair))
|
||||
else:
|
||||
k, v = pair.split('=', 1)
|
||||
k = k.lower()
|
||||
v = v.lower()
|
||||
out[k] = v
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def vfs_handler(ctx, param, value):
|
||||
if value:
|
||||
path, vsi, archive = parse_paths(None, value)
|
||||
if not vsi in ('gzip', 'zip', 'tar'):
|
||||
raise click.BadParameter(
|
||||
"VFS type {0} is unknown".format(vsi))
|
||||
if not os.path.exists(archive):
|
||||
raise click.BadParameter(
|
||||
"VFS archive {0} does not exist".format(archive))
|
||||
value = "{0}://{1}".format(vsi, os.path.abspath(archive))
|
||||
return value
|
||||
|
||||
|
||||
def file_in_handler(ctx, param, value):
|
||||
vfs = (ctx.obj and ctx.obj.get('vfs'))
|
||||
if vfs:
|
||||
path, vsi, archive = parse_paths(value, vfs)
|
||||
path = vsi_path(path, vsi, archive)
|
||||
"""Normalize ordinary filesystem and VFS paths"""
|
||||
path, scheme, archive = parse_paths(value)
|
||||
if scheme and not scheme in ('file', 'gzip', 'zip', 'tar'):
|
||||
raise click.BadParameter(
|
||||
"VFS type {0} is unknown".format(scheme))
|
||||
path_to_check = archive or path
|
||||
if not os.path.exists(path_to_check):
|
||||
raise click.BadParameter(
|
||||
"Input file {0} does not exist".format(path_to_check))
|
||||
if archive and scheme:
|
||||
archive = os.path.abspath(archive)
|
||||
path = "{0}://{1}!{2}".format(scheme, archive, path)
|
||||
else:
|
||||
if not os.path.exists(value):
|
||||
raise click.BadParameter(
|
||||
"Input file {0} does not exist".format(value))
|
||||
path = os.path.abspath(value)
|
||||
path = os.path.abspath(path)
|
||||
return path
|
||||
|
||||
|
||||
@ -188,11 +181,3 @@ rgb_opt = click.option(
|
||||
flag_value='rgb',
|
||||
default=False,
|
||||
help="Set RGB photometric interpretation.")
|
||||
|
||||
|
||||
vfs_opt = click.option(
|
||||
'--vfs', 'vfs',
|
||||
default=None,
|
||||
callback=vfs_handler,
|
||||
help="Use a zip:// or tar:// archive as a virtual file system "
|
||||
"('r' mode only).")
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
affine
|
||||
cligj
|
||||
coveralls>=0.4
|
||||
cython>=0.23.1
|
||||
cython>=0.23.4
|
||||
delocate
|
||||
enum34
|
||||
numpy>=1.8.0
|
||||
numpy>=1.8
|
||||
snuggs>=1.2
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-cov>=2.2.0
|
||||
setuptools>=0.9.8
|
||||
wheel
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
affine
|
||||
cligj
|
||||
enum34
|
||||
numpy>=1.8.0
|
||||
numpy>=1.8
|
||||
snuggs>=1.2
|
||||
setuptools
|
||||
|
||||
@ -60,30 +60,30 @@ ls -l $GDALINST
|
||||
if [ ! -d $GDALINST/gdal-1.9.2 ]; then
|
||||
cd $GDALBUILD
|
||||
wget http://download.osgeo.org/gdal/gdal-1.9.2.tar.gz
|
||||
tar -xzvf gdal-1.9.2.tar.gz
|
||||
tar -xzf gdal-1.9.2.tar.gz
|
||||
cd gdal-1.9.2
|
||||
./configure --prefix=$GDALINST/gdal-1.9.2 $GDALOPTS
|
||||
make -j 2
|
||||
make -s -j 2
|
||||
make install
|
||||
fi
|
||||
|
||||
if [ ! -d $GDALINST/gdal-1.11.2 ]; then
|
||||
cd $GDALBUILD
|
||||
wget http://download.osgeo.org/gdal/1.11.2/gdal-1.11.2.tar.gz
|
||||
tar -xzvf gdal-1.11.2.tar.gz
|
||||
tar -xzf gdal-1.11.2.tar.gz
|
||||
cd gdal-1.11.2
|
||||
./configure --prefix=$GDALINST/gdal-1.11.2 $GDALOPTS
|
||||
make -j 2
|
||||
make -s -j 2
|
||||
make install
|
||||
fi
|
||||
|
||||
if [ ! -d $GDALINST/gdal-2.0.1 ]; then
|
||||
cd $GDALBUILD
|
||||
wget http://download.osgeo.org/gdal/2.0.1/gdal-2.0.1.tar.gz
|
||||
tar -xzvf gdal-2.0.1.tar.gz
|
||||
tar -xzf gdal-2.0.1.tar.gz
|
||||
cd gdal-2.0.1
|
||||
./configure --prefix=$GDALINST/gdal-2.0.1 $GDALOPTS
|
||||
make -j 2
|
||||
make -s -j 2
|
||||
make install
|
||||
fi
|
||||
|
||||
|
||||
10
setup.cfg
10
setup.cfg
@ -1,8 +1,2 @@
|
||||
[nosetests]
|
||||
tests=rasterio/tests
|
||||
nocapture=True
|
||||
verbosity=3
|
||||
logging-filter=rasterio
|
||||
logging-level=DEBUG
|
||||
with-coverage=1
|
||||
cover-package=rasterio
|
||||
[pytest]
|
||||
testpaths = tests
|
||||
|
||||
26
setup.py
26
setup.py
@ -19,12 +19,10 @@ import sys
|
||||
from setuptools import setup
|
||||
from setuptools.extension import Extension
|
||||
|
||||
|
||||
logging.basicConfig()
|
||||
log = logging.getLogger()
|
||||
|
||||
# python -W all setup.py ...
|
||||
if 'all' in sys.warnoptions:
|
||||
log.level = logging.DEBUG
|
||||
|
||||
def check_output(cmd):
|
||||
# since subprocess.check_output doesn't exist in 2.6
|
||||
@ -39,6 +37,7 @@ def check_output(cmd):
|
||||
out, err = p.communicate()
|
||||
return out
|
||||
|
||||
|
||||
def copy_data_tree(datadir, destdir):
|
||||
try:
|
||||
shutil.rmtree(destdir)
|
||||
@ -46,6 +45,11 @@ def copy_data_tree(datadir, destdir):
|
||||
pass
|
||||
shutil.copytree(datadir, destdir)
|
||||
|
||||
|
||||
# python -W all setup.py ...
|
||||
if 'all' in sys.warnoptions:
|
||||
log.level = logging.DEBUG
|
||||
|
||||
# Parse the version from the rasterio module.
|
||||
with open('rasterio/__init__.py') as f:
|
||||
for line in f:
|
||||
@ -135,6 +139,13 @@ if not os.name == "nt":
|
||||
ext_options['extra_compile_args'] = ['-Wno-unused-parameter',
|
||||
'-Wno-unused-function']
|
||||
|
||||
cythonize_options = {}
|
||||
if os.environ.get('CYTHON_COVERAGE'):
|
||||
cythonize_options['compiler_directives'] = {'linetrace': True}
|
||||
cythonize_options['annotate'] = True
|
||||
ext_options['define_macros'] = [('CYTHON_TRACE', '1'),
|
||||
('CYTHON_TRACE_NOGIL', '1')]
|
||||
|
||||
log.debug('ext_options:\n%s', pprint.pformat(ext_options))
|
||||
|
||||
# When building from a repo, Cython is required.
|
||||
@ -164,7 +175,7 @@ if os.path.exists("MANIFEST.in") and "clean" not in sys.argv:
|
||||
'rasterio._err', ['rasterio/_err.pyx'], **ext_options),
|
||||
Extension(
|
||||
'rasterio._example', ['rasterio/_example.pyx'], **ext_options),
|
||||
], quiet=True)
|
||||
], quiet=True, **cythonize_options)
|
||||
|
||||
# If there's no manifest template, as in an sdist, we just specify .c files.
|
||||
else:
|
||||
@ -193,12 +204,7 @@ with open('README.rst') as f:
|
||||
readme = f.read()
|
||||
|
||||
# Runtime requirements.
|
||||
inst_reqs = [
|
||||
'affine>=1.0',
|
||||
'cligj>=0.2.0',
|
||||
'Numpy>=1.7',
|
||||
'snuggs>=1.3.1',
|
||||
'click-plugins']
|
||||
inst_reqs = ['affine', 'cligj', 'numpy', 'snuggs', 'click-plugins']
|
||||
|
||||
if sys.version_info < (3, 4):
|
||||
inst_reqs.append('enum34')
|
||||
|
||||
Binary file not shown.
@ -1,4 +1,13 @@
|
||||
import numpy
|
||||
import pytest
|
||||
|
||||
import rasterio
|
||||
from rasterio import (
|
||||
get_data_window, window_intersection, window_union, windows_intersect
|
||||
)
|
||||
|
||||
|
||||
DATA_WINDOW = ((3, 5), (2, 6))
|
||||
|
||||
|
||||
def test_index():
|
||||
@ -67,3 +76,119 @@ def test_window_full_cover():
|
||||
win = src.window(*bounds)
|
||||
bounds_calc = list(src.window_bounds(win))
|
||||
assert bound_covers(bounds_calc, bounds)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def data():
|
||||
data = numpy.zeros((10, 10), dtype='uint8')
|
||||
data[slice(*DATA_WINDOW[0]), slice(*DATA_WINDOW[1])] = 1
|
||||
return data
|
||||
|
||||
|
||||
def test_data_window_unmasked(data):
|
||||
window = get_data_window(data)
|
||||
assert window == ((0, data.shape[0]), (0, data.shape[1]))
|
||||
|
||||
|
||||
def test_data_window_masked(data):
|
||||
data = numpy.ma.masked_array(data, data == 0)
|
||||
window = get_data_window(data)
|
||||
assert window == DATA_WINDOW
|
||||
|
||||
|
||||
def test_data_window_nodata(data):
|
||||
window = get_data_window(data, nodata=0)
|
||||
assert window == DATA_WINDOW
|
||||
|
||||
window = get_data_window(numpy.ones_like(data), nodata=0)
|
||||
assert window == ((0, data.shape[0]), (0, data.shape[1]))
|
||||
|
||||
|
||||
def test_data_window_nodata_disjunct():
|
||||
data = numpy.zeros((3, 10, 10), dtype='uint8')
|
||||
data[0, :4, 1:4] = 1
|
||||
data[1, 2:5, 2:8] = 1
|
||||
data[2, 1:6, 1:6] = 1
|
||||
window = get_data_window(data, nodata=0)
|
||||
assert window == ((0, 6), (1, 8))
|
||||
|
||||
|
||||
def test_data_window_empty_result():
|
||||
data = numpy.zeros((3, 10, 10), dtype='uint8')
|
||||
window = get_data_window(data, nodata=0)
|
||||
assert window == ((0, 0), (0, 0))
|
||||
|
||||
|
||||
def test_data_window_masked_file():
|
||||
with rasterio.open('tests/data/RGB.byte.tif') as src:
|
||||
window = get_data_window(src.read(1, masked=True))
|
||||
assert window == ((3, 714), (13, 770))
|
||||
|
||||
window = get_data_window(src.read(masked=True))
|
||||
assert window == ((3, 714), (13, 770))
|
||||
|
||||
|
||||
def test_window_union():
|
||||
assert window_union([
|
||||
((0, 6), (3, 6)),
|
||||
((2, 4), (1, 5))
|
||||
]) == ((0, 6), (1, 6))
|
||||
|
||||
|
||||
def test_window_intersection():
|
||||
assert window_intersection([
|
||||
((0, 6), (3, 6)),
|
||||
((2, 4), (1, 5))
|
||||
]) == ((2, 4), (3, 5))
|
||||
|
||||
assert window_intersection([
|
||||
((0, 6), (3, 6)),
|
||||
((6, 10), (1, 5))
|
||||
]) == ((6, 6), (3, 5))
|
||||
|
||||
assert window_intersection([
|
||||
((0, 6), (3, 6)),
|
||||
((2, 4), (1, 5)),
|
||||
((3, 6), (0, 6))
|
||||
]) == ((3, 4), (3, 5))
|
||||
|
||||
|
||||
def test_window_intersection_disjunct():
|
||||
with pytest.raises(ValueError):
|
||||
window_intersection([
|
||||
((0, 6), (3, 6)),
|
||||
((100, 200), (0, 12)),
|
||||
((7, 12), (7, 12))
|
||||
])
|
||||
|
||||
|
||||
def test_windows_intersect():
|
||||
assert windows_intersect([
|
||||
((0, 6), (3, 6)),
|
||||
((2, 4), (1, 5))
|
||||
]) == True
|
||||
|
||||
assert windows_intersect([
|
||||
((0, 6), (3, 6)),
|
||||
((2, 4), (1, 5)),
|
||||
((3, 6), (0, 6))
|
||||
]) == True
|
||||
|
||||
|
||||
def test_windows_intersect_disjunct():
|
||||
assert windows_intersect([
|
||||
((0, 6), (3, 6)),
|
||||
((10, 20), (0, 6))
|
||||
]) == False
|
||||
|
||||
assert windows_intersect([
|
||||
((0, 6), (3, 6)),
|
||||
((2, 4), (1, 5)),
|
||||
((5, 6), (0, 6))
|
||||
]) == False
|
||||
|
||||
assert windows_intersect([
|
||||
((0, 6), (3, 6)),
|
||||
((2, 4), (1, 3)),
|
||||
((3, 6), (4, 6))
|
||||
]) == False
|
||||
@ -7,7 +7,7 @@ def test_cb_key_val():
|
||||
|
||||
pairs = ['KEY=val', '1==']
|
||||
expected = {
|
||||
'KEY': 'val',
|
||||
'key': 'val',
|
||||
'1': '=',
|
||||
}
|
||||
assert options._cb_key_val(None, None, pairs) == expected
|
||||
|
||||
@ -79,11 +79,26 @@ def test_profile_overlay():
|
||||
assert kwds['count'] == 3
|
||||
|
||||
|
||||
def test_dataset_profile_property(data):
|
||||
def test_dataset_profile_property_tiled(data):
|
||||
"""An tiled dataset's profile has block sizes"""
|
||||
with rasterio.open('tests/data/shade.tif') as src:
|
||||
assert src.profile['blockxsize'] == 256
|
||||
assert src.profile['blockysize'] == 256
|
||||
assert src.profile['tiled'] == True
|
||||
|
||||
|
||||
def test_dataset_profile_property_untiled(data):
|
||||
"""An untiled dataset's profile has no block sizes"""
|
||||
with rasterio.open('tests/data/RGB.byte.tif') as src:
|
||||
assert 'blockxsize' not in src.profile
|
||||
assert 'blockysize' not in src.profile
|
||||
assert src.profile['tiled'] == False
|
||||
|
||||
|
||||
def test_dataset_profile_creation_kwds(data):
|
||||
"""Updated creation keyword tags appear in profile"""
|
||||
tiffile = str(data.join('RGB.byte.tif'))
|
||||
with rasterio.open(tiffile, 'r+') as src:
|
||||
src.update_tags(ns='rio_creation_kwds', foo='bar')
|
||||
assert src.profile['blockxsize'] == 791
|
||||
assert src.profile['blockysize'] == 3
|
||||
assert src.profile['tiled'] == False
|
||||
assert src.profile['foo'] == 'bar'
|
||||
|
||||
@ -1,15 +1,41 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
import rasterio
|
||||
from rasterio.profiles import default_gtiff_profile
|
||||
|
||||
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
||||
|
||||
|
||||
def test_read_zip():
|
||||
def test_read_vfs_zip():
|
||||
with rasterio.open(
|
||||
'/RGB.byte.tif',
|
||||
vfs='zip://tests/data/files.zip') as src:
|
||||
assert src.name == '/vsizip/tests/data/files.zip/RGB.byte.tif'
|
||||
'zip://tests/data/files.zip!/RGB.byte.tif') as src:
|
||||
assert src.name == 'zip://tests/data/files.zip!/RGB.byte.tif'
|
||||
assert src.count == 3
|
||||
|
||||
|
||||
def test_read_vfs_file():
|
||||
with rasterio.open(
|
||||
'file://tests/data/RGB.byte.tif') as src:
|
||||
assert src.name == 'file://tests/data/RGB.byte.tif'
|
||||
assert src.count == 3
|
||||
|
||||
|
||||
def test_read_vfs_none():
|
||||
with rasterio.open(
|
||||
'tests/data/RGB.byte.tif') as src:
|
||||
assert src.name == 'tests/data/RGB.byte.tif'
|
||||
assert src.count == 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize('mode', ['r+', 'w'])
|
||||
def test_update_vfs(tmpdir, mode):
|
||||
"""VFS datasets can not be created or updated"""
|
||||
with pytest.raises(TypeError):
|
||||
_ = rasterio.open(
|
||||
'zip://{0}'.format(tmpdir), mode,
|
||||
**default_gtiff_profile(
|
||||
count=1, width=1, height=1))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user