diff -Nru rasterio-1.0.14/AUTHORS.txt rasterio-1.0.18/AUTHORS.txt --- rasterio-1.0.14/AUTHORS.txt 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/AUTHORS.txt 2019-02-07 17:43:57.000000000 +0000 @@ -68,4 +68,4 @@ * Vincent Schut * Alan D. * grovduck -* Dan Little +* Dan "Ducky" Little diff -Nru rasterio-1.0.14/CHANGES.txt rasterio-1.0.18/CHANGES.txt --- rasterio-1.0.14/CHANGES.txt 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/CHANGES.txt 2019-02-07 17:43:57.000000000 +0000 @@ -1,6 +1,38 @@ Changes ======= +1.0.18 (2019-02-07) +------------------- + +- Fix a regression reported in #1623. + +1.0.17 (2019-02-05) +------------------- + +- Fix a regression in evaluation of CRS equality (#1620). + +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) +------------------- + +- Google cloud storage support was *not* in fact added in 1.0.14, but is + present in 1.0.15. + 1.0.14 (2019-01-22) ------------------- diff -Nru rasterio-1.0.14/debian/changelog rasterio-1.0.18/debian/changelog --- rasterio-1.0.14/debian/changelog 2019-01-23 05:59:47.000000000 +0000 +++ rasterio-1.0.18/debian/changelog 2019-02-08 06:03:16.000000000 +0000 @@ -1,3 +1,31 @@ +rasterio (1.0.18-1) unstable; urgency=medium + + * Team upload. + * New upstream release. + + -- Bas Couwenberg Fri, 08 Feb 2019 07:03:16 +0100 + +rasterio (1.0.17-1) unstable; urgency=medium + + * Team upload. + * New upstream release. + + -- Bas Couwenberg Wed, 06 Feb 2019 07:41:11 +0100 + +rasterio (1.0.16-1) unstable; urgency=medium + + * Team upload. + * New upstream release. + + -- Bas Couwenberg Tue, 05 Feb 2019 15:34:38 +0100 + +rasterio (1.0.15-1) unstable; urgency=medium + + * Team upload. + * New upstream release. + + -- Bas Couwenberg Mon, 28 Jan 2019 07:21:11 +0100 + rasterio (1.0.14-1) unstable; urgency=medium * Team upload. diff -Nru rasterio-1.0.14/rasterio/_base.pyx rasterio-1.0.18/rasterio/_base.pyx --- rasterio-1.0.14/rasterio/_base.pyx 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/_base.pyx 2019-02-07 17:43:57.000000000 +0000 @@ -267,7 +267,7 @@ if wkt: return CRS.from_wkt(wkt) else: - return CRS() + return None def read_crs(self): """Return the GDAL dataset's stored CRS""" diff -Nru rasterio-1.0.14/rasterio/crs.py rasterio-1.0.18/rasterio/crs.py --- rasterio-1.0.14/rasterio/crs.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/crs.py 2019-02-07 17:43:57.000000000 +0000 @@ -66,13 +66,7 @@ data = dict(initialdata or {}) data.update(**kwargs) data = {k: v for k, v in data.items() if k in all_proj_keys} - - # always use lowercase 'epsg'. - if 'init' in data: - data['init'] = data['init'].replace('EPSG:', 'epsg:') - - proj = ' '.join(['+{}={}'.format(key, val) for key, val in data.items()]) - self._crs = _CRS.from_proj4(proj) + self._crs = _CRS.from_dict(data) else: self._crs = _CRS() @@ -157,11 +151,18 @@ dict """ - epsg_code = self.to_epsg() - if epsg_code: - return {'init': 'epsg:{}'.format(epsg_code)} + if self._crs is None: + raise CRSError("Undefined CRS has no dict representation") + else: - return self._crs.to_dict() + 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): @@ -404,6 +405,8 @@ elif isinstance(value, dict): return cls(**value) elif isinstance(value, string_types): - return cls.from_string(value, morph_from_esri_dialect=morph_from_esri_dialect) + obj = cls() + obj._crs = _CRS.from_user_input(value, morph_from_esri_dialect=morph_from_esri_dialect) + return obj else: raise CRSError("CRS is invalid: {!r}".format(value)) diff -Nru rasterio-1.0.14/rasterio/_crs.pyx rasterio-1.0.18/rasterio/_crs.pyx --- rasterio-1.0.14/rasterio/_crs.pyx 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/_crs.pyx 2019-02-07 17:43:57.000000000 +0000 @@ -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 @@ -58,16 +58,23 @@ cdef OGRSpatialReferenceH osr_o = NULL cdef _CRS crs_o = other - try: - osr_s = exc_wrap_pointer(OSRClone(self._osr)) - exc_wrap_ogrerr(OSRMorphFromESRI(osr_s)) - osr_o = exc_wrap_pointer(OSRClone(crs_o._osr)) - exc_wrap_ogrerr(OSRMorphFromESRI(osr_o)) - return bool(OSRIsSame(osr_s, osr_o) == 1) + epsg_s = self.to_epsg() + epsg_o = other.to_epsg() - finally: - _safe_osr_release(osr_s) - _safe_osr_release(osr_o) + if epsg_s is not None and epsg_o is not None and epsg_s == epsg_o: + return True + + else: + try: + osr_s = exc_wrap_pointer(OSRClone(self._osr)) + exc_wrap_ogrerr(OSRMorphFromESRI(osr_s)) + osr_o = exc_wrap_pointer(OSRClone(crs_o._osr)) + exc_wrap_ogrerr(OSRMorphFromESRI(osr_o)) + return bool(OSRIsSame(osr_s, osr_o) == 1) + + finally: + _safe_osr_release(osr_s) + _safe_osr_release(osr_o) def to_wkt(self, morph_to_esri_dialect=False): """An OGC WKT representation of the CRS @@ -114,14 +121,17 @@ exc_wrap_ogrerr(OSRMorphFromESRI(osr)) if OSRAutoIdentifyEPSG(osr) == 0: epsg_code = OSRGetAuthorityCode(osr, NULL) - return int(epsg_code.decode('utf-8')) + if epsg_code != NULL: + return int(epsg_code.decode('utf-8')) + else: + return None else: return None finally: _safe_osr_release(osr) - @classmethod - def from_epsg(cls, code): + @staticmethod + def from_epsg(code): """Make a CRS from an EPSG code Parameters @@ -138,9 +148,19 @@ CRS """ - if int(code) <= 0: + cdef _CRS obj = _CRS.__new__(_CRS) + + code = int(code) + + if code <= 0: raise CRSError("EPSG codes are positive integers") - return cls.from_proj4('+init=epsg:{}'.format(code)) + + try: + exc_wrap_ogrerr(exc_wrap_int(OSRImportFromEPSG(obj._osr, code))) + except CPLE_BaseError as exc: + raise CRSError("The EPSG code is unknown. {}".format(exc)) + else: + return obj @staticmethod def from_proj4(proj): @@ -158,7 +178,7 @@ """ cdef _CRS obj = _CRS.__new__(_CRS) - # Filter out nonsensical items. + # Filter out nonsensical items that might have crept in. items_filtered = [] items = proj.split() for item in items: @@ -172,10 +192,8 @@ try: exc_wrap_ogrerr(exc_wrap_int(OSRImportFromProj4(obj._osr, proj_b))) - except CPLE_BaseError as exc: raise CRSError("The PROJ4 dict could not be understood. {}".format(exc)) - else: return obj @@ -199,10 +217,13 @@ data.update(**kwargs) data = {k: v for k, v in data.items() if k in all_proj_keys} - # always use lowercase 'epsg'. - if 'init' in data: - data['init'] = data['init'].replace('EPSG:', 'epsg:') + # "+init=epsg:xxxx" is deprecated in GDAL. If we find this, we will + # extract the epsg code and dispatch to from_epsg. + if 'init' in data and data['init'].lower().startswith('epsg:'): + epsg_code = int(data['init'].split(':')[1]) + return _CRS.from_epsg(epsg_code) + # Continue with the general case. proj = ' '.join(['+{}={}'.format(key, val) for key, val in data.items()]) b_proj = proj.encode('utf-8') @@ -244,13 +265,46 @@ 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 + + @staticmethod + def from_user_input(text, morph_from_esri_dialect=False): + """Make a CRS from a WKT string + Parameters + ---------- + text : str + User input text of many different kinds. + morph_from_esri_dialect : bool, optional + If True, items in the input using Esri's dialect of WKT + will be replaced by OGC standard equivalents. + + Returns + ------- + CRS + + """ + cdef const char *text_c = NULL + + if not isinstance(text, string_types): + raise ValueError("A string is expected") + + text_b = text.encode('utf-8') + text_c = text_b + + cdef _CRS obj = _CRS.__new__(_CRS) + + try: + errcode = exc_wrap_ogrerr(OSRSetFromUserInput(obj._osr, text_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 @@ -411,12 +465,11 @@ +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'] +_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'] diff -Nru rasterio-1.0.14/rasterio/env.py rasterio-1.0.18/rasterio/env.py --- rasterio-1.0.14/rasterio/env.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/env.py 2019-02-07 17:43:57.000000000 +0000 @@ -8,7 +8,9 @@ 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 @@ 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) diff -Nru rasterio-1.0.14/rasterio/__init__.py rasterio-1.0.18/rasterio/__init__.py --- rasterio-1.0.14/rasterio/__init__.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/__init__.py 2019-02-07 17:43:57.000000000 +0000 @@ -42,7 +42,7 @@ __all__ = ['band', 'open', 'pad', 'Env'] -__version__ = "1.0.14" +__version__ = "1.0.18" __gdal_version__ = gdal_version() # Rasterio attaches NullHandler to the 'rasterio' logger and its diff -Nru rasterio-1.0.14/rasterio/_io.pyx rasterio-1.0.18/rasterio/_io.pyx --- rasterio-1.0.14/rasterio/_io.pyx 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/_io.pyx 2019-02-07 17:43:57.000000000 +0000 @@ -117,15 +117,20 @@ cdef float height = data.shape[-2] cdef float width = data.shape[-1] - if ndims == 2: - 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) - else: - raise ValueError("Specified data must have 2 or 3 dimensions") + try: + + if ndims == 2: + 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) + + 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,25 +678,22 @@ indexes_arr = np.array(indexes, dtype='intp') indexes_count = indexes_arr.shape[0] - if masks: - # Warn if nodata attribute is shadowing an alpha band. - if self.count == 4 and self.colorinterp[3] == ColorInterp.alpha: - for flags in self.mask_flag_enums: - if MaskFlags.nodata in flags: - warnings.warn(NodataShadowWarning()) - - retval = 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) - - if retval in (1, 2, 3): - raise IOError("Read or write failed") - elif retval == 4: - raise ValueError("NULL band") + try: + + if masks: + # Warn if nodata attribute is shadowing an alpha band. + if self.count == 4 and self.colorinterp[3] == ColorInterp.alpha: + for flags in self.mask_flag_enums: + if MaskFlags.nodata in flags: + warnings.warn(NodataShadowWarning()) + + io_multi_mask(self._hds, 0, xoff, yoff, width, height, out, indexes_arr, resampling=resampling) + + else: + io_multi_band(self._hds, 0, xoff, yoff, width, height, out, indexes_arr, resampling=resampling) + + except CPLE_BaseError as cplerr: + raise RasterioIOError("Read or write failed. {}".format(cplerr)) return out @@ -1354,13 +1356,11 @@ indexes_arr = np.array(indexes, dtype='intp') indexes_count = 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 @@ width = self.width height = self.height - 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) - else: - retval = io_band(mask, 1, xoff, yoff, width, height, mask_array) + 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) + io_band(mask, 1, xoff, yoff, width, height, array) + else: + 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 @@ 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: + io_auto(self._image, self.band(1), False) + else: + io_auto(self._image, self._hds, False) + + except CPLE_BaseError as cplerr: + raise RasterioIOError("Read or write failed. {}".format(cplerr)) - if self._image.ndim == 2: - exc_wrap_int(io_auto(self._image, self.band(1), False)) - else: - exc_wrap_int(io_auto(self._image, self._hds, False)) return self._image def write(self, np.ndarray image): self._image = image - if image.ndim == 2: - exc_wrap_int(io_auto(self._image, self.band(1), True)) - else: - exc_wrap_int(io_auto(self._image, self._hds, True)) + try: + if image.ndim == 2: + io_auto(self._image, self.band(1), True) + else: + 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 @@ 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, 0)) except Exception as exc: @@ -1969,7 +1984,7 @@ 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. diff -Nru rasterio-1.0.14/rasterio/path.py rasterio-1.0.18/rasterio/path.py --- rasterio-1.0.14/rasterio/path.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/path.py 2019-02-07 17:43:57.000000000 +0000 @@ -18,13 +18,14 @@ 'tar': 'tar', 'zip': 'zip', 'file': 'file', - 'oss': 'oss' + 'oss': 'oss', + 'gs': 'gs', } CURLSCHEMES = set([k for k, v in SCHEMES.items() if v == 'curl']) # TODO: extend for other cloud plaforms. -REMOTESCHEMES = set([k for k, v in SCHEMES.items() if v in ('curl', 's3', 'oss')]) +REMOTESCHEMES = set([k for k, v in SCHEMES.items() if v in ('curl', 's3', 'oss', 'gs')]) class Path(object): diff -Nru rasterio-1.0.14/rasterio/rio/info.py rasterio-1.0.18/rasterio/rio/info.py --- rasterio-1.0.14/rasterio/rio/info.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/rio/info.py 2019-02-07 17:43:57.000000000 +0000 @@ -92,8 +92,8 @@ if gcps: info['gcps'] = {'points': [p.asdict() for p in gcps]} - if crs: - epsg = crs.to_epsg() + if gcps_crs: + epsg = gcps_crs.to_epsg() if epsg: info['gcps']['crs'] = 'EPSG:{}'.format(epsg) else: diff -Nru rasterio-1.0.14/rasterio/session.py rasterio-1.0.18/rasterio/session.py --- rasterio-1.0.14/rasterio/session.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/session.py 2019-02-07 17:43:57.000000000 +0000 @@ -316,6 +316,53 @@ """ return {k.upper(): v for k, v in self.credentials.items()} + + +class GSSession(Session): + """Configures access to secured resources stored in Google Cloud Storage + """ + def __init__(self, google_application_credentials=None): + """Create new Google Cloude Storage session + + Parameters + ---------- + google_application_credentials: string + Path to the google application credentials JSON file. + """ + self._creds = {} + if google_application_credentials is not None: + self._creds['google_application_credentials'] = google_application_credentials + @classmethod + def hascreds(cls, config): + """Determine if the given configuration has proper credentials + Parameters + ---------- + cls : class + A Session class. + config : dict + GDAL configuration as a dict. + + Returns + ------- + bool + + """ + return 'GOOGLE_APPLICATION_CREDENTIALS' in config + + @property + def credentials(self): + """The session credentials as a dict""" + return self._creds + + def get_credential_options(self): + """Get credentials as GDAL configuration options + + Returns + ------- + dict + + """ + return {k.upper(): v for k, v in self.credentials.items()} diff -Nru rasterio-1.0.14/rasterio/_shim1.pyx rasterio-1.0.18/rasterio/_shim1.pyx --- rasterio-1.0.14/rasterio/_shim1.pyx 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/_shim1.pyx 2019-02-07 17:43:57.000000000 +0000 @@ -10,7 +10,7 @@ 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 @@ 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 xsize = width cdef int ysize = height - with nogil: - bandmap = CPLMalloc(count*sizeof(int)) - for i in range(count): - bandmap[i] = indexes[i] - retval = GDALDatasetRasterIO( - hds, mode, xoff, yoff, xsize, ysize, buf, - bufxsize, bufysize, buftype, count, bandmap, - bufpixelspace, buflinespace, bufbandspace) - CPLFree(bandmap) + bandmap = CPLMalloc(count*sizeof(int)) + for i in range(count): + bandmap[i] = indexes[i] - return retval + try: + with nogil: + retval = GDALDatasetRasterIO( + hds, mode, xoff, yoff, xsize, ysize, buf, + bufxsize, bufysize, buftype, count, bandmap, + bufpixelspace, buflinespace, bufbandspace) + + return exc_wrap_int(retval) + + finally: + CPLFree(bandmap) cdef int io_multi_mask( @@ -183,4 +187,4 @@ if retval: break - return retval + return exc_wrap_int(retval) diff -Nru rasterio-1.0.14/rasterio/shim_rasterioex.pxi rasterio-1.0.18/rasterio/shim_rasterioex.pxi --- rasterio-1.0.14/rasterio/shim_rasterioex.pxi 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/rasterio/shim_rasterioex.pxi 2019-02-07 17:43:57.000000000 +0000 @@ -6,6 +6,8 @@ cimport numpy as np +from rasterio._err cimport exc_wrap_int + cdef extern from "cpl_progress.h": @@ -73,7 +75,7 @@ band, 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 @@ extras.pfnProgress = NULL extras.pProgressData = NULL - with nogil: - bandmap = CPLMalloc(count*sizeof(int)) - for i in range(count): - bandmap[i] = indexes[i] - retval = GDALDatasetRasterIOEx( - hds, mode, xoff, yoff, xsize, ysize, buf, - bufxsize, bufysize, buftype, count, bandmap, - bufpixelspace, buflinespace, bufbandspace, &extras) - CPLFree(bandmap) + bandmap = CPLMalloc(count*sizeof(int)) + for i in range(count): + bandmap[i] = indexes[i] - return retval + try: + with nogil: + retval = GDALDatasetRasterIOEx( + hds, mode, xoff, yoff, xsize, ysize, buf, + bufxsize, bufysize, buftype, count, bandmap, + bufpixelspace, buflinespace, bufbandspace, &extras) + + 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 @@ retval = GDALRasterIOEx( hmask, mode, xoff, yoff, xsize, ysize, buf, bufxsize, bufysize, 1, bufpixelspace, buflinespace, &extras) + if retval: break - return retval + return exc_wrap_int(retval) diff -Nru rasterio-1.0.14/tests/test_crs.py rasterio-1.0.18/tests/test_crs.py --- rasterio-1.0.14/tests/test_crs.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/tests/test_crs.py 2019-02-07 17:43:57.000000000 +0000 @@ -42,21 +42,21 @@ """Can create a CRS from a dict""" crs = CRS({'init': 'epsg:3857'}) assert crs['init'] == 'epsg:3857' - assert 'PROJCS["unnamed",GEOGCS["unnamed ellipse",DATUM["unknown",SPHEROID["unnamed",6378137,0]' in crs.wkt + assert 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]' in crs.wkt def test_crs_constructor_keywords(): """Can create a CRS from keyword args, ignoring unknowns""" crs = CRS(init='epsg:3857', foo='bar') assert crs['init'] == 'epsg:3857' - assert 'PROJCS["unnamed",GEOGCS["unnamed ellipse",DATUM["unknown",SPHEROID["unnamed",6378137,0]' in crs.wkt + assert 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]' in crs.wkt def test_crs_constructor_crs_obj(): """Can create a CRS from a CRS obj""" crs = CRS(CRS(init='epsg:3857')) assert crs['init'] == 'epsg:3857' - assert 'PROJCS["unnamed",GEOGCS["unnamed ellipse",DATUM["unknown",SPHEROID["unnamed",6378137,0]' in crs.wkt + assert 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]' in crs.wkt @pytest.fixture(scope='session') @@ -88,6 +88,12 @@ } +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,43 @@ 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()) == '' + + +def test_issue1620(): + """Different forms of EPSG:3857 are equal""" + assert CRS.from_wkt('PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]') == CRS.from_dict(init='epsg:3857') diff -Nru rasterio-1.0.14/tests/test_env.py rasterio-1.0.18/tests/test_env.py --- rasterio-1.0.14/tests/test_env.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/tests/test_env.py 2019-02-07 17:43:57.000000000 +0000 @@ -126,6 +126,7 @@ 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'] diff -Nru rasterio-1.0.14/tests/test_gcps.py rasterio-1.0.18/tests/test_gcps.py --- rasterio-1.0.14/tests/test_gcps.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/tests/test_gcps.py 2019-02-07 17:43:57.000000000 +0000 @@ -1,5 +1,6 @@ """Tests of ground control points""" +import numpy import pytest import rasterio @@ -62,6 +63,35 @@ 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_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) diff -Nru rasterio-1.0.14/tests/test_session.py rasterio-1.0.18/tests/test_session.py --- rasterio-1.0.14/tests/test_session.py 2019-01-23 03:16:09.000000000 +0000 +++ rasterio-1.0.18/tests/test_session.py 2019-02-07 17:43:57.000000000 +0000 @@ -2,7 +2,7 @@ import pytest -from rasterio.session import DummySession, AWSSession, Session, OSSSession +from rasterio.session import DummySession, AWSSession, Session, OSSSession, GSSession def test_dummy_session(): @@ -133,3 +133,10 @@ assert isinstance(sesh, OSSSession) assert sesh.get_credential_options()['OSS_ACCESS_KEY_ID'] == 'foo' assert sesh.get_credential_options()['OSS_SECRET_ACCESS_KEY'] == 'bar' + +def test_gs_session_class(): + """GSSession works""" + gs_session = GSSession( + google_application_credentials='foo') + assert gs_session._creds + assert gs_session.get_credential_options()['GOOGLE_APPLICATION_CREDENTIALS'] == 'foo'