Skip to content

Commit

Permalink
Switch to GDALOpenEx for fine driver control
Browse files Browse the repository at this point in the history
A WIP toward resolving #1000
  • Loading branch information
Sean C. Gillies committed Nov 9, 2017
1 parent b51056a commit 9b2e05f
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 56 deletions.
2 changes: 2 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Bug fixes:
- Pad boundless read VRTs so we don't overflow due to rounding (#1184).
- Boundless reads of WarpedVRTs are enabled by setting an effective crs
(#1188).
- Raise ValueError when invalid width/height are passed to the
``InMemoryRaster`` constructor (#1144, #1194).

1.0a11 (2017-10-30)
-------------------
Expand Down
4 changes: 2 additions & 2 deletions rasterio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,13 @@ def fp_writer(fp):
# be taken over by the dataset's context manager if it is not
# None.
if mode == 'r':
s = DatasetReader(fp)
s = DatasetReader(fp, driver=driver, **kwargs)
elif mode == 'r-':
warnings.warn("'r-' mode is deprecated, use 'r'",
DeprecationWarning)
s = DatasetReader(fp)
elif mode == 'r+':
s = get_writer_for_path(fp)(fp, mode)
s = get_writer_for_path(fp)(fp, mode, driver=driver, **kwargs)
elif mode == 'w':
s = get_writer_for_driver(driver)(fp, mode, driver=driver,
width=width, height=height,
Expand Down
33 changes: 29 additions & 4 deletions rasterio/_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -109,26 +109,51 @@ def driver_can_create_copy(drivername):
cdef class DatasetBase(object):
"""Dataset base class."""

def __init__(self, path=None, options=None):
cdef GDALDriverH driver = NULL
def __init__(self, path=None, driver=None, **kwargs):
cdef GDALDatasetH hds = NULL
cdef const char *path_c = NULL
cdef const char *driver_c = NULL
cdef const char *key_c = NULL
cdef const char *val_c = NULL
cdef char **allowed_drivers = NULL
cdef char **options = NULL
cdef int flags = 0

self._hds = NULL

if path is not None:
path_b = vsi_path(*parse_path(path)).encode('utf-8')
path_c = path_b

for name in (driver or []):
name_b = name.encode('utf-8')
name_c = name_b
allowed_drivers = CSLAddString(allowed_drivers, name_c)

for key, val in kwargs.items():
key_b = key.encode('utf-8')
key_c = key_b
val_b = val.encode('utf-8')
val_c = val_b
options = CSLAddNameValue(options, key_c, val_c)

# Read-only + Raster + Sharing
flags = 0x00 | 0x02 | 0x20 | 0x40

try:
with nogil:
hds = GDALOpenShared(path_c, <GDALAccess>0)
hds = GDALOpenEx(
path_c, flags, allowed_drivers, options, NULL)
self._hds = exc_wrap_pointer(hds)
except CPLE_OpenFailedError as err:
raise RasterioIOError(str(err))
finally:
CSLDestroy(allowed_drivers)
CSLDestroy(options)

self.name = path
self.mode = 'r'
self.options = options or {}
self.options = kwargs.copy()
self._dtypes = []
self._block_shapes = None
self._nodatavals = []
Expand Down
79 changes: 51 additions & 28 deletions rasterio/_io.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,13 @@ cdef class DatasetReaderBase(DatasetBase):
dst_nodata=ndv,
src_crs=self.crs,
dst_crs=self.crs,
dst_width=max(self.width, window.width) + 10,
dst_height=max(self.height, window.height) + 10,
dst_width=max(self.width, window.width) + 1,
dst_height=max(self.height, window.height) + 1,
dst_transform=self.window_transform(window),
resampling=resampling) as vrt:
out = vrt._read(indexes, out, Window(0, 0, window.width, window.height), None)
out = vrt._read(
indexes, out, Window(0, 0, window.width, window.height),
None)

if masked:
if all_valid:
Expand Down Expand Up @@ -496,11 +498,13 @@ cdef class DatasetReaderBase(DatasetBase):
with WarpedVRT(
self,
dst_crs=self.crs,
dst_width=max(self.width, window.width) + 10,
dst_height=max(self.height, window.height) + 10,
dst_width=max(self.width, window.width) + 1,
dst_height=max(self.height, window.height) + 1,
dst_transform=self.window_transform(window),
resampling=resampling) as vrt:
out = vrt._read(indexes, out, Window(0, 0, window.width, window.height), None, masks=True)
out = vrt._read(
indexes, out, Window(0, 0, window.width, window.height),
None, masks=True)

if return2d:
out.shape = out.shape[1:]
Expand Down Expand Up @@ -869,13 +873,15 @@ cdef class DatasetWriterBase(DatasetReaderBase):
gcps=None, **kwargs):
"""Initialize a DatasetWriterBase instance."""

cdef char **allowed_drivers = NULL
cdef char **options = NULL
cdef char *key_c = NULL
cdef char *val_c = NULL
cdef const char *drv_name = NULL
cdef GDALDriverH drv = NULL
cdef GDALRasterBandH band = NULL
cdef const char *fname = NULL
cdef int flags = 0

# Validate write mode arguments.
log.debug("Path: %s, mode: %s, driver: %s", path, mode, driver)
Expand Down Expand Up @@ -914,6 +920,29 @@ cdef class DatasetWriterBase(DatasetReaderBase):
name_b = path.encode('utf-8')
fname = name_b

# Process dataset opening options.
for k, v in kwargs.items():
# Skip items that are definitely *not* valid driver
# options.
if k.lower() in ['affine']:
continue

k, v = k.upper(), str(v).upper()

# Guard against block size that exceed image size.
if k == 'BLOCKXSIZE' and int(v) > width:
raise ValueError("blockxsize exceeds raster width.")
if k == 'BLOCKYSIZE' and int(v) > height:
raise ValueError("blockysize exceeds raster height.")

key_b = k.encode('utf-8')
val_b = v.encode('utf-8')
key_c = key_b
val_c = val_b
options = CSLSetNameValue(options, key_c, val_c)
log.debug(
"Option: %r", (k, CSLFetchNameValue(options, key_c)))

if mode == 'w':

_delete_dataset_if_exists(path)
Expand All @@ -937,27 +966,6 @@ cdef class DatasetWriterBase(DatasetReaderBase):

# Create a GDAL dataset handle.
try:
for k, v in kwargs.items():
# Skip items that are definitely *not* valid driver
# options.
if k.lower() in ['affine']:
continue

k, v = k.upper(), str(v).upper()

# Guard against block size that exceed image size.
if k == 'BLOCKXSIZE' and int(v) > width:
raise ValueError("blockxsize exceeds raster width.")
if k == 'BLOCKYSIZE' and int(v) > height:
raise ValueError("blockysize exceeds raster height.")

key_b = k.encode('utf-8')
val_b = v.encode('utf-8')
key_c = key_b
val_c = val_b
options = CSLSetNameValue(options, key_c, val_c)
log.debug(
"Option: %r", (k, CSLFetchNameValue(options, key_c)))

self._hds = exc_wrap_pointer(
GDALCreate(drv, fname, width, height,
Expand All @@ -984,10 +992,25 @@ cdef class DatasetWriterBase(DatasetReaderBase):
raise RasterioIOError(str(err))

elif mode == 'r+':

for name in (driver or []):
name_b = name.encode('utf-8')
name_c = name_b
allowed_drivers = CSLAddString(allowed_drivers, name_c)

# Read-only + Raster
flags = 0x01 | 0x02 | 0x40

try:
self._hds = exc_wrap_pointer(GDALOpenShared(fname, <GDALAccess>1))
with nogil:
hds = GDALOpenEx(
fname, flags, allowed_drivers, options, NULL)
self._hds = exc_wrap_pointer(hds)
except CPLE_OpenFailedError as err:
raise RasterioIOError(str(err))
finally:
CSLDestroy(allowed_drivers)
CSLDestroy(options)

else:
# Raise an exception if we have any other mode.
Expand Down
2 changes: 2 additions & 0 deletions rasterio/gdal.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ cdef extern from "cpl_error.h" nogil:
cdef extern from "cpl_string.h" nogil:

int CSLCount(char **papszStrList)
char **CSLAddString(char **strlist, const char *string)
char **CSLAddNameValue(char **papszStrList, const char *pszName,
const char *pszValue)
char **CSLDuplicate(char **papszStrList)
Expand Down Expand Up @@ -170,6 +171,7 @@ cdef extern from "gdal.h" nogil:
void GDALSetDescription(GDALMajorObjectH obj, const char *text)
GDALDriverH GDALGetDriverByName(const char *name)
GDALDatasetH GDALOpen(const char *filename, GDALAccess access) # except -1
GDALDatasetH GDALOpenEx(const char *filename, int flags, const char **allowed_drivers, const char **options, const char **siblings) # except -1
GDALDatasetH GDALOpenShared(const char *filename, GDALAccess access) # except -1
void GDALFlushCache(GDALDatasetH hds)
void GDALClose(GDALDatasetH hds)
Expand Down
8 changes: 4 additions & 4 deletions rasterio/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def open(self, driver=None, width=None, height=None, count=None, crs=None,
transform=None, dtype=None, nodata=None, **kwargs):
"""Open the file and return a Rasterio dataset object.
If data has already been written, the file is opened in 'r+'
If data has already been written, the file is opened in 'r'
mode. Otherwise, the file is opened in 'w' mode.
Parameters
Expand All @@ -111,7 +111,7 @@ def open(self, driver=None, width=None, height=None, count=None, crs=None,
if self.closed:
raise IOError("I/O operation on closed file.")
if self.exists():
return DatasetReader(vsi_path, 'r+')
return DatasetReader(vsi_path, driver=driver, **kwargs)
else:
writer = get_writer_for_driver(driver)
return writer(vsi_path, 'w', driver=driver, width=width,
Expand All @@ -137,7 +137,7 @@ def __init__(self, file_or_bytes=None):
super(ZipMemoryFile, self).__init__(file_or_bytes, ext='zip')

@ensure_env
def open(self, path):
def open(self, path, driver=None, **kwargs):
"""Open a dataset within the zipped stream.
Parameters
Expand All @@ -154,7 +154,7 @@ def open(self, path):

if self.closed:
raise IOError("I/O operation on closed file.")
return DatasetReader(vsi_path, 'r')
return DatasetReader(vsi_path, driver=driver, **kwargs)


def get_writer_for_driver(driver):
Expand Down
18 changes: 0 additions & 18 deletions tests/test_deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,6 @@
from rasterio.errors import RasterioDeprecationWarning


def test_open_affine_and_transform(path_rgb_byte_tif):
"""Passsing both 'affine' and 'transform' to rasterio.open() should issue
some helpful warnings.
By settings the 'affine' kwarg to a wacky value we ensure that the
'transform' kwarg is used while ignoring the 'affine' kwarg.
"""
with pytest.warns(None) as record:
with rasterio.open(
path_rgb_byte_tif,
affine=rasterio,
transform=affine.Affine.identity()):
pass
assert len(record) == 2
assert "The 'affine' kwarg in rasterio.open() is deprecated" in str(record[0].message)
assert "choosing 'transform'" in str(record[1].message)


def test_open_transform_gdal_geotransform(path_rgb_byte_tif):
"""Passing a GDAL geotransform to rasterio.open(transform=...) should raise
an exception.
Expand Down

0 comments on commit 9b2e05f

Please sign in to comment.