Skip to content
Commits on Source (4)
Changes
=======
1.0.26 (2019-08-26)
-------------------
- Allow `nan` to be used as fill for float32 datasets in ``rasterize`` (#1761).
- A WarpedVRTs destination crs now will default to ``src_crs`` if given, else
to the crs of the source dataset (#1755).
- Prevent open's ``sharing`` keyword argument from being pass on to GDAL as a
dataset opening option for create-copy formats like JPEG and PNG (#1758).
- Allow complex datasets to be created with a specified nodata value (#1747).
- Several Python deprecation warnings have been eliminated (#1742).
- When a MemoryFile is opened in write mode, a TypeError will be raised if
integer width and height are not given, fixing #1748.
- Return False when checking equality with objects incompatible with
``CRS.from_user_input()`` (#1719)
- Support CRS.from_user_input with other CRS object with ``to_wkt()`` method
(#1718)
1.0.25 (2019-08-06)
-------------------
- In `features.rasterize()`, a shape with an associated value of `None` is now
burned in using the function's `fill` value and no longer triggers a
`TypeError` (#1738).
- In ``features.rasterize()``, a shape with an associated value of ``None`` is
now burned in using the function's ``fill`` value and no longer triggers a
TypeError (#1738).
- Instances of pytest's tmp_path fixture in tests/test_dataset.py have been
replaced by instances of the older style tmpdir fixture (#1697).
- Add support for using GDAL 3.x and PROJ 6 libraries (#1700, #1729). Please
......@@ -732,6 +749,7 @@ Packaging:
1.0a5 (2016-12-23)
------------------
- New binary wheels using version 1.2.0 of sgillies/frs-wheel-builds. See
https://github.com/sgillies/frs-wheel-builds/blob/master/CHANGES.txt.
......
rasterio (1.0.26-1) unstable; urgency=medium
* Team upload.
* New upstream release.
-- Bas Couwenberg <sebastic@debian.org> Tue, 27 Aug 2019 05:53:36 +0200
rasterio (1.0.25-1) unstable; urgency=medium
* Team upload.
......
......@@ -42,7 +42,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
__version__ = "1.0.25"
__version__ = "1.0.26"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
......
......@@ -420,6 +420,13 @@ cdef class DatasetBase(object):
for i in range(self._count):
band = self.band(i + 1)
dtype = _band_dtype(band)
# To address the issue in #1747.
if dtype == "complex128":
dtype = "float64"
elif dtype == "complex64":
dtype = "float32"
nodataval = GDALGetRasterNoDataValue(band, &success)
val = nodataval
# GDALGetRasterNoDataValue() has two ways of telling you that
......
......@@ -620,7 +620,7 @@ cdef class DatasetReaderBase(DatasetBase):
# 255 and log that we have done so.
out = np.where(out != 0, 255, 0)
log.warn("Nonzero values in mask have been converted to 255, see note in rasterio/_io.pyx, read_masks()")
log.warning("Nonzero values in mask have been converted to 255, see note in rasterio/_io.pyx, read_masks()")
if return2d:
out.shape = out.shape[1:]
......@@ -1048,7 +1048,7 @@ cdef class DatasetWriterBase(DatasetReaderBase):
# Validate write mode arguments.
log.debug("Path: %s, mode: %s, driver: %s", path, mode, driver)
if mode == 'w':
if mode in ('w', 'w+'):
if not isinstance(driver, string_types):
raise TypeError("A driver name string is required.")
try:
......@@ -1442,7 +1442,7 @@ cdef class DatasetWriterBase(DatasetReaderBase):
CSLDestroy(papszStrList)
if retval == 2:
log.warn("Tags accepted but may not be persisted.")
log.warning("Tags accepted but may not be persisted.")
elif retval == 3:
raise RuntimeError("Tag update failed.")
......@@ -1514,7 +1514,7 @@ cdef class DatasetWriterBase(DatasetReaderBase):
rgba = tuple(rgba) + (255,)
if i not in vals:
log.warn("Invalid colormap key %d", i)
log.warning("Invalid colormap key %d", i)
continue
color.c1, color.c2, color.c3, color.c4 = rgba
......@@ -1848,7 +1848,7 @@ cdef class BufferedDatasetWriterBase(DatasetWriterBase):
def __init__(self, path, mode='r', driver=None, width=None, height=None,
count=None, crs=None, transform=None, dtype=None, nodata=None,
gcps=None, **kwargs):
gcps=None, sharing=True, **kwargs):
"""Construct a new dataset
Parameters
......
......@@ -718,8 +718,9 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
warnings.warn(
"dst_crs will be removed in 1.1, use crs",
RasterioDeprecationWarning)
if crs is None:
crs = dst_crs if dst_crs is not None else src_dataset.crs
crs = dst_crs if dst_crs is not None else (src_crs or src_dataset.crs)
# End of `dst_parameter` deprecation and aliasing.
if add_alpha and gdal_version().startswith('1'):
......
......@@ -12,6 +12,7 @@ if sys.version_info[0] >= 3: # pragma: no cover
import configparser
from urllib.parse import urlparse
from collections import UserDict
from collections.abc import Mapping
from inspect import getfullargspec as getargspec
else: # pragma: no cover
string_types = basestring,
......@@ -22,3 +23,4 @@ else: # pragma: no cover
from urlparse import urlparse
from UserDict import UserDict
from inspect import getargspec
from collections import Mapping
......@@ -15,11 +15,11 @@ import json
import pickle
from rasterio._crs import _CRS, all_proj_keys
from rasterio.compat import string_types
from rasterio.compat import Mapping, string_types
from rasterio.errors import CRSError
class CRS(collections.Mapping):
class CRS(Mapping):
"""A geographic or projected coordinate reference system
CRS objects may be created by passing PROJ parameters as keyword
......@@ -86,7 +86,10 @@ class CRS(collections.Mapping):
__nonzero__ = __bool__
def __eq__(self, other):
try:
other = CRS.from_user_input(other)
except CRSError:
return False
return (self._crs == other._crs)
def __getstate__(self):
......@@ -427,6 +430,11 @@ class CRS(collections.Mapping):
"""
if isinstance(value, cls):
return value
elif hasattr(value, "to_wkt") and callable(value.to_wkt):
return cls.from_wkt(
value.to_wkt(),
morph_from_esri_dialect=morph_from_esri_dialect,
)
elif isinstance(value, int):
return cls.from_epsg(value)
elif isinstance(value, dict):
......
......@@ -158,7 +158,7 @@ def can_cast_dtype(values, dtype):
return True
elif values.dtype.kind == 'f':
return np.allclose(values, values.astype(dtype))
return np.allclose(values, values.astype(dtype), equal_nan=True)
else:
return np.array_equal(values, values.astype(dtype))
......
......@@ -127,10 +127,10 @@ def clip(ctx, files, output, bounds, like, driver, projection,
if 'blockxsize' in out_kwargs and out_kwargs['blockxsize'] > width:
del out_kwargs['blockxsize']
logger.warn("Blockxsize removed from creation options to accomodate small output width")
logger.warning("Blockxsize removed from creation options to accomodate small output width")
if 'blockysize' in out_kwargs and out_kwargs['blockysize'] > height:
del out_kwargs['blockysize']
logger.warn("Blockysize removed from creation options to accomodate small output height")
logger.warning("Blockysize removed from creation options to accomodate small output height")
with rasterio.open(output, 'w', **out_kwargs) as out:
out.write(src.read(window=out_window,
......
......@@ -4,3 +4,7 @@ filterwarnings =
ignore::rasterio.errors.NotGeoreferencedWarning
ignore::rasterio.errors.RasterioDeprecationWarning
ignore:numpy.ufunc size changed
markers =
wheel: tests of modules installed from a wheel
gdalbin: tests that require GDAL programs
network: tests that require a network connection
......@@ -36,3 +36,25 @@ def test_read_array(tempfile, dtype, height, width):
dataset.write(in_img, 1)
out_img = dataset.read(1)
assert (in_img == out_img).all()
def test_complex_nodata(tmpdir):
"""A complex dataset can be created with a real nodata value"""
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=Z1.dtype, crs='+proj=latlong', transform=transform1) as dst:
dst.write(Z1, 1)
with rasterio.open(tempfile) as dst:
assert dst.nodata == 0
......@@ -40,6 +40,11 @@ ESRI_PROJECTION_STRING = (
'PARAMETER["Direction",1.0],UNIT["Centimeter",0.01]]]')
class CustomCRS(object):
def to_wkt(self):
return CRS.from_epsg(4326).to_wkt()
def test_crs_constructor_dict():
"""Can create a CRS from a dict"""
crs = CRS({'init': 'epsg:3857'})
......@@ -471,3 +476,14 @@ def test_crs_hash_unequal():
def test_crs84():
"""Create CRS from OGC code"""
assert "WGS 84" in CRS.from_user_input("urn:ogc:def:crs:OGC::CRS84").wkt
@pytest.mark.parametrize("other", ["", 4.2, 0])
def test_equals_different_type(other):
"""Comparison to non-CRS objects is False"""
assert CRS.from_epsg(4326) != other
def test_from_user_input_custom_crs_class():
"""Support comparison to foreign objects that provide to_wkt()"""
assert CRS.from_user_input(CustomCRS()) == CRS.from_epsg(4326)
\ No newline at end of file
......@@ -12,8 +12,8 @@ from rasterio.dtypes import (
def test_is_ndarray():
assert is_ndarray(np.zeros((1,)))
assert is_ndarray([0]) == False
assert is_ndarray((0,)) == False
assert not is_ndarray([0])
assert not is_ndarray((0,))
def test_np_dt_uint8():
......@@ -25,7 +25,7 @@ def test_dt_ubyte():
def test_check_dtype_invalid():
assert check_dtype('foo') == False
assert not check_dtype('foo')
def test_gdal_name():
......@@ -52,19 +52,29 @@ def test_get_minimum_dtype():
def test_can_cast_dtype():
assert can_cast_dtype((1, 2, 3), np.uint8) == True
assert can_cast_dtype(np.array([1, 2, 3]), np.uint8) == True
assert can_cast_dtype(np.array([1, 2, 3], dtype=np.uint8), np.uint8) == True
assert can_cast_dtype(np.array([1, 2, 3]), np.float32) == True
assert can_cast_dtype(np.array([1.4, 2.1, 3.65]), np.float32) == True
assert can_cast_dtype(np.array([1.4, 2.1, 3.65]), np.uint8) == False
assert can_cast_dtype((1, 2, 3), np.uint8)
assert can_cast_dtype(np.array([1, 2, 3]), np.uint8)
assert can_cast_dtype(np.array([1, 2, 3], dtype=np.uint8), np.uint8)
assert can_cast_dtype(np.array([1, 2, 3]), np.float32)
assert can_cast_dtype(np.array([1.4, 2.1, 3.65]), np.float32)
assert not can_cast_dtype(np.array([1.4, 2.1, 3.65]), np.uint8)
@pytest.mark.parametrize("dtype", ["float64", "float32"])
def test_can_cast_dtype_nan(dtype):
assert can_cast_dtype([np.nan], dtype)
@pytest.mark.parametrize("dtype", ["uint8", "uint16", "uint32", "int32"])
def test_cant_cast_dtype_nan(dtype):
assert not can_cast_dtype([np.nan], dtype)
def test_validate_dtype():
assert validate_dtype([1, 2, 3], ('uint8', 'uint16')) == True
assert validate_dtype(np.array([1, 2, 3]), ('uint8', 'uint16')) == True
assert validate_dtype(np.array([1.4, 2.1, 3.65]), ('float32',)) == True
assert validate_dtype(np.array([1.4, 2.1, 3.65]), ('uint8',)) == False
assert validate_dtype([1, 2, 3], ('uint8', 'uint16'))
assert validate_dtype(np.array([1, 2, 3]), ('uint8', 'uint16'))
assert validate_dtype(np.array([1.4, 2.1, 3.65]), ('float32',))
assert not validate_dtype(np.array([1.4, 2.1, 3.65]), ('uint8',))
def test_complex(tmpdir):
......
......@@ -297,3 +297,17 @@ def test_memory_file_gdal_error_message(capsys):
captured = capsys.readouterr()
assert "ERROR 4" not in captured.err
assert "ERROR 4" not in captured.out
def test_write_plus_mode_requires_width():
"""Width is required"""
with MemoryFile() as memfile:
with pytest.raises(TypeError):
memfile.open(driver='GTiff', dtype='uint8', count=3, height=32, crs='epsg:3226', transform=Affine.identity() * Affine.scale(0.5, -0.5))
def test_write_plus_mode_blockxsize_requires_width():
"""Width is required"""
with MemoryFile() as memfile:
with pytest.raises(TypeError):
memfile.open(driver='GTiff', dtype='uint8', count=3, height=32, crs='epsg:3226', transform=Affine.identity() * Affine.scale(0.5, -0.5), blockxsize=128)
......@@ -157,6 +157,18 @@ def test_warped_vrt_set_src_crs(path_rgb_byte_tif, tmpdir):
assert vrt.src_crs == original_crs
def test_warped_vrt_set_src_crs_default(path_rgb_byte_tif, tmpdir):
"""A warped VRT's dst_src defaults to the given src_crs"""
path_crs_unset = str(tmpdir.join("rgb_byte_crs_unset.tif"))
_copy_update_profile(path_rgb_byte_tif, path_crs_unset, crs=None)
with rasterio.open(path_rgb_byte_tif) as src:
original_crs = src.crs
with rasterio.open(path_crs_unset) as src:
with WarpedVRT(src, src_crs=original_crs) as vrt:
assert vrt.src_crs == original_crs
assert vrt.dst_crs == original_crs
def test_wrap_file(path_rgb_byte_tif):
"""A VirtualVRT has the expected dataset properties."""
with rasterio.open(path_rgb_byte_tif) as src:
......
......@@ -356,3 +356,12 @@ def test_write_no_driver__issue_1203(tmpdir):
name = str(tmpdir.join("test.tif"))
with pytest.raises(ValueError), rasterio.open(name, 'w', height=1, width=1, count=1, dtype='uint8'):
print("TEST FAILED IF THIS IS REACHED.")
@pytest.mark.parametrize("mode", ["w", "w+"])
def test_require_width(tmpdir, mode):
"""width and height are required for w and w+ mode"""
name = str(tmpdir.join("test.tif"))
with pytest.raises(TypeError):
with rasterio.open(name, mode, driver="GTiff", height=1, count=1, dtype='uint8'):
print("TEST FAILED IF THIS IS REACHED.")