[Git][debian-gis-team/rasterio][experimental] 4 commits: New upstream version 1.4~b2

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Sat Aug 31 18:51:07 BST 2024



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


Commits:
0b06e660 by Bas Couwenberg at 2024-08-31T19:37:36+02:00
New upstream version 1.4~b2
- - - - -
dedc8dc4 by Bas Couwenberg at 2024-08-31T19:38:02+02:00
Update upstream source from tag 'upstream/1.4_b2'

Update to upstream version '1.4~b2'
with Debian dir dd505bf80e1c9611aaa6a5080fcfa6a1fc713760
- - - - -
15b69db4 by Bas Couwenberg at 2024-08-31T19:38:24+02:00
New upstream beta release.

- - - - -
89e1d718 by Bas Couwenberg at 2024-08-31T19:40:40+02:00
Set distribution to experimental.

- - - - -


23 changed files:

- CHANGES.txt
- debian/changelog
- docs/topics/features.rst
- rasterio/__init__.py
- rasterio/_env.pyx
- rasterio/_io.pxd
- rasterio/_io.pyx
- rasterio/_warp.pyx
- rasterio/enums.py
- rasterio/env.py
- rasterio/rio/overview.py
- rasterio/warp.py
- setup.py
- tests/conftest.py
- + tests/data/N10W110.hgt.zip
- tests/test__env.py
- tests/test_colormap.py
- tests/test_data_paths.py
- tests/test_enums.py
- tests/test_env.py
- tests/test_memoryfile.py
- tests/test_overviews.py
- tests/test_warp.py


Changes:

=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,25 @@
 Changes
 =======
 
+1.4b2 (2024-08-30)
+------------------
+
+Bug fixes:
+
+- When reprojecting a masked array, we now use the mask (reduced) as an alpha
+  band. There is now also an option to create an alpha band in the output, and
+  turn that into a mask when returning a mask array (#3156).
+- Find installed GDAL data directory by searching for gdalvrt.xsd (#3157).
+- Allow rasterio.open() to receive instances of MemoryFile (#3145).
+- Leaks of CSL string lists in get/set_proj_data_search_path() have been fixed
+  (#3140).
+- Color interpretation is correctly set to "palette" after a colormap is
+  written (#3133).
+
+Other changes:
+
+- _OverviewResampling enum renamed to OverviewResampling (#3151).
+
 1.4b1 (2024-08-10)
 ------------------
 


=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+rasterio (1.4~b2-1~exp1) experimental; urgency=medium
+
+  * Team upload.
+  * New upstream beta release.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Sat, 31 Aug 2024 19:40:28 +0200
+
 rasterio (1.4~b1-1~exp1) experimental; urgency=medium
 
   * Team upload.


=====================================
docs/topics/features.rst
=====================================
@@ -43,7 +43,7 @@ The shapes of the foreground features can be extracted like this:
     #   'type': 'Polygon'},
     # 253)
 
-The shapes iterator yields ``geometry, value`` pairs. The second item is the
+The shapes iterator yields ``(geometry, value)`` pairs. The second item is the
 value of the raster feature corresponding to the shape and the first is its
 geometry.  The coordinates of the geometries in this case are in pixel units
 with origin at the upper left of the image. If the source dataset was
@@ -53,6 +53,14 @@ georeferenced, you would get similarly georeferenced geometries like this:
 
     shapes = features.shapes(blue, mask=mask, transform=src.transform)
 
+For larger rasters, use the following shortcut with :py:func:`rasterio.band()` instead
+of reading the raster into an array.
+
+.. code-block:: python
+
+    with rasterio.open('13547682814_f2e459f7a5_o_d.png') as src:
+        shapes = features.shapes(rasterio.band(src, 3), mask=mask, transform=src.transform)
+
 Burning shapes into a raster
 ----------------------------
 


=====================================
rasterio/__init__.py
=====================================
@@ -7,7 +7,6 @@ import logging
 from logging import NullHandler
 import os
 import platform
-import warnings
 
 # On Windows we must explicitly register the directories that contain
 # the GDAL and supporting DLLs starting with Python 3.8. Presently, we
@@ -82,7 +81,7 @@ except ImportError:
     have_vsi_plugin = False
 
 __all__ = ['band', 'open', 'pad', 'Band', 'Env', 'CRS']
-__version__ = "1.4b1"
+__version__ = "1.4b2"
 __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()])
@@ -96,16 +95,6 @@ log = logging.getLogger(__name__)
 log.addHandler(NullHandler())
 
 
-# Remove this in 1.4.0 (see comment on gh-2423).
-def parse_path(path):
-    warnings.warn(
-        "rasterio.parse_path will be removed in version 1.4.",
-        RasterioDeprecationWarning,
-        stacklevel=2,
-    )
-    return _parse_path(path)
-
-
 @ensure_env_with_credentials
 def open(
     fp,
@@ -254,8 +243,10 @@ def open(
             or isinstance(fp, (os.PathLike, MemoryFile, FilePath))
         ):
             raise TypeError(f"invalid path or file: {fp!r}")
-    if mode and not isinstance(mode, str):
+    if not isinstance(mode, str):
         raise TypeError(f"invalid mode: {mode!r}")
+    elif mode[0] not in ("r", "w"):
+        raise ValueError(f"invalid mode: {mode!r}")
     if driver and not isinstance(driver, str):
         raise TypeError(f"invalid driver: {driver!r}")
     if dtype and not check_dtype(dtype):
@@ -272,6 +263,30 @@ def open(
             "driver '{}' in '{}' mode".format(driver, mode)
         )
 
+    # This check should come first, since MemoryFile has read/write methods
+    # TODO: test for a shared base class or abstract type.
+    elif isinstance(fp, (FilePath, MemoryFile)):
+        if mode.startswith("r"):
+            dataset = fp.open(driver=driver, sharing=sharing, **kwargs)
+
+        # Note: FilePath does not support writing and an exception will
+        # result from this.
+        else:
+            dataset = fp.open(
+                driver=driver,
+                width=width,
+                height=height,
+                count=count,
+                crs=crs,
+                transform=transform,
+                dtype=dtype,
+                nodata=nodata,
+                sharing=sharing,
+                **kwargs
+            )
+
+        return dataset
+
     # If the fp argument is a file-like object and can be adapted by
     # rasterio's FilePath we do so. Otherwise, we use a MemoryFile to
     # hold fp's contents and store that in an ExitStack attached to the
@@ -311,28 +326,6 @@ def open(
         dataset._env.callback(func)
         return dataset
 
-    # TODO: test for a shared base class or abstract type.
-    elif isinstance(fp, (FilePath, MemoryFile)):
-        if mode.startswith("r"):
-            dataset = fp.open(driver=driver, sharing=sharing, **kwargs)
-
-        # Note: FilePath does not support writing and an exception will
-        # result from this.
-        elif mode.startswith("w"):
-            dataset = fp.open(
-                driver=driver,
-                width=width,
-                height=height,
-                count=count,
-                crs=crs,
-                transform=transform,
-                dtype=dtype,
-                nodata=nodata,
-                sharing=sharing,
-                **kwargs
-            )
-        return dataset
-
     # At this point, the fp argument is a string or path-like object
     # which can be converted to a string.
     else:
@@ -400,7 +393,7 @@ def open(
 
 Band = namedtuple('Band', ['ds', 'bidx', 'dtype', 'shape'])
 Band.__doc__ = """
-Band of a Dataset.
+Band(s) of a Dataset.
 
 Parameters
 ----------


=====================================
rasterio/_env.pyx
=====================================
@@ -238,7 +238,7 @@ class GDALDataFinder:
         Parameters
         ----------
         basename : str
-            Basename of a data file such as "header.dxf"
+            Basename of a data file such as "gdalvrt.xsd"
 
         Returns
         -------
@@ -276,18 +276,18 @@ class GDALDataFinder:
         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, 'header.dxf')) else None
+        return datadir if os.path.exists(os.path.join(datadir, 'gdalvrt.xsd')) 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, 'header.dxf')) else None
+        return datadir if os.path.exists(os.path.join(datadir, 'gdalvrt.xsd')) else None
 
     def search_debian(self, prefix=sys.prefix):
         """Check Debian locations"""
         gdal_release_name = gdal_version()
         datadir = os.path.join(prefix, 'share', 'gdal', '{}.{}'.format(*gdal_release_name.split('.')[:2]))
-        return datadir if os.path.exists(os.path.join(datadir, 'header.dxf')) else None
+        return datadir if os.path.exists(os.path.join(datadir, 'gdalvrt.xsd')) else None
 
 
 @contextmanager
@@ -390,7 +390,7 @@ cdef class GDALEnv(ConfigEnv):
                             self.update_config_options(GDAL_DATA=path)
 
                         # See https://github.com/rasterio/rasterio/issues/1631.
-                        elif GDALDataFinder().find_file("header.dxf"):
+                        elif GDALDataFinder().find_file("gdalvrt.xsd"):
                             log.debug("GDAL data files are available at built-in paths.")
 
                         else:
@@ -473,20 +473,19 @@ cdef class GDALEnv(ConfigEnv):
 
 
 def set_proj_data_search_path(path):
-    """Set PROJ data search path"""
-    cdef char **paths = NULL
-    cdef const char *path_c = NULL
+    """Set PROJ data search path."""
     path_b = path.encode("utf-8")
-    path_c = path_b
-    paths = CSLAddString(paths, path_c)
-    OSRSetPROJSearchPaths(<const char *const *>paths)
+    cdef const char *path_c = path_b
+    cdef char **paths = CSLAddString(NULL, path_c)
+    try:
+        OSRSetPROJSearchPaths(<const char *const *>paths)
+    finally:
+        CSLDestroy(paths)
 
 
 def get_proj_data_search_paths():
     """
-    Get the PROJ DATA search paths
-
-    Requires GDAL 3.0.3+
+    Get the PROJ DATA search paths.
 
     Returns
     -------
@@ -498,12 +497,14 @@ def get_proj_data_search_paths():
     while paths[iii] != NULL:
         path_list.append(paths[iii])
         iii += 1
-    return path_list
-
+    try:
+        return path_list
+    finally:
+        CSLDestroy(paths)
 
 def get_gdal_data():
     """
-    Get the GDAL DATA path
+    Get the GDAL DATA path.
 
     Returns
     -------


=====================================
rasterio/_io.pxd
=====================================
@@ -42,3 +42,5 @@ ctypedef np.float64_t DTYPE_FLOAT64_t
 cdef bint in_dtype_range(value, dtype)
 
 cdef int io_auto(image, GDALRasterBandH band, bint write, int resampling=*) except -1
+cdef int io_band(GDALRasterBandH band, int mode, double x0, double y0, double width, double height, object data, int resampling=*) except -1
+cdef int io_multi_band(GDALDatasetH hds, int mode, double x0, double y0, double width, double height, object data, Py_ssize_t[:] indexes, int resampling=*) except -1


=====================================
rasterio/_io.pyx
=====================================
@@ -2036,7 +2036,7 @@ cdef class DatasetWriterBase(DatasetReaderBase):
                 GDALSetColorEntry(hTable, i, &color)
 
             # TODO: other color interpretations?
-            GDALSetRasterColorInterpretation(hBand, <GDALColorInterp>1)
+            GDALSetRasterColorInterpretation(hBand, GCI_PaletteIndex)
             GDALSetRasterColorTable(hBand, hTable)
 
         finally:


=====================================
rasterio/_warp.pyx
=====================================
@@ -38,7 +38,7 @@ from libc.math cimport HUGE_VAL
 from rasterio._base cimport get_driver_name
 from rasterio._err cimport exc_wrap, exc_wrap_pointer, exc_wrap_int, StackChecker
 from rasterio._io cimport (
-    DatasetReaderBase, MemoryDataset, in_dtype_range, io_auto)
+    DatasetReaderBase, MemoryDataset, in_dtype_range, io_auto, io_band, io_multi_band)
 from rasterio._features cimport GeomBuilder, OGRGeomBuilder
 from rasterio.crs cimport CRS
 
@@ -282,9 +282,9 @@ def _reproject(
         nodata value of the destination image (if set), the value of
         src_nodata, or 0 (gdal default).
     src_alpha : int, optional
-        Index of a band to use as the alpha band when warping.
+        Index of a band to use as the source alpha band when warping.
     dst_alpha : int, optional
-        Index of a band to use as the alpha band when warping.
+        Index of a band to use as the destination alpha band when warping.
     resampling : int
         Resampling method to use.  One of the following:
             Resampling.nearest,
@@ -360,35 +360,54 @@ def _reproject(
     cdef MemoryDataset src_mem = None
 
     try:
-
         # If the source is an ndarray, we copy to a MEM dataset.
         # We need a src_transform and src_dst in this case. These will
         # be copied to the MEM dataset.
         if dtypes.is_ndarray(source):
             if not src_crs:
                 raise CRSError("Missing src_crs.")
-            if src_nodata is None and hasattr(source, 'fill_value'):
-                # source is a masked array
-                src_nodata = source.fill_value
+
             # ensure data converted to numpy array
-            source = np.asanyarray(source)
+            if hasattr(source, "mask"):
+                source = np.ma.asanyarray(source)
+            else:
+                source = np.asanyarray(source)
+
             # Convert 2D single-band arrays to 3D multi-band.
             if len(source.shape) == 2:
                 source = source.reshape(1, *source.shape)
+
             src_count = source.shape[0]
             src_bidx = range(1, src_count + 1)
-            src_mem = MemoryDataset(source,
-                                         transform=format_transform(src_transform),
-                                         gcps=gcps,
-                                         rpcs=rpcs,
-                                         crs=src_crs,
-                                         copy=True)
+
+            if hasattr(source, "mask"):
+                mask = ~np.logical_or.reduce(source.mask) * np.uint8(255)
+                source_arr = np.concatenate((source.data, [mask]))
+                src_alpha = src_alpha or source_arr.shape[0]
+            else:
+                source_arr = source
+
+            src_mem = MemoryDataset(
+                source_arr,
+                transform=format_transform(src_transform),
+                gcps=gcps,
+                rpcs=rpcs,
+                crs=src_crs,
+                copy=True,
+            )
             src_dataset = src_mem.handle()
 
+            if src_alpha:
+                for i in range(source_arr.shape[0]):
+                    GDALDeleteRasterNoDataValue(GDALGetRasterBand(src_dataset, i+1))
+                GDALSetRasterColorInterpretation(
+                    GDALGetRasterBand(src_dataset, src_alpha),
+                    <GDALColorInterp>6,
+                )
+
         # If the source is a rasterio MultiBand, no copy necessary.
         # A MultiBand is a tuple: (dataset, bidx, dtype, shape(2d))
         elif isinstance(source, tuple):
-
             rdr, src_bidx, dtype, shape = source
             if isinstance(src_bidx, int):
                 src_bidx = [src_bidx]
@@ -408,12 +427,17 @@ def _reproject(
 
     # Next, do the same for the destination raster.
     try:
-
         if dtypes.is_ndarray(destination):
             if not dst_crs:
                 raise CRSError("Missing dst_crs.")
-            # ensure data converted to numpy array
-            destination = np.asanyarray(destination)
+
+            dst_nodata = dst_nodata or src_nodata
+
+            if hasattr(destination, "mask"):
+                destination = np.ma.asanyarray(destination)
+            else:
+                destination = np.asanyarray(destination)
+
             if len(destination.shape) == 2:
                 destination = destination.reshape(1, *destination.shape)
 
@@ -426,27 +450,33 @@ def _reproject(
                     raise ValueError("Invalid destination shape")
                 dst_bidx = src_bidx
 
-            mem_raster = MemoryDataset(destination, transform=format_transform(dst_transform), crs=dst_crs)
+            if hasattr(destination, "mask"):
+                count, height, width = destination.shape
+                msk = np.logical_or.reduce(destination.mask)
+                if msk == np.ma.nomask:
+                    msk = np.zeros((height, width), dtype="bool")
+                msk = ~msk * np.uint8(255)
+                dest_arr = np.concatenate((destination.data, [msk]))
+                dst_alpha = dst_alpha or dest_arr.shape[0]
+            else:
+                dest_arr = destination
+
+            mem_raster = MemoryDataset(dest_arr, transform=format_transform(dst_transform), crs=dst_crs)
             dst_dataset = mem_raster.handle()
 
             if dst_alpha:
-                for i in range(destination.shape[0]):
+                for i in range(dest_arr.shape[0]):
                     GDALDeleteRasterNoDataValue(GDALGetRasterBand(dst_dataset, i+1))
-
-                GDALSetRasterColorInterpretation(GDALGetRasterBand(dst_dataset, dst_alpha), <GDALColorInterp>6)
+                GDALSetRasterColorInterpretation(
+                    GDALGetRasterBand(dst_dataset, dst_alpha),
+                    <GDALColorInterp>6,
+                )
 
             GDALSetDescription(
                 dst_dataset, "Temporary destination dataset for _reproject()")
 
             log.debug("Created temp destination dataset.")
 
-            if dst_nodata is None:
-                if hasattr(destination, "fill_value"):
-                    # destination is a masked array
-                    dst_nodata = destination.fill_value
-                elif src_nodata is not None:
-                    dst_nodata = src_nodata
-
         elif isinstance(destination, tuple):
             udr, dst_bidx, _, _ = destination
             if isinstance(dst_bidx, int):
@@ -609,8 +639,27 @@ def _reproject(
         except CPLE_BaseError as base:
             raise WarpOperationError("Chunk and warp failed") from base
 
-        if dtypes.is_ndarray(destination):
-            exc_wrap_int(io_auto(destination, dst_dataset, 0))
+        if mem_raster is not None:
+            count, height, width = dest_arr.shape
+            if hasattr(destination, "mask"):
+                # Pick off the alpha band and make a mask of it.
+                # TODO: do this efficiently, not copying unless necessary.
+                indexes = np.arange(1, count, dtype='intp')
+                io_multi_band(dst_dataset, 0, 0.0, 0.0, width, height, destination, indexes)
+                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, 
+                    mask=np.repeat(
+                        ~(alpha_arr.astype("bool"))[np.newaxis, :, :],
+                        count - 1,
+                        axis=0,
+                    )
+                )
+            else:
+                exc_wrap_int(io_auto(destination, dst_dataset, 0))
+
+            return destination
 
     # Clean up transformer, warp options, and dataset handles.
     finally:
@@ -629,7 +678,6 @@ def _reproject(
         if src_mem is not None:
             src_mem.close()
 
-
 def _calculate_default_transform(
     src_crs,
     dst_crs,
@@ -1024,11 +1072,9 @@ cdef class WarpedVRTReaderBase(DatasetReaderBase):
         # raise an exception instead.
 
         if add_alpha:
-
             if src_alpha_band:
                 raise WarpOptionsError(
                     "The VRT already has an alpha band, adding a new one is not supported")
-
             else:
                 dst_alpha_band = src_dataset.count + 1
                 self.dst_nodata = None


=====================================
rasterio/enums.py
=====================================
@@ -115,7 +115,7 @@ class Resampling(IntEnum):
     rms = 14
 
 
-class _OverviewResampling(IntEnum):
+class OverviewResampling(IntEnum):
     """Available Overview resampling algorithms.
 
     The first 8, 'nearest', 'bilinear', 'cubic', 'cubic_spline',


=====================================
rasterio/env.py
=====================================
@@ -661,7 +661,7 @@ if 'GDAL_DATA' not in os.environ:
         set_gdal_config("GDAL_DATA", path)
 
     # See https://github.com/rasterio/rasterio/issues/1631.
-    elif GDALDataFinder().find_file("header.dxf"):
+    elif GDALDataFinder().find_file("gdalvrt.xsd"):
         log.debug("GDAL data files are available at built-in paths.")
 
     else:


=====================================
rasterio/rio/overview.py
=====================================
@@ -7,7 +7,7 @@ import click
 
 from . import options
 import rasterio
-from rasterio.enums import _OverviewResampling as OverviewResampling
+from rasterio.enums import OverviewResampling
 
 
 def build_handler(ctx, param, value):


=====================================
rasterio/warp.py
=====================================
@@ -172,6 +172,7 @@ def reproject(
     dst_resolution=None,
     src_alpha=0,
     dst_alpha=0,
+    masked=False,
     resampling=Resampling.nearest,
     num_threads=1,
     init_dest_nodata=True,
@@ -250,6 +251,8 @@ def reproject(
         Index of a band to use as the alpha band when warping.
     dst_alpha : int, optional
         Index of a band to use as the alpha band when warping.
+    masked: bool, optional
+        If True and destination is None, return a masked array.
     resampling: int, rasterio.enums.Resampling
         Resampling method to use.
         Default is :attr:`rasterio.enums.Resampling.nearest`.
@@ -378,8 +381,10 @@ def reproject(
             destination = np.empty(
                 (int(dst_count), int(dst_height), int(dst_width)), dtype=source.dtype
             )
+            if masked:
+                destination = np.ma.masked_array(destination).filled(dst_nodata)
 
-    _reproject(
+    dest = _reproject(
         source,
         destination,
         src_transform=src_transform,
@@ -400,7 +405,7 @@ def reproject(
         **kwargs
     )
 
-    return destination, dst_transform
+    return dest, dst_transform
 
 
 def aligned_target(transform, width, height, resolution):


=====================================
setup.py
=====================================
@@ -15,11 +15,11 @@ import logging
 import os
 import platform
 import pprint
+import re
 import shutil
 from subprocess import check_output
 import sys
 
-from pkg_resources import parse_version
 from setuptools import setup
 from setuptools.extension import Extension
 
@@ -129,16 +129,9 @@ if "clean" not in sys.argv:
                  "to gdal-config using a GDAL_CONFIG environment variable "
                  "or use a GDAL_VERSION environment variable.")
 
-    gdal_major_version, gdal_minor_version, gdal_patch_version = parse_version(
-        gdalversion
-    ).base_version.split(".", maxsplit=3)
-    gdal_major_version = int(gdal_major_version)
-    gdal_minor_version = int(gdal_minor_version)
-    gdal_patch_version = int(gdal_patch_version)
-
-    if (gdal_major_version, gdal_minor_version) < (3, 3):
-        raise SystemExit("ERROR: GDAL >= 3.3 is required for rasterio. "
-                 "Please upgrade GDAL.")
+    gdal_major_version, gdal_minor_version, gdal_patch_version = map(
+        int, re.findall("[0-9]+", gdalversion)[:3]
+    )
 
 # Conditionally copy the GDAL data. To be used in conjunction with
 # the bdist_wheel command to make self-contained binary wheels.
@@ -301,6 +294,7 @@ setup_args = dict(
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
         "Programming Language :: Python :: 3",
         "Topic :: Multimedia :: Graphics :: Graphics Conversion",
         "Topic :: Scientific/Engineering :: GIS",


=====================================
tests/conftest.py
=====================================
@@ -526,6 +526,12 @@ def path_rgb_byte_tif(data_dir):
     return os.path.join(data_dir, 'RGB.byte.tif')
 
 
+ at pytest.fixture(scope='session')
+def path_srtm_hgt(data_dir):
+    """Sample SRTM HGT file"""
+    return os.path.join(data_dir, 'N10W110.hgt.zip')
+
+
 @pytest.fixture(scope='session')
 def path_rgb_lzw_byte_tif(data_dir):
     """The original RGB test fixture with LZW compression."""


=====================================
tests/data/N10W110.hgt.zip
=====================================
Binary files /dev/null and b/tests/data/N10W110.hgt.zip differ


=====================================
tests/test__env.py
=====================================
@@ -18,7 +18,7 @@ def mock_wheel(tmpdir):
     moduledir = tmpdir.mkdir("rasterio")
     moduledir.ensure("__init__.py")
     moduledir.ensure("_env.py")
-    moduledir.ensure("gdal_data/header.dxf")
+    moduledir.ensure("gdal_data/gdalvrt.xsd")
     moduledir.ensure("proj_data/epsg")
     return moduledir
 
@@ -26,7 +26,7 @@ def mock_wheel(tmpdir):
 @pytest.fixture
 def mock_fhs(tmpdir):
     """A fake FHS system"""
-    tmpdir.ensure("share/gdal/header.dxf")
+    tmpdir.ensure("share/gdal/gdalvrt.xsd")
     tmpdir.ensure("share/proj/epsg")
     return tmpdir
 
@@ -34,11 +34,11 @@ def mock_fhs(tmpdir):
 @pytest.fixture
 def mock_debian(tmpdir):
     """A fake Debian multi-install system"""
-    tmpdir.ensure("share/gdal/3.3/header.dxf")
-    tmpdir.ensure("share/gdal/3.4/header.dxf")
-    tmpdir.ensure("share/gdal/3.5/header.dxf")
-    tmpdir.ensure("share/gdal/3.6/header.dxf")
-    tmpdir.ensure(f"share/gdal/{gdal_version.major}.{gdal_version.minor}/header.dxf")
+    tmpdir.ensure("share/gdal/3.3/gdalvrt.xsd")
+    tmpdir.ensure("share/gdal/3.4/gdalvrt.xsd")
+    tmpdir.ensure("share/gdal/3.5/gdalvrt.xsd")
+    tmpdir.ensure("share/gdal/3.6/gdalvrt.xsd")
+    tmpdir.ensure(f"share/gdal/{gdal_version.major}.{gdal_version.minor}/gdalvrt.xsd")
     tmpdir.ensure("share/proj/epsg")
     return tmpdir
 


=====================================
tests/test_colormap.py
=====================================
@@ -1,27 +1,26 @@
+"""Colormap tests."""
+
 import rasterio
+from rasterio.enums import ColorInterp
 
 
-def test_write_colormap_warn(tmpdir, recwarn):
-    with rasterio.open('tests/data/shade.tif') as src:
-        profile = src.meta
-    tiffname = str(tmpdir.join('foo.tif'))
-    with rasterio.open(tiffname, 'w', **profile) as dst:
-        dst.write_colormap(1, {0: (255, 0, 0, 255), 255: (0, 0, 0, 0)})
+def test_write_colormap(tmp_path):
+    with rasterio.open("tests/data/shade.tif") as src:
+        shade = src.read(1)
+        profile = src.profile
 
+    profile["driver"] = "PNG"
 
-def test_write_colormap(tmpdir):
-    with rasterio.open('tests/data/shade.tif') as src:
-        shade = src.read(1)
-        meta = src.meta
-    tiffname = str(tmpdir.join('foo.png'))
-    meta['driver'] = 'PNG'
-    with rasterio.open(tiffname, 'w', **meta) as dst:
+    with rasterio.open(tmp_path / "test.tif", "w", **profile) as dst:
         dst.write(shade, indexes=1)
         dst.write_colormap(1, {0: (255, 0, 0, 255), 255: (0, 0, 0, 0)})
+        assert dst.colorinterp == (ColorInterp.palette,)
         cmap = dst.colormap(1)
         assert cmap[0] == (255, 0, 0, 255)
         assert cmap[255] == (0, 0, 0, 0)
-    with rasterio.open(tiffname) as src:
+
+    with rasterio.open(tmp_path / "test.tif") as src:
+        assert src.colorinterp == (ColorInterp.palette,)
         cmap = src.colormap(1)
         assert cmap[0] == (255, 0, 0, 255)
         assert cmap[255] == (0, 0, 0, 0)


=====================================
tests/test_data_paths.py
=====================================
@@ -4,7 +4,7 @@ from rasterio._env import GDALDataFinder, PROJDataFinder
 
 def test_gdal_data_find_file():
     """Find_file shouldn't raise any exceptions"""
-    GDALDataFinder().find_file("header.dxf")
+    GDALDataFinder().find_file("gdalvrt.xsd")
 
 
 def test_proj_data_has_data():


=====================================
tests/test_enums.py
=====================================
@@ -15,7 +15,7 @@ def test_gray_gray():
     assert enums.ColorInterp.gray.name == "gray"
 
 
- at pytest.mark.parametrize("resamp", enums._OverviewResampling)
+ at pytest.mark.parametrize("resamp", enums.OverviewResampling)
 def test_resampling(resamp):
     """Make sure that resampling value are the same."""
     assert resamp.value == enums.Resampling[resamp.name].value


=====================================
tests/test_env.py
=====================================
@@ -129,7 +129,7 @@ def test_ensure_env_decorator_sets_gdal_data_prefix(find_file, gdalenv, monkeypa
 
     find_file.return_value = None
 
-    tmpdir.ensure("share/gdal/header.dxf")
+    tmpdir.ensure("share/gdal/gdalvrt.xsd")
     monkeypatch.delenv('GDAL_DATA', raising=False)
     monkeypatch.setattr(sys, 'prefix', str(tmpdir))
 
@@ -145,7 +145,7 @@ def test_ensure_env_decorator_sets_gdal_data_wheel(find_file, gdalenv, monkeypat
 
     find_file.return_value = None
 
-    tmpdir.ensure("gdal_data/header.dxf")
+    tmpdir.ensure("gdal_data/gdalvrt.xsd")
     monkeypatch.delenv('GDAL_DATA', raising=False)
     monkeypatch.setattr(_env, '__file__', str(tmpdir.join(os.path.basename(_env.__file__))))
 


=====================================
tests/test_memoryfile.py
=====================================
@@ -263,6 +263,17 @@ def test_file_object_read_variant2(rgb_file_bytes):
         assert src.read().shape == (3, 718, 791)
 
 
+def teste_srtm_hgt_object_read(path_srtm_hgt):
+    """An example of reading from a MemoryFile object with a driver which requires a filename"""
+    with rasterio.open(
+        MemoryFile(open(path_srtm_hgt, "rb").read(), filename=path_srtm_hgt)
+    ) as src:
+        assert src.driver == "SRTMHGT"
+        assert src.count == 1
+        assert src.dtypes == ("int16",)
+        assert src.read().shape == (1, 1201, 1201)
+
+
 def test_test_file_object_write(tmpdir, rgb_data_and_profile):
     """An example of writing to a file object"""
     data, profile = rgb_data_and_profile


=====================================
tests/test_overviews.py
=====================================
@@ -6,7 +6,7 @@ import pytest
 from .conftest import requires_gdal33
 
 import rasterio
-from rasterio.enums import _OverviewResampling as OverviewResampling
+from rasterio.enums import OverviewResampling
 from rasterio.enums import Resampling
 from rasterio.errors import OverviewCreationError
 


=====================================
tests/test_warp.py
=====================================
@@ -1333,7 +1333,25 @@ def test_reproject_array_interface(test3d, count_nonzero, path_rgb_byte_tif):
     assert np.count_nonzero(out.data[out.data != 99]) == count_nonzero
 
 
- at pytest.mark.parametrize("test3d,count_nonzero", [(True, 1309625), (False, 437686)])
+ at pytest.mark.parametrize(
+    "test3d,count_nonzero",
+    [
+        pytest.param(
+            True,
+            1308064,
+            marks=pytest.mark.skipif(
+                not gdal_version.at_least("3.8"), reason="Requires GDAL 3.8.x"
+            ),
+        ),
+        pytest.param(
+            False,
+            437686,
+            marks=pytest.mark.skipif(
+                not gdal_version.at_least("3.8"), reason="Requires GDAL 3.8.x"
+            ),
+        ),
+    ],
+)
 def test_reproject_masked(test3d, count_nonzero, path_rgb_byte_tif):
     with rasterio.open(path_rgb_byte_tif) as src:
         if test3d:
@@ -1352,6 +1370,44 @@ def test_reproject_masked(test3d, count_nonzero, path_rgb_byte_tif):
     )
     assert np.ma.is_masked(source)
     assert np.count_nonzero(out[out != 99]) == count_nonzero
+    assert not np.ma.is_masked(out)
+
+
+ at pytest.mark.parametrize(
+    "test3d,count_nonzero",
+    [
+        pytest.param(
+            True,
+            1312959,
+            marks=pytest.mark.skipif(
+                not gdal_version.at_least("3.8"), reason="Requires GDAL 3.8.x"
+            ),
+        ),
+        pytest.param(
+            False,
+            438113,
+            marks=pytest.mark.skipif(
+                not gdal_version.at_least("3.8"), reason="Requires GDAL 3.8.x"
+            ),
+        ),
+    ]
+)
+def test_reproject_masked_masked_output(test3d, count_nonzero, path_rgb_byte_tif):
+    with rasterio.open(path_rgb_byte_tif) as src:
+        if test3d:
+            inp = src.read(masked=True)
+        else:
+            inp = src.read(1, masked=True)
+    out = np.ma.masked_array(np.empty(inp.shape, dtype=np.uint8))
+    out, _ = reproject(
+        inp,
+        out,
+        src_transform=src.transform,
+        src_crs=src.crs,
+        dst_transform=DST_TRANSFORM,
+        dst_crs="EPSG:3857",
+    )
+    assert np.count_nonzero(out[out != np.ma.masked]) == count_nonzero
 
 
 @pytest.mark.parametrize("method", SUPPORTED_RESAMPLING)



View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/compare/2d0b6b8a62c0310f728d0d70135337897b07b468...89e1d71821c7fbeb36edfd9bf08ea9f1160f6e6c

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/rasterio/-/compare/2d0b6b8a62c0310f728d0d70135337897b07b468...89e1d71821c7fbeb36edfd9bf08ea9f1160f6e6c
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/20240831/7bfb45ff/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list