Skip to content
Commits on Source (4)
Changes
=======
1.0.16 (2019-02-04)
-------------------
- A bug preventing GCPs from being created with new BufferedDatasetWriter
instances (#1600) has been fixed (#1610).
- A previously unreported bug preventing BufferedDatasetWriters from being
opened in r+ mode has been fixed.
- A regression in creating CRS objects from PROJ4 strings that include
"+wktext" (#1609) has been fixed.
- Regressions in str representations of empty CRS objects and the handling of
unreferenced datasets in rasterio._base have been fixed (#1616).
- GDAL seems to work best if GDAL_DATA is set as early as possible. Ideally it
is set when building the library or in the environment before importing
Rasterio, but for wheels we patch GDAL_DATA into os.environ when rasterio.env
is imported. This resolves #1611.
1.0.15 (2019-01-27)
-------------------
......
rasterio (1.0.16-1) unstable; urgency=medium
* Team upload.
* New upstream release.
-- Bas Couwenberg <sebastic@debian.org> Tue, 05 Feb 2019 15:34:38 +0100
rasterio (1.0.15-1) unstable; urgency=medium
* Team upload.
......
......@@ -42,7 +42,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
__version__ = "1.0.15"
__version__ = "1.0.16"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
......
......@@ -267,7 +267,7 @@ cdef class DatasetBase(object):
if wkt:
return CRS.from_wkt(wkt)
else:
return CRS()
return None
def read_crs(self):
"""Return the GDAL dataset's stored CRS"""
......
......@@ -3,10 +3,10 @@
import logging
import rasterio._env
from rasterio._err import CPLE_BaseError, CPLE_NotSupportedError
from rasterio.compat import string_types
from rasterio.errors import CRSError
from rasterio.env import env_ctx_if_needed
from rasterio._base cimport _osr_from_crs as osr_from_crs
from rasterio._base cimport _safe_osr_release
......@@ -175,10 +175,8 @@ cdef class _CRS(object):
try:
exc_wrap_ogrerr(exc_wrap_int(OSRImportFromProj4(obj._osr, <const char *>proj_b)))
except CPLE_BaseError as exc:
raise CRSError("The PROJ4 dict could not be understood. {}".format(exc))
else:
return obj
......@@ -247,13 +245,10 @@ cdef class _CRS(object):
try:
errcode = exc_wrap_ogrerr(OSRImportFromWkt(obj._osr, &wkt_c))
if morph_from_esri_dialect:
exc_wrap_ogrerr(OSRMorphFromESRI(obj._osr))
except CPLE_BaseError as exc:
raise CRSError("The WKT could not be parsed. {}".format(exc))
else:
return obj
......@@ -414,12 +409,11 @@ _param_data = """
+vopt
+W
+westo
+wktext
+x_0 False easting
+y_0 False northing
+zone UTM zone
"""
with env_ctx_if_needed():
_lines = filter(lambda x: len(x) > 1, _param_data.split("\n"))
all_proj_keys = list(set(line.split()[0].lstrip("+").strip()
for line in _lines)) + ['no_mayo']
all_proj_keys = list(set(line.split()[0].lstrip("+").strip() for line in _lines)) + ['no_mayo']
......@@ -117,16 +117,21 @@ cdef int io_auto(data, GDALRasterBandH band, bint write, int resampling=0) excep
cdef float height = data.shape[-2]
cdef float width = data.shape[-1]
try:
if ndims == 2:
return io_band(band, write, 0.0, 0.0, width, height, data,
resampling=resampling)
return io_band(band, write, 0.0, 0.0, width, height, data, resampling=resampling)
elif ndims == 3:
indexes = np.arange(1, data.shape[0] + 1, dtype='intp')
return io_multi_band(band, write, 0.0, 0.0, width, height, data,
indexes, resampling=resampling)
return io_multi_band(band, write, 0.0, 0.0, width, height, data, indexes, resampling=resampling)
else:
raise ValueError("Specified data must have 2 or 3 dimensions")
except CPLE_BaseError as cplerr:
raise RasterioIOError(str(cplerr))
cdef class DatasetReaderBase(DatasetBase):
......@@ -673,6 +678,8 @@ cdef class DatasetReaderBase(DatasetBase):
indexes_arr = np.array(indexes, dtype='intp')
indexes_count = <int>indexes_arr.shape[0]
try:
if masks:
# Warn if nodata attribute is shadowing an alpha band.
if self.count == 4 and self.colorinterp[3] == ColorInterp.alpha:
......@@ -680,18 +687,13 @@ cdef class DatasetReaderBase(DatasetBase):
if MaskFlags.nodata in flags:
warnings.warn(NodataShadowWarning())
retval = io_multi_mask(
self._hds, 0, xoff, yoff, width, height,
out, indexes_arr, resampling=resampling)
io_multi_mask(self._hds, 0, xoff, yoff, width, height, out, indexes_arr, resampling=resampling)
else:
retval = io_multi_band(self._hds, 0, xoff, yoff, width, height,
out, indexes_arr, resampling=resampling)
io_multi_band(self._hds, 0, xoff, yoff, width, height, out, indexes_arr, resampling=resampling)
if retval in (1, 2, 3):
raise IOError("Read or write failed")
elif retval == 4:
raise ValueError("NULL band")
except CPLE_BaseError as cplerr:
raise RasterioIOError("Read or write failed. {}".format(cplerr))
return out
......@@ -1354,13 +1356,11 @@ cdef class DatasetWriterBase(DatasetReaderBase):
indexes_arr = np.array(indexes, dtype='intp')
indexes_count = <int>indexes_arr.shape[0]
retval = io_multi_band(self._hds, 1, xoff, yoff, width, height,
src, indexes_arr)
if retval in (1, 2, 3):
raise IOError("Read or write failed")
elif retval == 4:
raise ValueError("NULL band")
try:
io_multi_band(self._hds, 1, xoff, yoff, width, height, src, indexes_arr)
except CPLE_BaseError as cplerr:
raise RasterioIOError("Read or write failed. {}".format(cplerr))
def write_band(self, bidx, src, window=None):
"""Write the src array into the `bidx` band.
......@@ -1540,15 +1540,19 @@ cdef class DatasetWriterBase(DatasetReaderBase):
width = self.width
height = self.height
try:
if mask_array is True:
GDALFillRaster(mask, 255, 0)
elif mask_array is False:
GDALFillRaster(mask, 0, 0)
elif mask_array.dtype == np.bool:
array = 255 * mask_array.astype(np.uint8)
retval = io_band(mask, 1, xoff, yoff, width, height, array)
io_band(mask, 1, xoff, yoff, width, height, array)
else:
retval = io_band(mask, 1, xoff, yoff, width, height, mask_array)
io_band(mask, 1, xoff, yoff, width, height, mask_array)
except CPLE_BaseError as cplerr:
raise RasterioIOError("Read or write failed. {}".format(cplerr))
def build_overviews(self, factors, resampling=Resampling.nearest):
"""Build overviews at one or more decimation factors for all
......@@ -1782,22 +1786,32 @@ cdef class InMemoryRaster:
self._hds = NULL
def read(self):
if self._image is None:
raise IOError("You need to write data before you can read the data.")
raise RasterioIOError("You need to write data before you can read the data.")
try:
if self._image.ndim == 2:
exc_wrap_int(io_auto(self._image, self.band(1), False))
io_auto(self._image, self.band(1), False)
else:
exc_wrap_int(io_auto(self._image, self._hds, False))
io_auto(self._image, self._hds, False)
except CPLE_BaseError as cplerr:
raise RasterioIOError("Read or write failed. {}".format(cplerr))
return self._image
def write(self, np.ndarray image):
self._image = image
try:
if image.ndim == 2:
exc_wrap_int(io_auto(self._image, self.band(1), True))
io_auto(self._image, self.band(1), True)
else:
exc_wrap_int(io_auto(self._image, self._hds, True))
io_auto(self._image, self._hds, True)
except CPLE_BaseError as cplerr:
raise RasterioIOError("Read or write failed. {}".format(cplerr))
cdef class BufferedDatasetWriterBase(DatasetWriterBase):
......@@ -1956,10 +1970,11 @@ cdef class BufferedDatasetWriterBase(DatasetWriterBase):
self.write_transform(self._transform)
if self._crs:
self._set_crs(self._crs)
if self._gcps:
self._set_gcps(self._gcps, self._crs)
if self._init_gcps:
self._set_gcps(self._init_gcps, self._crs)
elif self.mode == 'r+':
fname = name_b
try:
temp = exc_wrap_pointer(GDALOpenShared(fname, <GDALAccess>0))
except Exception as exc:
......@@ -1969,7 +1984,7 @@ cdef class BufferedDatasetWriterBase(DatasetWriterBase):
GDALCreateCopy(memdrv, "temp", temp, 1, NULL, NULL, NULL))
drv = GDALGetDatasetDriver(temp)
self.driver = get_driver_name(drv)
self.driver = get_driver_name(drv).decode('utf-8')
GDALClose(temp)
# Instead of calling _begin() we do the following.
......
......@@ -10,7 +10,7 @@ from rasterio import dtypes
from rasterio.enums import Resampling
cimport numpy as np
from rasterio._err cimport exc_wrap_pointer
from rasterio._err cimport exc_wrap_int, exc_wrap_pointer
from rasterio.errors import GDALOptionNotImplementedError
......@@ -88,7 +88,7 @@ cdef int io_band(
band, mode, xoff, yoff, xsize, ysize, buf, bufxsize, bufysize,
buftype, bufpixelspace, buflinespace)
return retval
return exc_wrap_int(retval)
cdef int io_multi_band(
......@@ -121,17 +121,21 @@ cdef int io_multi_band(
cdef int xsize = <int>width
cdef int ysize = <int>height
with nogil:
bandmap = <int *>CPLMalloc(count*sizeof(int))
for i in range(count):
bandmap[i] = <int>indexes[i]
try:
with nogil:
retval = GDALDatasetRasterIO(
hds, mode, xoff, yoff, xsize, ysize, buf,
bufxsize, bufysize, buftype, count, bandmap,
bufpixelspace, buflinespace, bufbandspace)
CPLFree(bandmap)
return retval
return exc_wrap_int(retval)
finally:
CPLFree(bandmap)
cdef int io_multi_mask(
......@@ -183,4 +187,4 @@ cdef int io_multi_mask(
if retval:
break
return retval
return exc_wrap_int(retval)
......@@ -71,7 +71,17 @@ class CRS(collections.Mapping):
if 'init' in data:
data['init'] = data['init'].replace('EPSG:', 'epsg:')
proj = ' '.join(['+{}={}'.format(key, val) for key, val in data.items()])
proj_parts = []
for key, val in data.items():
if val is False or None:
continue
elif val is True:
proj_parts.append('+{}'.format(key))
else:
proj_parts.append('+{}={}'.format(key, val))
proj = ' '.join(proj_parts)
self._crs = _CRS.from_proj4(proj)
else:
......@@ -157,11 +167,18 @@ class CRS(collections.Mapping):
dict
"""
if self._crs is None:
raise CRSError("Undefined CRS has no dict representation")
else:
epsg_code = self.to_epsg()
if epsg_code:
return {'init': 'epsg:{}'.format(epsg_code)}
else:
try:
return self._crs.to_dict()
except CRSError:
return {}
@property
def data(self):
......
......@@ -8,7 +8,9 @@ import re
import threading
import warnings
from rasterio._env import GDALEnv, get_gdal_config, set_gdal_config
from rasterio._env import (
GDALEnv, get_gdal_config, set_gdal_config,
GDALDataFinder, PROJDataFinder)
from rasterio.compat import string_types, getargspec
from rasterio.errors import (
EnvError, GDALVersionError, RasterioDeprecationWarning)
......@@ -598,3 +600,22 @@ def require_gdal_version(version, param=None, values=None, is_max_version=False,
return wrapper
return decorator
# Patch the environment if needed, such as in the installed wheel case.
if 'GDAL_DATA' not in os.environ:
path = GDALDataFinder().search()
if path:
os.environ['GDAL_DATA'] = path
log.debug("GDAL_DATA not found in environment, set to %r.", path)
if 'PROJ_LIB' not in os.environ:
path = PROJDataFinder().search()
if path:
os.environ['PROJ_LIB'] = path
log.debug("PROJ data not found in environment, set to %r.", path)
......@@ -6,6 +6,8 @@ from rasterio.enums import Resampling
cimport numpy as np
from rasterio._err cimport exc_wrap_int
cdef extern from "cpl_progress.h":
......@@ -73,7 +75,7 @@ cdef int io_band(GDALRasterBandH band, int mode, float x0, float y0,
band, <GDALRWFlag>mode, xoff, yoff, xsize, ysize, buf, bufxsize, bufysize,
buftype, bufpixelspace, buflinespace, &extras)
return retval
return exc_wrap_int(retval)
cdef int io_multi_band(GDALDatasetH hds, int mode, float x0, float y0,
......@@ -117,17 +119,21 @@ cdef int io_multi_band(GDALDatasetH hds, int mode, float x0, float y0,
extras.pfnProgress = NULL
extras.pProgressData = NULL
with nogil:
bandmap = <int *>CPLMalloc(count*sizeof(int))
for i in range(count):
bandmap[i] = <int>indexes[i]
try:
with nogil:
retval = GDALDatasetRasterIOEx(
hds, <GDALRWFlag>mode, xoff, yoff, xsize, ysize, buf,
bufxsize, bufysize, buftype, count, bandmap,
bufpixelspace, buflinespace, bufbandspace, &extras)
CPLFree(bandmap)
return retval
return exc_wrap_int(retval)
finally:
CPLFree(bandmap)
cdef int io_multi_mask(GDALDatasetH hds, int mode, float x0, float y0,
......@@ -187,7 +193,8 @@ cdef int io_multi_mask(GDALDatasetH hds, int mode, float x0, float y0,
retval = GDALRasterIOEx(
hmask, <GDALRWFlag>mode, xoff, yoff, xsize, ysize, buf, bufxsize,
bufysize, <GDALDataType>1, bufpixelspace, buflinespace, &extras)
if retval:
break
return retval
return exc_wrap_int(retval)
......@@ -88,6 +88,12 @@ def test_read_esri_wkt():
}
def test_read_no_crs():
"""crs of a dataset with no SRS is None"""
with rasterio.open('tests/data/389225main_sw_1965_1024.jpg') as src:
assert src.crs is None
# Ensure that CRS sticks when we write a file.
@pytest.mark.gdalbin
def test_write_3857(tmpdir):
......@@ -385,3 +391,38 @@ def test_implicit_proj_dict(projection_string):
def test_capitalized_epsg_init():
"""Ensure that old behavior is preserved"""
assert CRS(init='EPSG:4326').to_epsg() == 4326
def test_issue1609_wktext_a():
"""Check on fix of issue 1609"""
src_proj = {'ellps': 'WGS84',
'proj': 'stere',
'lat_0': -90.0,
'lon_0': 0.0,
'x_0': 0.0,
'y_0': 0.0,
'lat_ts': -70,
'no_defs': True}
wkt = CRS(src_proj).wkt
assert 'PROJECTION["Polar_Stereographic"]' in wkt
assert 'PARAMETER["latitude_of_origin",-70]' in wkt
def test_issue1609_wktext_b():
"""Check on fix of issue 1609"""
dst_proj = {'ellps': 'WGS84',
'h': 9000000.0,
'lat_0': -78.0,
'lon_0': 0.0,
'proj': 'nsper',
'units': 'm',
'x_0': 0,
'y_0': 0,
'wktext': True}
wkt = CRS(dst_proj).wkt
assert '+wktext' in wkt
def test_empty_crs_str():
"""str(CRS()) should be empty string"""
assert str(CRS()) == ''
\ No newline at end of file
......@@ -126,6 +126,7 @@ def test_ensure_env_decorator_sets_gdal_data(gdalenv, monkeypatch):
def test_ensure_env_decorator_sets_gdal_data_prefix(gdalenv, monkeypatch, tmpdir):
"""ensure_env finds GDAL data under a prefix"""
@ensure_env
def f():
return getenv()['GDAL_DATA']
......
"""Tests of ground control points"""
import numpy
import pytest
import rasterio
......@@ -79,6 +80,35 @@ def test_write_read_gcps(tmpdir):
assert (200.0, 2000.0, 0.0) == (point.x, point.y, point.z)
def test_write_read_gcps_buffereddatasetwriter(tmpdir):
filename = str(tmpdir.join('test.jpg'))
gcps = [GroundControlPoint(1, 1, 100.0, 1000.0, z=0.0)]
with rasterio.open(filename, 'w', driver='JPEG', dtype='uint8', count=3,
width=10, height=10, crs='epsg:4326', gcps=gcps) as dst:
dst.write(numpy.ones((3, 10, 10), dtype='uint8'))
with rasterio.open(filename, 'r+') as dst:
gcps, crs = dst.gcps
assert crs.to_epsg() == 4326
assert len(gcps) == 1
point = gcps[0]
assert (1, 1) == (point.row, point.col)
assert (100.0, 1000.0, 0.0) == (point.x, point.y, point.z)
dst.gcps = [
GroundControlPoint(1, 1, 100.0, 1000.0, z=0.0),
GroundControlPoint(2, 2, 200.0, 2000.0, z=0.0)], crs
gcps, crs = dst.gcps
assert crs.to_epsg() == 4326
assert len(gcps) == 2
point = gcps[1]
assert (2, 2) == (point.row, point.col)
assert (200.0, 2000.0, 0.0) == (point.x, point.y, point.z)
def test_read_vrt_gcps(tmpdir):
vrtfile = tmpdir.join('test.vrt')
vrtfile.write("""
......