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

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Fri Aug 19 19:15:50 BST 2022



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


Commits:
727c0ddb by Bas Couwenberg at 2022-08-19T20:02:50+02:00
New upstream version 1.3.2
- - - - -


16 changed files:

- .travis.yml
- CHANGES.txt
- docs/faq.rst
- rasterio/__init__.py
- rasterio/_env.pyx
- rasterio/_io.pyx
- rasterio/env.py
- rasterio/rio/env.py
- rasterio/transform.py
- rasterio/windows.py
- setup.py
- tests/test_crs.py
- tests/test_features.py
- tests/test_transform.py
- tests/test_warp.py
- tests/test_windows.py


Changes:

=====================================
.travis.yml
=====================================
@@ -67,7 +67,7 @@ before_install:
   - . ./scripts/travis_proj_install.sh
   - travis_wait 20 . ./scripts/travis_gdal_install.sh
   - export GDAL_DATA=$GDALINST/gdal-$GDALVERSION/share/gdal
-  - export PROJ_LIB=$GDALINST/gdal-$GDALVERSION/share/proj
+  - export PROJ_DATA=$GDALINST/gdal-$GDALVERSION/share/proj
 
 install:
   - "if [ \"$GDALVERSION\" == \"master\" -o $(gdal-config --version) == \"$GDALVERSION\" ]; then echo \"Using gdal $GDALVERSION\"; else echo \"NOT using gdal $GDALVERSION as expected; aborting\"; exit 1; fi"


=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,14 @@
 Changes
 =======
 
+1.3.2 (2022-08-19)
+------------------
+
+Packaging:
+
+1.3.1 was released from a branch lacking the advertised bug fixes. 1.3.2
+contains the fixes listed under 1.3.1.
+
 1.3.1 (2022-08-17)
 ------------------
 


=====================================
docs/faq.rst
=====================================
@@ -37,12 +37,12 @@ it exists and see if that eliminates the error condition and the message.
 Why can't rasterio find proj.db (rasterio versions < 1.2.0)?
 ------------------------------------------------------------
 
-If you see ``rasterio.errors.CRSError: The EPSG code is unknown. PROJ: proj_create_from_database: Cannot find proj.db`` it is because the PROJ library (one of rasterio's dependencies) cannot find its database of projections and coordinate systems. In some installations the ``PROJ_LIB`` `environment variable must be set <https://proj.org/usage/environmentvars.html#envvar-PROJ_LIB>`__ for PROJ to work properly.
+If you see ``rasterio.errors.CRSError: The EPSG code is unknown. PROJ: proj_create_from_database: Cannot find proj.db`` it is because the PROJ library (one of rasterio's dependencies) cannot find its database of projections and coordinate systems. In some installations the ``PROJ_LIB`` (PROJ < 9.1) | ``PROJ_DATA`` (PROJ 9.1+) `environment variable must be set <https://proj.org/usage/environmentvars.html#envvar-PROJ_LIB>`__ for PROJ to work properly.
 
 .. important:: Activate your conda environments.
-   The PROJ conda package will set ``PROJ_LIB`` to the proper value if you activate your conda environment. If you don't activate your conda enviornment, you are likely to see the exception shown above.
+   The PROJ conda package will set ``PROJ_LIB`` (PROJ < 9.1) | ``PROJ_DATA`` (PROJ 9.1+) to the proper value if you activate your conda environment. If you don't activate your conda enviornment, you are likely to see the exception shown above.
 
 Why can't rasterio find proj.db (rasterio from PyPI versions >= 1.2.0)?
 -----------------------------------------------------------------------
 
-Starting with version 1.2.0, rasterio wheels on PyPI include PROJ 7.x and GDAL 3.x. The libraries and modules in these wheels are incompatible with older versions of PROJ that may be installed on your system. If ``PROJ_LIB`` is set in your program's environment and points to an older version of PROJ, you must unset this variable. Rasterio will then use the version of PROJ contained in the wheel. 
+Starting with version 1.2.0, rasterio wheels on PyPI include PROJ 7.x and GDAL 3.x. The libraries and modules in these wheels are incompatible with older versions of PROJ that may be installed on your system. If ``PROJ_LIB`` (PROJ < 9.1) | ``PROJ_DATA`` (PROJ 9.1+) is set in your program's environment and points to an older version of PROJ, you must unset this variable. Rasterio will then use the version of PROJ contained in the wheel.


=====================================
rasterio/__init__.py
=====================================
@@ -32,7 +32,7 @@ with rasterio._loading.add_gdal_dll_directories():
         complex_int16,
     )
     from rasterio.env import ensure_env_with_credentials, Env, env_ctx_if_needed
-    from rasterio.errors import RasterioIOError, DriverCapabilityError
+    from rasterio.errors import RasterioIOError, DriverCapabilityError, RasterioDeprecationWarning
     from rasterio.io import (
         DatasetReader, get_writer_for_path, get_writer_for_driver, MemoryFile)
     from rasterio.profiles import default_gtiff_profile
@@ -55,7 +55,7 @@ with rasterio._loading.add_gdal_dll_directories():
         have_vsi_plugin = False
 
 __all__ = ['band', 'open', 'pad', 'Env', 'CRS']
-__version__ = "1.3.1"
+__version__ = "1.3.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/_env.pyx
=====================================
@@ -387,7 +387,13 @@ cdef class GDALEnv(ConfigEnv):
                                 log.debug("GDAL data found in other locations: path=%r.", path)
                                 self.update_config_options(GDAL_DATA=path)
 
-                    if 'PROJ_LIB' in os.environ:
+                    if 'PROJ_DATA' in os.environ:
+                        # PROJ 9.1+
+                        log.debug("PROJ_DATA found in environment.")
+                        path = os.environ["PROJ_DATA"]
+                        set_proj_data_search_path(path)
+                    elif 'PROJ_LIB' in os.environ:
+                        # PROJ < 9.1
                         log.debug("PROJ_LIB found in environment.")
                         path = os.environ["PROJ_LIB"]
                         set_proj_data_search_path(path)


=====================================
rasterio/_io.pyx
=====================================
@@ -2068,6 +2068,16 @@ cdef class MemoryDataset(DatasetWriterBase):
             "BANDS": count,
             "DATATYPE": _gdal_typename(arr.dtype.name)
         }
+        strides = arr_info.get("strides", None)
+
+        if strides is not None:
+            if len(strides) == 2:
+                lineoffset, pixeloffset = strides
+                info.update(LINEOFFSET=lineoffset, PIXELOFFSET=pixeloffset)
+            else:
+                bandoffset, lineoffset, pixeloffset = strides
+                info.update(BANDOFFSET=bandoffset, LINEOFFSET=lineoffset, PIXELOFFSET=pixeloffset)
+
         dataset_options = ",".join(f"{name}={val}" for name, val in info.items())
         datasetname = f"MEM:::{dataset_options}"
 


=====================================
rasterio/env.py
=====================================
@@ -646,7 +646,12 @@ if 'GDAL_DATA' not in os.environ:
             set_gdal_config("GDAL_DATA", path)
             log.debug("GDAL data found in other locations: path=%r.", path)
 
-if "PROJ_LIB" in os.environ:
+if "PROJ_DATA" in os.environ:
+    # PROJ 9.1+
+    path = os.environ["PROJ_DATA"]
+    set_proj_data_search_path(path)
+elif "PROJ_LIB" in os.environ:
+    # PROJ < 9.1
     path = os.environ["PROJ_LIB"]
     set_proj_data_search_path(path)
 


=====================================
rasterio/rio/env.py
=====================================
@@ -29,4 +29,4 @@ def env(ctx, key):
         elif key == 'gdal_data':
             click.echo(os.environ.get('GDAL_DATA') or GDALDataFinder().search())
         elif key == 'proj_data':
-            click.echo(os.environ.get('PROJ_LIB') or PROJDataFinder().search())
+            click.echo(os.environ.get('PROJ_DATA', os.environ.get('PROJ_LIB')) or PROJDataFinder().search())


=====================================
rasterio/transform.py
=====================================
@@ -4,6 +4,7 @@ from collections.abc import Iterable
 from contextlib import ExitStack
 from functools import partial
 import math
+import numpy as np
 import sys
 
 from affine import Affine
@@ -318,8 +319,9 @@ class TransformerBase():
 
     def close(self):
         self.closed = True
-    
-    def _ensure_arr_input(self, xs, ys, zs=None):
+
+    @staticmethod
+    def _ensure_arr_input(xs, ys, zs=None):
         """Ensure all input coordinates are mapped to array-like objects
         
         Raises
@@ -327,20 +329,13 @@ class TransformerBase():
         TransformError
             If input coordinates are not all of the same length
         """
-        if (isinstance(xs, Iterable) and not isinstance(ys, Iterable)) or (
-            isinstance(ys, Iterable) and not isinstance(xs, Iterable)
-        ):
-            raise TransformError("Invalid inputs")
-        if not isinstance(xs, Iterable) and not isinstance(ys, Iterable):
-            xs = [xs]
-            ys = [ys]
-        if zs is None:
-            zs = [0] * len(xs)
-        elif not isinstance(zs, Iterable):
-            zs = [zs]
-        if len(set((len(xs), len(ys), len(zs)))) > 1:
-            raise TransformError("Input coordinates must be of equal length")
-        return xs, ys, zs
+        try:
+            xs, ys, zs = np.broadcast_arrays(xs, ys, 0 if zs is None else zs)
+        except ValueError as error:
+            raise TransformError(
+                "Input coordinates must be broadcastable to a 1d array"
+            ) from error
+        return np.atleast_1d(xs), np.atleast_1d(ys), np.atleast_1d(zs)
 
     def __enter__(self):
         self._env.enter_context(env_ctx_if_needed())


=====================================
rasterio/windows.py
=====================================
@@ -154,36 +154,39 @@ def get_data_window(arr, nodata=None):
     -------
     Window
     """
-
-    num_dims = len(arr.shape)
-    if num_dims > 3:
+    if not 0 < arr.ndim <=3 :
         raise WindowError(
-            "get_data_window input array must have no more than "
-            "3 dimensions")
-
-    if nodata is None:
-        if not hasattr(arr, 'mask'):
-            return Window.from_slices((0, arr.shape[-2]), (0, arr.shape[-1]))
+            "get_data_window input array must have 1, 2, or 3 dimensions")
+
+    # If nodata is defined, construct mask from that value
+    # Otherwise retrieve mask from array (if it is masked)
+    # Finally try returning a full window (nodata=None and nothing in arr is masked)
+    if nodata is not None:
+        arr_mask = arr != nodata
+    elif np.ma.is_masked(arr):
+        arr_mask = ~np.ma.getmask(arr)
     else:
-        arr = np.ma.masked_array(arr, arr == nodata)
-
-    if num_dims == 2:
-        data_rows, data_cols = np.where(np.equal(arr.mask, False))
-    else:
-        data_rows, data_cols = np.where(
-            np.any(np.equal(np.rollaxis(arr.mask, 0, 3), False), axis=2))
-
-    if data_rows.size:
-        row_range = (data_rows.min(), data_rows.max() + 1)
-    else:
-        row_range = (0, 0)
+        if arr.ndim == 1:
+            full_window = ((0, arr.size), (0, 0))
+        else:
+            full_window = ((0, arr.shape[-2]), (0, arr.shape[-1]))
+        return Window.from_slices(*full_window)
 
-    if data_cols.size:
-        col_range = (data_cols.min(), data_cols.max() + 1)
-    else:
-        col_range = (0, 0)
+    if arr.ndim == 3:
+        arr_mask = np.any(arr_mask, axis=0)
 
-    return Window.from_slices(row_range, col_range)
+    # We only have 1 or 2 dimension cases to process
+    v = []
+    for nz in arr_mask.nonzero():
+        if nz.size:
+            v.append((nz.min(), nz.max() + 1))
+        else:
+            v.append((0, 0))
+    
+    if arr_mask.ndim == 1:
+        v.append((0, 0))
+        
+    return Window.from_slices(*v)
 
 
 def _compute_union(w1, w2):


=====================================
setup.py
=====================================
@@ -154,8 +154,8 @@ if os.environ.get('PACKAGE_DATA'):
             log.info("Copying gdal_data from %s" % gdal_data)
             copy_data_tree(gdal_data, destdir)
 
-    # Conditionally copy PROJ.4 data.
-    projdatadir = os.environ.get('PROJ_LIB', '/usr/local/share/proj')
+    # Conditionally copy PROJ DATA.
+    projdatadir = os.environ.get('PROJ_DATA', os.environ.get('PROJ_LIB', '/usr/local/share/proj'))
     if os.path.exists(projdatadir):
         log.info("Copying proj_data from %s" % projdatadir)
         copy_data_tree(projdatadir, 'rasterio/proj_data')


=====================================
tests/test_crs.py
=====================================
@@ -356,8 +356,9 @@ def test_dataset_compound_crs():
 
 @pytest.mark.wheel
 def test_environ_patch(gdalenv, monkeypatch):
-    """PROJ_LIB is patched when rasterio._crs is imported"""
+    """PROJ_LIB (PROJ < 9.1) | PROJ_DATA (PROJ 9.1+) is patched when rasterio._crs is imported"""
     monkeypatch.delenv('GDAL_DATA', raising=False)
+    monkeypatch.delenv('PROJ_DATA', raising=False)
     monkeypatch.delenv('PROJ_LIB', raising=False)
     with env_ctx_if_needed():
         assert CRS.from_epsg(4326) != CRS(units='m', proj='aeqd', ellps='WGS84', datum='WGS84', lat_0=-17.0, lon_0=-44.0)


=====================================
tests/test_features.py
=====================================
@@ -910,6 +910,30 @@ def test_shapes(basic_image):
     assert value == 0
 
 
+def test_shapes_2509(basic_image):
+    """Test creation of shapes from pixel values, issue #2509."""
+    image_with_strides = np.pad(basic_image, 1)[1:-1, 1:-1]
+    np.testing.assert_array_equal(basic_image, image_with_strides)
+    assert image_with_strides.__array_interface__["strides"] is not None
+
+    results = list(shapes(image_with_strides))
+
+    assert len(results) == 2
+
+    shape, value = results[0]
+    assert shape == {
+        'coordinates': [
+            [(2, 2), (2, 5), (5, 5), (5, 2), (2, 2)]
+        ],
+        'type': 'Polygon'
+    }
+    assert value == 1
+
+    shape, value = results[1]
+    assert shapely.geometry.shape(shape).area == 91.0
+    assert value == 0
+
+
 def test_shapes_band(pixelated_image, pixelated_image_file):
     """Shapes from a band should match shapes from an array."""
     truth = list(shapes(pixelated_image))


=====================================
tests/test_transform.py
=====================================
@@ -200,7 +200,7 @@ def test_xy_input(rows, cols, exp_xy, aff):
 
 
 @pytest.mark.parametrize("aff", [Affine.identity()])
- at pytest.mark.parametrize("rows, cols", [(0, [0]), ("0", "0")])
+ at pytest.mark.parametrize("rows, cols", [([0, 1, 2], [0, 1]), ("0", "0")])
 def test_invalid_xy_input(rows, cols, aff):
     """Raise on invalid input."""
     with pytest.raises(TransformError):
@@ -294,7 +294,7 @@ def test_xy_rowcol_inverse(transform):
 
 
 @pytest.mark.parametrize("aff", [Affine.identity()])
- at pytest.mark.parametrize("xs, ys", [(0, [0]), ("0", "0")])
+ at pytest.mark.parametrize("xs, ys", [([0, 1, 2], [0, 1]), ("0", "0")])
 def test_invalid_rowcol_input(xs, ys, aff):
     """Raise on invalid input."""
     with pytest.raises(TransformError):
@@ -326,9 +326,15 @@ def test_transformer_open_closed(transformer_cls, transform):
 @pytest.mark.parametrize(
     'coords,expected',
     [
-        ((0,0), (0,0)),
-        (([0],[0]), ([0], [0])),
-        (([0,1],[0,1]), ([0,1],[0,1]))
+        ((0, 1), (1, 0)),
+        (([0],[1]), ([1], [0])),
+        ((0,[1]), ([1], [0])),
+        (([0], 1), ([1], [0])),
+        (([0, 1], [2, 3]), ([2, 3],[0, 1])),
+        ((0, [1, 2]), ([1, 2], [0, 0])),
+        (([0, 1], 2), ([2, 2], [0, 1])),
+        (([0], [1, 2]), ([1, 2], [0, 0])),
+        (([0, 1], [2]), ([2, 2], [0, 1])),
     ]
 )
 def test_ensure_arr_input(coords, expected):
@@ -338,7 +344,29 @@ def test_ensure_arr_input(coords, expected):
 def test_ensure_arr_input_same_shape():
     transformer = transform.AffineTransformer(Affine.identity())
     with pytest.raises(TransformError):
-        transformer.xy([0], [0, 1])
+        transformer.xy([0, 1, 2], [0, 1])
+
+
+def test_ensure_arr_input_with_default_zs():
+    assert AffineTransformer._ensure_arr_input(0, 1) == AffineTransformer._ensure_arr_input(0, 1, zs=0)
+    _, _, zs = AffineTransformer._ensure_arr_input(0, [1, 2], zs=0)
+    assert all(zs == [0, 0])
+
+
+def test_ensure_arr_input_with_zs():
+    _, _, zs = AffineTransformer._ensure_arr_input(0, 1, zs=2)
+    assert all(zs == [2])
+    _, _, zs = AffineTransformer._ensure_arr_input(0, [1, 2], zs=3)
+    assert all(zs == [3, 3])
+    _, _, zs = AffineTransformer._ensure_arr_input([0, 1], 2, zs=3)
+    assert all(zs == [3, 3])
+    xs, ys, zs = AffineTransformer._ensure_arr_input(0, 1, zs=[2, 3])
+    assert all(zs == [2, 3])
+    assert all(ys == [1, 1])
+    assert all(xs == [0, 0])
+    with pytest.raises(TransformError):
+        AffineTransformer._ensure_arr_input(0, [1, 2], zs=[3, 4, 5])
+
 
 @pytest.mark.parametrize(
     'transformer_cls,transform',
@@ -377,4 +405,4 @@ def test_2421_rpc_height_ignored():
         x1, y1 = src.xy(0, 0, z=0, transform_method=transform_method)
         x2, y2 = src.xy(0, 0, z=2000, transform_method=transform_method)
         assert abs(x2 - x1) > 0
-        assert abs(y2 - y1) > 0
\ No newline at end of file
+        assert abs(y2 - y1) > 0


=====================================
tests/test_warp.py
=====================================
@@ -453,6 +453,41 @@ def test_reproject_ndarray():
     )
     assert (out > 0).sum() == 438113
 
+    
+def test_reproject_ndarray_slice():
+    """Test for issue #2511, destination with strides"""
+
+    with rasterio.open("tests/data/RGB.byte.tif") as src:
+        source = src.read(1)
+
+    dst_crs = dict(
+        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=True,
+        no_defs=True,
+    )
+
+    out = np.zeros((src.count, src.height, src.width+2), dtype=np.uint8)[..., 1:-1]
+    assert out.__array_interface__["strides"] is not None
+    reproject(
+        source,
+        out,
+        src_transform=src.transform,
+        src_crs=src.crs,
+        dst_transform=DST_TRANSFORM,
+        dst_crs=dst_crs,
+        resampling=Resampling.nearest,
+    )
+    assert (out > 0).sum() == 438113
+
 
 def test_reproject_view():
     """Source views are reprojected properly"""


=====================================
tests/test_windows.py
=====================================
@@ -469,20 +469,37 @@ def test_read_with_window_class(path_rgb_byte_tif):
         assert subset.shape == (10, 10)
 
 
-def test_data_window_invalid_arr_dims():
+def test_data_window_invalid_4d():
     """An array of more than 3 dimensions is invalid."""
-    arr = np.ones((3, 3, 3, 3))
+    # Test > 3 dims
     with pytest.raises(WindowError):
-        get_data_window(arr)
+        get_data_window(np.ones((3, 3, 3, 3)))
 
 
-def test_data_window_full():
+def test_data_window_invalid_0d():
+    """An array of less than 1 dimension is invalid."""
+    # Test < 1 dim
+    with pytest.raises(WindowError):
+        get_data_window(np.ones(()))
+
+
+def test_data_window_full_2d():
     """Get window of entirely valid data array."""
     arr = np.ones((3, 3))
     window = get_data_window(arr)
     assert window == Window.from_slices((0, 3), (0, 3))
 
 
+def test_data_window_full_1d():
+    window = get_data_window(np.ones(3))
+    assert window == Window.from_slices((0, 3), (0, 0))
+
+
+def test_data_window_full_3d():
+    window = get_data_window(np.ones((3, 3, 3)))
+    assert window == Window.from_slices((0, 3), (0, 3))
+
+
 def test_data_window_nodata():
     """Get window of arr with nodata."""
     arr = np.ones((3, 3))



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

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/commit/727c0ddb1eff93b422ecf3d709f10708c918eedf
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/20220819/0c76b4c1/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list