[Git][debian-gis-team/rasterio][upstream] New upstream version 1.0.10
Bas Couwenberg
gitlab at salsa.debian.org
Sat Nov 17 07:02:07 GMT 2018
Bas Couwenberg pushed to branch upstream at Debian GIS Project / rasterio
Commits:
635117fd by Bas Couwenberg at 2018-11-17T06:43:31Z
New upstream version 1.0.10
- - - - -
21 changed files:
- CHANGES.txt
- rasterio/__init__.py
- rasterio/_base.pxd
- rasterio/_base.pyx
- rasterio/_crs.pyx
- rasterio/_env.pyx
- rasterio/_io.pyx
- rasterio/env.py
- rasterio/gdal.pxi
- rasterio/path.py
- rasterio/session.py
- tests/conftest.py
- + tests/data/compdcs.vrt
- + tests/data/test_esri_wkt.tif
- + tests/test__env.py
- tests/test_crs.py
- tests/test_env.py
- tests/test_rio_warp.py
- + tests/test_scale_offset.py
- tests/test_session.py
- tests/test_warp.py
Changes:
=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,17 @@
Changes
=======
+1.0.10 (2019-11-16)
+-------------------
+
+- Avoid segmentation fault when OSRGetAuthority* functions return null
+ pointers, making Rasterio more robust when reading CRS that don't map
+ perfectly to PROJ.
+- Ensure that GDAL and PROJ support files can be found in Rasterio wheels when
+ we call CRS methods (#1539).
+- Accomodate the Esri flavor of projection WKT (#1537).
+- Add missing raster offsets and scales properties (#1527).
+
1.0.9 (2019-10-25)
------------------
=====================================
rasterio/__init__.py
=====================================
@@ -43,7 +43,7 @@ import rasterio.path
__all__ = ['band', 'open', 'pad', 'Env']
-__version__ = "1.0.9"
+__version__ = "1.0.10"
__gdal_version__ = gdal_version()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
=====================================
rasterio/_base.pxd
=====================================
@@ -23,6 +23,8 @@ cdef class DatasetBase:
cdef public object _nodatavals
cdef public object _units
cdef public object _descriptions
+ cdef public object _scales
+ cdef public object _offsets
cdef public object _read
cdef public object _gcps
cdef GDALDatasetH handle(self) except NULL
=====================================
rasterio/_base.pyx
=====================================
@@ -18,13 +18,12 @@ from rasterio._shim cimport open_dataset
from rasterio.compat import string_types
from rasterio.control import GroundControlPoint
-from rasterio.crs import CRS
from rasterio import dtypes
from rasterio.coords import BoundingBox
from rasterio.crs import CRS
from rasterio.enums import (
ColorInterp, Compression, Interleaving, MaskFlags, PhotometricInterp)
-from rasterio.env import Env
+from rasterio.env import Env, env_ctx_if_needed
from rasterio.errors import (
RasterioIOError, CRSError, DriverRegistrationError, NotGeoreferencedWarning,
RasterBlockError, BandOverviewError)
@@ -208,6 +207,8 @@ cdef class DatasetBase(object):
self._nodatavals = []
self._units = ()
self._descriptions = ()
+ self._scales = ()
+ self._offsets = ()
self._gcps = None
self._read = False
@@ -261,70 +262,60 @@ cdef class DatasetBase(object):
def _handle_crswkt(self, wkt):
"""Return the GDAL dataset's stored CRS"""
- cdef char *proj = NULL
cdef OGRSpatialReferenceH osr = NULL
+ cdef const char *auth_key = NULL
+ cdef const char *auth_val = NULL
+
+ if not wkt:
+ log.debug("No projection detected.")
+ return None
+
wkt_b = wkt.encode('utf-8')
cdef const char *wkt_c = wkt_b
- # Test that the WKT definition isn't just an empty string, which
- # can happen when the source dataset is not georeferenced.
- if len(wkt) > 0:
- crs = CRS()
+ with env_ctx_if_needed():
+ try:
- osr = OSRNewSpatialReference(wkt_c)
- if osr == NULL:
- raise ValueError("Unexpected NULL spatial reference")
- log.debug("Got coordinate system")
-
- # Try to find an EPSG code in the spatial referencing.
- if OSRAutoIdentifyEPSG(osr) == 0:
- key = OSRGetAuthorityName(osr, NULL)
- val = OSRGetAuthorityCode(osr, NULL)
- log.debug("Authority key: %s, value: %s", key, val)
- crs['init'] = u'epsg:' + val
- else:
- log.debug("Failed to auto identify EPSG")
- OSRExportToProj4(osr, &proj)
- if proj == NULL:
- raise ValueError("Unexpected Null spatial reference")
-
- value = proj
- value = value.strip()
-
- for param in value.split():
- kv = param.split("=")
- if len(kv) == 2:
- k, v = kv
- try:
- v = float(v)
- if v % 1 == 0:
- v = int(v)
- except ValueError:
- # Leave v as a string
- pass
- elif len(kv) == 1:
- k, v = kv[0], True
- else:
- raise ValueError(
- "Unexpected proj parameter %s" % param)
- k = k.lstrip("+")
- crs[k] = v
-
- CPLFree(proj)
- _safe_osr_release(osr)
- return crs
+ osr = exc_wrap_pointer(OSRNewSpatialReference(wkt_c))
+ log.debug("Got coordinate system")
- else:
- log.debug("No projection detected.")
+ retval = OSRAutoIdentifyEPSG(osr)
+
+ if retval > 0:
+ log.debug("Failed to auto identify EPSG: %d", retval)
+
+ else:
+ log.debug("Auto identified EPSG: %d", retval)
+
+ try:
+ auth_key = OSRGetAuthorityName(osr, NULL)
+ auth_val = OSRGetAuthorityCode(osr, NULL)
+
+ except CPLE_NotSupportedError as exc:
+ log.debug("{}".format(exc))
+
+ if auth_key != NULL and auth_val != NULL:
+ return CRS({'init': u'{}:{}'.format(auth_key.lower(), auth_val)})
+
+ return CRS.from_wkt(wkt)
+
+ except CPLE_BaseError as exc:
+ raise CRSError("{}".format(exc))
+
+ finally:
+ _safe_osr_release(osr)
def read_crs(self):
"""Return the GDAL dataset's stored CRS"""
- cdef const char *wkt_b = GDALGetProjectionRef(self._hds)
- if wkt_b == NULL:
- raise ValueError("Unexpected NULL spatial reference")
+ cdef const char *wkt_b = NULL
- wkt = wkt_b
- return self._handle_crswkt(wkt)
+ with env_ctx_if_needed():
+ wkt_b = GDALGetProjectionRef(self._hds)
+ if wkt_b == NULL:
+ raise ValueError("Unexpected NULL spatial reference")
+ wkt = wkt_b
+ log.debug("WKT: %r", wkt)
+ return self._handle_crswkt(wkt)
def read_transform(self):
"""Return the stored GDAL GeoTransform"""
@@ -571,6 +562,12 @@ cdef class DatasetBase(object):
def _set_all_descriptions(self, value):
raise NotImplementedError
+ def _set_all_scales(self, value):
+ raise NotImplementedError
+
+ def _set_all_offsets(self, value):
+ raise NotImplementedError
+
def _set_all_units(self, value):
raise NotImplementedError
@@ -612,6 +609,44 @@ cdef class DatasetBase(object):
def __set__(self, value):
self.write_transform(value.to_gdal())
+ property offsets:
+ """Raster offset for each dataset band
+
+ To set offsets, one for each band is required.
+
+ Returns
+ -------
+ list of float
+ """
+ def __get__(self):
+ cdef int success = 0
+ if not self._offsets:
+ offsets = [GDALGetRasterOffset(self.band(j), &success) for j in self.indexes]
+ self._offsets = tuple(offsets)
+ return self._offsets
+
+ def __set__(self, value):
+ self._set_all_offsets(value)
+
+ property scales:
+ """Raster scale for each dataset band
+
+ To set scales, one for each band is required.
+
+ Returns
+ -------
+ list of float
+ """
+ def __get__(self):
+ cdef int success = 0
+ if not self._scales:
+ scales = [GDALGetRasterScale(self.band(j), &success) for j in self.indexes]
+ self._scales = tuple(scales)
+ return self._scales
+
+ def __set__(self, value):
+ self._set_all_scales(value)
+
property units:
"""A list of str: one units string for each dataset band
@@ -1240,10 +1275,19 @@ def _transform(src_crs, dst_crs, xs, ys, zs):
try:
transform = OCTNewCoordinateTransformation(src, dst)
transform = exc_wrap_pointer(transform)
+ exc_wrap_int(OCTTransform(transform, n, x, y, z))
- err = OCTTransform(transform, n, x, y, z)
- err = exc_wrap_int(err)
+ except CPLE_BaseError as exc:
+ log.debug("{}".format(exc))
+ except:
+ CPLFree(x)
+ CPLFree(y)
+ CPLFree(z)
+ _safe_osr_release(src)
+ _safe_osr_release(dst)
+
+ try:
res_xs = [0]*n
res_ys = [0]*n
for i in range(n):
@@ -1253,12 +1297,9 @@ def _transform(src_crs, dst_crs, xs, ys, zs):
res_zs = [0]*n
for i in range(n):
res_zs[i] = z[i]
- retval = (res_xs, res_ys, res_zs)
+ return (res_xs, res_ys, res_zs)
else:
- retval = (res_xs, res_ys)
-
- except CPLE_NotSupportedError as exc:
- raise CRSError(str(exc))
+ return (res_xs, res_ys)
finally:
CPLFree(x)
@@ -1267,45 +1308,25 @@ def _transform(src_crs, dst_crs, xs, ys, zs):
_safe_osr_release(src)
_safe_osr_release(dst)
- return retval
-
cdef OGRSpatialReferenceH _osr_from_crs(object crs) except NULL:
"""Returns a reference to memory that must be deallocated
by the caller."""
- if not crs:
- raise CRSError("A defined coordinate reference system is required")
-
- cdef OGRSpatialReferenceH osr = OSRNewSpatialReference(NULL)
+ crs = CRS.from_user_input(crs)
- if isinstance(crs, string_types):
- proj = crs.encode('utf-8')
+ # EPSG is a special case.
+ init = crs.get('init')
+ if init:
+ auth, val = init.strip().split(':')
- # Make a CRS object from provided dict.
+ if not val or auth.upper() != 'EPSG':
+ raise CRSError("Invalid CRS: {!r}".format(crs))
+ proj = 'EPSG:{}'.format(val).encode('utf-8')
else:
- crs = CRS(crs)
- # EPSG is a special case.
- init = crs.get('init')
- if init:
- auth, val = init.split(':')
-
- if not val:
- _safe_osr_release(osr)
- raise CRSError("Invalid CRS: {!r}".format(crs))
-
- if auth.upper() == 'EPSG':
- proj = 'EPSG:{}'.format(val).encode('utf-8')
- else:
- params = []
- for k, v in crs.items():
- if v is True or (k in ('no_defs', 'wktext') and v):
- params.append("+%s" % k)
- else:
- params.append("+%s=%s" % (k, v))
- proj = " ".join(params)
- log.debug("PROJ.4 to be imported: %r", proj)
- proj = proj.encode('utf-8')
+ proj = crs.to_string().encode('utf-8')
+ log.debug("PROJ.4 to be imported: %r", proj)
+ cdef OGRSpatialReferenceH osr = OSRNewSpatialReference(NULL)
try:
retval = exc_wrap_int(OSRSetFromUserInput(osr, <const char *>proj))
if retval:
=====================================
rasterio/_crs.pyx
=====================================
@@ -6,10 +6,10 @@ include "gdal.pxi"
import json
import logging
-from rasterio._err import CPLE_BaseError
-from rasterio.compat import UserDict
-from rasterio.compat import string_types
+from rasterio._err import CPLE_BaseError, CPLE_NotSupportedError
+from rasterio.compat import UserDict, 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
@@ -33,12 +33,13 @@ class _CRS(UserDict):
cdef OGRSpatialReferenceH osr_crs = NULL
cdef int retval
- try:
- osr_crs = osr_from_crs(self)
- retval = OSRIsGeographic(osr_crs)
- return bool(retval == 1)
- finally:
- _safe_osr_release(osr_crs)
+ with env_ctx_if_needed():
+ try:
+ osr_crs = osr_from_crs(self)
+ retval = OSRIsGeographic(osr_crs)
+ return bool(retval == 1)
+ finally:
+ _safe_osr_release(osr_crs)
@property
def is_projected(self):
@@ -51,12 +52,13 @@ class _CRS(UserDict):
cdef OGRSpatialReferenceH osr_crs = NULL
cdef int retval
- try:
- osr_crs = osr_from_crs(self)
- retval = OSRIsProjected(osr_crs)
- return bool(retval == 1)
- finally:
- _safe_osr_release(osr_crs)
+ with env_ctx_if_needed():
+ try:
+ osr_crs = osr_from_crs(self)
+ retval = OSRIsProjected(osr_crs)
+ return bool(retval == 1)
+ finally:
+ _safe_osr_release(osr_crs)
def __eq__(self, other):
cdef OGRSpatialReferenceH osr_crs1 = NULL
@@ -93,13 +95,14 @@ class _CRS(UserDict):
cdef char *srcwkt = NULL
cdef OGRSpatialReferenceH osr = NULL
- try:
- osr = osr_from_crs(self)
- OSRExportToWkt(osr, &srcwkt)
- return srcwkt.decode('utf-8')
- finally:
- CPLFree(srcwkt)
- _safe_osr_release(osr)
+ with env_ctx_if_needed():
+ try:
+ osr = osr_from_crs(self)
+ OSRExportToWkt(osr, &srcwkt)
+ return srcwkt.decode('utf-8')
+ finally:
+ CPLFree(srcwkt)
+ _safe_osr_release(osr)
def to_epsg(self):
"""The epsg code of the CRS
@@ -110,14 +113,16 @@ class _CRS(UserDict):
"""
cdef OGRSpatialReferenceH osr = NULL
- try:
- osr = osr_from_crs(self)
- if OSRAutoIdentifyEPSG(osr) == 0:
- epsg_code = OSRGetAuthorityCode(osr, NULL)
- return int(epsg_code.decode('utf-8'))
- finally:
- _safe_osr_release(osr)
- return None
+ with env_ctx_if_needed():
+ try:
+ osr = osr_from_crs(self)
+ if OSRAutoIdentifyEPSG(osr) == 0:
+ epsg_code = OSRGetAuthorityCode(osr, NULL)
+ return int(epsg_code.decode('utf-8'))
+ else:
+ return None
+ finally:
+ _safe_osr_release(osr)
@classmethod
def from_epsg(cls, code):
@@ -137,7 +142,7 @@ class _CRS(UserDict):
CRS
"""
if int(code) <= 0:
- raise ValueError("EPSG codes are positive integers")
+ raise CRSError("EPSG codes are positive integers")
return cls(init="epsg:%s" % code, no_defs=True)
@classmethod
@@ -157,7 +162,10 @@ class _CRS(UserDict):
raise CRSError("CRS is empty or invalid: {!r}".format(s))
elif s.strip().upper().startswith('EPSG:'):
- return cls.from_epsg(s.strip().split(':')[1])
+ auth, val = s.strip().split(':')
+ if not val:
+ raise CRSError("Invalid CRS: {!r}".format(s))
+ return cls.from_epsg(val)
elif '{' in s:
# may be json, try to decode it
@@ -222,19 +230,29 @@ class _CRS(UserDict):
if isinstance(s, string_types):
b_s = s.encode('utf-8')
+ log.debug("Encoded WKT: %r", b_s)
try:
- retval = exc_wrap_int(OSRSetFromUserInput(osr, <const char *>b_s))
- if retval:
- _safe_osr_release(osr)
- raise CRSError("Invalid CRS: {!r}".format(s))
+ exc_wrap_int(OSRSetFromUserInput(osr, <const char *>b_s))
+ except CPLE_NotSupportedError as exc:
+ log.debug("{}".format(exc))
except CPLE_BaseError as exc:
_safe_osr_release(osr)
raise CRSError(str(exc))
try:
- OSRExportToProj4(osr, &prj)
+ exc_wrap_int(OSRExportToProj4(osr, &prj))
+ except CPLE_NotSupportedError as exc:
+ log.debug("{}".format(exc))
+
+ try:
return cls.from_string(prj.decode('utf-8'))
+ except CRSError:
+ if OSRMorphFromESRI(osr) == 0:
+ OSRExportToProj4(osr, &prj)
+ return cls.from_string(prj.decode('utf-8'))
+ else:
+ raise
finally:
CPLFree(prj)
_safe_osr_release(osr)
@@ -260,7 +278,7 @@ class _CRS(UserDict):
return cls.from_epsg(value)
elif isinstance(value, dict):
return cls(**value)
- elif isinstance(value, str):
+ elif isinstance(value, string_types):
return cls.from_string(value)
else:
raise CRSError("CRS is invalid: {!r}".format(value))
=====================================
rasterio/_env.pyx
=====================================
@@ -186,6 +186,59 @@ cdef class ConfigEnv(object):
return {k: get_gdal_config(k) for k in self.options}
+class GDALDataFinder(object):
+ """Finds GDAL and PROJ data files"""
+
+ def search(self, prefix=None):
+ """Returns GDAL_DATA location"""
+ path = self.search_wheel(prefix or __file__)
+ if not path:
+ path = self.search_prefix(prefix or sys.prefix)
+ if not path:
+ path = self.search_debian(prefix or sys.prefix)
+ return path
+
+ def search_wheel(self, prefix=None):
+ """Check wheel location"""
+ if prefix is None:
+ prefix = __file__
+ datadir = os.path.abspath(os.path.join(os.path.dirname(prefix), "gdal_data"))
+ return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
+
+ def search_prefix(self, prefix=sys.prefix):
+ """Check sys.prefix location"""
+ datadir = os.path.join(prefix, 'share/gdal')
+ return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
+
+ def search_debian(self, prefix=sys.prefix):
+ """Check Debian locations"""
+ gdal_release_name = GDALVersionInfo("RELEASE_NAME")
+ datadir = os.path.join(prefix, 'share/gdal', '{}.{}'.format(*gdal_release_name.split('.')[:2]))
+ return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
+
+
+class PROJDataFinder(object):
+
+ def search(self, prefix=None):
+ """Returns PROJ_LIB location"""
+ path = self.search_wheel(prefix or __file__)
+ if not path:
+ path = self.search_prefix(prefix or sys.prefix)
+ return path
+
+ def search_wheel(self, prefix=None):
+ """Check wheel location"""
+ if prefix is None:
+ prefix = __file__
+ datadir = os.path.abspath(os.path.join(os.path.dirname(prefix), "proj_data"))
+ return datadir if os.path.exists(datadir) else None
+
+ def search_prefix(self, prefix=sys.prefix):
+ """Check sys.prefix location"""
+ datadir = os.path.join(prefix, 'share/proj')
+ return datadir if os.path.exists(datadir) else None
+
+
cdef class GDALEnv(ConfigEnv):
"""Configuration and driver management"""
@@ -210,40 +263,23 @@ cdef class GDALEnv(ConfigEnv):
if 'GDAL_DATA' not in os.environ:
- # We will try a few well-known paths, starting with the
- # official wheel path.
- whl_datadir = os.path.abspath(
- os.path.join(os.path.dirname(__file__), "gdal_data"))
- fhs_share_datadir = os.path.join(sys.prefix, 'share/gdal')
-
- # Debian supports multiple GDAL installs.
- gdal_release_name = GDALVersionInfo("RELEASE_NAME")
- deb_share_datadir = os.path.join(
- fhs_share_datadir,
- "{}.{}".format(*gdal_release_name.split('.')[:2]))
-
- # If we find GDAL data at the well-known paths, we will
- # add a GDAL_DATA key to the config options dict.
- if os.path.exists(os.path.join(whl_datadir, 'pcs.csv')):
- self.update_config_options(GDAL_DATA=whl_datadir)
+ path = GDALDataFinder().search()
- elif os.path.exists(os.path.join(deb_share_datadir, 'pcs.csv')):
- self.update_config_options(GDAL_DATA=deb_share_datadir)
+ if path:
+ log.debug("GDAL data found in %r", path)
+ self.update_config_options(GDAL_DATA=path)
- elif os.path.exists(os.path.join(fhs_share_datadir, 'pcs.csv')):
- self.update_config_options(GDAL_DATA=fhs_share_datadir)
+ else:
+ self.update_config_options(GDAL_DATA=os.environ['GDAL_DATA'])
if 'PROJ_LIB' not in os.environ:
- whl_datadir = os.path.abspath(
- os.path.join(os.path.dirname(__file__), 'proj_data'))
- share_datadir = os.path.join(sys.prefix, 'share/proj')
+ path = PROJDataFinder().search()
- if os.path.exists(whl_datadir):
- os.environ['PROJ_LIB'] = whl_datadir
+ if path:
+ log.debug("PROJ data found in %r", path)
+ os.environ['PROJ_LIB'] = path
- elif os.path.exists(share_datadir):
- os.environ['PROJ_LIB'] = share_datadir
if driver_count() == 0:
CPLPopErrorHandler()
=====================================
rasterio/_io.pyx
=====================================
@@ -1296,6 +1296,30 @@ cdef class DatasetWriterBase(DatasetReaderBase):
else:
raise ValueError("One description for each band is required")
+ def _set_all_scales(self, value):
+ """Supports the scales property setter"""
+ cdef GDALRasterBandH hband = NULL
+ if len(value) == self.count:
+ for (bidx, val) in zip(self.indexes, value):
+ hband = self.band(bidx)
+ GDALSetRasterScale(hband, val)
+ # Invalidate cached scales.
+ self._scales = ()
+ else:
+ raise ValueError("One scale for each band is required")
+
+ def _set_all_offsets(self, value):
+ """Supports the offsets property setter"""
+ cdef GDALRasterBandH hband = NULL
+ if len(value) == self.count:
+ for (bidx, val) in zip(self.indexes, value):
+ hband = self.band(bidx)
+ GDALSetRasterOffset(hband, val)
+ # Invalidate cached offsets.
+ self._offsets = ()
+ else:
+ raise ValueError("One offset for each band is required")
+
def _set_all_units(self, value):
"""Supports the units property setter"""
# require that we have a unit for every band.
=====================================
rasterio/env.py
=====================================
@@ -338,6 +338,29 @@ def delenv():
local._env = None
+class NullContextManager(object):
+ def __init__(self):
+ pass
+ def __enter__(self):
+ return self
+ def __exit__(self, *args):
+ pass
+
+
+def env_ctx_if_needed():
+ """Return an Env if one does not exist
+
+ Returns
+ -------
+ Env or a do-nothing context manager
+
+ """
+ if local._env:
+ return NullContextManager()
+ else:
+ return Env.from_defaults()
+
+
def ensure_env(f):
"""A decorator that ensures an env exists before a function
calls any GDAL C functions."""
=====================================
rasterio/gdal.pxi
=====================================
@@ -83,6 +83,7 @@ cdef extern from "ogr_srs_api.h" nogil:
int OCTTransform(OGRCoordinateTransformationH ct, int nCount, double *x,
double *y, double *z)
int OSRAutoIdentifyEPSG(OGRSpatialReferenceH srs)
+ int OSRMorphFromESRI(OGRSpatialReferenceH srs)
void OSRCleanup()
OGRSpatialReferenceH OSRClone(OGRSpatialReferenceH srs)
int OSRExportToProj4(OGRSpatialReferenceH srs, char **params)
@@ -265,6 +266,10 @@ cdef extern from "gdal.h" nogil:
char** GDALGetFileList(GDALDatasetH hDS)
CPLErr GDALCopyDatasetFiles (GDALDriverH hDriver, const char * pszNewName, const char * pszOldName)
+ double GDALGetRasterScale(GDALRasterBandH hBand, int * pbSuccess)
+ double GDALGetRasterOffset(GDALRasterBandH hBand, int * pbSuccess)
+ CPLErr GDALSetRasterScale(GDALRasterBandH hBand, double dfNewScale)
+ CPLErr GDALSetRasterOffset(GDALRasterBandH hBand, double dfNewOffset)
cdef extern from "ogr_api.h" nogil:
=====================================
rasterio/path.py
=====================================
@@ -17,13 +17,14 @@ SCHEMES = {
's3': 's3',
'tar': 'tar',
'zip': 'zip',
- 'file': 'file'
+ 'file': 'file',
+ 'oss': 'oss'
}
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')])
+REMOTESCHEMES = set([k for k, v in SCHEMES.items() if v in ('curl', 's3', 'oss')])
class Path(object):
=====================================
rasterio/session.py
=====================================
@@ -92,6 +92,9 @@ class Session(object):
elif path.scheme == "s3" or "amazonaws.com" in path.path:
return AWSSession
+ elif path.scheme == "oss" or "aliyuncs.com" in path.path:
+ return OSSSession
+
# This factory can be extended to other cloud providers here.
# elif path.scheme == "cumulonimbus": # for example.
# return CumulonimbusSession(*args, **kwargs)
@@ -257,3 +260,62 @@ class AWSSession(Session):
return {'AWS_NO_SIGN_REQUEST': 'YES'}
else:
return {k.upper(): v for k, v in self.credentials.items()}
+
+
+class OSSSession(Session):
+ """Configures access to secured resources stored in Alibaba Cloud OSS.
+ """
+ def __init__(self, oss_access_key_id, oss_secret_access_key, oss_endpoint='oss-us-east-1.aliyuncs.com'):
+ """Create new Alibaba Cloud OSS session
+
+ Parameters
+ ----------
+ oss_access_key_id: string
+ An access key id
+ oss_secret_access_key: string
+ An secret access key
+ oss_endpoint: string, default 'oss-us-east-1.aliyuncs.com'
+ the region attached to the bucket
+ """
+
+ self._creds = {
+ "oss_access_key_id": oss_access_key_id,
+ "oss_secret_access_key": oss_secret_access_key,
+ "oss_endpoint": oss_endpoint
+ }
+
+ @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 'OSS_ACCESS_KEY_ID' in config and 'OSS_SECRET_ACCESS_KEY' 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()}
+
+
+
=====================================
tests/conftest.py
=====================================
@@ -48,18 +48,16 @@ def runner():
@pytest.fixture(scope='function')
-def data():
+def data(tmpdir):
"""A temporary directory containing a copy of the files in data."""
- tmpdir = py.test.ensuretemp('tests/data')
for filename in test_files:
shutil.copy(filename, str(tmpdir))
return tmpdir
@pytest.fixture(scope='function')
-def red_green():
+def red_green(tmpdir):
"""A temporary directory containing copies of red.tif, green.tif."""
- tmpdir = py.test.ensuretemp('tests/data')
for filename in ['tests/data/red.tif', 'tests/data/red.tif.ovr', 'tests/data/green.tif', 'tests/data/green.tif.ovr']:
shutil.copy(filename, str(tmpdir))
return tmpdir
@@ -560,15 +558,15 @@ def path_alpha_tif(data_dir):
@pytest.fixture(scope='session')
-def path_zip_file():
+def path_zip_file(data_dir):
"""Creates ``coutwildrnp.zip`` if it does not exist and returns
the absolute file path."""
- path = '{}/white-gemini-iv.zip'.format(data_dir())
+ path = '{}/white-gemini-iv.zip'.format(data_dir)
if not os.path.exists(path):
with zipfile.ZipFile(path, 'w') as zip:
for filename in ['white-gemini-iv.vrt',
'389225main_sw_1965_1024.jpg']:
- zip.write(os.path.join(data_dir(), filename), filename)
+ zip.write(os.path.join(data_dir, filename), filename)
return path
=====================================
tests/data/compdcs.vrt
=====================================
@@ -0,0 +1,37 @@
+<VRTDataset rasterXSize="1024" rasterYSize="768">
+<SRS>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]]]</SRS>
+<GCPList Projection="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]]]">
+ <GCP Id="1" Info="a" Pixel="0.0" Line="0.0" X="157168.0" Y="2818194.0" Z="0.0" />
+ <GCP Id="2" Info="b" Pixel="1024.0" Line="0.0" X="338615.0" Y="2786088.0" Z="0.0" />
+ <GCP Id="3" Info="c" Pixel="0.0" Line="768.0" X="116792" Y="2651340.0" Z="0.0" />
+</GCPList>
+ <VRTRasterBand dataType="Byte" band="1">
+ <ColorInterp>Gray</ColorInterp>
+ <SimpleSource>
+ <SourceFilename relativeToVRT="1">389225main_sw_1965_1024.jpg</SourceFilename>
+ <SourceBand>1</SourceBand>
+ <SrcRect xOff="0" yOff="0" xSize="1024" ySize="768"/>
+ <DstRect xOff="0" yOff="0" xSize="1024" ySize="768"/>
+ </SimpleSource>
+ </VRTRasterBand>
+ <VRTRasterBand dataType="Byte" band="2">
+ <ColorInterp>Gray</ColorInterp>
+ <SimpleSource>
+ <SourceFilename relativeToVRT="1">389225main_sw_1965_1024.jpg</SourceFilename>
+ <SourceBand>2</SourceBand>
+ <SrcRect xOff="0" yOff="0" xSize="1024" ySize="768"/>
+ <DstRect xOff="0" yOff="0" xSize="1024" ySize="768"/>
+ </SimpleSource>
+ </VRTRasterBand>
+ <VRTRasterBand dataType="Byte" band="3">
+ <ColorInterp>Gray</ColorInterp>
+ <SimpleSource>
+ <SourceFilename relativeToVRT="1">389225main_sw_1965_1024.jpg</SourceFilename>
+ <SourceBand>3</SourceBand>
+ <SrcRect xOff="0" yOff="0" xSize="1024" ySize="768"/>
+ <DstRect xOff="0" yOff="0" xSize="1024" ySize="768"/>
+ </SimpleSource>
+ </VRTRasterBand>
+
+</VRTDataset>
+
=====================================
tests/data/test_esri_wkt.tif
=====================================
Binary files /dev/null and b/tests/data/test_esri_wkt.tif differ
=====================================
tests/test__env.py
=====================================
@@ -0,0 +1,125 @@
+"""Tests of _env util module"""
+
+import pytest
+
+from rasterio._env import GDALDataFinder, PROJDataFinder
+
+from .conftest import gdal_version
+
+
+ at pytest.fixture
+def mock_wheel(tmpdir):
+ """A fake rasterio wheel"""
+ moduledir = tmpdir.mkdir("rasterio")
+ moduledir.ensure("__init__,py")
+ moduledir.ensure("_env.py")
+ moduledir.ensure("gdal_data/pcs.csv")
+ moduledir.ensure("proj_data/epsg")
+ return moduledir
+
+
+ at pytest.fixture
+def mock_fhs(tmpdir):
+ """A fake FHS system"""
+ tmpdir.ensure("share/gdal/pcs.csv")
+ tmpdir.ensure("share/proj/epsg")
+ return tmpdir
+
+
+ at pytest.fixture
+def mock_debian(tmpdir):
+ """A fake Debian multi-install system"""
+ tmpdir.ensure("share/gdal/1.11/pcs.csv")
+ tmpdir.ensure("share/gdal/2.0/pcs.csv")
+ tmpdir.ensure("share/gdal/2.1/pcs.csv")
+ tmpdir.ensure("share/gdal/2.2/pcs.csv")
+ tmpdir.ensure("share/gdal/2.3/pcs.csv")
+ tmpdir.ensure("share/gdal/2.4/pcs.csv")
+ tmpdir.ensure("share/proj/epsg")
+ return tmpdir
+
+
+def test_search_wheel_gdal_data_failure(tmpdir):
+ """Fail to find GDAL data in a non-wheel"""
+ finder = GDALDataFinder()
+ assert not finder.search_wheel(str(tmpdir))
+
+
+def test_search_wheel_gdal_data(mock_wheel):
+ """Find GDAL data in a wheel"""
+ finder = GDALDataFinder()
+ assert finder.search_wheel(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("gdal_data"))
+
+
+def test_search_prefix_gdal_data_failure(tmpdir):
+ """Fail to find GDAL data in a bogus prefix"""
+ finder = GDALDataFinder()
+ assert not finder.search_prefix(str(tmpdir))
+
+
+def test_search_prefix_gdal_data(mock_fhs):
+ """Find GDAL data under prefix"""
+ finder = GDALDataFinder()
+ assert finder.search_prefix(str(mock_fhs)) == str(mock_fhs.join("share/gdal"))
+
+
+def test_search_debian_gdal_data_failure(tmpdir):
+ """Fail to find GDAL data in a bogus Debian location"""
+ finder = GDALDataFinder()
+ assert not finder.search_debian(str(tmpdir))
+
+
+def test_search_debian_gdal_data(mock_debian):
+ """Find GDAL data under Debian locations"""
+ finder = GDALDataFinder()
+ assert finder.search_debian(str(mock_debian)) == str(mock_debian.join("share/gdal/{}".format(str(gdal_version))))
+
+
+def test_search_gdal_data_wheel(mock_wheel):
+ finder = GDALDataFinder()
+ assert finder.search(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("gdal_data"))
+
+
+def test_search_gdal_data_fhs(mock_fhs):
+ finder = GDALDataFinder()
+ assert finder.search(str(mock_fhs)) == str(mock_fhs.join("share/gdal"))
+
+
+def test_search_gdal_data_debian(mock_debian):
+ """Find GDAL data under Debian locations"""
+ finder = GDALDataFinder()
+ assert finder.search(str(mock_debian)) == str(mock_debian.join("share/gdal/{}".format(str(gdal_version))))
+
+
+def test_search_wheel_proj_data_failure(tmpdir):
+ """Fail to find GDAL data in a non-wheel"""
+ finder = PROJDataFinder()
+ assert not finder.search_wheel(str(tmpdir))
+
+
+def test_search_wheel_proj_data(mock_wheel):
+ """Find GDAL data in a wheel"""
+ finder = PROJDataFinder()
+ assert finder.search_wheel(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("proj_data"))
+
+
+def test_search_prefix_proj_data_failure(tmpdir):
+ """Fail to find GDAL data in a bogus prefix"""
+ finder = PROJDataFinder()
+ assert not finder.search_prefix(str(tmpdir))
+
+
+def test_search_prefix_proj_data(mock_fhs):
+ """Find GDAL data under prefix"""
+ finder = PROJDataFinder()
+ assert finder.search_prefix(str(mock_fhs)) == str(mock_fhs.join("share/proj"))
+
+
+def test_search_proj_data_wheel(mock_wheel):
+ finder = PROJDataFinder()
+ assert finder.search(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("proj_data"))
+
+
+def test_search_proj_data_fhs(mock_fhs):
+ finder = PROJDataFinder()
+ assert finder.search(str(mock_fhs)) == str(mock_fhs.join("share/proj"))
=====================================
tests/test_crs.py
=====================================
@@ -24,6 +24,22 @@ def test_read_epsg(tmpdir):
assert src.crs.to_dict() == {'init': 'epsg:32618'}
+def test_read_esri_wkt(tmpdir):
+ with rasterio.open('tests/data/test_esri_wkt.tif') as src:
+ assert src.crs.to_dict() == {
+ 'datum': 'NAD83',
+ 'lat_0': 23,
+ 'lat_1': 29.5,
+ 'lat_2': 45.5,
+ 'lon_0': -96,
+ 'no_defs': True,
+ 'proj': 'aea',
+ 'units': 'm',
+ 'x_0': 0,
+ 'y_0': 0,
+ }
+
+
def test_read_epsg3857(tmpdir):
tiffname = str(tmpdir.join('lol.tif'))
subprocess.call([
@@ -274,3 +290,40 @@ def test_from_wkt_invalid():
def test_from_user_input_epsg():
assert 'init' in CRS.from_user_input('EPSG:4326')
+
+
+def test_from_esri_wkt():
+ 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]]]')
+ proj_crs_str = CRS.from_string(projection_string)
+ proj_crs_wkt = CRS.from_wkt(projection_string)
+ assert proj_crs_str.to_string() == proj_crs_wkt.to_string()
+ assert proj_crs_str.to_string() == \
+ ("+datum=NAD83 +lat_0=23 +lat_1=29.5 +lat_2=45.5 "
+ "+lon_0=-96 +no_defs +proj=aea +units=m +x_0=0 +y_0=0")
+
+
+def test_compound_crs():
+ 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).wkt.startswith('GEOGCS["WGS 84"')
+
+
+def test_dataset_compound_crs():
+ with rasterio.open("tests/data/compdcs.vrt") as dataset:
+ assert dataset.crs.wkt.startswith('GEOGCS["WGS 84"')
=====================================
tests/test_env.py
=====================================
@@ -2,18 +2,20 @@
# Collected here to make them easier to skip/xfail.
import logging
+import os
import sys
import boto3
import pytest
import rasterio
+from rasterio import _env
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.rio.main import main_group
-from rasterio.session import AWSSession
+from rasterio.session import AWSSession, OSSSession
from .conftest import requires_gdal21
@@ -112,6 +114,42 @@ def test_ensure_env_decorator(gdalenv):
assert f() is True
+def test_ensure_env_decorator_sets_gdal_data(gdalenv, monkeypatch):
+ """ensure_env finds GDAL from environment"""
+ @ensure_env
+ def f():
+ return getenv()['GDAL_DATA']
+
+ monkeypatch.setenv('GDAL_DATA', '/lol/wut')
+ assert f() == '/lol/wut'
+
+
+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']
+
+ tmpdir.ensure("share/gdal/pcs.csv")
+ monkeypatch.delenv('GDAL_DATA', raising=False)
+ monkeypatch.setattr(sys, 'prefix', str(tmpdir))
+
+ assert f() == str(tmpdir.join("share/gdal"))
+
+
+def test_ensure_env_decorator_sets_gdal_data_wheel(gdalenv, monkeypatch, tmpdir):
+ """ensure_env finds GDAL data in a wheel"""
+ @ensure_env
+ def f():
+ return getenv()['GDAL_DATA']
+
+ tmpdir.ensure("gdal_data/pcs.csv")
+ monkeypatch.delenv('GDAL_DATA', raising=False)
+ monkeypatch.setattr(_env, '__file__', str(tmpdir.join(os.path.basename(_env.__file__))))
+
+ assert f() == str(tmpdir.join("gdal_data"))
+
+
def test_ensure_env_credentialled_decorator(monkeypatch, gdalenv):
"""Credentialization is ensured by wrapper"""
monkeypatch.setenv('AWS_ACCESS_KEY_ID', 'id')
@@ -730,6 +768,11 @@ def test_require_gdal_version_chaining():
def test_rio_env_no_credentials(tmpdir, monkeypatch, runner):
"""Confirm that we can get drivers without any credentials"""
credentials_file = tmpdir.join('credentials')
+ credentials_file.write("""
+[default]
+aws_secret_access_key = foo
+aws_access_key_id = bar
+""")
monkeypatch.setenv('AWS_SHARED_CREDENTIALS_FILE', str(credentials_file))
monkeypatch.delenv('AWS_ACCESS_KEY_ID', raising=False)
# Assert that we don't have any AWS credentials by accident.
@@ -757,3 +800,16 @@ def test_nested_credentials(monkeypatch):
gdalenv = fake_opener('s3://foo/bar')
assert gdalenv['AWS_ACCESS_KEY_ID'] == 'foo'
assert gdalenv['AWS_SECRET_ACCESS_KEY'] == 'bar'
+
+
+def test_oss_session_credentials(gdalenv):
+ """Create an Env with a oss session."""
+ oss_session = OSSSession(
+ oss_access_key_id='id',
+ oss_secret_access_key='key',
+ oss_endpoint='null-island-1')
+ with rasterio.env.Env(session=oss_session) as s:
+ s.credentialize()
+ assert getenv()['OSS_ACCESS_KEY_ID'] == 'id'
+ assert getenv()['OSS_SECRET_ACCESS_KEY'] == 'key'
+ assert getenv()['OSS_ENDPOINT'] == 'null-island-1'
=====================================
tests/test_rio_warp.py
=====================================
@@ -46,7 +46,7 @@ def test_dst_crs_error_epsg(runner, tmpdir):
result = runner.invoke(main_group, [
'warp', srcname, outputname, '--dst-crs', 'EPSG:'])
assert result.exit_code == 2
- assert 'for dst_crs: invalid literal for int()' in result.output
+ assert "for dst_crs: Invalid CRS:" in result.output
def test_dst_crs_error_epsg_2(runner, tmpdir):
=====================================
tests/test_scale_offset.py
=====================================
@@ -0,0 +1,48 @@
+import pytest
+
+import rasterio
+from rasterio.profiles import default_gtiff_profile
+
+
+def test_set_scales(tmpdir):
+ """Scales can be set when dataset is open"""
+ tmptiff = str(tmpdir.join('test.tif'))
+ with rasterio.open(
+ tmptiff, 'w', count=3, height=256, width=256,
+ **default_gtiff_profile) as dst:
+ assert dst.scales == (1.0,) * 3
+ dst.scales = [0.1] * 3
+ assert dst.scales == (0.1,) * 3
+
+
+ at pytest.mark.parametrize('value', [[0.1], [2.0] * 3, []])
+def test_set_scales_error(tmpdir, value):
+ """Number of values must match band count"""
+ tmptiff = str(tmpdir.join('test.tif'))
+ with rasterio.open(
+ tmptiff, 'w', count=2, height=256, width=256,
+ **default_gtiff_profile) as dst:
+ with pytest.raises(ValueError):
+ dst.scales = value
+
+
+def test_set_offsets(tmpdir):
+ """Scales can be set when dataset is open"""
+ tmptiff = str(tmpdir.join('test.tif'))
+ with rasterio.open(
+ tmptiff, 'w', count=3, height=256, width=256,
+ **default_gtiff_profile) as dst:
+ assert dst.offsets == (0.0,) * 3
+ dst.offsets = [0.1] * 3
+ assert dst.offsets == (0.1,) * 3
+
+
+ at pytest.mark.parametrize('value', [[0.1], [2.0] * 3, []])
+def test_set_offsets_error(tmpdir, value):
+ """Number of values must match band count"""
+ tmptiff = str(tmpdir.join('test.tif'))
+ with rasterio.open(
+ tmptiff, 'w', count=2, height=256, width=256,
+ **default_gtiff_profile) as dst:
+ with pytest.raises(ValueError):
+ dst.offsets = value
=====================================
tests/test_session.py
=====================================
@@ -2,7 +2,7 @@
import pytest
-from rasterio.session import DummySession, AWSSession, Session
+from rasterio.session import DummySession, AWSSession, Session, OSSSession
def test_dummy_session():
@@ -114,3 +114,22 @@ def test_requester_pays():
sesh = AWSSession(aws_access_key_id='foo', aws_secret_access_key='bar', requester_pays=True)
assert sesh._session
assert sesh.get_credential_options()['AWS_REQUEST_PAYER'] == 'requester'
+
+
+def test_oss_session_class():
+ """OSSSession works"""
+ oss_session = OSSSession(
+ 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'
+ assert oss_session.get_credential_options()['OSS_SECRET_ACCESS_KEY'] == 'bar'
+
+
+def test_session_factory_oss_kwargs():
+ """Get an OSSSession for oss:// paths with keywords"""
+ sesh = Session.from_path("oss://lol/wut", oss_access_key_id='foo', oss_secret_access_key='bar')
+ assert isinstance(sesh, OSSSession)
+ assert sesh.get_credential_options()['OSS_ACCESS_KEY_ID'] == 'foo'
+ assert sesh.get_credential_options()['OSS_SECRET_ACCESS_KEY'] == 'bar'
=====================================
tests/test_warp.py
=====================================
@@ -179,6 +179,43 @@ def test_transform_bounds():
)
+def test_transform_bounds__esri_wkt():
+ left, bottom, right, top = \
+ (-78.95864996545055, 23.564991210854686,
+ -76.57492370013823, 25.550873767433984)
+ dst_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]]]')
+ assert np.allclose(
+ transform_bounds({"init": "EPSG:4326"},
+ dst_projection_string,
+ left,
+ bottom,
+ right,
+ top),
+ (
+ 1721263.7931814701,
+ 219684.49332178483,
+ 2002926.56696663,
+ 479360.16562217404),
+ )
+
+
def test_transform_bounds_densify():
# This transform is non-linear along the edges, so densification produces
# a different result than otherwise
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/commit/635117fd54de669b589d68fd1250100c9ab47c43
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/commit/635117fd54de669b589d68fd1250100c9ab47c43
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/20181117/4af6550a/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list