[Git][debian-gis-team/rasterio][upstream] New upstream version 1.0.19

Bas Couwenberg gitlab at salsa.debian.org
Wed Feb 27 06:04:48 GMT 2019


Bas Couwenberg pushed to branch upstream at Debian GIS Project / rasterio


Commits:
ba9dc2ac by Bas Couwenberg at 2019-02-27T05:51:15Z
New upstream version 1.0.19
- - - - -


23 changed files:

- CHANGES.txt
- rasterio/__init__.py
- rasterio/_base.pyx
- rasterio/_crs.pyx
- rasterio/_env.pyx
- rasterio/_io.pyx
- rasterio/compat.py
- rasterio/crs.py
- rasterio/dtypes.py
- rasterio/env.py
- rasterio/gdal.pxi
- rasterio/session.py
- setup.py
- + tests/test__crs.py
- tests/test_crs.py
- tests/test_data_paths.py
- tests/test_env.py
- tests/test_profile.py
- tests/test_read.py
- + tests/test_read_dtype.py
- tests/test_rio_info.py
- tests/test_session.py
- tests/test_write.py


Changes:

=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,21 @@
 Changes
 =======
 
+1.0.19 (2019-02-26)
+-------------------
+
+- Do not set GDAL_DATA and PROJ_LIB when data files can be found at their built
+  in locations (#1631).
+- Add linear_units property to CRS (#1638).
+- Ensure that AWS_NO_SIGN_REQUESTS is sufficient for accessing public S3
+  datasets and that import of boto3 is not required (#1637).
+- An out_dtype parameter has been added to DatasetReaderBase.read, enabling
+  on-the-fly casting of raster values to the desired type (#1637). This uses
+  the same latent feature used to get support for "int8" data.
+- Restore pickle protocol for CRS, using WKT as state (#1625).
+- Support for signed 8-bit integer datasets ("int8" dtype) has been added
+  (#1595).
+
 1.0.18 (2019-02-07)
 -------------------
 


=====================================
rasterio/__init__.py
=====================================
@@ -22,7 +22,7 @@ except ImportError:  # pragma: no cover
 from rasterio._base import gdal_version
 from rasterio.drivers import is_blacklisted
 from rasterio.dtypes import (
-    bool_, ubyte, uint8, uint16, int16, uint32, int32, float32, float64,
+    bool_, ubyte, sbyte, uint8, int8, uint16, int16, uint32, int32, float32, float64,
     complex_, check_dtype)
 from rasterio.env import ensure_env_with_credentials, Env
 from rasterio.errors import RasterioIOError
@@ -42,7 +42,7 @@ import rasterio.path
 
 
 __all__ = ['band', 'open', 'pad', 'Env']
-__version__ = "1.0.18"
+__version__ = "1.0.19"
 __gdal_version__ = gdal_version()
 
 # Rasterio attaches NullHandler to the 'rasterio' logger and its


=====================================
rasterio/_base.pyx
=====================================
@@ -9,6 +9,7 @@ import logging
 import math
 import os
 import warnings
+from libc.string cimport strncmp
 
 from rasterio._err import (
     GDALError, CPLE_BaseError, CPLE_IllegalArgError, CPLE_OpenFailedError,
@@ -115,6 +116,20 @@ def driver_can_create_copy(drivername):
     """Return True if the driver has CREATE_COPY capability"""
     return driver_supports_mode(drivername, 'DCAP_CREATECOPY')
 
+cdef _band_dtype(GDALRasterBandH band):
+    """Resolve dtype of a given band, deals with signed/unsigned byte ambiguity"""
+    cdef const char * ptype
+    cdef int gdal_dtype = GDALGetRasterDataType(band)
+    if gdal_dtype == GDT_Byte:
+        # Can be uint8 or int8, need to check PIXELTYPE property
+        ptype = GDALGetMetadataItem(band, 'PIXELTYPE', 'IMAGE_STRUCTURE')
+        if ptype and strncmp(ptype, 'SIGNEDBYTE', 10) == 0:
+            return 'int8'
+        else:
+            return 'uint8'
+
+    return dtypes.dtype_fwd[gdal_dtype]
+
 
 cdef class DatasetBase(object):
     """Dataset base class
@@ -363,8 +378,7 @@ cdef class DatasetBase(object):
         if not self._dtypes:
             for i in range(self._count):
                 band = self.band(i + 1)
-                self._dtypes.append(
-                    dtypes.dtype_fwd[GDALGetRasterDataType(band)])
+                self._dtypes.append(_band_dtype(band))
 
         return tuple(self._dtypes)
 
@@ -402,7 +416,7 @@ cdef class DatasetBase(object):
 
             for i in range(self._count):
                 band = self.band(i + 1)
-                dtype = dtypes.dtype_fwd[GDALGetRasterDataType(band)]
+                dtype = _band_dtype(band)
                 nodataval = GDALGetRasterNoDataValue(band, &success)
                 val = nodataval
                 # GDALGetRasterNoDataValue() has two ways of telling you that


=====================================
rasterio/_crs.pyx
=====================================
@@ -53,6 +53,26 @@ cdef class _CRS(object):
         except CPLE_BaseError as exc:
             raise CRSError("{}".format(exc))
 
+    @property
+    def linear_units(self):
+        """Get linear units of the CRS
+
+        Returns
+        -------
+        str
+
+        """
+        cdef char *units_c = NULL
+        cdef double fmeter
+
+        try:
+            fmeter = OSRGetLinearUnits(self._osr, &units_c)
+        except CPLE_BaseError as exc:
+            raise CRSError("{}".format(exc))
+        else:
+            units_b = units_c
+            return units_b.decode('utf-8')
+
     def __eq__(self, other):
         cdef OGRSpatialReferenceH osr_s = NULL
         cdef OGRSpatialReferenceH osr_o = NULL


=====================================
rasterio/_env.pyx
=====================================
@@ -16,6 +16,10 @@ import os.path
 import sys
 import threading
 
+from rasterio._base cimport _safe_osr_release
+from rasterio._err import CPLE_BaseError
+from rasterio._err cimport exc_wrap_ogrerr, exc_wrap_int
+
 
 level_map = {
     0: 0,
@@ -192,6 +196,21 @@ class GDALDataFinder(object):
     Note: this is not part of the public API in 1.0.x.
 
     """
+    def find_file(self, basename):
+        """Returns path of a GDAL data file or None
+
+        Parameters
+        ----------
+        basename : str
+            Basename of a data file such as "header.dxf"
+
+        Returns
+        -------
+        str (on success) or None (on failure)
+
+        """
+        path = CPLFindFile("gdal", basename.encode('utf-8'))
+        return path
 
     def search(self, prefix=None):
         """Returns GDAL data directory
@@ -203,6 +222,7 @@ class GDALDataFinder(object):
         str or None
 
         """
+        # 
         path = self.search_wheel(prefix or __file__)
         if not path:
             path = self.search_prefix(prefix or sys.prefix)
@@ -235,6 +255,25 @@ class PROJDataFinder(object):
     Note: this is not part of the public API in 1.0.x.
 
     """
+    def has_data(self):
+        """Returns True if PROJ's data files can be found
+
+        Returns
+        -------
+        bool
+
+        """
+        cdef OGRSpatialReferenceH osr = OSRNewSpatialReference(NULL)
+
+        try:
+            exc_wrap_ogrerr(exc_wrap_int(OSRImportFromProj4(osr, "+init=epsg:4326")))
+        except CPLE_BaseError:
+            return False
+        else:
+            return True
+        finally:
+            _safe_osr_release(osr)
+
 
     def search(self, prefix=None):
         """Returns PROJ data directory
@@ -288,6 +327,10 @@ cdef class GDALEnv(ConfigEnv):
                         self.update_config_options(GDAL_DATA=os.environ['GDAL_DATA'])
                         log.debug("GDAL_DATA found in environment: %r.", os.environ['GDAL_DATA'])
 
+                    # See https://github.com/mapbox/rasterio/issues/1631.
+                    elif GDALDataFinder().find_file("header.dxf"):
+                        log.debug("GDAL data files are available at built-in paths")
+
                     else:
                         path = GDALDataFinder().search()
 
@@ -295,8 +338,13 @@ cdef class GDALEnv(ConfigEnv):
                             self.update_config_options(GDAL_DATA=path)
                             log.debug("GDAL_DATA not found in environment, set to %r.", path)
 
-                    if 'PROJ_LIB' not in os.environ:
+                    if 'PROJ_LIB' in os.environ:
+                        log.debug("PROJ_LIB found in environment: %r.", os.environ['PROJ_LIB'])
 
+                    elif PROJDataFinder().has_data():
+                        log.debug("PROJ data files are available at built-in paths")
+
+                    else:
                         path = PROJDataFinder().search()
 
                         if path:


=====================================
rasterio/_io.pyx
=====================================
@@ -137,7 +137,7 @@ cdef class DatasetReaderBase(DatasetBase):
 
     def read(self, indexes=None, out=None, window=None, masked=False,
             out_shape=None, boundless=False, resampling=Resampling.nearest,
-            fill_value=None):
+            fill_value=None, out_dtype=None):
         """Read a dataset's raw pixels as an N-d array
 
         This data is read from the dataset's band cache, which means
@@ -163,6 +163,10 @@ cdef class DatasetReaderBase(DatasetBase):
 
             This parameter cannot be combined with `out_shape`.
 
+        out_dtype : str or numpy dtype
+            The desired output data type. For example: 'uint8' or
+            rasterio.uint16.
+
         out_shape : tuple, optional
             A tuple describing the shape of a new output array. See
             `out` (above) for notes on image decimation and
@@ -266,6 +270,9 @@ cdef class DatasetReaderBase(DatasetBase):
         else:
             dtype = check_dtypes.pop()
 
+        if out_dtype is not None:
+            dtype = out_dtype
+
         # Get the natural shape of the read window, boundless or not.
         # The window can have float values. In this case, we round up
         # when computing the shape.
@@ -292,16 +299,14 @@ cdef class DatasetReaderBase(DatasetBase):
         if out is not None and out_shape is not None:
             raise ValueError("out and out_shape are exclusive")
 
-        # `out` takes precedence over `out_shape`.
+        # `out` takes precedence over `out_shape` and `out_dtype`.
         elif out is not None:
+
             if out.dtype != dtype:
-                raise ValueError(
-                    "the array's dtype '%s' does not match "
-                    "the file's dtype '%s'" % (out.dtype, dtype))
+                dtype == out.dtype
+
             if out.shape[0] != win_shape[0]:
-                raise ValueError(
-                    "'out' shape %s does not match window shape %s" %
-                    (out.shape, win_shape))
+                raise ValueError("'out' shape {} does not match window shape {}".format(out.shape, win_shape))
 
         else:
             if out_shape is not None:
@@ -1101,6 +1106,9 @@ cdef class DatasetWriterBase(DatasetReaderBase):
             else:
                 gdal_dtype = dtypes.dtype_rev.get(self._init_dtype)
 
+            if self._init_dtype == 'int8':
+                options = CSLSetNameValue(options, 'PIXELTYPE', 'SIGNEDBYTE')
+
             # Create a GDAL dataset handle.
             try:
 


=====================================
rasterio/compat.py
=====================================
@@ -13,6 +13,7 @@ if sys.version_info[0] >= 3:   # pragma: no cover
     from urllib.parse import urlparse
     from collections import UserDict
     from inspect import getfullargspec as getargspec
+    from unittest import mock
 else:  # pragma: no cover
     string_types = basestring,
     text_type = unicode
@@ -22,3 +23,4 @@ else:  # pragma: no cover
     from urlparse import urlparse
     from UserDict import UserDict
     from inspect import getargspec
+    import mock


=====================================
rasterio/crs.py
=====================================
@@ -89,6 +89,12 @@ class CRS(collections.Mapping):
         other = CRS.from_user_input(other)
         return (self._crs == other._crs)
 
+    def __getstate__(self):
+        return self.wkt
+
+    def __setstate__(self, state):
+        self._crs = _CRS.from_wkt(state)
+
     def to_proj4(self):
         """Convert CRS to a PROJ4 string
 
@@ -223,6 +229,19 @@ class CRS(collections.Mapping):
         except CRSError:
             return False
 
+    @property
+    def linear_units(self):
+        """The linear units of the CRS
+
+        Possible values include "metre" and "US survey foot".
+
+        Returns
+        -------
+        str
+
+        """
+        return self._crs.linear_units
+
     def to_string(self):
         """Convert CRS to a PROJ4 or WKT string
 


=====================================
rasterio/dtypes.py
=====================================
@@ -12,6 +12,7 @@ do something like this:
 
 bool_ = 'bool'
 ubyte = uint8 = 'uint8'
+sbyte = int8 = 'int8'
 uint16 = 'uint16'
 int16 = 'int16'
 uint32 = 'uint32'
@@ -41,6 +42,7 @@ dtype_fwd = {
 
 dtype_rev = dict((v, k) for k, v in dtype_fwd.items())
 dtype_rev['uint8'] = 1
+dtype_rev['int8'] = 1
 
 typename_fwd = {
     0: 'Unknown',
@@ -59,6 +61,7 @@ typename_fwd = {
 typename_rev = dict((v, k) for k, v in typename_fwd.items())
 
 dtype_ranges = {
+    'int8': (-128, 127),
     'uint8': (0, 255),
     'uint16': (0, 65535),
     'int16': (-32768, 32767),


=====================================
rasterio/env.py
=====================================
@@ -606,16 +606,26 @@ def require_gdal_version(version, param=None, values=None, is_max_version=False,
 
 if 'GDAL_DATA' not in os.environ:
 
-    path = GDALDataFinder().search()
+    # See https://github.com/mapbox/rasterio/issues/1631.
+    if GDALDataFinder().find_file("header.dxf"):
+        log.debug("GDAL data files are available at built-in paths")
 
-    if path:
-        os.environ['GDAL_DATA'] = path
-        log.debug("GDAL_DATA not found in environment, set to %r.", path)
+    else:
+        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()
+    # See https://github.com/mapbox/rasterio/issues/1631.
+    if PROJDataFinder().has_data():
+        log.debug("PROJ data files are available at built-in paths")
+
+    else:
+        path = PROJDataFinder().search()
 
-    if path:
-        os.environ['PROJ_LIB'] = path
-        log.debug("PROJ data not found in environment, set to %r.", path)
+        if path:
+            os.environ['PROJ_LIB'] = path
+            log.debug("PROJ data not found in environment, set to %r.", path)


=====================================
rasterio/gdal.pxi
=====================================
@@ -9,7 +9,8 @@ cdef extern from "cpl_conv.h" nogil:
     void CPLFree(void* ptr)
     void CPLSetThreadLocalConfigOption(const char* key, const char* val)
     void CPLSetConfigOption(const char* key, const char* val)
-    const char* CPLGetConfigOption(const char* key, const char* default)
+    const char *CPLGetConfigOption(const char* key, const char* default)
+    const char *CPLFindFile(const char *pszClass, const char *pszBasename)
 
 
 cdef extern from "cpl_error.h" nogil:
@@ -103,6 +104,7 @@ cdef extern from "ogr_srs_api.h" nogil:
     void OSRRelease(OGRSpatialReferenceH srs)
     int OSRSetFromUserInput(OGRSpatialReferenceH srs, const char *input)
     OGRErr OSRValidate(OGRSpatialReferenceH srs)
+    double OSRGetLinearUnits(OGRSpatialReferenceH srs, char **ppszName)
 
 cdef extern from "gdal.h" nogil:
 


=====================================
rasterio/session.py
=====================================
@@ -1,6 +1,5 @@
 """Abstraction for sessions in various clouds."""
 
-
 from rasterio.path import parse_path, UnparsedPath
 
 
@@ -34,7 +33,7 @@ class Session(object):
         bool
 
         """
-        return NotImplementedError
+        return NotImplemented
 
     def get_credential_options(self):
         """Get credentials as GDAL configuration options
@@ -44,7 +43,7 @@ class Session(object):
         dict
 
         """
-        return NotImplementedError
+        return NotImplemented
 
     @staticmethod
     def from_foreign_session(session, cls=None):
@@ -200,6 +199,8 @@ class AWSSession(Session):
 
         if session:
             self._session = session
+        elif aws_unsigned:
+            self._session = None
         else:
             self._session = boto3.Session(
                 aws_access_key_id=aws_access_key_id,
@@ -210,7 +211,7 @@ class AWSSession(Session):
 
         self.requester_pays = requester_pays
         self.unsigned = aws_unsigned
-        self._creds = self._session._session.get_credentials()
+        self._creds = self._session._session.get_credentials() if self._session else None
 
     @classmethod
     def hascreds(cls, config):
@@ -228,13 +229,13 @@ class AWSSession(Session):
         bool
 
         """
-        return 'AWS_ACCESS_KEY_ID' in config and 'AWS_SECRET_ACCESS_KEY' in config
+        return ('AWS_ACCESS_KEY_ID' in config and 'AWS_SECRET_ACCESS_KEY' in config) or 'AWS_NO_SIGN_REQUEST' in config
 
     @property
     def credentials(self):
         """The session credentials as a dict"""
         res = {}
-        if self._creds:
+        if self._creds: # pragma: no branch
             frozen_creds = self._creds.get_frozen_credentials()
             if frozen_creds.access_key:  # pragma: no branch
                 res['aws_access_key_id'] = frozen_creds.access_key
@@ -316,7 +317,7 @@ class OSSSession(Session):
 
         """
         return {k.upper(): v for k, v in self.credentials.items()}
-    
+
 
 class GSSession(Session):
     """Configures access to secured resources stored in Google Cloud Storage
@@ -330,10 +331,11 @@ class GSSession(Session):
             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
-    
+            self._creds = {'google_application_credentials': google_application_credentials}
+        else:
+            self._creds = {}
+
     @classmethod
     def hascreds(cls, config):
         """Determine if the given configuration has proper credentials


=====================================
setup.py
=====================================
@@ -348,7 +348,7 @@ extra_reqs = {
 
 # Add futures to 'test' for Python < 3.2.
 if sys.version_info < (3, 2):
-    extra_reqs['test'].append('futures')
+    extra_reqs['test'].extend(['futures', 'mock'])
 
 # Add all extra requirements
 extra_reqs['all'] = list(set(itertools.chain(*extra_reqs.values())))


=====================================
tests/test__crs.py
=====================================
@@ -0,0 +1,148 @@
+"""crs module tests"""
+
+import json
+import logging
+import os
+import subprocess
+
+import pytest
+
+import rasterio
+from rasterio._crs import _CRS
+from rasterio.env import env_ctx_if_needed, Env
+from rasterio.errors import CRSError
+
+from .conftest import requires_gdal21, requires_gdal22
+
+
+# Items like "D_North_American_1983" characterize the Esri dialect
+# of WKT SRS.
+ESRI_PROJECTION_STRING = (
+    'PROJCS["USA_Contiguous_Albers_Equal_Area_Conic_USGS_version",'
+    'GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",'
+    'SPHEROID["GRS_1980",6378137.0,298.257222101]],'
+    'PRIMEM["Greenwich",0.0],'
+    'UNIT["Degree",0.0174532925199433]],'
+    'PROJECTION["Albers"],'
+    'PARAMETER["false_easting",0.0],'
+    'PARAMETER["false_northing",0.0],'
+    'PARAMETER["central_meridian",-96.0],'
+    'PARAMETER["standard_parallel_1",29.5],'
+    'PARAMETER["standard_parallel_2",45.5],'
+    'PARAMETER["latitude_of_origin",23.0],'
+    'UNIT["Meter",1.0],'
+    'VERTCS["NAVD_1988",'
+    'VDATUM["North_American_Vertical_Datum_1988"],'
+    'PARAMETER["Vertical_Shift",0.0],'
+    'PARAMETER["Direction",1.0],UNIT["Centimeter",0.01]]]')
+
+
+def test_from_dict():
+    """Can create a _CRS from a dict"""
+    crs = _CRS.from_dict({'init': 'epsg:3857'})
+    assert crs.to_dict()['proj'] == 'merc'
+    assert 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"' in crs.to_wkt()
+
+
+def test_from_dict_keywords():
+    """Can create a CRS from keyword args, ignoring unknowns"""
+    crs = _CRS.from_dict(init='epsg:3857', foo='bar')
+    assert crs.to_dict()['proj'] == 'merc'
+    assert 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"' in crs.to_wkt()
+
+
+def test_from_epsg():
+    """Can create a CRS from EPSG code"""
+    crs = _CRS.from_epsg(4326)
+    assert crs.to_dict()['proj'] == 'longlat'
+
+
+ at pytest.mark.parametrize('code', [0, -1, float('nan'), 1.3])
+def test_from_epsg_error(code):
+    """Raise exception with invalid EPSG code"""
+    with pytest.raises(ValueError):
+        assert _CRS.from_epsg(code)
+
+
+ at pytest.mark.parametrize('proj,expected', [({'init': 'epsg:4326'}, True), ({'init': 'epsg:3857'}, False)])
+def test_is_geographic(proj, expected):
+    """CRS is or is not geographic"""
+    assert _CRS.from_dict(proj).is_geographic is expected
+
+
+ at pytest.mark.parametrize('proj,expected', [({'init': 'epsg:4326'}, False), ({'init': 'epsg:3857'}, True)])
+def test_is_projected(proj, expected):
+    """CRS is or is not projected"""
+    assert _CRS.from_dict(proj).is_projected is expected
+
+
+def test_equality():
+    """CRS are or are not equal"""
+    _CRS.from_epsg(4326) == _CRS.from_proj4('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs')
+
+
+def test_to_wkt():
+    """CRS converts to WKT"""
+    assert _CRS.from_dict({'init': 'epsg:4326'}).to_wkt().startswith('GEOGCS["WGS 84",DATUM')
+
+
+ at pytest.mark.parametrize('proj_string', ['+init=epsg:4326', '+proj=longlat +datum=WGS84 +no_defs'])
+def test_to_epsg(proj_string):
+    """CRS has EPSG code"""
+    assert _CRS.from_proj4(proj_string).to_epsg() == 4326
+
+
+ at pytest.mark.parametrize('proj_string', [ESRI_PROJECTION_STRING])
+def test_esri_wkt_to_epsg(proj_string):
+    """CRS has no EPSG code"""
+    assert _CRS.from_wkt(proj_string, morph_from_esri_dialect=True).to_epsg() is None
+
+
+def test_epsg_no_code_available():
+    """CRS has no EPSG code"""
+    lcc_crs = _CRS.from_proj4('+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc +x_0=0 +units=m +lat_2=77 +lat_1=49 +lat_0=0')
+    assert lcc_crs.to_epsg() is None
+
+
+def test_from_wkt_invalid():
+    """Raise exception if input WKT is invalid"""
+    with pytest.raises(CRSError):
+        _CRS.from_wkt('bogus')
+
+
+ at pytest.mark.parametrize('projection_string', [ESRI_PROJECTION_STRING])
+def test_from_esri_wkt_no_fix(projection_string):
+    """Test ESRI CRS morphing with no datum fixing"""
+    with Env():
+        crs = _CRS.from_wkt(projection_string)
+        assert 'DATUM["D_North_American_1983"' in crs.to_wkt()
+
+
+ at pytest.mark.parametrize('projection_string', [ESRI_PROJECTION_STRING])
+def test_from_esri_wkt_fix_datum(projection_string):
+    """Test ESRI CRS morphing with datum fixing"""
+    with Env(GDAL_FIX_ESRI_WKT='DATUM'):
+        crs = _CRS.from_wkt(projection_string, morph_from_esri_dialect=True)
+        assert 'DATUM["North_American_Datum_1983"' in crs.to_wkt()
+
+
+def test_to_esri_wkt_fix_datum():
+    """Morph to Esri form"""
+    assert 'DATUM["D_North_American_1983"' in _CRS.from_dict(init='epsg:26913').to_wkt(morph_to_esri_dialect=True)
+
+
+def test_compound_crs():
+    """Parse compound WKT"""
+    wkt = """COMPD_CS["unknown",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]],VERT_CS["unknown",VERT_DATUM["unknown",2005],UNIT["metre",1.0,AUTHORITY["EPSG","9001"]],AXIS["Up",UP]]]"""
+    assert _CRS.from_wkt(wkt).to_wkt().startswith('COMPD_CS')
+
+
+def test_exception_proj4():
+    """Get the exception message we expect"""
+    with pytest.raises(CRSError):
+        _CRS.from_proj4("+proj=bogus")
+
+
+def test_linear_units():
+    """CRS linear units can be had"""
+    assert _CRS.from_epsg(3857).linear_units == 'metre'


=====================================
tests/test_crs.py
=====================================
@@ -3,6 +3,7 @@
 import json
 import logging
 import os
+import pickle
 import subprocess
 
 import pytest
@@ -42,21 +43,21 @@ def test_crs_constructor_dict():
     """Can create a CRS from a dict"""
     crs = CRS({'init': 'epsg:3857'})
     assert crs['init'] == 'epsg:3857'
-    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
+    assert 'PROJCS["WGS 84 / Pseudo-Mercator"' 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["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
+    assert 'PROJCS["WGS 84 / Pseudo-Mercator"' 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["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
+    assert 'PROJCS["WGS 84 / Pseudo-Mercator"' in crs.wkt
 
 
 @pytest.fixture(scope='session')
@@ -431,3 +432,16 @@ def test_empty_crs_str():
 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')
+
+
+ at pytest.mark.parametrize('factory,arg', [(CRS.from_epsg, 3857), (CRS.from_dict, {'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})])
+def test_pickle(factory, arg):
+    """A CRS is pickleable"""
+    crs1 = factory(arg)
+    crs2 = pickle.loads(pickle.dumps(crs1))
+    assert crs2 == crs1
+
+
+def test_linear_units():
+    """CRS linear units can be had"""
+    assert CRS.from_epsg(3857).linear_units == 'metre'


=====================================
tests/test_data_paths.py
=====================================
@@ -16,12 +16,22 @@ def test_gdal_data():
     assert GDALDataFinder().search() == os.path.join(os.path.dirname(rasterio.__file__), 'gdal_data')
 
 
+def test_gdal_data_find_file():
+    """Find_file shouldn't raise any exceptions"""
+    GDALDataFinder().find_file("header.dxf")
+
+
 @pytest.mark.wheel
 def test_proj_data():
     """Get GDAL data path from a wheel"""
     assert PROJDataFinder().search() == os.path.join(os.path.dirname(rasterio.__file__), 'proj_data')
 
 
+def test_proj_data_has_data():
+    """has_data shouldn't raise any exceptions"""
+    PROJDataFinder().has_data()
+
+
 @pytest.mark.wheel
 def test_env_gdal_data():
     runner = CliRunner()


=====================================
tests/test_env.py
=====================================
@@ -14,6 +14,7 @@ from rasterio._env import del_gdal_config, get_gdal_config, set_gdal_config
 from rasterio.env import Env, defenv, delenv, getenv, setenv, ensure_env, ensure_env_credentialled
 from rasterio.env import GDALVersion, require_gdal_version
 from rasterio.errors import EnvError, RasterioIOError, GDALVersionError
+from rasterio.compat import mock
 from rasterio.rio.main import main_group
 from rasterio.session import AWSSession, OSSSession
 
@@ -124,13 +125,16 @@ def test_ensure_env_decorator_sets_gdal_data(gdalenv, monkeypatch):
     assert f() == '/lol/wut'
 
 
-def test_ensure_env_decorator_sets_gdal_data_prefix(gdalenv, monkeypatch, tmpdir):
+ at mock.patch("rasterio._env.GDALDataFinder.find_file")
+def test_ensure_env_decorator_sets_gdal_data_prefix(find_file, gdalenv, monkeypatch, tmpdir):
     """ensure_env finds GDAL data under a prefix"""
 
     @ensure_env
     def f():
         return getenv()['GDAL_DATA']
 
+    find_file.return_value = None
+
     tmpdir.ensure("share/gdal/pcs.csv")
     monkeypatch.delenv('GDAL_DATA', raising=False)
     monkeypatch.setattr(sys, 'prefix', str(tmpdir))
@@ -138,12 +142,15 @@ def test_ensure_env_decorator_sets_gdal_data_prefix(gdalenv, monkeypatch, tmpdir
     assert f() == str(tmpdir.join("share").join("gdal"))
 
 
-def test_ensure_env_decorator_sets_gdal_data_wheel(gdalenv, monkeypatch, tmpdir):
+ at mock.patch("rasterio._env.GDALDataFinder.find_file")
+def test_ensure_env_decorator_sets_gdal_data_wheel(find_file, gdalenv, monkeypatch, tmpdir):
     """ensure_env finds GDAL data in a wheel"""
     @ensure_env
     def f():
         return getenv()['GDAL_DATA']
 
+    find_file.return_value = None
+
     tmpdir.ensure("gdal_data/pcs.csv")
     monkeypatch.delenv('GDAL_DATA', raising=False)
     monkeypatch.setattr(_env, '__file__', str(tmpdir.join(os.path.basename(_env.__file__))))


=====================================
tests/test_profile.py
=====================================
@@ -1,3 +1,6 @@
+"""Profile tests"""
+
+import pickle
 import warnings
 
 import pytest
@@ -77,8 +80,8 @@ def test_blockysize_guard(tmpdir):
         rasterio.open(tiffname, 'w', **profile)
 
 
-def test_profile_overlay():
-    with rasterio.open('tests/data/RGB.byte.tif') as src:
+def test_profile_overlay(path_rgb_byte_tif):
+    with rasterio.open(path_rgb_byte_tif) as src:
         kwds = src.profile
     kwds.update(**default_gtiff_profile)
     assert kwds['tiled']
@@ -94,9 +97,9 @@ def test_dataset_profile_property_tiled(data):
         assert src.profile['tiled'] is True
 
 
-def test_dataset_profile_property_untiled(data):
+def test_dataset_profile_property_untiled(data, path_rgb_byte_tif):
     """An untiled dataset's profile has no block sizes"""
-    with rasterio.open('tests/data/RGB.byte.tif') as src:
+    with rasterio.open(path_rgb_byte_tif) as src:
         assert 'blockxsize' not in src.profile
         assert 'blockysize' not in src.profile
         assert src.profile['tiled'] is False
@@ -108,3 +111,14 @@ def test_profile_affine_set():
     profile['transform'] = 'foo'
     with pytest.raises(TypeError):
         profile['affine'] = 'bar'
+
+
+def test_profile_pickle():
+    """Standard profile can be pickled"""
+    assert pickle.loads(pickle.dumps(DefaultGTiffProfile())) == DefaultGTiffProfile()
+
+
+def test_dataset_profile_pickle(path_rgb_byte_tif):
+    """Dataset profiles can be pickled"""
+    with rasterio.open(path_rgb_byte_tif) as src:
+        assert pickle.loads(pickle.dumps(src.profile)) == src.profile


=====================================
tests/test_read.py
=====================================
@@ -233,9 +233,6 @@ class ReaderContextTest(unittest.TestCase):
             b = s.read(2, a, ((310, 330), (320, 330)), False)
             self.assertEqual(id(a), id(b))
             self.assertEqual(a.ndim, 2)
-            # different data types
-            a = np.empty((3, 718, 791), np.float64)
-            self.assertRaises(ValueError, s.read, out=a)
             # different number of array dimensions
             a = np.empty((20, 10), np.ubyte)
             self.assertRaises(ValueError, s.read, [2], out=a)


=====================================
tests/test_read_dtype.py
=====================================
@@ -0,0 +1,33 @@
+"""Tests of out_dtype in read()"""
+
+import numpy
+
+import rasterio
+
+
+def test_uint8_default(path_rgb_byte_tif):
+    """Get uint8 array from uint8 dataset"""
+    with rasterio.open(path_rgb_byte_tif) as dataset:
+        assert dataset.read().dtype == numpy.dtype('uint8')
+
+
+def test_uint8_to_float32(path_rgb_byte_tif):
+    """Get float32 array from uint8 dataset"""
+    with rasterio.open(path_rgb_byte_tif) as dataset:
+        assert dataset.read(out_dtype='float32').dtype == numpy.dtype('float32')
+
+
+def test_uint8_to_float32_out_param(path_rgb_byte_tif):
+    """Get float32 array from uint8 dataset via out parameter"""
+    with rasterio.open(path_rgb_byte_tif) as dataset:
+        assert dataset.read(out=numpy.zeros((dataset.count, dataset.height, dataset.width), dtype='float32')).dtype == numpy.dtype('float32')
+
+
+def test_float32_to_int16():
+    """Get int16 array from float32 dataset"""
+    with rasterio.open('tests/data/float_nan.tif') as dataset:
+        data = dataset.read(out_dtype='int16')
+
+    assert data.dtype == numpy.dtype('int16')
+    assert (data == numpy.array([[[ 1,  1,  0], [ 0,  0, -2]]], dtype='int16')).all()
+


=====================================
tests/test_rio_info.py
=====================================
@@ -453,3 +453,12 @@ def test_info_no_credentials(tmpdir, monkeypatch):
         main_group,
         ['info', 'tests/data/RGB.byte.tif'])
     assert result.exit_code == 0
+
+
+ at requires_gdal21(reason="S3 raster access requires GDAL 2.1+")
+ at pytest.mark.network
+def test_info_aws_unsigned():
+    """Unsigned access to public dataset works (see #1637)"""
+    runner = CliRunner()
+    result = runner.invoke(main_group, ['--aws-no-sign-requests', 'info', 's3://landsat-pds/L8/139/045/LC81390452014295LGN00/LC81390452014295LGN00_B1.TIF'])
+    assert result.exit_code == 0


=====================================
tests/test_session.py
=====================================
@@ -5,6 +5,16 @@ import pytest
 from rasterio.session import DummySession, AWSSession, Session, OSSSession, GSSession
 
 
+def test_base_session_hascreds_notimpl():
+    """Session.hascreds must be overridden"""
+    assert Session.hascreds({}) is NotImplemented
+
+
+def test_base_session_get_credential_options_notimpl():
+    """Session.get_credential_options must be overridden"""
+    assert Session().get_credential_options() is NotImplemented
+
+
 def test_dummy_session():
     """DummySession works"""
     sesh = DummySession()
@@ -33,7 +43,7 @@ def test_aws_session_class_unsigned():
     """AWSSession works"""
     pytest.importorskip("boto3")
     sesh = AWSSession(aws_unsigned=True)
-    assert sesh._session
+    assert sesh._session is None
     assert sesh.get_credential_options()['AWS_NO_SIGN_REQUEST'] == 'YES'
 
 
@@ -119,8 +129,8 @@ def test_requester_pays():
 def test_oss_session_class():
     """OSSSession works"""
     oss_session = OSSSession(
-        oss_access_key_id='foo', 
-        oss_secret_access_key='bar', 
+        oss_access_key_id='foo',
+        oss_secret_access_key='bar',
         oss_endpoint='null-island-1')
     assert oss_session._creds
     assert oss_session.get_credential_options()['OSS_ACCESS_KEY_ID'] == 'foo'
@@ -134,9 +144,16 @@ def test_session_factory_oss_kwargs():
     assert sesh.get_credential_options()['OSS_ACCESS_KEY_ID'] == 'foo'
     assert sesh.get_credential_options()['OSS_SECRET_ACCESS_KEY'] == 'bar'
 
+
+def test_google_session_ctor_no_arg():
+    session = GSSession()
+    assert not session._creds
+
+
 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'
+    assert gs_session.hascreds({'GOOGLE_APPLICATION_CREDENTIALS': 'foo'})


=====================================
tests/test_write.py
=====================================
@@ -33,14 +33,14 @@ def test_validate_dtype_str(tmpdir):
             dtype='Int16')
 
 
-def test_validate_dtype_int8(tmpdir, basic_image):
+def test_validate_dtype_float128(tmpdir, basic_image):
     """Raise TypeError if dtype is unsupported by GDAL."""
-    name = str(tmpdir.join('int8.tif'))
-    basic_image_int8 = basic_image.astype('int8')
-    height, width = basic_image_int8.shape
+    name = str(tmpdir.join('float128.tif'))
+    basic_image_f128 = basic_image.astype('float128')
+    height, width = basic_image_f128.shape
     with pytest.raises(TypeError):
         rasterio.open(name, 'w', driver='GTiff', width=width, height=height,
-                      count=1, dtype=basic_image_int8.dtype)
+                      count=1, dtype=basic_image_f128.dtype)
 
 
 def test_validate_count_None(tmpdir):
@@ -101,6 +101,20 @@ def test_write_ubyte(tmpdir):
     assert "Minimum=127.000, Maximum=127.000, Mean=127.000, StdDev=0.000" in info
 
 
+ at pytest.mark.gdalbin
+def test_write_sbyte(tmpdir):
+    name = str(tmpdir.mkdir("sub").join("test_write_sbyte.tif"))
+    a = np.ones((100, 100), dtype=rasterio.sbyte) * -33
+    with rasterio.open(
+            name, 'w',
+            driver='GTiff', width=100, height=100, count=1,
+            dtype=a.dtype) as s:
+        s.write(a, indexes=1)
+    info = subprocess.check_output(["gdalinfo", "-stats", name]).decode('utf-8')
+    assert "Minimum=-33.000, Maximum=-33.000, Mean=-33.000, StdDev=0.000" in info
+    assert 'SIGNEDBYTE' in info
+
+
 @pytest.mark.gdalbin
 def test_write_ubyte_multi(tmpdir):
     name = str(tmpdir.mkdir("sub").join("test_write_ubyte_multi.tif"))



View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/commit/ba9dc2ac1b9b5a7605f3b96f5bf49accc6665a7e

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/commit/ba9dc2ac1b9b5a7605f3b96f5bf49accc6665a7e
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20190227/23fe395a/attachment-0001.html>


More information about the Pkg-grass-devel mailing list