[Git][debian-gis-team/rasterio][upstream] New upstream version 1.4.2
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Wed Oct 30 14:18:27 GMT 2024
Bas Couwenberg pushed to branch upstream at Debian GIS Project / rasterio
Commits:
6ccc37e8 by Bas Couwenberg at 2024-10-30T15:05:09+01:00
New upstream version 1.4.2
- - - - -
15 changed files:
- .github/workflows/tests.yaml
- CHANGES.txt
- docs/topics/tags.rst
- rasterio/__init__.py
- rasterio/_warp.pyx
- rasterio/crs.pxd
- rasterio/crs.pyx
- rasterio/enums.py
- rasterio/gdal.pxi
- rasterio/transform.py
- tests/test_colorinterp.py
- tests/test_crs.py
- tests/test_rio_edit_info.py
- tests/test_transform.py
- tests/test_warp.py
Changes:
=====================================
.github/workflows/tests.yaml
=====================================
@@ -2,7 +2,7 @@ name: Tests
on:
push:
- branches: [ main ]
+ branches: [ main, maint-1.4 ]
paths:
- '.github/workflows/tests.yaml'
- 'requirements*.txt'
@@ -14,7 +14,7 @@ on:
- 'rasterio/**'
- 'tests/**'
pull_request:
- branches: [ main ]
+ branches: [ main, maint-1.4 ]
paths:
- '.github/workflows/tests.yaml'
- 'requirements*.txt'
@@ -98,8 +98,10 @@ jobs:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
- gdal-version: ['3.9.2']
+ gdal-version: ['3.9.3']
include:
+ - python-version: '3.12'
+ gdal-version: 'latest'
- python-version: '3.9'
gdal-version: '3.8.5'
- python-version: '3.9'
=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,23 @@
Changes
=======
+1.4.2 (TBD)
+-----------
+
+Version 1.4.2 fixes a regression and further improves compatibility with
+GDAL 3.10.
+
+Bug fixes:
+
+- The reproject() function now always returns a 2-D array, masked or
+ non-masked, when requested (#3223).
+- The various rowcol() methods once again return integers by default as
+ they did in 1.3.11 (#3219).
+- Internal usage of CRS.to_epsg(), which is slow, has been reduced, and
+ CRS.__eq__() has been made much faster (#3216).
+- The warper's use of a MEM:: dataset has been made fully compatible with
+ changes coming in GDAL 3.10 (#3212).
+
1.4.1 (2024-09-30)
------------------
@@ -20,6 +37,7 @@ Other changes:
- New color interpretation constants of GDAL 3.10 have been added to the
ColorInterp enum (#3194).
+
1.4.0 (2024-09-23)
------------------
=====================================
docs/topics/tags.rst
=====================================
@@ -23,8 +23,6 @@ namespace, call :meth:`~.DatasetReader.tags` with no arguments.
.. code-block:: pycon
- >>> import rasterio
- >>> src = rasterio.open("tests/data/RGB.byte.tif")
>>> src.tags()
{'AREA_OR_POINT': 'Area'}
=====================================
rasterio/__init__.py
=====================================
@@ -81,7 +81,7 @@ except ImportError:
have_vsi_plugin = False
__all__ = ['band', 'open', 'pad', 'Band', 'Env', 'CRS']
-__version__ = "1.4.1"
+__version__ = "1.4.2"
__gdal_version__ = gdal_version()
__proj_version__ = ".".join([str(version) for version in get_proj_version()])
__geos_version__ = ".".join([str(version) for version in get_geos_version()])
=====================================
rasterio/_warp.pyx
=====================================
@@ -22,7 +22,7 @@ from rasterio._err import (
from rasterio import dtypes
from rasterio.control import GroundControlPoint
from rasterio.enums import Resampling, MaskFlags, ColorInterp
-from rasterio.env import GDALVersion
+from rasterio.env import Env, GDALVersion
from rasterio.crs import CRS
from rasterio.errors import (
GDALOptionNotImplementedError,
@@ -423,6 +423,8 @@ def _reproject(
raise
# Next, do the same for the destination raster.
+ dest_2d = False
+
try:
if dtypes.is_ndarray(destination):
if not dst_crs:
@@ -436,6 +438,7 @@ def _reproject(
destination = np.asanyarray(destination)
if len(destination.shape) == 2:
+ dest_2d = True
destination = destination.reshape(1, *destination.shape)
if destination.shape[0] == src_count:
@@ -531,9 +534,10 @@ def _reproject(
log.debug("Set _reproject Transformer option {0!r}={1!r}".format(key, val))
try:
- hTransformArg = exc_wrap_pointer(
- GDALCreateGenImgProjTransformer2(
- src_dataset, dst_dataset, imgProjOptions))
+ with Env(GDAL_MEM_ENABLE_OPEN=True):
+ hTransformArg = exc_wrap_pointer(
+ GDALCreateGenImgProjTransformer2(
+ src_dataset, dst_dataset, imgProjOptions))
if bUseApproxTransformer:
hTransformArg = exc_wrap_pointer(
GDALCreateApproxTransformer(
@@ -623,7 +627,7 @@ def _reproject(
0, 0, cols, rows)
try:
- with stack_errors() as checker:
+ with stack_errors() as checker, Env(GDAL_MEM_ENABLE_OPEN=True):
if num_threads > 1:
with nogil:
err = oWarper.ChunkAndWarpMulti(0, 0, cols, rows)
@@ -646,7 +650,7 @@ def _reproject(
alpha_arr = np.empty((height, width), dtype=dest_arr.dtype)
io_band(mem_raster.band(count), 0, 0.0, 0.0, width, height, alpha_arr)
destination = np.ma.masked_array(
- destination.data,
+ destination.data,
mask=np.repeat(
~(alpha_arr.astype("bool"))[np.newaxis, :, :],
count - 1,
@@ -656,6 +660,9 @@ def _reproject(
else:
exc_wrap_int(io_auto(destination, dst_dataset, 0))
+ if dest_2d:
+ destination = destination[0]
+
return destination
# Clean up transformer, warp options, and dataset handles.
@@ -778,9 +785,10 @@ def _calculate_default_transform(
)
bUseApproxTransformer = False
- hTransformArg = exc_wrap_pointer(
- GDALCreateGenImgProjTransformer2(hds, NULL, imgProjOptions)
- )
+ with Env(GDAL_MEM_ENABLE_OPEN=True):
+ hTransformArg = exc_wrap_pointer(
+ GDALCreateGenImgProjTransformer2(hds, NULL, imgProjOptions)
+ )
pfnTransformer = GDALGenImgProjTransform
log.debug("Created exact transformer")
=====================================
rasterio/crs.pxd
=====================================
@@ -5,5 +5,5 @@ cdef class CRS:
cdef OGRSpatialReferenceH _osr
cdef object _data
- cdef object _epsg
- cdef object _wkt
+ cdef public object _epsg
+ cdef public object _wkt
=====================================
rasterio/crs.pyx
=====================================
@@ -78,7 +78,6 @@ cdef class CRS:
The from_string method takes a variety of input.
>>> crs = CRS.from_string("EPSG:3005")
-
"""
def __init__(self, initialdata=None, **kwargs):
"""Make a CRS from a PROJ dict or mapping.
@@ -140,7 +139,7 @@ cdef class CRS:
"""
try:
- return bool(self.to_epsg())
+ return bool(self._epsg)
except CRSError:
return False
@@ -300,10 +299,6 @@ cdef class CRS:
text = self._projjson()
return json.loads(text) if text else {}
- epsg_code = self.to_epsg()
-
- if epsg_code:
- return {'init': 'epsg:{}'.format(epsg_code)}
else:
try:
osr = exc_wrap_pointer(OSRClone(self._osr))
@@ -659,45 +654,59 @@ cdef class CRS:
CRSError
"""
+ cdef const char *text_c = NULL
+ cdef CRS obj
+
if initialdata is not None:
data = dict(initialdata.items())
else:
data = {}
data.update(**kwargs)
- if not ("init" in data or "proj" in data):
- # We've been given a PROJ JSON-encoded text.
- return CRS.from_user_input(json.dumps(data))
-
# "+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.
- pjargs = []
- for key in data.keys() & all_proj_keys:
- val = data[key]
- if val is None or val is True:
- pjargs.append('+{}'.format(key))
- elif val is False:
- pass
+ elif not ("init" in data or "proj" in data):
+ # We've been given a PROJ JSON-encoded text.
+ text_b = json.dumps(data).encode('utf-8')
+ text_c = text_b
+ obj = CRS.__new__(CRS)
+ try:
+ errcode = exc_wrap_ogrerr(OSRSetFromUserInput(obj._osr, text_c))
+ except CPLE_BaseError as exc:
+ raise CRSError("The WKT could not be parsed. {}".format(exc))
else:
- pjargs.append('+{}={}'.format(key, val))
+ osr_set_traditional_axis_mapping_strategy(obj._osr)
+ obj._data = data
+ return obj
- proj = ' '.join(pjargs)
- b_proj = proj.encode('utf-8')
+ else:
+ # Continue with the general case.
+ pjargs = []
+ for key in data.keys() & all_proj_keys:
+ val = data[key]
+ if val is None or val is True:
+ pjargs.append('+{}'.format(key))
+ elif val is False:
+ pass
+ else:
+ pjargs.append('+{}={}'.format(key, val))
- cdef CRS obj = CRS.__new__(CRS)
+ proj = ' '.join(pjargs)
+ b_proj = proj.encode('utf-8')
+ obj = CRS.__new__(CRS)
- try:
- exc_wrap_ogrerr(OSRImportFromProj4(obj._osr, <const char *>b_proj))
- except CPLE_BaseError as exc:
- raise CRSError("The PROJ4 dict could not be understood. {}".format(exc))
- else:
- osr_set_traditional_axis_mapping_strategy(obj._osr)
- return obj
+ try:
+ exc_wrap_ogrerr(OSRImportFromProj4(obj._osr, <const char *>b_proj))
+ except CPLE_BaseError as exc:
+ raise CRSError("The PROJ4 dict could not be understood. {}".format(exc))
+ else:
+ osr_set_traditional_axis_mapping_strategy(obj._osr)
+ obj._data = data
+ return obj
@staticmethod
def from_wkt(wkt, morph_from_esri_dialect=False):
@@ -769,20 +778,9 @@ cdef class CRS:
elif isinstance(value, int):
return CRS.from_epsg(value)
elif isinstance(value, dict):
- return CRS(**value)
-
+ return CRS.from_dict(value)
elif isinstance(value, str):
- text_b = value.encode('utf-8')
- text_c = text_b
- obj = CRS.__new__(CRS)
- try:
- errcode = exc_wrap_ogrerr(OSRSetFromUserInput(obj._osr, text_c))
- except CPLE_BaseError as exc:
- raise CRSError("The WKT could not be parsed. {}".format(exc))
- else:
- osr_set_traditional_axis_mapping_strategy(obj._osr)
- return obj
-
+ return CRS.from_string(value)
else:
raise CRSError("CRS is invalid: {!r}".format(value))
@@ -795,7 +793,6 @@ cdef class CRS:
Parameters
----------
auth_name: str
- The name of the authority.
code : int or str
The code used by the authority.
@@ -829,8 +826,10 @@ cdef class CRS:
Raises
------
CRSError
-
"""
+ cdef const char *text_c = NULL
+ cdef CRS obj
+
try:
value = value.strip()
except AttributeError:
@@ -862,7 +861,16 @@ cdef class CRS:
elif "=" in value:
return CRS.from_proj4(value)
else:
- return CRS.from_user_input(value, morph_from_esri_dialect=morph_from_esri_dialect)
+ text_b = value.encode('utf-8')
+ text_c = text_b
+ obj = CRS.__new__(CRS)
+ try:
+ errcode = exc_wrap_ogrerr(OSRSetFromUserInput(obj._osr, text_c))
+ except CPLE_BaseError as exc:
+ raise CRSError("The WKT could not be parsed. {}".format(exc))
+ else:
+ osr_set_traditional_axis_mapping_strategy(obj._osr)
+ return obj
def __cinit__(self):
self._osr = OSRNewSpatialReference(NULL)
@@ -914,38 +922,23 @@ cdef class CRS:
return self.to_string()
def __repr__(self):
- epsg_code = self.to_epsg()
- if epsg_code:
- return "CRS.from_epsg({})".format(epsg_code)
+ if self._epsg:
+ return "CRS.from_epsg({})".format(self._epsg)
else:
return "CRS.from_wkt('{}')".format(self.wkt)
def __eq__(self, other):
- cdef OGRSpatialReferenceH osr_s = NULL
- cdef OGRSpatialReferenceH osr_o = NULL
cdef CRS crs_o
+ cdef const char* options[2]
+ options[0] = b"IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=NO"
+ options[1] = NULL
try:
crs_o = CRS.from_user_input(other)
+ return bool(OSRIsSameEx(self._osr, crs_o._osr, options) == 1)
except CRSError:
return False
- epsg_s = self.to_epsg()
- epsg_o = crs_o.to_epsg()
-
- 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))
- osr_o = exc_wrap_pointer(OSRClone(crs_o._osr))
- return bool(OSRIsSame(osr_s, osr_o) == 1)
-
- finally:
- _safe_osr_release(osr_s)
- _safe_osr_release(osr_o)
-
def _projjson(self):
"""Get a PROJ JSON representation.
=====================================
rasterio/enums.py
=====================================
@@ -43,6 +43,28 @@ class ColorInterp(IntEnum):
Y = 14
Cb = 15
Cr = 16
+ # Below values since GDAL 3.10
+ pan = 17
+ coastal = 18
+ rededge = 19
+ nir = 20
+ swir = 21
+ mwir = 22
+ lwir = 23
+ tir = 24
+ other_ir = 25
+ # GCI_IR_Reserved_1 = 26
+ # GCI_IR_Reserved_2 = 27
+ # GCI_IR_Reserved_3 = 28
+ # GCI_IR_Reserved_4 = 29
+ sar_ka = 30
+ sar_k = 31
+ sar_ku = 32
+ sar_x = 33
+ sar_c = 34
+ sar_s = 35
+ sar_l = 36
+ sar_p = 37
class Resampling(IntEnum):
=====================================
rasterio/gdal.pxi
=====================================
@@ -176,6 +176,7 @@ cdef extern from "ogr_srs_api.h" nogil:
int OSRIsGeographic(OGRSpatialReferenceH srs)
int OSRIsProjected(OGRSpatialReferenceH srs)
int OSRIsSame(OGRSpatialReferenceH srs1, OGRSpatialReferenceH srs2)
+ int OSRIsSameEx(OGRSpatialReferenceH srs1, OGRSpatialReferenceH srs2, const char* const* options)
OGRSpatialReferenceH OSRNewSpatialReference(const char *wkt)
void OSRRelease(OGRSpatialReferenceH srs)
int OSRSetFromUserInput(OGRSpatialReferenceH srs, const char *input)
@@ -265,22 +266,70 @@ cdef extern from "gdal.h" nogil:
GRIORA_Mode
GRIORA_Gauss
- ctypedef enum GDALColorInterp:
- GCI_Undefined
- GCI_GrayIndex
- GCI_PaletteIndex
- GCI_RedBand
- GCI_GreenBand
- GCI_BlueBand
- GCI_AlphaBand
- GCI_HueBand
- GCI_SaturationBand
- GCI_LightnessBand
- GCI_CyanBand
- GCI_YCbCr_YBand
- GCI_YCbCr_CbBand
- GCI_YCbCr_CrBand
- GCI_Max
+
+IF (CTE_GDAL_MAJOR_VERSION, CTE_GDAL_MINOR_VERSION) >= (3, 10):
+ cdef extern from "gdal.h" nogil:
+
+ ctypedef enum GDALColorInterp:
+ GCI_Undefined
+ GCI_GrayIndex
+ GCI_PaletteIndex
+ GCI_RedBand
+ GCI_GreenBand
+ GCI_BlueBand
+ GCI_AlphaBand
+ GCI_HueBand
+ GCI_SaturationBand
+ GCI_LightnessBand
+ GCI_CyanBand
+ GCI_YCbCr_YBand
+ GCI_YCbCr_CbBand
+ GCI_YCbCr_CrBand
+ GCI_PanBand
+ GCI_CoastalBand
+ GCI_RedEdgeBand
+ GCI_NIRBand
+ GCI_SWIRBand
+ GCI_MWIRBand
+ GCI_LWIRBand
+ GCI_TIRBand
+ GCI_OtherIRBand
+ GCI_IR_Reserved_1
+ GCI_IR_Reserved_2
+ GCI_IR_Reserved_3
+ GCI_IR_Reserved_4
+ GCI_SAR_Ka_Band
+ GCI_SAR_K_Band
+ GCI_SAR_Ku_Band
+ GCI_SAR_X_Band
+ GCI_SAR_C_Band
+ GCI_SAR_S_Band
+ GCI_SAR_L_Band
+ GCI_SAR_P_Band
+ GCI_SAR_Reserved_1
+ GCI_SAR_Reserved_2
+ GCI_Max
+ELSE:
+ cdef extern from "gdal.h" nogil:
+
+ ctypedef enum GDALColorInterp:
+ GCI_Undefined
+ GCI_GrayIndex
+ GCI_PaletteIndex
+ GCI_RedBand
+ GCI_GreenBand
+ GCI_BlueBand
+ GCI_AlphaBand
+ GCI_HueBand
+ GCI_SaturationBand
+ GCI_LightnessBand
+ GCI_CyanBand
+ GCI_YCbCr_YBand
+ GCI_YCbCr_CbBand
+ GCI_YCbCr_CrBand
+
+
+cdef extern from "gdal.h" nogil:
ctypedef struct GDALColorEntry:
short c1
=====================================
rasterio/transform.py
=====================================
@@ -86,7 +86,7 @@ class TransformMethodsMixin:
x,
y,
z=None,
- op=np.floor,
+ op=None,
precision=None,
transform_method=TransformMethod.affine,
**rpc_options
@@ -100,14 +100,15 @@ class TransformMethodsMixin:
y : float
y value in coordinate reference system
z : float, optional
- Height associated with coordinates. Primarily used for RPC based
- coordinate transformations. Ignored for affine based
+ Height associated with coordinates. Primarily used for RPC
+ based coordinate transformations. Ignored for affine based
transformations. Default: 0.
- op : function, optional (default: math.floor)
- Function to convert fractional pixels to whole numbers (floor,
- ceiling, round)
+ op : function, optional (default: numpy.floor)
+ Function to convert fractional pixels to whole numbers
+ (floor, ceiling, round)
transform_method: TransformMethod, optional
- The coordinate transformation method. Default: `TransformMethod.affine`.
+ The coordinate transformation method. Default:
+ `TransformMethod.affine`.
rpc_options: dict, optional
Additional arguments passed to GDALCreateRPCTransformer
precision : int, optional
@@ -116,7 +117,7 @@ class TransformMethodsMixin:
Returns
-------
- tuple
+ tuple: int, int
(row index, col index)
"""
@@ -251,13 +252,22 @@ def xy(transform, rows, cols, zs=None, offset='center', **rpc_options):
return transformer.xy(rows, cols, zs=zs, offset=offset)
-def rowcol(transform, xs, ys, zs=None, op=np.floor, precision=None, **rpc_options):
+def rowcol(
+ transform,
+ xs,
+ ys,
+ zs=None,
+ op=None,
+ precision=None,
+ **rpc_options,
+):
"""Get rows and cols of the pixels containing (x, y).
Parameters
----------
transform : Affine or sequence of GroundControlPoint or RPC
- Transform suitable for input to AffineTransformer, GCPTransformer, or RPCTransformer.
+ Transform suitable for input to AffineTransformer,
+ GCPTransformer, or RPCTransformer.
xs : list or float
x values in coordinate reference system.
ys : list or float
@@ -266,9 +276,9 @@ def rowcol(transform, xs, ys, zs=None, op=np.floor, precision=None, **rpc_option
Height associated with coordinates. Primarily used for RPC based
coordinate transformations. Ignored for affine based
transformations. Default: 0.
- op : function
- Function to convert fractional pixels to whole numbers (floor, ceiling,
- round).
+ op : function, optional (default: numpy.floor)
+ Function to convert fractional pixels to whole numbers (floor,
+ ceiling, round)
precision : int or float, optional
This parameter is unused, deprecated in rasterio 1.3.0, and
will be removed in version 2.0.0.
@@ -277,10 +287,10 @@ def rowcol(transform, xs, ys, zs=None, op=np.floor, precision=None, **rpc_option
Returns
-------
- rows : list of ints
- list of row indices
- cols : list of ints
- list of column indices
+ rows : array of ints or floats
+ cols : array of ints or floats
+ Integers are the default. The numerical type is determined by
+ the type returned by op().
"""
if precision is not None:
@@ -350,7 +360,7 @@ class TransformerBase:
def __exit__(self, *args):
pass
- def rowcol(self, xs, ys, zs=None, op=np.floor, precision=None):
+ def rowcol(self, xs, ys, zs=None, op=None, precision=None):
"""Get rows and cols coordinates given geographic coordinates.
Parameters
@@ -358,24 +368,28 @@ class TransformerBase:
xs, ys : float or list of float
Geographic coordinates
zs : float or list of float, optional
- Height associated with coordinates. Primarily used for RPC based
- coordinate transformations. Ignored for affine based
+ Height associated with coordinates. Primarily used for RPC
+ based coordinate transformations. Ignored for affine based
transformations. Default: 0.
- op : function, optional (default: math.floor)
- Function to convert fractional pixels to whole numbers (floor,
- ceiling, round)
+ op : function, optional (default: numpy.floor)
+ Function to convert fractional pixels to whole numbers
+ (floor, ceiling, round)
precision : int, optional (default: None)
This parameter is unused, deprecated in rasterio 1.3.0, and
will be removed in version 2.0.0.
Raises
------
+ TypeError
+ If coordinate transformation fails.
ValueError
- If input coordinates are not all equal length
+ If input coordinates are not all equal length.
Returns
-------
- tuple of float or list of float.
+ tuple of numbers or array of numbers.
+ Integers are the default. The numerical type is determined
+ by the type returned by op().
"""
if precision is not None:
@@ -392,8 +406,10 @@ class TransformerBase:
xs, ys, zs, transform_direction=TransformDirection.reverse
)
- is_op_ufunc = isinstance(op, np.ufunc)
- if is_op_ufunc:
+ if op is None:
+ new_rows = np.floor(new_rows).astype(dtype="int32")
+ new_cols = np.floor(new_cols).astype(dtype="int32")
+ elif isinstance(op, np.ufunc):
op(new_rows, out=new_rows)
op(new_cols, out=new_cols)
else:
@@ -404,6 +420,7 @@ class TransformerBase:
return new_rows[0], new_cols[0]
else:
return new_rows, new_cols
+
except TypeError:
raise TransformError("Invalid inputs")
=====================================
tests/test_colorinterp.py
=====================================
@@ -4,7 +4,9 @@ import pytest
import rasterio
from rasterio.enums import ColorInterp
+from rasterio.env import GDALVersion
+from .conftest import gdal_version
def test_cmyk_interp(tmpdir):
"""A CMYK TIFF has cyan, magenta, yellow, black bands."""
@@ -80,7 +82,7 @@ def test_set_colorinterp(path_rgba_byte_tif, tmpdir, dtype):
assert src.colorinterp == dst_ci
- at pytest.mark.parametrize("ci", ColorInterp.__members__.values())
+ at pytest.mark.parametrize("ci", ColorInterp.__members__.values() if gdal_version >= GDALVersion(3, 10) else list(filter(lambda x: x <= 16, ColorInterp.__members__.values())))
def test_set_colorinterp_all(path_4band_no_colorinterp, ci):
"""Test setting with all color interpretations."""
=====================================
tests/test_crs.py
=====================================
@@ -45,21 +45,19 @@ class CustomCRS:
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"' in crs.wkt
def test_crs_constructor_keywords():
"""Can create a CRS from keyword args, ignoring unknowns"""
+ # Note: `init="epsg:dddd"` is no longer recommended usage.
crs = CRS(init='epsg:3857', foo='bar')
- assert crs['init'] == 'epsg:3857'
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'
+ crs = CRS(CRS.from_epsg(3857))
assert 'PROJCS["WGS 84 / Pseudo-Mercator"' in crs.wkt
@@ -123,21 +121,21 @@ def test_from_proj4_json():
def test_from_epsg():
- crs_dict = CRS.from_epsg(4326)
- assert crs_dict['init'].lower() == 'epsg:4326'
+ assert CRS.from_epsg(4326)._epsg == 4326
- # Test with invalid EPSG code
+
+def test_from_epsg_fail():
with pytest.raises(ValueError):
- assert CRS.from_epsg(0)
+ CRS.from_epsg(0)
def test_from_epsg_string():
- crs_dict = CRS.from_string('epsg:4326')
- assert crs_dict['init'].lower() == 'epsg:4326'
+ assert CRS.from_string("epsg:4326")._epsg == 4326
+
- # Test with invalid EPSG code
+def test_from_epsg_string_fail():
with pytest.raises(ValueError):
- assert CRS.from_string('epsg:xyz')
+ CRS.from_string("epsg:xyz")
def test_from_epsg_overflow():
@@ -147,12 +145,20 @@ def test_from_epsg_overflow():
def test_from_string():
- wgs84_crs = CRS.from_string('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs')
- assert wgs84_crs.to_dict() == {'init': 'epsg:4326'}
+ wgs84_crs = CRS.from_string("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs")
+ assert wgs84_crs.to_dict() == {"proj": "longlat", "datum": "WGS84", "no_defs": True}
+
+def test_from_string_2():
# Make sure this doesn't get handled using the from_epsg() even though 'epsg' is in the string
- epsg_init_crs = CRS.from_string('+init=epsg:26911')
- assert epsg_init_crs.to_dict() == {'init': 'epsg:26911'}
+ epsg_init_crs = CRS.from_string("+init=epsg:26911")
+ assert epsg_init_crs.to_dict() == {
+ "proj": "utm",
+ "zone": 11,
+ "datum": "NAD83",
+ "units": "m",
+ "no_defs": True,
+ }
@pytest.mark.parametrize('proj,expected', [({'init': 'epsg:4326'}, True), ({'init': 'epsg:3857'}, False)])
@@ -194,15 +200,6 @@ def test_equality_from_dict(epsg_code):
def test_is_same_crs():
- crs1 = CRS({'init': 'epsg:4326'})
- crs2 = CRS({'init': 'epsg:3857'})
-
- assert crs1 == crs1
- assert crs1 != crs2
-
- wgs84_crs = CRS.from_string('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs')
- assert crs1 == wgs84_crs
-
# Make sure that same projection with different parameter are not equal
lcc_crs1 = CRS.from_string('+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')
lcc_crs2 = CRS.from_string('+lon_0=-95 +ellps=GRS80 +y_0=0 +no_defs=True +proj=lcc +x_0=0 +units=m +lat_2=77 +lat_1=45 +lat_0=0')
@@ -291,9 +288,7 @@ def test_epsg__no_code_available():
def test_crs_OSR_equivalence():
crs1 = CRS.from_string('+proj=longlat +datum=WGS84 +no_defs')
crs2 = CRS.from_string('+proj=latlong +datum=WGS84 +no_defs')
- crs3 = CRS({'init': 'epsg:4326'})
assert crs1 == crs2
- assert crs1 == crs3
def test_crs_OSR_no_equivalence():
@@ -328,7 +323,7 @@ def test_from_wkt_invalid():
def test_from_user_input_epsg():
- assert 'init' in CRS.from_user_input('epsg:4326')
+ assert CRS.from_user_input("epsg:4326")._epsg == 4326
@pytest.mark.parametrize("version", ["WKT2_2019", WktVersion.WKT2_2019])
@@ -658,3 +653,9 @@ def test_crs_proj_json__from_string():
def test_crs_compound_epsg():
assert CRS.from_string("EPSG:4326+3855").to_wkt().startswith("COMPD")
+
+
+ at pytest.mark.parametrize("crs", [CRS.from_epsg(4326), CRS.from_string("EPSG:4326")])
+def test_epsg_4326_ogc_crs84(crs):
+ """EPSG:4326 not equivalent to OGC:CRS84."""
+ assert CRS.from_string("OGC:CRS84") != crs
=====================================
tests/test_rio_edit_info.py
=====================================
@@ -8,10 +8,15 @@ import numpy
import pytest
import rasterio
+from rasterio import CRS
from rasterio.enums import ColorInterp
from rasterio.rio.edit_info import (
- all_handler, crs_handler, tags_handler, transform_handler,
- colorinterp_handler)
+ all_handler,
+ crs_handler,
+ tags_handler,
+ transform_handler,
+ colorinterp_handler,
+)
from rasterio.rio.main import main_group
import rasterio.shutil
@@ -97,7 +102,7 @@ def test_edit_crs_epsg(data, runner):
main_group, ['edit-info', inputfile, '--crs', 'EPSG:32618'])
assert result.exit_code == 0
with rasterio.open(inputfile) as src:
- assert src.crs == {'init': 'epsg:32618'}
+ assert src.crs == CRS.from_epsg(32618)
def test_edit_crs_proj4(data, runner):
@@ -106,7 +111,7 @@ def test_edit_crs_proj4(data, runner):
main_group, ['edit-info', inputfile, '--crs', '+init=epsg:32618'])
assert result.exit_code == 0
with rasterio.open(inputfile) as src:
- assert src.crs == {'init': 'epsg:32618'}
+ assert src.crs == CRS.from_epsg(32618)
def test_edit_crs_obj(data, runner):
@@ -116,7 +121,13 @@ def test_edit_crs_obj(data, runner):
['edit-info', inputfile, '--crs', '{"init": "epsg:32618"}'])
assert result.exit_code == 0
with rasterio.open(inputfile) as src:
- assert src.crs.to_dict() == {'init': 'epsg:32618'}
+ assert src.crs.to_dict() == {
+ "datum": "WGS84",
+ "no_defs": True,
+ "proj": "utm",
+ "units": "m",
+ "zone": 18,
+ }
def test_edit_transform_err_not_json(data, runner):
@@ -192,12 +203,12 @@ def test_edit_crs_like(data, runner):
# Set up the file to be edited.
inputfile = str(data.join('RGB.byte.tif'))
with rasterio.open(inputfile, 'r+') as dst:
- dst.crs = {'init': 'epsg:32617'}
+ dst.crs = "EPSG:32617"
dst.nodata = 1.0
# Double check.
with rasterio.open(inputfile) as src:
- assert src.crs == {'init': 'epsg:32617'}
+ assert src.crs == CRS.from_epsg(32617)
assert src.nodata == 1.0
# The test.
@@ -215,12 +226,12 @@ def test_edit_nodata_like(data, runner):
# Set up the file to be edited.
inputfile = str(data.join('RGB.byte.tif'))
with rasterio.open(inputfile, 'r+') as dst:
- dst.crs = {'init': 'epsg:32617'}
+ dst.crs = "EPSG:32617"
dst.nodata = 1.0
# Double check.
with rasterio.open(inputfile) as src:
- assert src.crs == {'init': 'epsg:32617'}
+ assert src.crs == CRS.from_epsg(32617)
assert src.nodata == 1.0
# The test.
@@ -230,19 +241,19 @@ def test_edit_nodata_like(data, runner):
['edit-info', inputfile, '--like', templatefile, '--nodata', 'like'])
assert result.exit_code == 0
with rasterio.open(inputfile) as src:
- assert src.crs == {'init': 'epsg:32617'}
+ assert src.crs == CRS.from_epsg(32617)
assert src.nodata == 0.0
def test_edit_all_like(data, runner):
inputfile = str(data.join('RGB.byte.tif'))
with rasterio.open(inputfile, 'r+') as dst:
- dst.crs = {'init': 'epsg:32617'}
+ dst.crs = "EPSG:32617"
dst.nodata = 1.0
# Double check.
with rasterio.open(inputfile) as src:
- assert src.crs == {'init': 'epsg:32617'}
+ assert src.crs == CRS.from_epsg(32617)
assert src.nodata == 1.0
templatefile = 'tests/data/RGB.byte.tif'
=====================================
tests/test_transform.py
=====================================
@@ -525,8 +525,16 @@ def test_gcp_transformer_tps_option():
assert gcp.col == pytest.approx(col_)
-def test_transform_grid():
+def test_transform_xy_grid():
"""Accept a grid, see gh-3191."""
with rasterio.open('tests/data/RGB.byte.tif') as src:
cols, rows = numpy.mgrid[0:3, 0:3]
xs, ys = src.xy(cols, rows)
+
+
+def test_transform_rowcol_grid():
+ """Accept a grid, see gh-3191."""
+ with rasterio.open("tests/data/RGB.byte.tif") as src:
+ left, bottom, right, top = src.bounds
+ xs, ys = numpy.mgrid[left:right:3j, bottom:top:3j]
+ rows, cols = AffineTransformer(src.transform).rowcol(xs, ys)
=====================================
tests/test_warp.py
=====================================
@@ -1362,6 +1362,7 @@ def test_reproject_masked(test3d, count_nonzero, path_rgb_byte_tif):
dst_nodata=99,
)
assert np.ma.is_masked(source)
+ assert source.shape == out.shape
assert np.count_nonzero(out[out != 99]) == count_nonzero
assert not np.ma.is_masked(out)
@@ -1400,6 +1401,7 @@ def test_reproject_masked_masked_output(test3d, count_nonzero, path_rgb_byte_tif
dst_transform=DST_TRANSFORM,
dst_crs="EPSG:3857",
)
+ assert inp.shape == out.shape
assert np.count_nonzero(out[out != np.ma.masked]) == count_nonzero
@@ -2147,7 +2149,7 @@ def test_reproject_error_propagation(http_error_server, caplog):
dst_transform=src.transform,
)
- assert len([rec for rec in caplog.records if "Retrying again" in rec.message]) == 2
+ assert len([rec for rec in caplog.records if "Retrying again" in rec.message]) >= 2
def test_reproject_to_specified_output_bands():
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/commit/6ccc37e8b4decfb798a44db8ab31bbf2dae02d3e
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/commit/6ccc37e8b4decfb798a44db8ab31bbf2dae02d3e
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/20241030/c9561042/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list