diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 776e1a0217..189fcd35f1 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -34,6 +34,12 @@ This document explains the changes made to Iris for this release horizontal grid. (:issue:`5770`, :pull:`6581`) +#. `@hsteptoe`_ and `@trexfeathers`_ (reviewer) added :func:`iris.util.mask_cube_from_shape` + to handle additional Point and Line shape types. This change also facilitates the use of + shapefiles that use a different projection system to the cube that they are being applied to, + and makes performance improvements to the mask weighting calculations. + (:issue:`6126`, :pull:`6129`). + #. `@bjlittle`_ extended ``zlib`` compression of :class:`~iris.cube.Cube` data payload when saving to NetCDF to also include any attached `CF-UGRID`_ :class:`~iris.mesh.components.MeshXY`. Additionally, @@ -75,7 +81,12 @@ This document explains the changes made to Iris for this release 💣 Incompatible Changes ======================= -#. N/A +#. Existing users of :func:`iris.util.mask_cube_from_shapefile` will need to + install the additional dependencies `rasterio`_ and `affine`_ to continue + using this function. These dependencies are necessary to support bug fixes + implemented in (:issue:`6126`, :pull:`6129`). Note that this function will + be deprecated in a future version of Iris in favour of the new + :func:`iris.util.mask_cube_from_shape`, which offers richer shape handling. 🚀 Performance @@ -110,8 +121,9 @@ This document explains the changes made to Iris for this release 🔗 Dependencies =============== -#. N/A - +#. `@hsteptoe`_ added `rasterio`_ and `affine`_ as optional dependencies that facilitate + :func:`iris.util.mask_cube_from_shape`. These packages support new functionality that + handles additional shapefile types and projections. (:issue:`6126`, :pull:`6129`) 📚 Documentation ================ @@ -119,6 +131,10 @@ This document explains the changes made to Iris for this release #. `@rcomer`_ updated all Cartopy references to point to the new location at https://cartopy.readthedocs.io (:pull:`6636`) +#. `@hsteptoe`_ added additional worked examples to the :func:`iris.util.mask_cube_from_shape` + documentation, to demonstrate how to use the function with different types of shapefiles. + (:pull:`6129`) + 💼 Internal =========== @@ -142,6 +158,7 @@ This document explains the changes made to Iris for this release Whatsnew author names (@github name) in alphabetical order. Note that, core dev names are automatically included by the common_links.inc: +.. _@hsteptoe: https://github.com/hsteptoe .. _@melissaKG: https://github.com/melissaKG @@ -149,4 +166,6 @@ This document explains the changes made to Iris for this release .. comment Whatsnew resources in alphabetical order: +.. _affine: https://affine.readthedocs.io/en/latest/ .. _netcdf-c#3183: https://github.com/Unidata/netcdf-c/issues/3183 +.. _rasterio: https://rasterio.readthedocs.io/en/stable/index.html diff --git a/lib/iris/_shapefiles.py b/lib/iris/_shapefiles.py index 74b24b6627..a6bc3c4d1c 100644 --- a/lib/iris/_shapefiles.py +++ b/lib/iris/_shapefiles.py @@ -3,46 +3,66 @@ # This file is part of Iris and is released under the BSD license. # See LICENSE in the root of the repository for full licensing details. -# Much of this code is originally based off the ASCEND library, developed in -# the Met Office by Chris Kent, Emilie Vanvyve, David Bentley, Joana Mendes -# many thanks to them. Converted to iris by Alex Chamberlain-Clay - +from __future__ import annotations from itertools import product +from typing import TYPE_CHECKING, Optional import warnings +from affine import Affine +import cartopy.crs as ccrs import numpy as np +from pyproj import CRS, Transformer +import rasterio.features as rfeatures import shapely -import shapely.errors import shapely.geometry as sgeom import shapely.ops -from iris.warnings import IrisDefaultingWarning, IrisUserWarning +import iris +from iris.coords import DimCoord +from iris.exceptions import IrisError +from iris.warnings import IrisUserWarning + +if TYPE_CHECKING: + from iris.util import Axis -def create_shapefile_mask( - geometry, - cube, - minimum_weight=0.0, -): - """Make a mask for a cube from a shape. +def create_shape_mask( + geometry: shapely.Geometry, + cube: iris.cube.Cube, + geometry_crs: Optional[ccrs.CRS | CRS] = None, + minimum_weight: float = 0.0, + all_touched: Optional[bool] = None, + invert: Optional[bool] = None, +) -> np.array: + """Make a mask for a cube from a shape geometry. Get the mask of the intersection between the given shapely geometry and cube with x/y DimCoords. - Can take a minimum weight and evaluate area overlaps instead + Can take a minimum weight and evaluate area overlaps instead. + + Transforming is performed by GDAL warp. Parameters ---------- geometry : :class:`shapely.Geometry` cube : :class:`iris.cube.Cube` A :class:`~iris.cube.Cube` which has 1d x and y coordinates. - minimum_weight : float, default 0.0 - A float between 0 and 1 determining what % of a cell - a shape must cover for the cell to remain unmasked. - eg: 0.1 means that at least 10% of the shape overlaps the cell - to be unmasked. - Requires geometry to be a Polygon or MultiPolygon - Defaults to 0.0 (eg only test intersection). + geometry_crs : :class:`cartopy.crs`, optional + A :class:`~iris.coord_systems` object describing + the coord_system of the shapefile. Defaults to None, + in which case the geometry_crs is assumed to be the + same as the :class:`iris.cube.Cube`. + minimum_weight : float, default=0.0 + The minimum weight of the geometry to be included in the mask. + If the weight is less than this value, the geometry will not be + included in the mask. Defaults to 0.0. + all_touched : bool, optional + If True, all pixels touched by the geometry will be included in the mask. + If False, only pixels fully covered by the geometry will be included in the mask. + invert : bool, optional + If True, the mask will be inverted, so that pixels not covered by the geometry + will be included in the mask. Returns ------- @@ -50,194 +70,411 @@ def create_shapefile_mask( An array of the shape of the x & y coordinates of the cube, with points to mask equal to True. + Raises + ------ + TypeError + If the cube is not a :class:`~iris.cube.Cube`. + IrisError + If the cube does not have a coordinate reference system defined. + TypeError + If the geometry is not a valid shapely geometry. + ValueError + If the :class:`~iris.cube.Cube` has a semi-structured model grid. + ValueError + If the minimum_weight is not between 0.0 and 1.0. + ValueError + If the minimum_weight is greater than 0.0 and all_touched is True. + + Warns + ----- + IrisUserWarning + If the geometry CRS does not match the cube CRS, and the geometry is transformed + to the cube CRS using pyproj. + + Notes + ----- + For best masking results, both the :class:`iris.cube.Cube` _and_ masking geometry should have a + coordinate reference system (CRS) defined. Masking results will be most reliable + when the :class:`iris.cube.Cube` and masking geometry have the same CRS. + + Where the :class:`iris.cube.Cube` CRS and the geometry CRS differ, the geometry will be + transformed to the cube CRS using the pyproj library. This is a best-effort + transformation and may not be perfect, especially for complex geometries and + non-standard coordinate reference systems. Consult the + `pyproj documentation `_ for + more information. + + If a CRS is not provided for the the masking geometry, the CRS of the :class:`iris.cube.Cube` is assumed. + + Note that `minimum_weight` and `all_touched` are mutually exclusive options: an error will be raised if + a `minimum_weight` > 0 *and* `all_touched` is set to `True`. This is because + `all_touched=True` is equivalent to `minimum_weight=0`. + + Warnings + -------- + Because shape vectors are inherently Cartesian in nature, they contain no inherent + understanding of the spherical geometry underpinning geographic coordinate systems. + For this reason, **shapefiles or shape vectors that cross the antimeridian or poles + are not supported by this function** to avoid unexpected masking behaviour. + + Shape geometries can be checked prior to masking using the :func:`is_geometry_valid`. + + See Also + -------- + :func:`is_geometry_valid` + Check the validity of a shape geometry. """ - from iris.cube import Cube, CubeList - - try: - msg = "Geometry is not a valid Shapely object" - if not shapely.is_valid(geometry): - raise TypeError(msg) - except Exception: - raise TypeError(msg) - if not isinstance(cube, Cube): - if isinstance(cube, CubeList): - msg = "Received CubeList object rather than Cube - \ - to mask a CubeList iterate over each Cube" - raise TypeError(msg) + # Check cube is a Cube + if not isinstance(cube, iris.cube.Cube): # type: ignore[unreachable] + if isinstance(cube, iris.cube.CubeList): # type: ignore[unreachable] + err_msg = ( + "Received CubeList object rather than Cube - " + "to mask a CubeList iterate over each Cube" + ) + raise TypeError(err_msg) else: - msg = "Received non-Cube object where a Cube is expected" - raise TypeError(msg) - if minimum_weight > 0.0 and isinstance( - geometry, - ( - sgeom.Point, - sgeom.LineString, - sgeom.LinearRing, - sgeom.MultiPoint, - sgeom.MultiLineString, - ), - ): - minimum_weight = 0.0 - warnings.warn( - """Shape is of invalid type for minimum weight masking, - must use a Polygon rather than Line shape.\n - Masking based off intersection instead. """, - category=IrisDefaultingWarning, + err_msg = "Received non-Cube object where a Cube is expected" + raise TypeError(err_msg) + + # Check cube coordinate system + cube_crs = cube.coord_system() + if cube_crs is None: + err_msg = ( + "Cube coordinates do not have a coordinate references system (CRS) " + "defined. A CRS must be defined to ensure reliable results." + ) + raise IrisError(err_msg) + + if geometry_crs is None: + # If no geometry CRS is provided, assume it is the same as the cube CRS + geometry_crs = cube_crs.as_cartopy_projection() + # Check validity of geometry CRS + is_geometry_valid(geometry, geometry_crs) + + # Check compatibility of function arguments + # all_touched and minimum_weight are mutually exclusive + if (all_touched is True) and (minimum_weight > 0): + err_msg = "Cannot use minimum_weight > 0.0 with all_touched=True." + raise ValueError(err_msg) + + # Check minimum_weight is within range + if (minimum_weight < 0.0) or (minimum_weight > 1.0): + err_msg = "Minimum weight must be between 0.0 and 1.0" + raise ValueError(err_msg) + + # Get cube coordinates + axes: tuple[Axis, Axis] = ("X", "Y") + x_coord, y_coord = [cube.coord(axis=a, dim_coords=True) for a in axes] + # Check if cube lons units are in degrees, and if so do they exist in [0, 360] or [-180, 180] + if x_coord.units.origin == "radians": + x_coord.convert_units("degrees") + y_coord.convert_units("degrees") + if (x_coord.units.origin == "degrees") and (x_coord.points.max() > 180): + # Convert to [-180, 180] domain + cube = cube.intersection(iris.coords.CoordExtent(x_coord.name(), -180, 180)) + # Get revised x coordinate + x_coord = cube.coord(axis="X", dim_coords=True) + + assert isinstance(x_coord, DimCoord) + assert isinstance(y_coord, DimCoord) + + # Check for CRS equality and transform if necessary + cube_cartopy_crs = cube_crs.as_cartopy_projection() + if not geometry_crs.equals(cube_cartopy_crs): + transform_warning_msg = ( + "Geometry CRS does not match cube CRS. Iris will attempt to " + "transform the geometry onto the cube CRS..." + ) + warnings.warn(transform_warning_msg, category=IrisUserWarning) + geometry = _transform_geometry( + geometry=geometry, + geometry_crs=geometry_crs, + cube_crs=cube_cartopy_crs, ) - # prepare 2D cube - y_name, x_name = _cube_primary_xy_coord_names(cube) - cube_2d = cube.slices([y_name, x_name]).next() - for coord in cube_2d.dim_coords: - if not coord.has_bounds(): - coord.guess_bounds() - trans_geo = _transform_coord_system(geometry, cube_2d) - - y_coord, x_coord = [cube_2d.coord(n) for n in (y_name, x_name)] - x_bounds = _get_mod_rebased_coord_bounds(x_coord) - y_bounds = _get_mod_rebased_coord_bounds(y_coord) - # prepare array for dark - box_template = [ - sgeom.box(x[0], y[0], x[1], y[1]) for x, y in product(x_bounds, y_bounds) - ] - # shapely can do lazy evaluation of intersections if it's given a list of grid box shapes - # delayed lets us do it in parallel - intersect_template = shapely.intersects(trans_geo, box_template) - # we want areas not under shapefile to be True (to mask) - intersect_template = np.invert(intersect_template) - # now calc area overlaps if doing weights and adjust mask - if minimum_weight > 0.0: - intersections = np.array(box_template)[~intersect_template] - intersect_template[~intersect_template] = [ - trans_geo.intersection(box).area / box.area <= minimum_weight - for box in intersections - ] - mask_template = np.reshape(intersect_template, cube_2d.shape[::-1]).T + w = len(x_coord.points) + h = len(y_coord.points) + + # Mask by weight if minimum_weight > 0.0 + if minimum_weight > 0: + mask_template = _get_weighted_mask( + geometry=geometry, + cube=cube, + minimum_weight=minimum_weight, + ) + else: + if (minimum_weight == 0) and (all_touched is None): + # For speed, if minimum_weight is 0, then + # we can use the geometry_mask function directly + # This is equivalent to all_touched=True + all_touched = True + + # Define raster transform based on cube + # This maps the geometry domain onto the cube domain + tr = _make_raster_cube_transform(x_coord=x_coord, y_coord=y_coord) # type: ignore[arg-type] + # Generate mask from geometry + mask_template = rfeatures.geometry_mask( + geometries=shapely.get_parts(geometry), + out_shape=(h, w), + transform=tr, + all_touched=all_touched, + ) + + # If cube was on circular domain, then the transformed + # mask template needs shifting to match the cube domain + if x_coord.circular: # type: ignore[union-attr] + mask_template = np.roll(mask_template, w // 2, axis=1) + + if invert: + # Invert the mask + mask_template = np.logical_not(mask_template) + return mask_template -def _transform_coord_system(geometry, cube, geometry_system=None): - """Project the shape onto another coordinate system. +def is_geometry_valid( + geometry: shapely.Geometry, + geometry_crs: ccrs.CRS | CRS, +) -> None: + """Check the validity of the shape geometry. + + This function checks that: + 1) The geometry is a valid shapely geometry. + 2) The geometry falls within bounds equivalent to + lon = [-180, 180] and lat = [-90, 90]. + 3) The geometry does not cross the antimeridian, + based on the assumption that the shape will + cross the antimeridian if the difference between + the shape bound longitudes is greater than 180. + 4) The geometry does not cross the poles. Parameters ---------- - geometry : :class:`shapely.Geometry` - cube : :class:`iris.cube.Cube` - :class:`~iris.cube.Cube` with the coord_system to be projected to and - a x coordinate. - geometry_system : :class:`iris.coord_systems`, optional - A :class:`~iris.coord_systems` object describing - the coord_system of the shapefile. Defaults to None, - which is treated as GeogCS. + geometry : :class:`shapely.geometry.base.BaseGeometry` + The geometry to check. + geometry_crs : :class:`cartopy.crs` + The coordinate reference system of the geometry. Returns ------- - :class:`shapely.Geometry` - A transformed copy of the provided :class:`shapely.Geometry`. - + None if the geometry is valid. + + Raises + ------ + TypeError + If the geometry is not a valid shapely geometry. + ValueError + If the geometry is not valid for the given coordinate system. + This most likely occurs when the geometry coordinates are not + within the bounds of the geometry coordinates reference system. + ValueError + If the geometry crosses the antimeridian. + ValueError + If the geometry crosses the poles. + + Examples + -------- + >>> from shapely.geometry import box + >>> from pyproj import CRS + >>> from iris._shapefiles import is_geometry_valid + + Create a valid geometry covering Canada, and check + its validity for the WGS84 coordinate system: + + >>> canada = box(-143.5,42.6,-37.8,84.0) + >>> wgs84 = CRS.from_epsg(4326) + >>> is_geometry_valid(canada, wgs84) + + The function returns silently if the geometry is valid. + + Now create an invalid geometry covering the Bering Sea, + and check its validity for the WGS84 coordinate system. + + >>> bering_sea = box(148.42,49.1,-138.74,73.12) + >>> wgs84 = CRS.from_epsg(4326) + >>> is_geometry_valid(bering_sea, wgs84) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last) + ValueError: Geometry crossing the 180th meridian is not supported. """ - y_name, x_name = _cube_primary_xy_coord_names(cube) - import iris.analysis.cartography - - DEFAULT_CS = iris.coord_systems.GeogCS( - iris.analysis.cartography.DEFAULT_SPHERICAL_EARTH_RADIUS + WGS84_crs = CRS.from_epsg(4326) + + # Check crs is valid type + if not isinstance(geometry_crs, (ccrs.CRS | CRS)): + err_msg = f"Geometry CRS must be a cartopy.crs or pyproj.CRS object, not {type(geometry_crs)}." + raise TypeError(err_msg) + + # Check geometry is valid shapely geometry + if not shapely.is_valid_input(geometry).any(): + err_msg = "Shape geometry is not a valid shape (not well formed)." + raise TypeError(err_msg) + + # Check that the geometry is within the bounds of the coordinate system + # If the geometry is not in WGS84, transform the geometry to WGS84 + # This is more reliable than transforming the lon_lat_bounds to the geometry CRS + lon_lat_bounds = shapely.geometry.Polygon.from_bounds( + xmin=-180.0, ymin=-90.0, xmax=180.0, ymax=90.0 ) - target_system = cube.coord_system() - if not target_system: - warnings.warn( - "Cube has no coord_system; using default GeogCS lat/lon", - category=IrisDefaultingWarning, - ) - target_system = DEFAULT_CS - if geometry_system is None: - geometry_system = DEFAULT_CS - target_proj = target_system.as_cartopy_projection() - source_proj = geometry_system.as_cartopy_projection() - - trans_geometry = target_proj.project_geometry(geometry, source_proj) - # A GeogCS in iris can be either -180 to 180 or 0 to 360. If cube is 0-360, shift geom to match - if ( - isinstance(target_system, iris.coord_systems.GeogCS) - and cube.coord(x_name).points[-1] > 180 - ): - # chop geom at 0 degree line very finely then transform - prime_meridian_line = shapely.LineString([(0, 90), (0, -90)]) - trans_geometry = trans_geometry.difference(prime_meridian_line.buffer(0.00001)) - trans_geometry = shapely.transform(trans_geometry, _trans_func) - - if (not isinstance(target_system, iris.coord_systems.GeogCS)) and cube.coord( - x_name - ).points[-1] > 180: - # this may lead to incorrect masking or not depending on projection type so warn user - warnings.warn( - """Cube has x-coordinates over 180E and a non-standard projection type.\n - This may lead to incorrect masking. \n - If the result is not as expected, you might want to transform the x coordinate points of your cube to -180-180 """, - category=IrisUserWarning, + if not geometry_crs.equals(WGS84_crs): + # Make pyproj transformer + # Transforms the input geometry to the WGS84 coordinate system + geometry = _transform_geometry(geometry, geometry_crs, WGS84_crs) + + geom_valid = lon_lat_bounds.contains(shapely.get_parts(geometry)) + if not geom_valid.all(): + err_msg = ( + f"Geometry {shapely.get_parts(geometry)[~geom_valid]} is not valid " + f"for the given coordinate system {geometry_crs.to_string()}.\n" + "Check that your coordinates are correctly specified." ) - return trans_geometry - - -def _trans_func(geometry): - """Pocket function for transforming the x coord of a geometry from -180 to 180 to 0-360.""" - for point in geometry: - if point[0] < 0: - point[0] = 360 - np.abs(point[0]) - return geometry - - -def _cube_primary_xy_coord_names(cube): - """Return the primary latitude and longitude coordinate names, or long names, from a cube. + raise ValueError(err_msg) + + # Check if shape crosses the 180th meridian (or equivalent) + # Exception for MultiPoint geometries where sequential points + # may be separated by more than 180 degrees + if not isinstance(geometry, sgeom.MultiPoint): + if bool(abs(geometry.bounds[2] - geometry.bounds[0]) > 180.0): + antimeridian_warning_msg = ( + "Geometry crossing the antimeridian is not supported. " + "Cannot verify non-crossing given current geometry bounds." + ) + warnings.warn(antimeridian_warning_msg, category=IrisUserWarning) + + # Check if the geometry crosses the poles + npole = sgeom.Point(0, 90) + spole = sgeom.Point(0, -90) + if geometry.intersects(npole) or geometry.intersects(spole): + err_msg = "Geometry crossing the poles is not supported." + raise ValueError(err_msg) + + return + + +def _transform_geometry( + geometry: shapely.Geometry, geometry_crs: ccrs.CRS | CRS, cube_crs: ccrs.CRS +) -> shapely.Geometry: + """Transform a geometry to the cube CRS using pyproj. Parameters ---------- - cube : :class:`iris.cube.Cube` + geometry : :class:`shapely.Geometry` + The geometry to transform. + geometry_crs : :class:`cartopy.crs`, :class:`pyproj.CRS` + The coordinate reference system of the geometry. + cube_crs : :class:`cartopy.crs`, :class:`pyproj.CRS` + The coordinate reference system of the cube. Returns ------- - tuple of str - The names of the primary latitude and longitude coordinates. - + :class:`shapely.Geometry` + The transformed geometry. """ - latc = ( - cube.coords(axis="y", dim_coords=True)[0] - if cube.coords(axis="y", dim_coords=True) - else -1 - ) - lonc = ( - cube.coords(axis="x", dim_coords=True)[0] - if cube.coords(axis="x", dim_coords=True) - else -1 - ) + # Set-up transform via pyproj + t = Transformer.from_crs( + crs_from=geometry_crs, crs_to=cube_crs, always_xy=True + ).transform + # Transform geometry + transformed_geometry = shapely.ops.transform(t, geometry) + # Check for Inf in transformed geometry which indicates a failed transform + if np.isinf(transformed_geometry.bounds).any(): + raise ValueError( + "Error transforming geometry: geometry contains Inf coordinates. This is likely due to a failed CRS transformation." + "\nFailed transforms are often caused by network issues, often due to incorrectly configured SSL certificate paths." + ) + + return transformed_geometry - if -1 in (latc, lonc): - msg = "Error retrieving 1d xy coordinates in cube: {!r}" - raise ValueError(msg.format(cube)) - latitude = latc.name() - longitude = lonc.name() - return latitude, longitude +def _get_weighted_mask( + cube: iris.cube.Cube, + geometry: shapely.Geometry, + minimum_weight: float, +) -> np.array: + """Get a mask based on the geometry and minimum weight. + This function creates a mask for the cube based on the intersection + of the geometry with the cube's grid boxes. The mask will + only include areas where the geometry has a weight greater than + the specified minimum weight. -def _get_mod_rebased_coord_bounds(coord): - """Take in a coord and returns a array of the bounds of that coord rebased to the modulus. + Cube grid boxes are rendered in ``shapely`` and uses a STRtree spatial index + for efficient spatial querying. Parameters ---------- - coord : :class:`iris.coords.Coord` - An Iris coordinate with a modulus. + cube : :class:`iris.cube.Cube` + The cube to mask. + geometry : :class:`shapely.Geometry` + The geometry to use for masking. + minimum_weight : float + The minimum weight of the geometry to be included in the mask. + Should be a value between 0.0 and 1.0. If the weight is less than + this value, the geometry will not be included in the mask. Returns ------- :class:`np.array` - A 1d Numpy array of [start,end] pairs for bounds of the coord. + An array of the shape of the x & y coordinates of the cube, with points + to mask equal to True. + """ + axes: tuple[Axis, Axis] = ("X", "Y") + x_coord, y_coord = [cube.coord(axis=a, dim_coords=True) for a in axes] + # Get the shape of the cube + w, h = [len(c.points) for c in (x_coord, y_coord)] + # Check and get the bounds of the cube + for coord in (x_coord, y_coord): + if not coord.has_bounds(): + coord.guess_bounds() + x_bounds, y_bounds = [c.bounds for c in (x_coord, y_coord)] + + # Generate Sort-Tile-Recursive (STR) packed R-tree of bounding boxes + # https://shapely.readthedocs.io/en/stable/strtree.html + grid_boxes = [ + sgeom.box(x[0], y[0], x[1], y[1]) for y, x in product(y_bounds, x_bounds) + ] + grid_tree = shapely.STRtree(grid_boxes) + # Find grid boxes indexes that intersect with the geometry + idxs = grid_tree.query(geometry, predicate="intersects") + # Get grid box indexes that intersect with the minimum weight criteria + mask_idxs_bool = [ + grid_boxes[idx].intersection(geometry).area / grid_boxes[idx].area + >= minimum_weight + for idx in idxs + ] + mask_idxs = idxs[mask_idxs_bool] + mask_xy = [list(product(range(h), range(w)))[i] for i in mask_idxs] + # Create mask from grid box indices + weighted_mask_template = np.ones((h, w), dtype=bool) + # If there are grid box indices that intersect the geometry + # ie. mask_xy is not empty, then set mask = False for + # grid box indices identified above + if mask_xy: + ys, xs = zip(*mask_xy) + weighted_mask_template[ys, xs] = False + return weighted_mask_template + + +def _make_raster_cube_transform( + x_coord: iris.coords.DimCoord, y_coord: iris.coords.DimCoord +) -> Affine: + """Create a rasterio transform for the cube. + + Raises + ------ + CoordinateNotRegularError + If the cube dimension coordinates are not regular, + such that :func:`iris.util.regular_step` returns an error. + Returns + ------- + :class:`affine.Affine` + An affine transform object that maps the geometry domain onto the cube domain. """ - modulus = coord.units.modulus - # Force realisation (rather than core_bounds) - more efficient for the - # repeated indexing happening downstream. - result = np.array(coord.bounds) - if modulus: - result[result < 0.0] = (np.abs(result[result < 0.0]) % modulus) * -1 - result[np.isclose(result, modulus, 1e-10)] = 0.0 - return result + x_points = x_coord.points + y_points = y_coord.points + dx = iris.util.regular_step(x_coord) + dy = iris.util.regular_step(y_coord) + # Create a rasterio transform based on the cube + # This maps the geometry domain onto the cube domain + trans = Affine.translation(xoff=x_points[0] - dx / 2, yoff=y_points[0] - dy / 2) + scale = Affine.scale(dx, dy) + return trans * scale diff --git a/lib/iris/tests/integration/test_mask_cube_from_shape.py b/lib/iris/tests/integration/test_mask_cube_from_shape.py new file mode 100644 index 0000000000..9937818d27 --- /dev/null +++ b/lib/iris/tests/integration/test_mask_cube_from_shape.py @@ -0,0 +1,235 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Integration tests for :func:`iris.util.mask_cube_from_shape`.""" + +import cartopy.io.shapereader as shpreader +import numpy as np +from pyproj import CRS +import pytest +from pytest import approx +from shapely.geometry import LineString, MultiLineString, MultiPoint, Point + +import iris +from iris._deprecation import IrisDeprecation +from iris.coord_systems import GeogCS +import iris.tests as tests +from iris.util import mask_cube_from_shape, mask_cube_from_shapefile +from iris.warnings import IrisUserWarning + + +@pytest.fixture +def wgs84_crs(): + return CRS.from_epsg(4326) + + +@pytest.fixture +def shp_reader(): + ne_countries = shpreader.natural_earth( + resolution="10m", category="cultural", name="admin_0_countries" + ) + return shpreader.Reader(ne_countries) + + +@pytest.mark.parametrize( + ("minimum_weight", "all_touched", "invert", "expected_sum"), + [ + (0.0, None, None, 10522684.77), # Minimum weight == 0 + (0.0, None, False, 10522684.77), # Minimum weight == 0 + (0.0, True, False, 10522684.77), # All touched == True + (0.5, None, False, 8965584.47), # Minimum weight == 0.5 + (1.0, None, False, 7504361.29), # Minimum weight == 1 + (0.0, False, False, 8953582.05), # All touched == False + (0.0, True, True, 605637718.12), # All touched == True, Invert == True + ], +) +def test_global_proj_china( + minimum_weight, all_touched, invert, expected_sum, shp_reader, wgs84_crs +): + """Test masking with a shape for China with various parameter combinations.""" + path = tests.get_data_path(["NetCDF", "global", "xyt", "SMALL_total_column_co2.nc"]) + test_global = iris.load_cube(path) + test_global.coord("latitude").coord_system = GeogCS(6371229) + test_global.coord("longitude").coord_system = GeogCS(6371229) + + ne_china = [ + country.geometry + for country in shp_reader.records() + if "China" in country.attributes["NAME_LONG"] + ][0] + masked_test = mask_cube_from_shape( + test_global, + ne_china, + shape_crs=wgs84_crs, + minimum_weight=minimum_weight, + all_touched=all_touched, + invert=invert, + ) + assert masked_test.ndim == 3 + assert approx(np.sum(masked_test.data), rel=0.001) == expected_sum + + +def test_global_proj_russia(shp_reader, wgs84_crs): + """Test masking with a shape that crosses the antimeridian.""" + path = tests.get_data_path(["NetCDF", "global", "xyt", "SMALL_total_column_co2.nc"]) + test_global = iris.load_cube(path) + test_global.coord("latitude").coord_system = GeogCS(6371229) + test_global.coord("longitude").coord_system = GeogCS(6371229) + ne_russia = [ + country.geometry + for country in shp_reader.records() + if "Russia" in country.attributes["NAME_LONG"] + ][0] + + with pytest.warns( + IrisUserWarning, + match=( + "Geometry crossing the antimeridian is not supported. " + "Cannot verify non-crossing given current geometry bounds." + ), + ): + _ = mask_cube_from_shape(test_global, ne_russia, shape_crs=wgs84_crs) + + +def test_rotated_pole_proj_uk(shp_reader, wgs84_crs): + """Test masking a rotated pole projection cube for the UK with lat/lon shape.""" + path = tests.get_data_path( + ["NetCDF", "rotated", "xy", "rotPole_landAreaFraction.nc"] + ) + test_rotated = iris.load_cube(path) + ne_uk = [ + country.geometry + for country in shp_reader.records() + if "United Kingdom" in country.attributes["NAME_LONG"] + ][0] + masked_test = mask_cube_from_shape(test_rotated, ne_uk, shape_crs=wgs84_crs) + assert masked_test.ndim == 2 + assert approx(np.sum(masked_test.data), rel=0.001) == 102.77 + + +def test_transverse_mercator_proj_uk(shp_reader, wgs84_crs): + """Test masking a transverse mercator projection cube for the UK with lat/lon shape.""" + path = tests.get_data_path(["NetCDF", "transverse_mercator", "tmean_1910_1910.nc"]) + test_transverse = iris.load_cube(path) + ne_uk = [ + country.geometry + for country in shp_reader.records() + if "United Kingdom" in country.attributes["NAME_LONG"] + ][0] + masked_test = mask_cube_from_shape(test_transverse, ne_uk, shape_crs=wgs84_crs) + assert masked_test.ndim == 3 + assert approx(np.sum(masked_test.data), rel=0.001) == 90740.25 + + +def test_rotated_pole_proj_germany_weighted_area(shp_reader, wgs84_crs): + """Test masking a rotated pole projection cube for Germany with weighted area.""" + path = tests.get_data_path( + ["NetCDF", "rotated", "xy", "rotPole_landAreaFraction.nc"] + ) + test_rotated = iris.load_cube(path) + ne_germany = [ + country.geometry + for country in shp_reader.records() + if "Germany" in country.attributes["NAME_LONG"] + ][0] + masked_test = mask_cube_from_shape( + test_rotated, ne_germany, shape_crs=wgs84_crs, minimum_weight=0.9 + ) + assert masked_test.ndim == 2 + assert approx(np.sum(masked_test.data), rel=0.001) == 125.60199 + + +def test_4d_global_proj_brazil(shp_reader, wgs84_crs): + """Test masking a 4D global projection cube for Brazil with lat/lon shape.""" + path = tests.get_data_path(["NetCDF", "global", "xyz_t", "GEMS_CO2_Apr2006.nc"]) + test_4d_brazil = iris.load_cube(path, "Carbon Dioxide") + test_4d_brazil.coord("latitude").coord_system = GeogCS(6371229) + test_4d_brazil.coord("longitude").coord_system = GeogCS(6371229) + ne_brazil = [ + country.geometry + for country in shp_reader.records() + if "Brazil" in country.attributes["NAME_LONG"] + ][0] + masked_test = mask_cube_from_shape( + test_4d_brazil, ne_brazil, shape_crs=wgs84_crs, all_touched=True + ) + assert masked_test.ndim == 4 + assert approx(np.sum(masked_test.data), rel=0.001) == 18616921.2 + + +@pytest.mark.parametrize( + # `expected_value` entries are pre-calculated Known Good Outputs. + ("shape", "expected_value"), + [ + (Point(-3.475446894622651, 50.72770791320487), 12061.74), # (x,y) + ( + LineString( + [ + (-5.712431305030631, 50.06590599588483), + (-3.0704940433528947, 58.644091639685456), + ] + ), + 120530.41, + ), # (x,y) to (x,y) + ( + MultiPoint( + [ + (-5.712431305030631, 50.06590599588483), + (-3.0704940433528947, 58.644091639685456), + ] + ), + 24097.47, + ), + ( + MultiLineString( + [ + [ + (-5.206405826948041, 49.95891620303525), + (-3.376975634580173, 58.67197421392852), + ], + [ + (-6.2276386132877475, 56.71561805509071), + (1.7626540441873777, 52.48118683241357), + ], + ] + ), + 253248.44, + ), + ], +) +def test_global_proj_uk_shapes(shape, expected_value, wgs84_crs): + """Test masking with a variety of shape types.""" + path = tests.get_data_path(["NetCDF", "global", "xyt", "SMALL_total_column_co2.nc"]) + test_global = iris.load_cube(path) + test_global.coord("latitude").coord_system = GeogCS(6371229) + test_global.coord("longitude").coord_system = GeogCS(6371229) + masked_test = mask_cube_from_shape( + test_global, + shape, + shape_crs=wgs84_crs, + ) + assert masked_test.ndim == 3 + assert approx(np.sum(masked_test.data), rel=0.001) == expected_value + + +def test_mask_cube_from_shapefile_depreciation(shp_reader): + """Test that the mask_cube_from_shapefile function raises a deprecation warning.""" + path = tests.get_data_path(["NetCDF", "global", "xyt", "SMALL_total_column_co2.nc"]) + test_global = iris.load_cube(path) + test_global.coord("latitude").coord_system = GeogCS(6371229) + test_global.coord("longitude").coord_system = GeogCS(6371229) + ne_china = [ + country.geometry + for country in shp_reader.records() + if "China" in country.attributes["NAME_LONG"] + ][0] + + with pytest.warns( + IrisDeprecation, + match=( + "iris.util.mask_cube_from_shapefile has been deprecated, and will be removed in a " + "future release. Please use iris.util.mask_cube_from_shape instead." + ), + ): + mask_cube_from_shapefile(test_global, ne_china) diff --git a/lib/iris/tests/integration/test_mask_cube_from_shapefile.py b/lib/iris/tests/integration/test_mask_cube_from_shapefile.py deleted file mode 100644 index 52fd02615d..0000000000 --- a/lib/iris/tests/integration/test_mask_cube_from_shapefile.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright Iris contributors -# -# This file is part of Iris and is released under the BSD license. -# See LICENSE in the root of the repository for full licensing details. -"""Integration tests for :func:`iris.util.mask_cube_from_shapefile`.""" - -import math - -import cartopy.io.shapereader as shpreader -import numpy as np - -import iris -import iris.tests as tests -from iris.util import mask_cube_from_shapefile - - -@tests.skip_data -class TestCubeMasking(tests.IrisTest): - """integration tests of mask_cube_from_shapefile - using different projections in iris_test_data - - values are the KGO calculated using ASCEND. - """ - - def setUp(self): - ne_countries = shpreader.natural_earth( - resolution="10m", category="cultural", name="admin_0_countries" - ) - self.reader = shpreader.Reader(ne_countries) - - def test_global_proj_russia(self): - path = tests.get_data_path( - ["NetCDF", "global", "xyt", "SMALL_hires_wind_u_for_ipcc4.nc"] - ) - test_global = iris.load_cube(path) - ne_russia = [ - country.geometry - for country in self.reader.records() - if "Russia" in country.attributes["NAME_LONG"] - ][0] - masked_test = mask_cube_from_shapefile(test_global, ne_russia) - print(np.sum(masked_test.data)) - assert math.isclose(np.sum(masked_test.data), 76845.37, rel_tol=0.001), ( - "Global data with Russia mask failed test" - ) - - def test_rotated_pole_proj_germany(self): - path = tests.get_data_path( - ["NetCDF", "rotated", "xy", "rotPole_landAreaFraction.nc"] - ) - test_rotated = iris.load_cube(path) - ne_germany = [ - country.geometry - for country in self.reader.records() - if "Germany" in country.attributes["NAME_LONG"] - ][0] - masked_test = mask_cube_from_shapefile(test_rotated, ne_germany) - assert math.isclose(np.sum(masked_test.data), 179.46872, rel_tol=0.001), ( - "rotated europe data with German mask failed test" - ) - - def test_transverse_mercator_proj_uk(self): - path = tests.get_data_path( - ["NetCDF", "transverse_mercator", "tmean_1910_1910.nc"] - ) - test_transverse = iris.load_cube(path) - ne_uk = [ - country.geometry - for country in self.reader.records() - if "United Kingdom" in country.attributes["NAME_LONG"] - ][0] - masked_test = mask_cube_from_shapefile(test_transverse, ne_uk) - assert math.isclose(np.sum(masked_test.data), 90740.25, rel_tol=0.001), ( - "transverse mercator UK data with UK mask failed test" - ) - - def test_rotated_pole_proj_germany_weighted_area(self): - path = tests.get_data_path( - ["NetCDF", "rotated", "xy", "rotPole_landAreaFraction.nc"] - ) - test_rotated = iris.load_cube(path) - ne_germany = [ - country.geometry - for country in self.reader.records() - if "Germany" in country.attributes["NAME_LONG"] - ][0] - masked_test = mask_cube_from_shapefile( - test_rotated, ne_germany, minimum_weight=0.9 - ) - assert math.isclose(np.sum(masked_test.data), 125.60199, rel_tol=0.001), ( - "rotated europe data with 0.9 weight germany mask failed test" - ) - - def test_4d_global_proj_brazil(self): - path = tests.get_data_path(["NetCDF", "global", "xyz_t", "GEMS_CO2_Apr2006.nc"]) - test_4d_brazil = iris.load_cube(path, "Carbon Dioxide") - ne_brazil = [ - country.geometry - for country in self.reader.records() - if "Brazil" in country.attributes["NAME_LONG"] - ][0] - masked_test = mask_cube_from_shapefile( - test_4d_brazil, - ne_brazil, - ) - print(np.sum(masked_test.data)) - # breakpoint() - assert math.isclose(np.sum(masked_test.data), 18616921.2, rel_tol=0.001), ( - "4d data with brazil mask failed test" - ) diff --git a/lib/iris/tests/unit/_shapefiles/__init__.py b/lib/iris/tests/unit/_shapefiles/__init__.py new file mode 100644 index 0000000000..0d51a683b8 --- /dev/null +++ b/lib/iris/tests/unit/_shapefiles/__init__.py @@ -0,0 +1,5 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for the :mod:`iris._shapefiles` module.""" diff --git a/lib/iris/tests/unit/_shapefiles/test_create_shape_mask.py b/lib/iris/tests/unit/_shapefiles/test_create_shape_mask.py new file mode 100644 index 0000000000..b4d943bbc5 --- /dev/null +++ b/lib/iris/tests/unit/_shapefiles/test_create_shape_mask.py @@ -0,0 +1,279 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for :func:`iris._shapefiles.create_shape_mask`.""" + +import numpy as np +from pyproj import CRS +import pytest +from shapely.geometry import Point, Polygon + +from iris._shapefiles import create_shape_mask +from iris.coord_systems import GeogCS +from iris.coords import DimCoord +from iris.cube import Cube, CubeList +from iris.exceptions import IrisError +from iris.warnings import IrisUserWarning + + +@pytest.fixture(scope="session") +def wgs84_crs(): + return CRS.from_epsg(4326) + + +@pytest.fixture +def square_polygon(): + # Create a a 3x3 square polygon from (3,3) to (6,6) using shapely + return Polygon([(3, 3), (6, 3), (6, 6), (3, 6)]) + + +@pytest.fixture +def circle_polygon(): + # Create a a circular polygon centred on (5,5) with radius (2,) using shapely + return Point(5, 5).buffer(2) + + +@pytest.fixture +def mock_cube(): + """Create a mock 9x9 Iris cube for testing.""" + x_points = np.linspace(1, 9, 9) - 0.5 # Specify cube cell midpoints + y_points = np.linspace(1, 9, 9) - 0.5 + x_coord = DimCoord( + x_points, + standard_name="longitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + y_coord = DimCoord( + y_points, + standard_name="latitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + data = np.ones((len(y_points), len(x_points))) + cube = Cube(data, dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)]) + return cube + + +def test_basic_create_shape_mask(square_polygon, wgs84_crs, mock_cube): + """Test the create_shape_mask function.""" + # Create a mask using the square polygon + mask = create_shape_mask( + geometry=square_polygon, geometry_crs=wgs84_crs, cube=mock_cube + ) + + # Check that the mask is a boolean array with the same shape as the cube data + assert mask.shape == mock_cube.data.shape + assert mask.dtype == np.bool_ + + # Check that the masked area corresponds to the square polygon + expected_mask = np.ones_like(mock_cube.data, dtype=bool) + expected_mask[3:6, 3:6] = False # The square polygon covers this area + assert np.array_equal(mask, expected_mask) + + +def test_basic_create_shape_mask_with_None_crs(square_polygon, mock_cube): + """Test the create_shape_mask function.""" + # Create a mask using the square polygon + # Here we pass None for geometry_crs to test default behaviour + # which assumes the geometry is in the same CRS as the cube + mask = create_shape_mask(geometry=square_polygon, geometry_crs=None, cube=mock_cube) + + # Check that the mask is a boolean array with the same shape as the cube data + assert mask.shape == mock_cube.data.shape + assert mask.dtype == np.bool_ + + # Check that the masked area corresponds to the square polygon + expected_mask = np.ones_like(mock_cube.data, dtype=bool) + expected_mask[3:6, 3:6] = False # The square polygon covers this area + assert np.array_equal(mask, expected_mask) + + +def test_basic_create_shape_mask_radians(square_polygon, wgs84_crs, mock_cube): + """Test the create_shape_mask function.""" + # Convert mock cube coordinates to radians + mock_cube.coord("longitude").convert_units("radians") + mock_cube.coord("latitude").convert_units("radians") + + # Create a mask using the square polygon + mask = create_shape_mask( + geometry=square_polygon, geometry_crs=wgs84_crs, cube=mock_cube + ) + + # Check that the mask is a boolean array with the same shape as the cube data + assert mask.shape == mock_cube.data.shape + assert mask.dtype == np.bool_ + + # Check that the masked area corresponds to the square polygon + expected_mask = np.ones_like(mock_cube.data, dtype=bool) + expected_mask[3:6, 3:6] = False # The square polygon covers this area + assert np.array_equal(mask, expected_mask) + + +def test_invert_create_shape_mask(square_polygon, wgs84_crs, mock_cube): + """Test the create_shape_mask function.""" + # Create a mask using the square polygon + mask = create_shape_mask( + geometry=square_polygon, geometry_crs=wgs84_crs, cube=mock_cube, invert=True + ) + + # Check that the mask is a boolean array with the same shape as the cube data + assert mask.shape == mock_cube.data.shape + assert mask.dtype == np.bool_ + + # Check that the masked area corresponds to the square polygon + expected_mask = np.zeros_like(mock_cube.data, dtype=bool) + expected_mask[3:6, 3:6] = True # The square polygon covers this area + assert np.array_equal(mask, expected_mask) + + +def test_all_touched_true_create_shape_mask(circle_polygon, wgs84_crs, mock_cube): + """Test the create_shape_mask function.""" + # Create a mask using the circular polygon + mask = create_shape_mask( + geometry=circle_polygon, + geometry_crs=wgs84_crs, + cube=mock_cube, + all_touched=True, + ) + + # Check that the mask is a boolean array with the same shape as the cube data + assert mask.shape == mock_cube.data.shape + assert mask.dtype == np.bool_ + + # Check that the masked area corresponds to the circular polygon + expected_mask = np.array( + [ + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 0, 0, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 0, 1, 1], + [1, 1, 1, 0, 0, 0, 0, 1, 1], + [1, 1, 1, 0, 0, 0, 0, 1, 1], + [1, 1, 1, 0, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + ], + dtype=bool, + ) + assert np.array_equal(mask, expected_mask) + + +def test_all_touched_false_create_shape_mask(circle_polygon, wgs84_crs, mock_cube): + """Test the create_shape_mask function.""" + # Create a mask using the circular polygon + mask = create_shape_mask( + geometry=circle_polygon, + geometry_crs=wgs84_crs, + cube=mock_cube, + all_touched=False, + ) + + # Check that the mask is a boolean array with the same shape as the cube data + assert mask.shape == mock_cube.data.shape + assert mask.dtype == np.bool_ + + # Check that the masked area corresponds to the circular polygon + expected_mask = np.array( + [ + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 0, 0, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 0, 1, 1], + [1, 1, 1, 0, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 0, 0, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + ], + dtype=bool, + ) + assert np.array_equal(mask, expected_mask) + + +class TestCreateShapeMaskErrors: + def test_invalid_cube_type(self, square_polygon, wgs84_crs): + # Pass an invalid cube type (e.g., a string or CubeList) + err_message = "Received non-Cube object where a Cube is expected" + with pytest.raises(TypeError, match=err_message): + create_shape_mask( + geometry=square_polygon, geometry_crs=wgs84_crs, cube="not_a_cube" + ) + + def test_invalid_cubelist_type(self, square_polygon, wgs84_crs): + err_message = ( + "Received CubeList object rather than Cube - " + "to mask a CubeList iterate over each Cube" + ) + with pytest.raises(TypeError, match=err_message): + create_shape_mask( + geometry=square_polygon, geometry_crs=wgs84_crs, cube=CubeList() + ) + + def test_invalid_cube_crs(self, square_polygon, wgs84_crs): + # Pass a cube without a coordinate system + cube = Cube(np.ones((10, 10)), dim_coords_and_dims=[]) + err_message = ( + "Cube coordinates do not have a coordinate references system \\(CRS\\) " + "defined. A CRS must be defined to ensure reliable results." + ) + with pytest.raises(IrisError, match=err_message): + create_shape_mask( + geometry=square_polygon, geometry_crs=wgs84_crs, cube=cube + ) + + @pytest.mark.parametrize( + "minimum_weight, error_type", + [(-1, ValueError), (2, ValueError)], + ) + def test_invalid_minimum_weight( + self, square_polygon, wgs84_crs, mock_cube, minimum_weight, error_type + ): + # Pass invalid minimum_weight values + err_message = "Minimum weight must be between 0.0 and 1.0" + with pytest.raises(error_type, match=err_message): + create_shape_mask( + geometry=square_polygon, + geometry_crs=wgs84_crs, + cube=mock_cube, + minimum_weight=minimum_weight, + all_touched=None, + ) + + @pytest.mark.parametrize( + "minimum_weight, error_type", + [(-1, ValueError), (2, ValueError)], + ) + def test_invalid_minimum_weight_with_all_touched( + self, square_polygon, wgs84_crs, mock_cube, minimum_weight, error_type + ): + # Pass invalid minimum_weight values + err_message = "Minimum weight must be between 0.0 and 1.0" + with pytest.raises(error_type, match=err_message): + create_shape_mask( + geometry=square_polygon, + geometry_crs=wgs84_crs, + cube=mock_cube, + minimum_weight=minimum_weight, + all_touched=False, + ) + + def test_invalid_args(self, square_polygon, wgs84_crs, mock_cube): + # Pass invalid minimum_weight values + err_message = "Cannot use minimum_weight > 0.0 with all_touched=True." + with pytest.raises(ValueError, match=err_message): + create_shape_mask( + geometry=square_polygon, + geometry_crs=wgs84_crs, + cube=mock_cube, + minimum_weight=0.5, + all_touched=True, + ) + + def test_incompatible_crs_warning(self, square_polygon, mock_cube): + # Pass a CRS that does not match the cube's CRS + crs = CRS.from_epsg(3857) # Web Mercator, different from WGS84 + warn_message = "Geometry CRS does not match cube CRS. Iris will attempt to transform the geometry onto the cube CRS..." + with pytest.warns(IrisUserWarning, match=warn_message): + create_shape_mask(geometry=square_polygon, geometry_crs=crs, cube=mock_cube) diff --git a/lib/iris/tests/unit/_shapefiles/test_get_weighted_mask.py b/lib/iris/tests/unit/_shapefiles/test_get_weighted_mask.py new file mode 100644 index 0000000000..406a9dbc59 --- /dev/null +++ b/lib/iris/tests/unit/_shapefiles/test_get_weighted_mask.py @@ -0,0 +1,107 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for :func:`iris._shapefiles._get_weighted_mask`.""" + +import numpy as np +import pytest +from shapely.geometry import box + +from iris._shapefiles import _get_weighted_mask +from iris.coord_systems import GeogCS +from iris.coords import DimCoord +from iris.cube import Cube + + +@pytest.fixture +def square_polygon(): + # Create a roughly 3x3 square polygon + return box(2.4, 2.4, 6.4, 6.4) + + +@pytest.fixture +def mock_cube(): + """Create a mock 9x9 Iris cube for testing.""" + x_points = np.linspace(1, 9, 9) - 0.5 # Specify cube cell midpoints + y_points = np.linspace(1, 9, 9) - 0.5 + x_coord = DimCoord( + x_points, + standard_name="longitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + y_coord = DimCoord( + y_points, + standard_name="latitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + data = np.ones((len(y_points), len(x_points))) + cube = Cube(data, dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)]) + return cube + + +@pytest.mark.parametrize( + "minimum_weight, expected_mask", + [ + ( + 0.0, + np.array( + [ + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + ], + dtype=bool, + ), + ), + ( + 0.5, + np.array( + [ + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 1, 1, 1], + [1, 1, 0, 0, 0, 0, 1, 1, 1], + [1, 1, 0, 0, 0, 0, 1, 1, 1], + [1, 1, 0, 0, 0, 0, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + ], + dtype=bool, + ), + ), + ( + 1.0, + np.array( + [ + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1], + ], + dtype=bool, + ), + ), + ], +) +def test_basic_weighted_mask(mock_cube, square_polygon, minimum_weight, expected_mask): + """Test the create_shape_mask function with different minimum weights.""" + # Create a mask using the square polygon + mask = _get_weighted_mask(mock_cube, square_polygon, minimum_weight=minimum_weight) + + # Check that the masked area corresponds to the square polygon + assert np.array_equal(mask, expected_mask) diff --git a/lib/iris/tests/unit/_shapefiles/test_is_geometry_valid.py b/lib/iris/tests/unit/_shapefiles/test_is_geometry_valid.py new file mode 100644 index 0000000000..46ff058af2 --- /dev/null +++ b/lib/iris/tests/unit/_shapefiles/test_is_geometry_valid.py @@ -0,0 +1,220 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for :func:`iris._shapefiles.is_geometry_valid`.""" + +from pyproj import CRS +import pytest +from shapely.geometry import ( + LineString, + MultiLineString, + MultiPoint, + MultiPolygon, + Point, + box, +) + +from iris._shapefiles import is_geometry_valid +from iris.warnings import IrisUserWarning + + +# Shareable shape fixtures used in: +# - util/test_mask_cube_from_shapefile.py +# - _shapefiles/test_is_geometry_valid.py +@pytest.fixture() +def wgs84_crs(): + return CRS.from_epsg(4326) + + +@pytest.fixture() +def osgb_crs(): + return CRS.from_epsg(27700) + + +@pytest.fixture() +def basic_polygon_geometry(): + # Define the coordinates of a basic rectangle + min_lon = -90 + min_lat = -45 + max_lon = 90 + max_lat = 45 + + # Create the rectangular geometry + return box(min_lon, min_lat, max_lon, max_lat) + + +@pytest.fixture() +def basic_wide_polygon_geometry(): + # Define the coordinates of a basic rectangle + min_lon = -170 + min_lat = -45 + max_lon = 170 + max_lat = 45 + + # Create the rectangular geometry + return box(min_lon, min_lat, max_lon, max_lat) + + +@pytest.fixture() +def basic_multipolygon_geometry(): + # Define the coordinates of a basic rectangle + min_lon = 0 + min_lat = 0 + max_lon = 8 + max_lat = 8 + + # Create the rectangular geometry + return MultiPolygon( + [ + box(min_lon, min_lat, max_lon, max_lat), + box(min_lon + 10, min_lat + 10, max_lon + 10, max_lat + 10), + ] + ) + + +@pytest.fixture() +def basic_point_geometry(): + # Define the coordinates of a basic point (lon, lat) + return Point((-3.476204, 50.727059)) + + +@pytest.fixture() +def basic_line_geometry(): + # Define the coordinates of a basic line + return LineString([(0, 0), (10, 10)]) + + +@pytest.fixture() +def basic_multiline_geometry(): + # Define the coordinates of a basic line + return MultiLineString([[(0, 0), (10, 10)], [(20, 20), (30, 30)]]) + + +@pytest.fixture() +def basic_point_collection(): + # Define the coordinates of a basic collection of points + # as (lon, lat) tuples, assuming a WGS84 projection. + points = MultiPoint( + [ + (0, 0), + (10, 10), + (-10, -10), + (-3.476204, 50.727059), + (174.761067, -36.846211), + (-77.032801, 38.892717), + ] + ) + + return points + + +@pytest.fixture() +def canada_geometry(): + # Define the coordinates of a rectangle that covers Canada + return box(-143.5, 42.6, -37.8, 84.0) + + +@pytest.fixture() +def bering_sea_geometry(): + # Define the coordinates of a rectangle that covers the Bering Sea + return box(148.42, 49.1, -138.74, 73.12) + + +@pytest.fixture() +def uk_geometry(): + # Define the coordinates of a rectangle that covers the UK + return box(-10, 49, 2, 61) + + +@pytest.fixture() +def invalid_geometry_poles(): + # Define the coordinates of a rectangle that crosses the poles + return box(-10, -90, 10, 90) + + +@pytest.fixture() +def invalid_geometry_bounds(): + # Define the coordinates of a rectangle that is outside the bounds of the coordinate system + return box(-200, -100, 200, 100) + + +@pytest.fixture() +def not_a_valid_geometry(): + # Return an invalid geometry type + # This is not a valid geometry, e.g., a string + return "This is not a valid geometry" + + +# Test validity of different geometries +@pytest.mark.parametrize( + "test_input", + [ + "basic_polygon_geometry", + "basic_multipolygon_geometry", + "basic_point_geometry", + "basic_point_collection", + "basic_line_geometry", + "basic_multiline_geometry", + "canada_geometry", + ], +) +def test_valid_geometry(test_input, request, wgs84_crs): + # Assert that all valid geometries are return None + assert is_geometry_valid(request.getfixturevalue(test_input), wgs84_crs) is None + + +# Fixtures retrieved from conftest.py +# N.B. error message comparison is done with regex so +# any parentheses in the error message must be escaped (\) +@pytest.mark.parametrize( + "test_input, errortype, err_message", + [ + ( + "invalid_geometry_poles", + ValueError, + "Geometry crossing the poles is not supported.", + ), + ( + "invalid_geometry_bounds", + ValueError, + r"Geometry \[\] is not valid for the given coordinate system EPSG:4326.\nCheck that your coordinates are correctly specified.", + ), + ( + "not_a_valid_geometry", + TypeError, + r"Shape geometry is not a valid shape \(not well formed\).", + ), + ], +) +def test_invalid_geometry(test_input, errortype, err_message, request, wgs84_crs): + # Assert that all invalid geometries raise the expected error + with pytest.raises(errortype, match=err_message): + is_geometry_valid(request.getfixturevalue(test_input), wgs84_crs) + + +@pytest.mark.parametrize( + "test_input", + ( + "basic_wide_polygon_geometry", + "bering_sea_geometry", + ), +) +def test_warning_geometry(test_input, request, wgs84_crs): + # Assert that all invalid geometries raise the expected error + warn_message = ( + "Geometry crossing the antimeridian is not supported. " + "Cannot verify non-crossing given current geometry bounds." + ) + with pytest.warns(IrisUserWarning, match=warn_message): + is_geometry_valid(request.getfixturevalue(test_input), wgs84_crs) + + +def test_invalid_crs(basic_polygon_geometry): + # Assert that an invalid crs raise the expected error + err_message = ( + "Geometry CRS must be a cartopy.crs or pyproj.CRS object, not ." + ) + # Using a string as an invalid CRS type + with pytest.raises(TypeError, match=err_message): + is_geometry_valid(basic_polygon_geometry, "invalid_crs") diff --git a/lib/iris/tests/unit/_shapefiles/test_make_raster_cube_transform.py b/lib/iris/tests/unit/_shapefiles/test_make_raster_cube_transform.py new file mode 100644 index 0000000000..239cce15b2 --- /dev/null +++ b/lib/iris/tests/unit/_shapefiles/test_make_raster_cube_transform.py @@ -0,0 +1,71 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for :func:`iris._shapefiles._make_raster_cube_transform`.""" + +from affine import Affine +import numpy as np +import pytest + +from iris._shapefiles import _make_raster_cube_transform +from iris.coords import DimCoord +from iris.cube import Cube +from iris.exceptions import CoordinateNotRegularError +from iris.util import regular_step + + +@pytest.fixture +def mock_cube(): + """Create a mock Iris cube for testing.""" + x_points = np.array([0.0, 1.0, 2.0, 3.0]) + y_points = np.array([0.0, 1.0, 2.0, 3.0]) + x_coord = DimCoord(x_points, standard_name="longitude", units="degrees") + y_coord = DimCoord(y_points, standard_name="latitude", units="degrees") + data = np.zeros((len(y_points), len(x_points))) + cube = Cube(data, dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)]) + return cube + + +@pytest.fixture +def mock_nonregular_cube(): + """Create a mock Iris cube for testing.""" + x_points = np.array( + [0.0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 9.5, 10.0] + ) + y_points = np.array( + [0.0, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 9.5, 10.0] + ) + x_coord = DimCoord(x_points, standard_name="longitude", units="degrees") + y_coord = DimCoord(y_points, standard_name="latitude", units="degrees") + data = np.zeros((len(y_points), len(x_points))) + cube = Cube(data, dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)]) + return cube + + +def test_make_raster_cube_transform(mock_cube): + """Test the `_make_raster_cube__transform` function.""" + x_name = "longitude" + y_name = "latitude" + x_coord, y_coord = [mock_cube.coord(a) for a in (x_name, y_name)] + + # Call the function + transform = _make_raster_cube_transform(x_coord, y_coord) + + # Validate the result + dx = regular_step(x_coord) + dy = regular_step(y_coord) + expected_transform = Affine.translation(-dx / 2, -dy / 2) * Affine.scale(dx, dy) + + assert isinstance(transform, Affine) + assert transform == expected_transform + + +def test_invalid_cube(mock_nonregular_cube): + x_coord, y_coord = [ + mock_nonregular_cube.coord(a) for a in ("longitude", "latitude") + ] + # Assert that all invalid geometries raise the expected error + errormessage = "Coord longitude is not regular" + with pytest.raises(CoordinateNotRegularError, match=errormessage): + _make_raster_cube_transform(x_coord, y_coord) diff --git a/lib/iris/tests/unit/_shapefiles/test_transform_geometry.py b/lib/iris/tests/unit/_shapefiles/test_transform_geometry.py new file mode 100644 index 0000000000..ca5debb9a9 --- /dev/null +++ b/lib/iris/tests/unit/_shapefiles/test_transform_geometry.py @@ -0,0 +1,157 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for :func:`iris._shapefiles._transform_geometry`.""" + +import numpy as np +import pyproj +from pyproj import CRS +from pyproj import exceptions as pyproj_exceptions +import pytest +import shapely +from shapely.geometry import LineString, Point, Polygon + +import iris +from iris._shapefiles import _transform_geometry +from iris.tests import _shared_utils, stock + + +@pytest.fixture +def wgs84_crs(): + return CRS.from_epsg(4326) # WGS84 coordinate system + + +@pytest.mark.parametrize( + "input_geometry, wgs84_crs, input_cube_crs, output_expected_geometry", + [ + ( # Basic geometry in WGS84, no transformation needed + shapely.geometry.box(-10, 50, 2, 60), + "wgs84_crs", + stock.simple_pp().coord_system()._crs, + shapely.geometry.box(-10, 50, 2, 60), + ), + ( # Basic geometry in WGS84, transformed to OSGB + shapely.geometry.box(-10, 50, 2, 60), + "wgs84_crs", + iris.load_cube( + _shared_utils.get_data_path( + ("NetCDF", "transverse_mercator", "tmean_1910_1910.nc") + ) + ) + .coord_system() + .as_cartopy_projection(), + Polygon( # Known Good Output + [ + (686600.5247600826, 18834.835866007765), + (622998.2965261642, 1130592.5248690117), + (-45450.06302316813, 1150844.967615187), + (-172954.59474739246, 41898.60193228102), + (686600.5247600826, 18834.835866007765), + ] + ), + ), + ( # Basic geometry in WGS84, no transformation needed + LineString([(-10, 50), (2, 60)]), + "wgs84_crs", + stock.simple_pp().coord_system()._crs, + LineString([(-10, 50), (2, 60)]), + ), + ( # Basic geometry in WGS84, no transformation needed + Point((-10, 50)), + "wgs84_crs", + stock.simple_pp().coord_system()._crs, + Point((-10, 50)), + ), + ], + indirect=["wgs84_crs"], +) +def test_transform_geometry( + input_geometry, + wgs84_crs, + input_cube_crs, + output_expected_geometry, +): + # Check PROJ network settings and disable network access for the test + default_pyproj_network = pyproj.network.is_network_enabled() + if default_pyproj_network: + pyproj.network.set_network_enabled(active=False) + + out_geometry = _transform_geometry( + geometry=input_geometry, geometry_crs=wgs84_crs, cube_crs=input_cube_crs + ) + assert isinstance(out_geometry, shapely.geometry.base.BaseGeometry) + assert output_expected_geometry == out_geometry + + # Reset PROJ network settings to default state + if default_pyproj_network: + pyproj.network.set_network_enabled(active=True) + + +# Assert that an invalid inputs raise the expected errors +@pytest.mark.parametrize( + "input_geometry, input_geometry_crs, input_cube_crs, expected_error", + [ + ( # Basic geometry in WGS84, no transformation needed + "bad_input_geometry", + "wgs84_crs", + stock.simple_pp().coord_system()._crs, + AttributeError, + ), + ( # Basic geometry in WGS84, no transformation needed + shapely.geometry.box(-10, 50, 2, 60), + "bad_input_crs", + stock.simple_pp().coord_system()._crs, + pyproj_exceptions.CRSError, + ), + ( # Basic geometry in WGS84, no transformation needed + shapely.geometry.box(-10, 50, 2, 60), + wgs84_crs, + "bad_input_cube_crs", + pyproj_exceptions.CRSError, + ), + ], +) +def test_transform_geometry_invalid_input( + input_geometry, input_geometry_crs, input_cube_crs, expected_error +): + with pytest.raises(expected_error): + _transform_geometry(input_geometry, input_geometry_crs, input_cube_crs) + + +@pytest.mark.parametrize( + "input_geometry, wgs84_crs, input_cube_crs", + [ + ( # Basic geometry in WGS84, transformed to OSGB + shapely.geometry.box(np.inf, np.inf, np.inf, np.inf), + "wgs84_crs", + iris.load_cube( + _shared_utils.get_data_path( + ("NetCDF", "transverse_mercator", "tmean_1910_1910.nc") + ) + ) + .coord_system() + .as_cartopy_projection(), + ) + ], + indirect=["wgs84_crs"], +) +def test_transform_geometry_pyproj_network_edgecase( + input_geometry, wgs84_crs, input_cube_crs +): + # Simulate an edge case where PROJ network access is required to perform + # a transformation but fails due to network related issues. + # In this case pyproj will return a transformed geometry with Inf coordinates. + # Check that this rasises an error with an appropriate message. + # Note that in this case we supply an input geometry with Inf coordinates directly + # to simulate the failed transformation as we cannot reliably simulate network + # issues in a test environment. + + err_message = ( + "Error transforming geometry: geometry contains Inf coordinates. This is likely due to a failed CRS transformation." + "\nFailed transforms are often caused by network issues, often due to incorrectly configured SSL certificate paths." + ) + with pytest.raises(ValueError, match=err_message): + _transform_geometry( + geometry=input_geometry, geometry_crs=wgs84_crs, cube_crs=input_cube_crs + ) diff --git a/lib/iris/tests/unit/util/test_mask_cube_from_shape.py b/lib/iris/tests/unit/util/test_mask_cube_from_shape.py new file mode 100644 index 0000000000..93e7921509 --- /dev/null +++ b/lib/iris/tests/unit/util/test_mask_cube_from_shape.py @@ -0,0 +1,205 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Unit tests for :func:`iris.util.mask_cube_from_shapefile`.""" + +import numpy as np +from pyproj import CRS +import pytest +from shapely.geometry import box + +from iris.coord_systems import GeogCS +from iris.coords import DimCoord +from iris.cube import Cube +from iris.util import array_equal, is_masked, mask_cube_from_shape + + +@pytest.fixture +def square_polygon(): + # Create a roughly 3x3 square polygon + return box(2.4, 2.4, 6.4, 6.4) + + +@pytest.fixture +def mock_cube(): + """Create a mock 9x9 Iris cube for testing.""" + x_points = np.linspace(1, 9, 9) - 0.5 # Specify cube cell midpoints + y_points = np.linspace(1, 9, 9) - 0.5 + x_coord = DimCoord( + x_points, + standard_name="longitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + y_coord = DimCoord( + y_points, + standard_name="latitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + data = np.ones((len(y_points), len(x_points))) + cube = Cube(data, dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)]) + return cube + + +def test_mask_cube_from_shape_inplace(mock_cube, square_polygon): + masked_cube = mask_cube_from_shape( + cube=mock_cube, + shape=square_polygon, + shape_crs=CRS.from_epsg(4326), + in_place=True, + ) + assert masked_cube is None + assert is_masked(mock_cube.data) + + +def test_mask_cube_from_shape_not_inplace(mock_cube, square_polygon): + masked_cube = mask_cube_from_shape( + cube=mock_cube, + shape=square_polygon, + shape_crs=CRS.from_epsg(4326), + in_place=False, + ) + assert masked_cube is not None + assert is_masked(masked_cube.data) + # Original cube should remain unmasked + assert not is_masked(mock_cube.data) + + +@pytest.mark.parametrize( + "minimum_weight, expected_output", + [ + ( + 0.0, + np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ), + ), + ( + 0.5, + np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ), + ), + ( + 1.0, + np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ), + ), + ], +) +def test_basic_mask_cube_from_shape( + mock_cube, square_polygon, minimum_weight, expected_output +): + """Test the create_shape_mask function with different minimum weights.""" + expected_cube = mock_cube.copy( + data=np.ma.array( + expected_output, dtype=float, mask=np.logical_not(expected_output) + ) + ) + # Create a mask using the square polygon + mask = mask_cube_from_shape( + cube=mock_cube, + shape=square_polygon, + shape_crs=None, + minimum_weight=minimum_weight, + ) + + assert array_equal(mask.data, expected_cube.data) + + +def test_mask_cube_from_shape_invert(mock_cube, square_polygon): + """Test the create_shape_mask function with different minimum weights.""" + expected_output = np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ) + + expected_cube = mock_cube.copy( + data=np.ma.array( + np.logical_not(expected_output), dtype=float, mask=expected_output + ) + ) + # Create a mask using the square polygon + mask = mask_cube_from_shape( + cube=mock_cube, + shape=square_polygon, + shape_crs=None, + minimum_weight=0, + invert=True, + ) + + assert array_equal(mask.data, expected_cube.data) + + +def test_mask_cube_from_shape_all_touched(mock_cube, square_polygon): + """Test the create_shape_mask function with different minimum weights.""" + expected_output = np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ) + + expected_cube = mock_cube.copy( + data=np.ma.array( + expected_output, dtype=float, mask=np.logical_not(expected_output) + ) + ) + # Create a mask using the square polygon + mask = mask_cube_from_shape( + cube=mock_cube, + shape=square_polygon, + shape_crs=None, + all_touched=True, + ) + + assert array_equal(mask.data, expected_cube.data) diff --git a/lib/iris/tests/unit/util/test_mask_cube_from_shapefile.py b/lib/iris/tests/unit/util/test_mask_cube_from_shapefile.py index 0bd2afda21..a12e2b146a 100644 --- a/lib/iris/tests/unit/util/test_mask_cube_from_shapefile.py +++ b/lib/iris/tests/unit/util/test_mask_cube_from_shapefile.py @@ -6,116 +6,123 @@ import numpy as np import pytest -import shapely +from shapely.geometry import box -from iris.coord_systems import RotatedGeogCS +from iris.coord_systems import GeogCS from iris.coords import DimCoord -import iris.cube -from iris.util import mask_cube_from_shapefile -from iris.warnings import IrisUserWarning - - -class TestBasicCubeMasking: - """Unit tests for mask_cube_from_shapefile function.""" - - @pytest.fixture(autouse=True) - def _setup(self): - basic_data = np.array([[1, 2, 3], [4, 8, 12]]) - self.basic_cube = iris.cube.Cube(basic_data) - coord = DimCoord( - np.array([0, 1.0]), - standard_name="projection_y_coordinate", - bounds=[[0, 0.5], [0.5, 1]], - units="1", - ) - self.basic_cube.add_dim_coord(coord, 0) - coord = DimCoord( - np.array([0, 1.0, 1.5]), - standard_name="projection_x_coordinate", - bounds=[[0, 0.5], [0.5, 1], [1, 1.5]], - units="1", - ) - self.basic_cube.add_dim_coord(coord, 1) - - def test_basic_cube_intersect(self): - shape = shapely.geometry.box(0.6, 0.6, 0.9, 0.9) - masked_cube = mask_cube_from_shapefile(self.basic_cube, shape) - assert np.sum(masked_cube.data) == 8, ( - f"basic cube masking failed test - expected 8 got {np.sum(masked_cube.data)}" - ) - - def test_basic_cube_intersect_in_place(self): - shape = shapely.geometry.box(0.6, 0.6, 0.9, 0.9) - cube = self.basic_cube.copy() - mask_cube_from_shapefile(cube, shape, in_place=True) - assert np.sum(cube.data) == 8, ( - f"basic cube masking failed test - expected 8 got {np.sum(cube.data)}" - ) - - def test_basic_cube_intersect_low_weight(self): - shape = shapely.geometry.box(0.1, 0.6, 1, 1) - masked_cube = mask_cube_from_shapefile( - self.basic_cube, shape, minimum_weight=0.2 - ) - assert np.sum(masked_cube.data) == 12, ( - f"basic cube masking weighting failed test - expected 12 got {np.sum(masked_cube.data)}" +from iris.cube import Cube +from iris.util import array_equal, mask_cube_from_shapefile + + +@pytest.fixture +def square_polygon(): + # Create a roughly 3x3 square polygon + return box(2.4, 2.4, 6.4, 6.4) + + +@pytest.fixture +def mock_cube(): + """Create a mock 9x9 Iris cube for testing.""" + x_points = np.linspace(1, 9, 9) - 0.5 # Specify cube cell midpoints + y_points = np.linspace(1, 9, 9) - 0.5 + x_coord = DimCoord( + x_points, + standard_name="longitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + y_coord = DimCoord( + y_points, + standard_name="latitude", + units="degrees", + coord_system=GeogCS(6371229), + ) + data = np.ones((len(y_points), len(x_points))) + cube = Cube(data, dim_coords_and_dims=[(y_coord, 0), (x_coord, 1)]) + return cube + + +def test_mask_cube_from_shapefile_inplace( + mock_cube, +): + shape = box(0, 0, 10, 10) + masked_cube = mask_cube_from_shapefile(mock_cube, shape, in_place=True) + assert masked_cube is None + + +def test_mask_cube_from_shapefile_not_inplace(mock_cube): + shape = box(0, 0, 10, 10) + masked_cube = mask_cube_from_shapefile(mock_cube, shape, in_place=False) + assert masked_cube is not None + + +@pytest.mark.parametrize( + "minimum_weight, expected_output", + [ + ( + 0.0, + np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ), + ), + ( + 0.5, + np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ), + ), + ( + 1.0, + np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + ), + ), + ], +) +def test_basic_mask_cube_from_shape( + mock_cube, square_polygon, minimum_weight, expected_output +): + """Test the create_shape_mask function with different minimum weights.""" + expected_cube = mock_cube.copy( + data=np.ma.array( + expected_output, dtype=float, mask=np.logical_not(expected_output) ) - - def test_basic_cube_intersect_high_weight(self): - shape = shapely.geometry.box(0.1, 0.6, 1, 1) - masked_cube = mask_cube_from_shapefile( - self.basic_cube, shape, minimum_weight=0.7 - ) - assert np.sum(masked_cube.data) == 8, ( - f"basic cube masking weighting failed test- expected 8 got {np.sum(masked_cube.data)}" - ) - - def test_cube_list_error(self): - cubelist = iris.cube.CubeList([self.basic_cube]) - shape = shapely.geometry.box(1, 1, 2, 2) - with pytest.raises(TypeError, match="CubeList object rather than Cube"): - mask_cube_from_shapefile(cubelist, shape) - - def test_non_cube_error(self): - fake = None - shape = shapely.geometry.box(1, 1, 2, 2) - with pytest.raises(TypeError, match="Received non-Cube object"): - mask_cube_from_shapefile(fake, shape) - - def test_line_shape_warning(self): - shape = shapely.geometry.LineString([(0, 0.75), (2, 0.75)]) - with pytest.warns(IrisUserWarning, match="invalid type"): - masked_cube = mask_cube_from_shapefile( - self.basic_cube, shape, minimum_weight=0.1 - ) - assert np.sum(masked_cube.data) == 24, ( - f"basic cube masking against line failed test - expected 24 got {np.sum(masked_cube.data)}" - ) - - def test_cube_coord_mismatch_warning(self): - shape = shapely.geometry.box(0.6, 0.6, 0.9, 0.9) - cube = self.basic_cube - cube.coord("projection_x_coordinate").points = [180, 360, 540] - cube.coord("projection_x_coordinate").coord_system = RotatedGeogCS(30, 30) - with pytest.warns(IrisUserWarning, match="masking"): - mask_cube_from_shapefile( - cube, - shape, - ) - - def test_missing_xy_coord(self): - shape = shapely.geometry.box(0.6, 0.6, 0.9, 0.9) - cube = self.basic_cube - cube.remove_coord("projection_x_coordinate") - with pytest.raises(ValueError, match="1d xy coordinates"): - mask_cube_from_shapefile(cube, shape) - - def test_shape_not_shape(self): - shape = [5, 6, 7, 8] # random array - with pytest.raises(TypeError, match="valid Shapely"): - mask_cube_from_shapefile(self.basic_cube, shape) - - def test_shape_invalid(self): - shape = shapely.box(0, 1, 1, 1) - with pytest.raises(TypeError, match="valid Shapely"): - mask_cube_from_shapefile(self.basic_cube, shape) + ) + # Create a mask using the square polygon + mask = mask_cube_from_shapefile( + cube=mock_cube, + shape=square_polygon, + minimum_weight=minimum_weight, + ) + + assert array_equal(mask.data, expected_cube.data) diff --git a/lib/iris/util.py b/lib/iris/util.py index b3ce7941c5..582f0e0478 100644 --- a/lib/iris/util.py +++ b/lib/iris/util.py @@ -29,7 +29,6 @@ from iris._deprecation import warn_deprecated from iris._lazy_data import is_lazy_data, is_lazy_masked_data -from iris._shapefiles import create_shapefile_mask from iris.common import SERVICES from iris.common.lenient import _lenient_client from iris.coord_systems import GeogCS @@ -37,7 +36,10 @@ import iris.warnings if TYPE_CHECKING: + import cartopy from numpy.typing import ArrayLike + import pyproj + import shapely from iris.cube import Cube, CubeList @@ -2249,26 +2251,27 @@ def _strip_metadata_from_dims(cube, dims): return reduced_cube -def mask_cube_from_shapefile(cube, shape, minimum_weight=0.0, in_place=False): - """Take a shape object and masks all points not touching it in a cube. - - Finds the overlap between the `shape` and the `cube` in 2D xy space and - masks out any cells with less % overlap with shape than set. - Default behaviour is to count any overlap between shape and cell as valid +def mask_cube_from_shapefile( + cube: iris.cube.Cube, + shape: shapely.Geometry, + minimum_weight: float = 0.0, + in_place: bool = False, +) -> iris.cube.Cube | None: + """Mask all points in a cube that do not intersect a shapefile object. Parameters ---------- cube : :class:`~iris.cube.Cube` object - The `Cube` object to masked. Must be singular, rather than a `CubeList`. + The ``Cube`` object to masked. Must be singular, rather than a ``CubeList``. shape : Shapely.Geometry object - A single `shape` of the area to remain unmasked on the `cube`. + A single `shape` of the area to remain unmasked on the ``cube``. If it a line object of some kind then minimum_weight will be ignored, because you cannot compare the area of a 1D line and 2D Cell. minimum_weight : float , default=0.0 A number between 0-1 describing what % of a cube cell area must the shape overlap to include it. in_place : bool, default=False - Whether to mask the `cube` in-place or return a newly masked `cube`. + Whether to mask the ``cube`` in-place or return a newly masked ``cube``. Defaults to False. Returns @@ -2280,32 +2283,203 @@ def mask_cube_from_shapefile(cube, shape, minimum_weight=0.0, in_place=False): -------- :func:`~iris.util.mask_cube` Mask any cells in the cube’s data array. + :func:`~iris.util.mask_cube_from_shape` + Mask any cells in the cube’s data array. Notes ----- - This function allows masking a cube with any cartopy projection by a shape object, - most commonly from Natural Earth Shapefiles via cartopy. - To mask a cube from a shapefile, both must first be on the same coordinate system. - Shapefiles are mostly on a lat/lon grid with a projection very similar to GeogCS - The shapefile is projected to the coord system of the cube using cartopy, then each cell - is compared to the shapefile to determine overlap and populate a true/false array - This array is then used to mask the cube using the `iris.util.mask_cube` function - This uses numpy arithmetic logic for broadcasting, so you may encounter unexpected - results if your cube has other dimensions the same length as the x/y dimensions + .. deprecated:: 3.14 + + :func:`mask_cube_from_shapefile` function is scheduled for removal in a + future release, being replaced by :func:`iris.util.mask_cube_from_shape`, + which offers richer shape handling. + + Warnings + -------- + This function requires additional dependencies: ``rasterio`` and ``affine``. + """ + message = ( + "iris.util.mask_cube_from_shapefile has been deprecated, and will be removed in a " + "future release. Please use iris.util.mask_cube_from_shape instead." + ) + warn_deprecated(message) + + # Make depreciated function defaults: + # https://github.com/SciTools/iris/blob/fa6d61dd5f358c9e6b585a132cc213acebf95b70/lib/iris/_shapefiles.py#L142C18-L144C6 + from iris.analysis.cartography import DEFAULT_SPHERICAL_EARTH_RADIUS + + shape_crs = iris.coord_systems.GeogCS( + DEFAULT_SPHERICAL_EARTH_RADIUS + ).as_cartopy_projection() + # Call the new function `mask_cube_from_shape`with the same parameters. + return mask_cube_from_shape( + cube, + shape, + shape_crs=shape_crs, + in_place=in_place, + minimum_weight=minimum_weight, + ) + + +def mask_cube_from_shape( + cube: iris.cube.Cube, + shape: shapely.Geometry, + shape_crs: cartopy.crs | pyproj.CRS = None, + in_place: bool = False, + minimum_weight: float = 0.0, + all_touched: bool | None = None, + invert: bool = False, +) -> iris.cube.Cube | None: + """Mask all points in a cube that do not intersect a shape object. + + Mask a :class:`~iris.cube.Cube` with any shape object, (e.g. Natural Earth Shapefiles + via ``cartopy``). Finds the overlap between the ``shape`` and the :class:`~iris.cube.Cube` + and masks out any cells that *do not* intersect the shape. + + Shapes can be Polygons, Lines or Points, or their multi-part equivalents. + + By default, all cells touched by geometries are kept (equivalent to ``minimum_weight=0``). + This behaviour can be changed by increasing the ``minimum_weight`` keyword argument or + setting ``all_touched=False``, then only the only cells whose *centre* is within the + polygon or that are selected by Bresenham’s line algorithm (for line type shapes) are kept. + + For points, ``minimum_weight`` is ignored, and the cell that intersects the point + is kept. + + Parameters + ---------- + cube : :class:`~iris.cube.Cube` object + The ``Cube`` object to masked. Must be singular, rather than a ``CubeList``. + shape : shapely.Geometry object + A single ``shape`` of the area to remain unmasked on the ``cube``. + If it a line object of some kind then minimum_weight will be ignored, + because you cannot compare the area of a 1D line and 2D Cell. + shape_crs : cartopy.crs.CRS, default=None + The coordinate reference system of the shape object. + in_place : bool, default=False + Whether to mask the ``cube`` in-place or return a newly masked ``cube``. + Defaults to ``False``. + minimum_weight : float, default=0.0 + A number between 0-1 describing what percentage of a cube cell area must the shape overlap to be masked. + Only applied to polygon shapes. If the shape is a line or point then this is ignored. + all_touched : bool, default=None + If ``True``, all cells touched by the shape are kept. If ``False``, only cells whose + center is within the polygon or that are selected by Bresenham’s line algorithm + (for line type shape) are kept. Note that ``minimum_weight`` and ``all_touched`` are + mutually exclusive options: an error will be raised if a ``minimum_weight`` > 0 *and* + ``all_touched`` is set to ``True``. This is because ``all_touched=True`` is equivalent + to ``minimum_weight=0``. + invert : bool, default=False + If ``True``, the mask is inverted, meaning that cells that intersect the shape are masked out + and cells that do not intersect the shape are kept. If ``False``, the mask is applied normally, + meaning that cells that intersect the shape are kept and cells that do not intersect the shape + are masked out. + + Returns + ------- + iris.Cube + A masked version of the input cube, if ``in_place`` is ``False``. + + See Also + -------- + :func:`~iris.util.mask_cube` + Mask any cells in the cube’s data array. Examples -------- + >>> import numpy as np >>> import shapely - >>> from iris.util import mask_cube_from_shapefile + >>> from pyproj import CRS + >>> from iris.util import mask_cube_from_shape + + Extract a rectangular region covering the UK from a stereographic projection cube: + + >>> cube = iris.load_cube(iris.sample_data_path("toa_brightness_stereographic.nc")) + >>> shape = shapely.geometry.box(-10, 50, 2, 60) # box around the UK + >>> wgs84 = CRS.from_epsg(4326) # WGS84 coordinate system + >>> masked_cube = mask_cube_from_shape(cube, shape, wgs84) + + Note that there is no change in the dimensions of the masked cube, only in the mask + applied to data values: + + >>> print(cube.summary(shorten=True)) + toa_brightness_temperature / (K) (projection_y_coordinate: 160; projection_x_coordinate: 256) + >>> print(f"Masked cells: {np.sum(cube.data.mask)} of {np.multiply(*cube.shape)}") + Masked cells: 3152 of 40960 + >>> print(masked_cube.summary(shorten=True)) + toa_brightness_temperature / (K) (projection_y_coordinate: 160; projection_x_coordinate: 256) + >>> print(f"Masked cells: {sum(sum(masked_cube.data.mask))} of {np.multiply(*masked_cube.shape)}") + Masked cells: 40062 of 40960 + + Extract a trajectory by using a line shapefile: + + >>> from shapely import LineString + >>> line = LineString([(-45, 40), (-28, 53), (-2, 55), (19, 45)]) + >>> masked_cube = mask_cube_from_shape(cube, line, wgs84) + + Standard shapely manipulations can be applied. For example, to extract a trajectory + with a 1 degree buffer around it: + + >>> buffer = line.buffer(1) + >>> masked_cube = mask_cube_from_shape(cube, buffer, wgs84) + + You can load more complex shapes from other libraries. For example, to extract the + Canadian provience of Ontario from a cube: + + >>> import cartopy.io.shapereader as shpreader + >>> admin1 = shpreader.natural_earth(resolution='110m', + ... category='cultural', + ... name='admin_1_states_provinces_lakes') + >>> admin1shp = shpreader.Reader(admin1).geometries() + >>> cube = iris.load_cube(iris.sample_data_path("E1_north_america.nc")) >>> shape = shapely.geometry.box(-100,30, -80,40) # box between 30N-40N 100W-80W - >>> masked_cube = mask_cube_from_shapefile(cube, shape) + >>> wgs84 = CRS.from_epsg(4326) + >>> masked_cube = mask_cube_from_shape(cube, shape, wgs84) + + Notes + ----- + Iris does not handle the shape loading so it is agnostic to the source type of the shape. + The shape can be loaded from an Esri shapefile, created using the ``shapely`` library, or + any other source that can be interpreted as a ``shapely.Geometry`` object, such as shapes + encoded in a geoJSON or KML file. + + Warnings + -------- + For best masking results, both the cube _and_ masking geometry should have a + coordinate reference system (CRS) defined. Note that CRS of the masking geometry + must be provided explicitly to this function (via ``shape_crs``), whereas the + cube CRS is read from the cube itself. The cube **must** have a coord_system defined. + + Masking results will be most consistent when the cube and masking geometry have the same CRS. + + If a CRS is _not_ provided for the the masking geometry, the CRS of the cube is assumed. + + This function requires additional dependencies: ``rasterio`` and ``affine``. + + Because shape vectors are inherently Cartesian in nature, they contain no inherent + understanding of the spherical geometry underpinning geographic coordinate systems. + For this reason, **shapefiles or shape vectors that cross the antimeridian or poles + are not supported by this function** to avoid unexpected masking behaviour. For shapes + that do cross these boundaries, this function expects the user to undertake fixes upstream + of Iris, using tools like `GDAL `_ or + `antimeridian `_ to fix shape wrapping. """ - shapefile_mask = create_shapefile_mask(shape, cube, minimum_weight) + from iris._shapefiles import create_shape_mask + + shapefile_mask = create_shape_mask( + geometry=shape, + geometry_crs=shape_crs, + cube=cube, + minimum_weight=minimum_weight, + all_touched=all_touched, + invert=invert, + ) masked_cube = mask_cube(cube, shapefile_mask, in_place=in_place) if not in_place: return masked_cube + return None def equalise_cubes( diff --git a/requirements/locks/py311-linux-64.lock b/requirements/locks/py311-linux-64.lock index fd40f3987e..1cba26996b 100644 --- a/requirements/locks/py311-linux-64.lock +++ b/requirements/locks/py311-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 2ab37bb9a0b7ddd216650e61026560917ec230278b2d71d0671ffed30830e199 +# input_hash: 6c6ab43a5749a04c62e2b0efd6ca7915b9147b007730596ee540d452b4c9e4fb @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 @@ -14,7 +14,6 @@ https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222 https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda#a480ee3eb9c95364a229673a28384899 https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d @@ -27,11 +26,12 @@ https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda#7913 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda#f7f0d6cc2dc986d42ac2689ec88192be https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda#f9f81ea472684d75b9dd8d0b328cf655 +https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f5dbc9ac808e31c00650f7be1db93f https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3 https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda#1d29d2e33fe59954af82ef54a8af3fe1 https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 -https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda#35f29eec58405aaf55e01cb470d8c26a https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-hcd61629_7.conda#f116940d825ffc9104400f0d7f1a4551 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88 @@ -45,6 +45,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.cond https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1 https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 +https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda#45161d96307e3a447cc3eb5896cf6f8c https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e @@ -54,10 +55,11 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.c https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2024.1-hb9d3cd8_1.conda#7c21106b851ec72c037b162c216d8f05 https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.3-hb47aa4a_0.conda#607e13a8caac17f9a664bcab5302ce06 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a -https://conda.anaconda.org/conda-forge/linux-64/cli11-2.5.0-h3f2d84a_0.conda#4a7c3dc241bb268078116434b4d2ad12 +https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda#ddf9fed4661bace13f33f08fe38a5f45 https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda#0c2f855a88fab6afa92a7aa41217dc8e https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.0-h480dda7_0.conda#5dc479effdabf54a0ff240d565287495 +https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda#2cd94587f3a401ae05e03a6caf09539d https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda#7bdc5e2cc11cb0a0f795bdad9732b0f2 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 @@ -85,7 +87,7 @@ https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda#b1 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_0.conda#3d8da0248bdae970b4ade636a104b7f5 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 -https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda#0f2ca7906bf166247d1d760c3422cb8a +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda#035da2e4f5770f036ff704fa17aace24 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 @@ -95,74 +97,97 @@ https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda#00e https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4 https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1aa0949_3.conda#72cc69c30de0b6d39c7f97f501fdbb1c https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda#8e7251989bca326a28f4a5ffbd74557a -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.0-h1fed272_0.conda#b8e4c93f4ab70c3b6f6499299627dbdc +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.0-h32235b2_1.conda#a400fd9bad095c7cdf74661552ef802f https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda#b499ce4b026493a13774bcf0f4c33849 https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_2.conda#dfc5aae7b043d9f56ba99514d5e60625 +https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h96cd706_19.conda#212a9378a85ad020b8dc94853fdbeb6c https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda#553281a034e9cf8693c9df49f6c78ea1 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h8261f1e_0.conda#72b531694ebe4e8aa6f5745d1015c1b4 -https://conda.anaconda.org/conda-forge/linux-64/python-3.11.13-h9e4cc4f_0_cpython.conda#8c399445b6dc73eab839659e6c7b5ad1 +https://conda.anaconda.org/conda-forge/linux-64/minizip-4.0.10-h05a5f5f_0.conda#da01bb40572e689bd1535a5cee6b1d68 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.50.4-hbc0de68_0.conda#8376bd3854542be0c8c7cd07525d31c6 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1 +https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 +https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda#f730d54ba9cd543666d7220c9f7ed563 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda#ecb5d11305b8ba1801543002e69d2f2f +https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda#c379d67c686fb83475c1a6ed41cc41ff +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.0-hf516916_1.conda#25d53803877008c7c2a2c9b44cb637b6 +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-37_h4a7cf45_openblas.conda#8bc098f29d8a7e3517bac5b25aab39b1 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.16.0-h4e3cde8_0.conda#a401aa9329350320c7d3809a7a5a1640 +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda#f4084e4e6577797150f9b04a4560ceb0 +https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c +https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1022.conda#00f0f4a9d2eb174015931b1a234d61ca +https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda#e7733bc6785ec009e47a224a71917e84 +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/python-3.11.14-hd63d673_2_cpython.conda#c4202a55b4486314fbb8c11bc43a29a0 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 +https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda#9dda9667feba914e0e80b95b82f7402b +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda#71ae752a748962161b4740eaff510258 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829 https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda#18fd895e0e775622906cdabfc3cf0fb4 https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda#1fd9696649f65fd6611fcdb4ffec738a -https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda#f730d54ba9cd543666d7220c9f7ed563 https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311h1ddb823_4.conda#7138a06a7b0d11a23cfae323e6010a08 https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_1.conda#57df494053e17dce2ac3a0b33e1b2a2e -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda#7e7d5ef1b9ed630e4a1c358d6bc62284 +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a https://conda.anaconda.org/conda-forge/noarch/click-8.3.0-pyh707e725_0.conda#e76c4ba9e1837847679421b8d549b784 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.1-pyhd8ed1ab_0.conda#364ba6c9fb03886ac979b482f39ebb92 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 https://conda.anaconda.org/conda-forge/noarch/colorcet-3.1.0-pyhd8ed1ab_1.conda#91d7152c744dc0f18ef8beb3cbc9980a https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 -https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.4-py311h91b4c63_2.conda#6ce39de98d9e0adb4e8b00a96b0903fd -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.6-py311h0daaf2c_0.conda#93e9700f9bc5fb4d69d5dfad5a8c62e6 https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda#003b8ba0a94e2f1e117d0bd46aebc901 https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda#24c1ca34138ee57de72a943237cde4cc https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda#66b8b26023b8efdf8fcb23bac4b6325d +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda#4afc585cd97ba8a23809406cd8a9eda8 https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py311h52bc045_0.conda#d9be554be03e3f2012655012314167d6 https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.9.0-pyhd8ed1ab_0.conda#76f492bd8ba8a0fb80ffe16fc1a75b3b -https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.3-h2b0a6b4_0.conda#8a6c305c6c6b169f839eefb951608e29 -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.0-hf516916_0.conda#1a8e49615381c381659de1bc6a3bf9ec -https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda#c74d83614aec66227ae5199d98852aaf https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac -https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7 +https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 -https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda#9614359868482abba1bd15ce465e3c42 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.5.2-pyhd8ed1ab_0.conda#895f6625dd8a246fece9279fcc12c1de https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py311h724c32c_1.conda#92720706b174926bc7238cc24f3b5956 -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-36_h4a7cf45_openblas.conda#2a6122504dc8ea139337046d34a110cb -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.14.1-h332b0f4_0.conda#45f6713cb00f124af300342512219182 -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda#f4084e4e6577797150f9b04a4560ceb0 -https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c -https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.0-ha9997c6_1.conda#b24dd2bd61cd8e4f8a13ee2a945a723c +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-37_h0358290_openblas.conda#3794858d4d6910a7fc3c181519e0b77a +https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a +https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_2.conda#27ac5ae872a21375d980bd4a6f99edf3 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-37_h47877c9_openblas.conda#8305e6a5ed432ad3e5a609e8024dbc17 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda#e512be7dc1f84966d50959e900ca121f https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda#49647ac1de4d1e4b49124aedf3934e02 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_0.conda#0954f1a6a26df4a510b54f73b2a0345c https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py311hdf67eae_0.conda#a8c86d338939ab629a921edcd2610cb2 https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py311h2dc5d0c_0.conda#f368028b53e029409e2964707e03dcaf https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py311h98278a2_3.conda#76839149314cc1d07f270174801576b0 https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2 https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda#438e75abf4d8c9c1d9e483b6c3f36282 https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py311h2dc5d0c_0.conda#c75eb8c91d69fe0385fce584f3ce193a -https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py311h49ec1c0_0.conda#eaf20d52642262d2987c3cdc7423649e +https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.1-py311h49ec1c0_0.conda#fdf6ddf7278ca192158d25760f2c110a https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda#6c8979be6d7a17692793114fa26916e8 @@ -181,123 +206,117 @@ https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#1 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda#fa839b5ff59e192f411ccc7dae6588bb https://conda.anaconda.org/conda-forge/noarch/tblib-3.1.0-pyhd8ed1ab_0.conda#a15c62b8a306b8978f094f76da2f903f https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53 -https://conda.anaconda.org/conda-forge/noarch/toolz-1.0.0-pyhd8ed1ab_1.conda#40d0ed782a8aaa16ef248e68c06c168d +https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py311h49ec1c0_1.conda#18a98f4444036100d78b230c94453ff4 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py311h49ec1c0_1.conda#3457bd5c93b085bec51cdab58fbd1882 https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda#71ae752a748962161b4740eaff510258 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda#5e2eb9bf77394fc2e5918beefec9f9ab +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda#74ac5069774cdbc53910ec4d631a3999 https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda#421a865222cd0c9d83ff08bc78bf3a61 https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4 -https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h5b438cf_0.conda#6cb6c4d57d12dfa0ecdd19dbe758ffc9 +https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.conda#3912e4373de46adafd8f1e97e4bd166b https://conda.anaconda.org/conda-forge/noarch/click-default-group-1.2.4-pyhd8ed1ab_1.conda#7cd83dd6831b61ad9624a694e4afd7dc -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.10.7-py311h3778330_0.conda#53fdad3b032eee40cf74ac0de87e4518 -https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.0.1-py311h9ecbd09_0.conda#69a0a85acdcc5e6d0f1cc915c067ad4c +https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 +https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.11.0-py311h3778330_0.conda#deeadabf222aa80df52056aac13f971c +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py311h49ec1c0_1.conda#907579fcaf43b90c8c198ae8d43320ef https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py311h3778330_0.conda#91f834f85ac92978cfc3c1c178573e85 -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda#4afc585cd97ba8a23809406cd8a9eda8 +https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda#ff1966654a6cd1cf06a6e44c13e60b8a https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda#c74d83614aec66227ae5199d98852aaf https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-36_h0358290_openblas.conda#13a3fe5f9812ac8c5710ef8c03105121 -https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a -https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_2.conda#27ac5ae872a21375d980bd4a6f99edf3 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-36_h47877c9_openblas.conda#55daaac7ecf8ebd169cdbe34dc79549e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.0-h26afc86_1.conda#8337b675e0cad517fbcb3daf7588087a +https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.2-gpl_h7be2006_100.conda#9d0eaa26e3c5d7af747b3ddee928327b +https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_2.conda#53e7cbb2beb03d69a478631e23e340e9 +https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda#c01021ae525a76fe62720c7346212d74 +https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.4-hf7376ad_0.conda#da21f286c4466912cc579911068034b6 +https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda#3ccff1066c05a1e6c221356eecc40581 +https://conda.anaconda.org/conda-forge/linux-64/libpq-18.0-h3675c94_0.conda#064887eafa473cbfae9ee8bedd3b7432 +https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda#372a62464d47d9e966b630ffae3abe73 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.12.2-hca5e8e5_0.conda#3c3e5ccbb2d96ac75e1b8b028586db5c +https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.1-h26afc86_0.conda#1b92b7d1b901bd832f8279ef18cac1f4 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda#7ba3f09fceae6a120d664217e58fe686 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.4-py311h2e04523_0.conda#d84afde5a6f028204f24180ff87cf429 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py311h98278a2_3.conda#76839149314cc1d07f270174801576b0 https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5 -https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda#438e75abf4d8c9c1d9e483b6c3f36282 +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py311h4e6619b_2.conda#8092837339ae2e3697980df6686f996e https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.3-pyhd8ed1ab_0.conda#bf0dc4c8a32e91290e3fae5403798c63 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda#5e2eb9bf77394fc2e5918beefec9f9ab -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f https://conda.anaconda.org/conda-forge/linux-64/yarl-1.20.1-py311h2dc5d0c_0.conda#18c288aa6aae90e2fd8d1cf01d655e4f https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.0-py311h1c70031_0.conda#65e31314752a1d297a4178937643427f https://conda.anaconda.org/conda-forge/noarch/asv_runner-0.2.1-pyhd8ed1ab_0.conda#fdcbeb072c80c805a2ededaa5f91cd79 -https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2 -https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.9.1-pyhcf101f3_0.conda#c49de33395d775a92ea90e0cb34c3577 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee -https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_2.conda#d10d9393680734a8febc4b362a4c94f2 -https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_2.conda#53e7cbb2beb03d69a478631e23e340e9 -https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda#c01021ae525a76fe62720c7346212d74 -https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.3-hf7376ad_0.conda#5728d01354f55d4f7e6f5e3073919a32 -https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda#3ccff1066c05a1e6c221356eecc40581 -https://conda.anaconda.org/conda-forge/linux-64/libpq-18.0-h3675c94_0.conda#064887eafa473cbfae9ee8bedd3b7432 -https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda#372a62464d47d9e966b630ffae3abe73 -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.12.0-hca5e8e5_0.conda#38d9cae31d66c1b73ab34e786bb3afe5 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.3-py311h2e04523_0.conda#3b0d0a2241770397d3146fdcab3b49f8 -https://conda.anaconda.org/conda-forge/noarch/pbr-7.0.1-pyhd8ed1ab_0.conda#0d118f1c98cec5ebd0cdfd066e49a446 -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py311h4e6619b_2.conda#8092837339ae2e3697980df6686f996e -https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda#1f987505580cb972cf28dc5f74a0f81b -https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-9.2.0-pyhd8ed1ab_0.conda#ad8f901272d56cfb6bf22bb89e9be59b -https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311hd18a35c_5.conda#4e8447ca8558a203ec0577b4730073f3 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f -https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py311haee01d2_0.conda#0fd242142b0691eb9311dc32c1d4ab76 https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2#8cb2fc4cd6cc63f1369cfa318f581cc3 +https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.4-py311h0372a8f_2.conda#c85f7b94f099fddb08358e0997dded95 https://conda.anaconda.org/conda-forge/noarch/colorspacious-1.1.2-pyhecae5ae_1.conda#04151bb8e351c6209caad045e4b1f4bd https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py311hdf67eae_2.conda#bb6a0f88cf345f7e7a143d349dae6d9f -https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda#25f954b7dae6dd7b0dc004dab74f1ce9 -https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_2.conda#bb0230917e2473c77d615104dbe8a49d -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.2-default_h99862b1_3.conda#7076df5c94a595a25d9d55361098ac12 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.2-default_h746c552_3.conda#c26d396fc2d5c800e84e4e81a687bc6a +https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.9.1-pyhcf101f3_0.conda#c49de33395d775a92ea90e0cb34c3577 +https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_2.conda#d10d9393680734a8febc4b362a4c94f2 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.4-default_h99862b1_0.conda#5eb56f7a1892309ba09d1024068714cc +https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.4-default_h746c552_0.conda#bb842304ab95206d6f335861aa4270d8 https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_2.conda#b513eb83b3137eca1192c34bf4f013a7 https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h6f5c62b_11.conda#68fc66282364981589ef36868b1a7c78 +https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hcb59c51_118.conda#ce07b32efdf860ed996fdedcff3ea96e https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.3.1-py311h0372a8f_2.conda#487756753716efba090d06c71fb73eb6 https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.2-nompi_h90de81b_102.conda#f4c67a50ac3008a578530e7fc32f3d98 https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py311hed34c8f_1.conda#72e3452bf0ff08132e86de0272f2fbb0 +https://conda.anaconda.org/conda-forge/noarch/pbr-7.0.1-pyhd8ed1ab_0.conda#0d118f1c98cec5ebd0cdfd066e49a446 https://conda.anaconda.org/conda-forge/linux-64/pykdtree-1.4.3-py311h0372a8f_1.conda#0f984cff0ca059c3254ad2bf83b9b371 -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda#6891acad5e136cb62a8c2ed2679d6528 -https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.15.1-pyhd8ed1ab_0.conda#0511afbe860b1a653125d77c719ece53 -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 -https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.4.0-py311h49ec1c0_1.conda#dc220b268052bf1dcb3040c96f2e454a +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda#1f987505580cb972cf28dc5f74a0f81b https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.9.0-py311h0372a8f_1.conda#31838811238427e85f86a89fea0421dc https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.2-py311h1e13796_0.conda#124834cd571d0174ad1c22701ab63199 +https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-9.2.2-pyhd8ed1ab_0.conda#e2e4d7094d0580ccd62e2a41947444f3 https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py311hdb2cc66_0.conda#7c0a99c77c423ff83b327e4eff3f82f5 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860 +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.2.0-hb60516a_1.conda#29ed2be4b47b5aa1b07689e12407fbfd -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a -https://conda.anaconda.org/conda-forge/noarch/wslink-2.4.0-pyhd8ed1ab_0.conda#a7c17eeb817efebaf59a48fdeab284a4 +https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311hd18a35c_5.conda#4e8447ca8558a203ec0577b4730073f3 +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py311haee01d2_0.conda#0fd242142b0691eb9311dc32c1d4ab76 https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2#6b889f174df1e0f816276ae69281af4d https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.3.0-py311h0372a8f_1.conda#ef8650a6059a907122d59a740207f50e -https://conda.anaconda.org/conda-forge/noarch/distributed-2025.9.1-pyhcf101f3_0.conda#f140b63da44c9a3fc7ae75cb9cc53c47 https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda#057083b06ccf1c2778344b6dabace38b https://conda.anaconda.org/conda-forge/linux-64/esmf-8.9.0-nompi_h8d4c64c_3.conda#9102d5f5b8d01fcfa81efec370815772 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.1.0-h15599e2_0.conda#7704b1edaa8316b8792424f254c1f586 +https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda#25f954b7dae6dd7b0dc004dab74f1ce9 https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.2-pyhd8ed1ab_0.conda#efbc812363856906a80344b496389d2e -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.6-py311h0f3be63_1.conda#65ddf155ea43aa3bb366cb03ddf3436a +https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_2.conda#bb0230917e2473c77d615104dbe8a49d +https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.10.3-hec83942_22.conda#e9393caea4437bf4958f7df06847329c +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.7-py311h0f3be63_0.conda#b4ec935aa9298e5498613ea66b3c3a98 https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.2-nompi_py311h6623971_104.conda#fc6c31f2b2bd3fce50d5496ce30d04ff -https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_0.conda#bc6c44af2a9e6067dd7e949ef10cdfba -https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda#6891acad5e136cb62a8c2ed2679d6528 +https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.15.1-pyhd8ed1ab_0.conda#0511afbe860b1a653125d77c719ece53 +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.4.0-py311h49ec1c0_1.conda#dc220b268052bf1dcb3040c96f2e454a +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a +https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda#8fa415e696acd9af59ce0a4425fd1b38 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.25.0-py311hed34c8f_1.conda#17c710c32010688086b28c088a8be579 https://conda.anaconda.org/conda-forge/noarch/cmocean-4.0.3-pyhd8ed1ab_1.conda#dd71e4ec2fbffe38c0359976505f816e +https://conda.anaconda.org/conda-forge/noarch/distributed-2025.9.1-pyhcf101f3_0.conda#f140b63da44c9a3fc7ae75cb9cc53c47 https://conda.anaconda.org/conda-forge/noarch/esmpy-8.9.0-pyhc455866_0.conda#37e9756efac1b155daface77417f0463 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_1.conda#9a2be7d0089f5934b550933ca0d9fe85 https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda#79f71230c069a287efe3a8614069ddf1 -https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_3.conda#d2bbbd293097e664ffb01fc4cdaf5729 +https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_0.conda#bc6c44af2a9e6067dd7e949ef10cdfba https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_0.conda#cc0bffcaf6410aba9c6581dfdc18012d +https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.4.3-py311hf423b78_3.conda#1bbad49e5cfdf862d197e1f0df306b91 +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4 https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.43-h021d004_4.conda#a891e341072432fafb853b3762957cbf https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda#91e6d4d684e237fba31b9815c4b40edf -https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py311h0db28e7_6.conda#535f0648bce83592b3f9887797e97e37 +https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_3.conda#d2bbbd293097e664ffb01fc4cdaf5729 +https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.2-py311h0db28e7_0.conda#793cf932c8a65e4bbb9459fc6ea86118 https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda#b3f0179590f3c0637b7eb5309898f79e https://conda.anaconda.org/conda-forge/noarch/pyvista-0.46.3-pyhd8ed1ab_1.conda#ff39551518c75398a7a84de513024856 https://conda.anaconda.org/conda-forge/noarch/geovista-0.5.3-pyhd8ed1ab_1.conda#64348d05eedb1b1b5676f63101d004f2 @@ -312,3 +331,4 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda#00534ebcc0375929b45c3039b5ba7636 https://conda.anaconda.org/conda-forge/noarch/sphinx-8.1.3-pyhd8ed1ab_1.conda#1a3281a0dc355c02b5506d87db2d78ac https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda#3bc61f7161d28137797e038263c04c54 + diff --git a/requirements/locks/py312-linux-64.lock b/requirements/locks/py312-linux-64.lock index a302f84c4f..a9f5416e68 100644 --- a/requirements/locks/py312-linux-64.lock +++ b/requirements/locks/py312-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: bf2a25a17b2c07e25119f33dada708044990c97795a068075294e902de374798 +# input_hash: 5d6c9b7255123c2aed9c2c9ce89ee4adfa92dbc30496f56df48d4a0a989ddff5 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 @@ -14,7 +14,6 @@ https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222 https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda#a480ee3eb9c95364a229673a28384899 https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d @@ -27,11 +26,12 @@ https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda#7913 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda#f7f0d6cc2dc986d42ac2689ec88192be https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda#f9f81ea472684d75b9dd8d0b328cf655 +https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f5dbc9ac808e31c00650f7be1db93f https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3 https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda#1d29d2e33fe59954af82ef54a8af3fe1 https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 -https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda#35f29eec58405aaf55e01cb470d8c26a https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-hcd61629_7.conda#f116940d825ffc9104400f0d7f1a4551 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88 @@ -45,6 +45,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.cond https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1 https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 +https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda#45161d96307e3a447cc3eb5896cf6f8c https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e @@ -54,10 +55,11 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.c https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2024.1-hb9d3cd8_1.conda#7c21106b851ec72c037b162c216d8f05 https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.3-hb47aa4a_0.conda#607e13a8caac17f9a664bcab5302ce06 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a -https://conda.anaconda.org/conda-forge/linux-64/cli11-2.5.0-h3f2d84a_0.conda#4a7c3dc241bb268078116434b4d2ad12 +https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda#ddf9fed4661bace13f33f08fe38a5f45 https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda#0c2f855a88fab6afa92a7aa41217dc8e https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.0-h480dda7_0.conda#5dc479effdabf54a0ff240d565287495 +https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda#2cd94587f3a401ae05e03a6caf09539d https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda#7bdc5e2cc11cb0a0f795bdad9732b0f2 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 @@ -85,7 +87,7 @@ https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda#b1 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_0.conda#3d8da0248bdae970b4ade636a104b7f5 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 -https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda#0f2ca7906bf166247d1d760c3422cb8a +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda#035da2e4f5770f036ff704fa17aace24 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 @@ -95,74 +97,97 @@ https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda#00e https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4 https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1aa0949_3.conda#72cc69c30de0b6d39c7f97f501fdbb1c https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda#8e7251989bca326a28f4a5ffbd74557a -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.0-h1fed272_0.conda#b8e4c93f4ab70c3b6f6499299627dbdc +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.0-h32235b2_1.conda#a400fd9bad095c7cdf74661552ef802f https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda#b499ce4b026493a13774bcf0f4c33849 https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_2.conda#dfc5aae7b043d9f56ba99514d5e60625 +https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h96cd706_19.conda#212a9378a85ad020b8dc94853fdbeb6c https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda#553281a034e9cf8693c9df49f6c78ea1 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h8261f1e_0.conda#72b531694ebe4e8aa6f5745d1015c1b4 -https://conda.anaconda.org/conda-forge/linux-64/python-3.12.11-h9e4cc4f_0_cpython.conda#94206474a5608243a10c92cefbe0908f +https://conda.anaconda.org/conda-forge/linux-64/minizip-4.0.10-h05a5f5f_0.conda#da01bb40572e689bd1535a5cee6b1d68 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.50.4-hbc0de68_0.conda#8376bd3854542be0c8c7cd07525d31c6 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1 +https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 +https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda#f730d54ba9cd543666d7220c9f7ed563 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda#ecb5d11305b8ba1801543002e69d2f2f +https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda#c379d67c686fb83475c1a6ed41cc41ff +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.0-hf516916_1.conda#25d53803877008c7c2a2c9b44cb637b6 +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-37_h4a7cf45_openblas.conda#8bc098f29d8a7e3517bac5b25aab39b1 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.16.0-h4e3cde8_0.conda#a401aa9329350320c7d3809a7a5a1640 +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda#f4084e4e6577797150f9b04a4560ceb0 +https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c +https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1022.conda#00f0f4a9d2eb174015931b1a234d61ca +https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda#e7733bc6785ec009e47a224a71917e84 +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hd63d673_1_cpython.conda#5c00c8cea14ee8d02941cab9121dce41 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 +https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda#9dda9667feba914e0e80b95b82f7402b +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda#71ae752a748962161b4740eaff510258 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829 https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda#18fd895e0e775622906cdabfc3cf0fb4 https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda#1fd9696649f65fd6611fcdb4ffec738a -https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda#f730d54ba9cd543666d7220c9f7ed563 https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h1289d80_4.conda#fd0e7746ed0676f008daacb706ce69e4 https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_1.conda#57df494053e17dce2ac3a0b33e1b2a2e -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda#7e7d5ef1b9ed630e4a1c358d6bc62284 +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a https://conda.anaconda.org/conda-forge/noarch/click-8.3.0-pyh707e725_0.conda#e76c4ba9e1837847679421b8d549b784 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.1-pyhd8ed1ab_0.conda#364ba6c9fb03886ac979b482f39ebb92 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 https://conda.anaconda.org/conda-forge/noarch/colorcet-3.1.0-pyhd8ed1ab_1.conda#91d7152c744dc0f18ef8beb3cbc9980a https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 -https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.4-py312h7c45ced_2.conda#f21d3916ebe56e6bd02388fefba141af -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.6-py312h68e6be4_0.conda#d8c6357a89dd513de1a0f1eea97e25f9 https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda#003b8ba0a94e2f1e117d0bd46aebc901 https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda#24c1ca34138ee57de72a943237cde4cc https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda#66b8b26023b8efdf8fcb23bac4b6325d +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda#4afc585cd97ba8a23809406cd8a9eda8 https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py312h447239a_0.conda#63e20cf7b7460019b423fc06abb96c60 https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.9.0-pyhd8ed1ab_0.conda#76f492bd8ba8a0fb80ffe16fc1a75b3b -https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.3-h2b0a6b4_0.conda#8a6c305c6c6b169f839eefb951608e29 -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.0-hf516916_0.conda#1a8e49615381c381659de1bc6a3bf9ec -https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda#c74d83614aec66227ae5199d98852aaf https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac -https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7 +https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 -https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda#9614359868482abba1bd15ce465e3c42 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.5.2-pyhd8ed1ab_0.conda#895f6625dd8a246fece9279fcc12c1de https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_1.conda#cec5c1ea565944a94f82cdd6fba7cc76 -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-36_h4a7cf45_openblas.conda#2a6122504dc8ea139337046d34a110cb -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.14.1-h332b0f4_0.conda#45f6713cb00f124af300342512219182 -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda#f4084e4e6577797150f9b04a4560ceb0 -https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c -https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.0-ha9997c6_1.conda#b24dd2bd61cd8e4f8a13ee2a945a723c +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-37_h0358290_openblas.conda#3794858d4d6910a7fc3c181519e0b77a +https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a +https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_2.conda#27ac5ae872a21375d980bd4a6f99edf3 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-37_h47877c9_openblas.conda#8305e6a5ed432ad3e5a609e8024dbc17 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda#e512be7dc1f84966d50959e900ca121f https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda#49647ac1de4d1e4b49124aedf3934e02 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda#f775a43412f7f3d7ed218113ad233869 https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py312hd9148b4_0.conda#43ef66a3b00b749383617e0ab6718d1d https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py312h178313f_0.conda#f4e246ec4ccdf73e50eefb0fa359a64e https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h7b42cdd_3.conda#1d7f05c3f8bb4e98d02fca45f0920b23 https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2 https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda#438e75abf4d8c9c1d9e483b6c3f36282 https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py312h178313f_0.conda#0cf580c1b73146bb9ff1bbdb4d4c8cf9 -https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py312h4c3975b_0.conda#d99ab14339ac25676f1751b76b26c9b2 +https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.1-py312h4c3975b_0.conda#8713607fa1730cbd870fed86a3230926 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda#6c8979be6d7a17692793114fa26916e8 @@ -181,123 +206,117 @@ https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#1 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda#fa839b5ff59e192f411ccc7dae6588bb https://conda.anaconda.org/conda-forge/noarch/tblib-3.1.0-pyhd8ed1ab_0.conda#a15c62b8a306b8978f094f76da2f903f https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53 -https://conda.anaconda.org/conda-forge/noarch/toolz-1.0.0-pyhd8ed1ab_1.conda#40d0ed782a8aaa16ef248e68c06c168d +https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py312h4c3975b_1.conda#66b988f7f1dc9fcc9541483cb0ab985b https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py312h4c3975b_1.conda#4da303c1e91703d178817252615ca0a7 https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda#71ae752a748962161b4740eaff510258 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda#5e2eb9bf77394fc2e5918beefec9f9ab +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda#74ac5069774cdbc53910ec4d631a3999 https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda#421a865222cd0c9d83ff08bc78bf3a61 https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4 -https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h35888ee_0.conda#60b9cd087d22272885a6b8366b1d3d43 +https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda#648ee28dcd4e07a1940a17da62eccd40 https://conda.anaconda.org/conda-forge/noarch/click-default-group-1.2.4-pyhd8ed1ab_1.conda#7cd83dd6831b61ad9624a694e4afd7dc -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.10.7-py312h8a5da7c_0.conda#03d83efc728a6721a0f1616a04a7fc84 -https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.0.1-py312h66e93f0_0.conda#6198b134b1c08173f33653896974d477 +https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 +https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.11.0-py312h8a5da7c_0.conda#c23f0ca5a6e2f0ef5735825f14fbe007 +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py312h4c3975b_1.conda#693cda60b9223f55d0836c885621611b https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py312h8a5da7c_0.conda#b12bb9cc477156ce84038e0be6d0f763 -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda#4afc585cd97ba8a23809406cd8a9eda8 +https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda#ff1966654a6cd1cf06a6e44c13e60b8a https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda#c74d83614aec66227ae5199d98852aaf https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-36_h0358290_openblas.conda#13a3fe5f9812ac8c5710ef8c03105121 -https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a -https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_2.conda#27ac5ae872a21375d980bd4a6f99edf3 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-36_h47877c9_openblas.conda#55daaac7ecf8ebd169cdbe34dc79549e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.0-h26afc86_1.conda#8337b675e0cad517fbcb3daf7588087a +https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.2-gpl_h7be2006_100.conda#9d0eaa26e3c5d7af747b3ddee928327b +https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_2.conda#53e7cbb2beb03d69a478631e23e340e9 +https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda#c01021ae525a76fe62720c7346212d74 +https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.4-hf7376ad_0.conda#da21f286c4466912cc579911068034b6 +https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda#3ccff1066c05a1e6c221356eecc40581 +https://conda.anaconda.org/conda-forge/linux-64/libpq-18.0-h3675c94_0.conda#064887eafa473cbfae9ee8bedd3b7432 +https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda#372a62464d47d9e966b630ffae3abe73 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.12.2-hca5e8e5_0.conda#3c3e5ccbb2d96ac75e1b8b028586db5c +https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.1-h26afc86_0.conda#1b92b7d1b901bd832f8279ef18cac1f4 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda#7ba3f09fceae6a120d664217e58fe686 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.4-py312h33ff503_0.conda#23494fd5bbca946e6e70ecc648352b2f https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py312h7b42cdd_3.conda#1d7f05c3f8bb4e98d02fca45f0920b23 https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh8b19718_0.conda#dfce4b2af4bfe90cdcaf56ca0b28ddf5 -https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda#438e75abf4d8c9c1d9e483b6c3f36282 +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312h9b6a7d9_2.conda#573b9a879a3a42990f9c51d7376dce6b https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.3-pyhd8ed1ab_0.conda#bf0dc4c8a32e91290e3fae5403798c63 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda#5e2eb9bf77394fc2e5918beefec9f9ab -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f https://conda.anaconda.org/conda-forge/linux-64/yarl-1.20.1-py312h178313f_0.conda#3b3fa80c71d6a8d0380e9e790f5a4a8a https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.0-py312h033f2cf_0.conda#1063ec5a1ae783cba0befa2b768a6af2 https://conda.anaconda.org/conda-forge/noarch/asv_runner-0.2.1-pyhd8ed1ab_0.conda#fdcbeb072c80c805a2ededaa5f91cd79 -https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2 -https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.9.1-pyhcf101f3_0.conda#c49de33395d775a92ea90e0cb34c3577 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee -https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_2.conda#d10d9393680734a8febc4b362a4c94f2 -https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_2.conda#53e7cbb2beb03d69a478631e23e340e9 -https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda#c01021ae525a76fe62720c7346212d74 -https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.3-hf7376ad_0.conda#5728d01354f55d4f7e6f5e3073919a32 -https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda#3ccff1066c05a1e6c221356eecc40581 -https://conda.anaconda.org/conda-forge/linux-64/libpq-18.0-h3675c94_0.conda#064887eafa473cbfae9ee8bedd3b7432 -https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda#372a62464d47d9e966b630ffae3abe73 -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.12.0-hca5e8e5_0.conda#38d9cae31d66c1b73ab34e786bb3afe5 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.3-py312h33ff503_0.conda#261a82ff799db441c7122f6a83ade061 -https://conda.anaconda.org/conda-forge/noarch/pbr-7.0.1-pyhd8ed1ab_0.conda#0d118f1c98cec5ebd0cdfd066e49a446 -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py312h9b6a7d9_2.conda#573b9a879a3a42990f9c51d7376dce6b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda#1f987505580cb972cf28dc5f74a0f81b -https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-9.2.0-pyhd8ed1ab_0.conda#ad8f901272d56cfb6bf22bb89e9be59b -https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py312h68727a3_5.conda#f9664ee31aed96c85b7319ab0a693341 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f -https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_0.conda#05d73100768745631ab3de9dc1e08da2 https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2#8cb2fc4cd6cc63f1369cfa318f581cc3 +https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.4-py312h4f23490_2.conda#ff4b5976814bc00861f962276c8fb87f https://conda.anaconda.org/conda-forge/noarch/colorspacious-1.1.2-pyhecae5ae_1.conda#04151bb8e351c6209caad045e4b1f4bd https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_2.conda#bce621e43978c245261c76b45edeaa3d -https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda#25f954b7dae6dd7b0dc004dab74f1ce9 -https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_2.conda#bb0230917e2473c77d615104dbe8a49d -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.2-default_h99862b1_3.conda#7076df5c94a595a25d9d55361098ac12 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.2-default_h746c552_3.conda#c26d396fc2d5c800e84e4e81a687bc6a +https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.9.1-pyhcf101f3_0.conda#c49de33395d775a92ea90e0cb34c3577 +https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_2.conda#d10d9393680734a8febc4b362a4c94f2 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.4-default_h99862b1_0.conda#5eb56f7a1892309ba09d1024068714cc +https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.4-default_h746c552_0.conda#bb842304ab95206d6f335861aa4270d8 https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_2.conda#b513eb83b3137eca1192c34bf4f013a7 https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h6f5c62b_11.conda#68fc66282364981589ef36868b1a7c78 +https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hcb59c51_118.conda#ce07b32efdf860ed996fdedcff3ea96e https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.3.1-py312h4f23490_2.conda#cec5bc5f7d374f8f8095f8e28e31f6cb https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.2-nompi_h90de81b_102.conda#f4c67a50ac3008a578530e7fc32f3d98 https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda#e597b3e812d9613f659b7d87ad252d18 +https://conda.anaconda.org/conda-forge/noarch/pbr-7.0.1-pyhd8ed1ab_0.conda#0d118f1c98cec5ebd0cdfd066e49a446 https://conda.anaconda.org/conda-forge/linux-64/pykdtree-1.4.3-py312h4f23490_1.conda#3f5d247166c318572e94c334696c0604 -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda#6891acad5e136cb62a8c2ed2679d6528 -https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.15.1-pyhd8ed1ab_0.conda#0511afbe860b1a653125d77c719ece53 -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 -https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.4.0-py312h4c3975b_1.conda#27524eb30dee3a984035840d3b29e640 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda#1f987505580cb972cf28dc5f74a0f81b https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.9.0-py312h4f23490_1.conda#b182ab534776c2cc76fba59aa9916254 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.2-py312h7a1785b_0.conda#86a0cf3ba594247a8c44bd2111d7549c +https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-9.2.2-pyhd8ed1ab_0.conda#e2e4d7094d0580ccd62e2a41947444f3 https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py312h950be2a_0.conda#16f95b810ff3253eabdcbb0b258c56dd -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860 +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.2.0-hb60516a_1.conda#29ed2be4b47b5aa1b07689e12407fbfd -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a -https://conda.anaconda.org/conda-forge/noarch/wslink-2.4.0-pyhd8ed1ab_0.conda#a7c17eeb817efebaf59a48fdeab284a4 +https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py312h68727a3_5.conda#f9664ee31aed96c85b7319ab0a693341 +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_0.conda#05d73100768745631ab3de9dc1e08da2 https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2#6b889f174df1e0f816276ae69281af4d https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.3.0-py312h4f23490_1.conda#ac0a1a874ce9e3f8940a3a908ff74da9 -https://conda.anaconda.org/conda-forge/noarch/distributed-2025.9.1-pyhcf101f3_0.conda#f140b63da44c9a3fc7ae75cb9cc53c47 https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda#057083b06ccf1c2778344b6dabace38b https://conda.anaconda.org/conda-forge/linux-64/esmf-8.9.0-nompi_h8d4c64c_3.conda#9102d5f5b8d01fcfa81efec370815772 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.1.0-h15599e2_0.conda#7704b1edaa8316b8792424f254c1f586 +https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda#25f954b7dae6dd7b0dc004dab74f1ce9 https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.2-pyhd8ed1ab_0.conda#efbc812363856906a80344b496389d2e -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.6-py312he3d6523_1.conda#94926ee1d68e678fb4cfdb0727a0927e +https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_2.conda#bb0230917e2473c77d615104dbe8a49d +https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.10.3-hec83942_22.conda#e9393caea4437bf4958f7df06847329c +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.7-py312he3d6523_0.conda#066291f807305cff71a8ec1683fc9958 https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.2-nompi_py312hf6400b3_104.conda#e3f2d3ba3f36a10a32219dff624f4ea0 -https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_0.conda#bc6c44af2a9e6067dd7e949ef10cdfba -https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda#6891acad5e136cb62a8c2ed2679d6528 +https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.15.1-pyhd8ed1ab_0.conda#0511afbe860b1a653125d77c719ece53 +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.4.0-py312h4c3975b_1.conda#27524eb30dee3a984035840d3b29e640 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a +https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda#8fa415e696acd9af59ce0a4425fd1b38 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.25.0-py312hf79963d_1.conda#6c913a686cb4060cbd7639a36fa144f0 https://conda.anaconda.org/conda-forge/noarch/cmocean-4.0.3-pyhd8ed1ab_1.conda#dd71e4ec2fbffe38c0359976505f816e +https://conda.anaconda.org/conda-forge/noarch/distributed-2025.9.1-pyhcf101f3_0.conda#f140b63da44c9a3fc7ae75cb9cc53c47 https://conda.anaconda.org/conda-forge/noarch/esmpy-8.9.0-pyhc455866_0.conda#37e9756efac1b155daface77417f0463 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_1.conda#9a2be7d0089f5934b550933ca0d9fe85 https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda#79f71230c069a287efe3a8614069ddf1 -https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_3.conda#d2bbbd293097e664ffb01fc4cdaf5729 +https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_0.conda#bc6c44af2a9e6067dd7e949ef10cdfba https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_0.conda#cc0bffcaf6410aba9c6581dfdc18012d +https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.4.3-py312h762fea3_3.conda#f02bbf618034615a013dd8afcf9a0899 +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4 https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.43-h021d004_4.conda#a891e341072432fafb853b3762957cbf https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda#91e6d4d684e237fba31b9815c4b40edf -https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py312h5d43ed7_6.conda#d45f3fa1c052720f1be14d1e545560d4 +https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_3.conda#d2bbbd293097e664ffb01fc4cdaf5729 +https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.2-py312h5d43ed7_0.conda#9bc525bfa506ce335fd168f90dda0031 https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda#b3f0179590f3c0637b7eb5309898f79e https://conda.anaconda.org/conda-forge/noarch/pyvista-0.46.3-pyhd8ed1ab_1.conda#ff39551518c75398a7a84de513024856 https://conda.anaconda.org/conda-forge/noarch/geovista-0.5.3-pyhd8ed1ab_1.conda#64348d05eedb1b1b5676f63101d004f2 @@ -312,3 +331,4 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda#00534ebcc0375929b45c3039b5ba7636 https://conda.anaconda.org/conda-forge/noarch/sphinx-8.1.3-pyhd8ed1ab_1.conda#1a3281a0dc355c02b5506d87db2d78ac https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda#3bc61f7161d28137797e038263c04c54 + diff --git a/requirements/locks/py313-linux-64.lock b/requirements/locks/py313-linux-64.lock index b3682778f6..4487db7fab 100644 --- a/requirements/locks/py313-linux-64.lock +++ b/requirements/locks/py313-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 1c3b094e421df3076cd379373be66793051a8f4eb4ab3f4f5c7545ac740d1367 +# input_hash: 04f3324513076f17d47453c7cf63015a0d1fa5f119c85140e809830050a16164 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 @@ -14,7 +14,6 @@ https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222 https://conda.anaconda.org/conda-forge/linux-64/utfcpp-4.0.8-ha770c72_0.conda#a480ee3eb9c95364a229673a28384899 https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda#f9e5fbc24009179e8b0409624691758a https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-ha97dd6f_2.conda#14bae321b8127b63cba276bd53fac237 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda#f7b4d76975aac7e5d9e6ad13845f92fe https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d @@ -27,17 +26,19 @@ https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda#7913 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda#51a19bba1b8ebfb60df25cde030b7ebc https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda#f7f0d6cc2dc986d42ac2689ec88192be https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda#f9f81ea472684d75b9dd8d0b328cf655 +https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda#38f5dbc9ac808e31c00650f7be1db93f https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda#b38117a3c920364aff79f870c984b4a3 https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb03c661_4.conda#1d29d2e33fe59954af82ef54a8af3fe1 https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 -https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda#35f29eec58405aaf55e01cb470d8c26a https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda#280ea6eee9e2ddefde25ff799c4f0363 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-hcd61629_7.conda#f116940d825ffc9104400f0d7f1a4551 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda#915f5995e94f60e9a4826e0b0920ee88 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda#9fa334557db9f63da6c9285fd2a48638 https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1 +https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda#68e52064ed3897463c0e958ab5c8f91b https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda#70e3400cbbfa03e96dcde7fc13e38c7b @@ -45,6 +46,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.cond https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda#80c07c68d2f6870250959dcc95b209d1 https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 +https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda#45161d96307e3a447cc3eb5896cf6f8c https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda#14edad12b59ccbfa3910d42c72adc2a0 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e @@ -54,10 +56,11 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.c https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2024.1-hb9d3cd8_1.conda#7c21106b851ec72c037b162c216d8f05 https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.3-hb47aa4a_0.conda#607e13a8caac17f9a664bcab5302ce06 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a -https://conda.anaconda.org/conda-forge/linux-64/cli11-2.5.0-h3f2d84a_0.conda#4a7c3dc241bb268078116434b4d2ad12 +https://conda.anaconda.org/conda-forge/linux-64/cli11-2.6.0-h54a6638_0.conda#ddf9fed4661bace13f33f08fe38a5f45 https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 https://conda.anaconda.org/conda-forge/linux-64/fmt-11.2.0-h07f6e7f_0.conda#0c2f855a88fab6afa92a7aa41217dc8e https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.0-h480dda7_0.conda#5dc479effdabf54a0ff240d565287495 +https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda#2cd94587f3a401ae05e03a6caf09539d https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda#7bdc5e2cc11cb0a0f795bdad9732b0f2 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 @@ -85,7 +88,7 @@ https://conda.anaconda.org/conda-forge/linux-64/pugixml-1.15-h3f63f65_0.conda#b1 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_0.conda#3d8da0248bdae970b4ade636a104b7f5 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 -https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda#0f2ca7906bf166247d1d760c3422cb8a +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda#035da2e4f5770f036ff704fa17aace24 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 @@ -95,75 +98,98 @@ https://conda.anaconda.org/conda-forge/linux-64/gl2ps-1.4.2-hae5d5c5_1.conda#00e https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4 https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1aa0949_3.conda#72cc69c30de0b6d39c7f97f501fdbb1c https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda#8e7251989bca326a28f4a5ffbd74557a -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.0-h1fed272_0.conda#b8e4c93f4ab70c3b6f6499299627dbdc +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.0-h32235b2_1.conda#a400fd9bad095c7cdf74661552ef802f https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda#b499ce4b026493a13774bcf0f4c33849 https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_2.conda#dfc5aae7b043d9f56ba99514d5e60625 +https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h96cd706_19.conda#212a9378a85ad020b8dc94853fdbeb6c https://conda.anaconda.org/conda-forge/linux-64/libtheora-1.1.1-h4ab18f5_1006.conda#553281a034e9cf8693c9df49f6c78ea1 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h8261f1e_0.conda#72b531694ebe4e8aa6f5745d1015c1b4 -https://conda.anaconda.org/conda-forge/linux-64/python-3.13.7-h2b335a9_100_cp313.conda#724dcf9960e933838247971da07fe5cf +https://conda.anaconda.org/conda-forge/linux-64/minizip-4.0.10-h05a5f5f_0.conda#da01bb40572e689bd1535a5cee6b1d68 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.50.4-hbc0de68_0.conda#8376bd3854542be0c8c7cd07525d31c6 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1 +https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda#d71d3a66528853c0a1ac2c02d79a0284 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 +https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda#f730d54ba9cd543666d7220c9f7ed563 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda#ecb5d11305b8ba1801543002e69d2f2f +https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.4-h2b0a6b4_0.conda#c379d67c686fb83475c1a6ed41cc41ff +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.0-hf516916_1.conda#25d53803877008c7c2a2c9b44cb637b6 +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-37_h4a7cf45_openblas.conda#8bc098f29d8a7e3517bac5b25aab39b1 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.16.0-h4e3cde8_0.conda#a401aa9329350320c7d3809a7a5a1640 +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda#f4084e4e6577797150f9b04a4560ceb0 +https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c +https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1022.conda#00f0f4a9d2eb174015931b1a234d61ca +https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda#e7733bc6785ec009e47a224a71917e84 +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/python-3.13.9-hc97d973_101_cp313.conda#4780fe896e961722d0623fa91d0d3378 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 +https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-h988505b_2.conda#9dda9667feba914e0e80b95b82f7402b +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda#71ae752a748962161b4740eaff510258 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/noarch/affine-2.4.0-pyhd8ed1ab_1.conda#8c4061f499edec6b8ac7000f6d586829 https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda#18fd895e0e775622906cdabfc3cf0fb4 https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda#1fd9696649f65fd6611fcdb4ffec738a -https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda#f730d54ba9cd543666d7220c9f7ed563 https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda#c7944d55af26b6d2d7629e27e9a972c1 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb03c661_4.conda#eaf3fbd2aa97c212336de38a51fe404e https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py313h7033f15_4.conda#bc8624c405856b1d047dd0a81829b08c https://conda.anaconda.org/conda-forge/noarch/certifi-2025.10.5-pyhd8ed1ab_0.conda#257ae203f1d204107ba389607d375ded https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_1.conda#57df494053e17dce2ac3a0b33e1b2a2e -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.3-pyhd8ed1ab_0.conda#7e7d5ef1b9ed630e4a1c358d6bc62284 +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda#a22d1fd9bf98827e280a02875d9a007a https://conda.anaconda.org/conda-forge/noarch/click-8.3.0-pyh707e725_0.conda#e76c4ba9e1837847679421b8d549b784 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.1-pyhd8ed1ab_0.conda#364ba6c9fb03886ac979b482f39ebb92 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 https://conda.anaconda.org/conda-forge/noarch/colorcet-3.1.0-pyhd8ed1ab_1.conda#91d7152c744dc0f18ef8beb3cbc9980a https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 -https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.4-py313h3484ee8_2.conda#778102be2ae2df0ca7a4927ee3938453 -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.6-py313hc80a56d_0.conda#132c85408e44764952c93db5a37a065f https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda#003b8ba0a94e2f1e117d0bd46aebc901 https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda#24c1ca34138ee57de72a943237cde4cc https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda#66b8b26023b8efdf8fcb23bac4b6325d +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda#4afc585cd97ba8a23809406cd8a9eda8 https://conda.anaconda.org/conda-forge/linux-64/frozenlist-1.7.0-py313h6b9daa2_0.conda#3a0be7abedcbc2aee92ea228efea8eba https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.9.0-pyhd8ed1ab_0.conda#76f492bd8ba8a0fb80ffe16fc1a75b3b -https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.3-h2b0a6b4_0.conda#8a6c305c6c6b169f839eefb951608e29 -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.0-hf516916_0.conda#1a8e49615381c381659de1bc6a3bf9ec -https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda#c74d83614aec66227ae5199d98852aaf https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac -https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7 +https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda#53abe63df7e10a6ba605dc5f9f961d36 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 -https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda#9614359868482abba1bd15ce465e3c42 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.5.2-pyhd8ed1ab_0.conda#895f6625dd8a246fece9279fcc12c1de https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py313hc8edb43_1.conda#87215c60837a8494bf3453d08b404eed -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-36_h4a7cf45_openblas.conda#2a6122504dc8ea139337046d34a110cb -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.14.1-h332b0f4_0.conda#45f6713cb00f124af300342512219182 -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda#f4084e4e6577797150f9b04a4560ceb0 -https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c -https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.0-ha9997c6_1.conda#b24dd2bd61cd8e4f8a13ee2a945a723c +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-37_h0358290_openblas.conda#3794858d4d6910a7fc3c181519e0b77a +https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a +https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_2.conda#27ac5ae872a21375d980bd4a6f99edf3 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-37_h47877c9_openblas.conda#8305e6a5ed432ad3e5a609e8024dbc17 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda#e512be7dc1f84966d50959e900ca121f https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/noarch/loguru-0.7.3-pyh707e725_0.conda#49647ac1de4d1e4b49124aedf3934e02 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py313h3dea7bd_0.conda#c14389156310b8ed3520d84f854be1ee https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.1.2-py313h7037e92_0.conda#3d35f7d3fd814124efa1a5b5d03144a4 https://conda.anaconda.org/conda-forge/linux-64/multidict-6.6.3-py313h8060acc_0.conda#0cabb3f2ba71300370fcebe973d9ae38 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py313ha492abd_3.conda#3354141a95eee5d29000147578dbc13f https://conda.anaconda.org/conda-forge/noarch/pip-25.2-pyh145f28c_0.conda#e7ab34d5a93e0819b62563c78635d937 https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda#5c7a868f8241e64e1cf5fdf4962f23e2 https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda#438e75abf4d8c9c1d9e483b6c3f36282 https://conda.anaconda.org/conda-forge/linux-64/propcache-0.3.1-py313h8060acc_0.conda#b62867739241368f43f164889b45701b -https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.0-py313h07c4f96_0.conda#f25cdf145885936fe458452d73d991dc +https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.1-py313h07c4f96_0.conda#98ab666fc28aaf6ab5067601ce2dc94d https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda#6c8979be6d7a17692793114fa26916e8 @@ -182,120 +208,114 @@ https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8-pyhd8ed1ab_0.conda#1 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda#fa839b5ff59e192f411ccc7dae6588bb https://conda.anaconda.org/conda-forge/noarch/tblib-3.1.0-pyhd8ed1ab_0.conda#a15c62b8a306b8978f094f76da2f903f https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda#d2732eb636c264dc9aa4cbee404b1a53 -https://conda.anaconda.org/conda-forge/noarch/toolz-1.0.0-pyhd8ed1ab_1.conda#40d0ed782a8aaa16ef248e68c06c168d +https://conda.anaconda.org/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda#c07a6153f8306e45794774cf9b13bd32 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py313h07c4f96_1.conda#45821154b9cb2fb63c2b354c76086954 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.46-hb03c661_0.conda#71ae752a748962161b4740eaff510258 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda#ba231da7fccf9ea1e768caf5c7099b84 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda#5e2eb9bf77394fc2e5918beefec9f9ab +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda#e52c2ef711ccf31bb7f70ca87d144b9e https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda#74ac5069774cdbc53910ec4d631a3999 https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda#421a865222cd0c9d83ff08bc78bf3a61 https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4 -https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf01b4d8_0.conda#062317cc1cd26fbf6454e86ddd3622c4 +https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf46b229_1.conda#d0616e7935acab407d1543b28c446f6f https://conda.anaconda.org/conda-forge/noarch/click-default-group-1.2.4-pyhd8ed1ab_1.conda#7cd83dd6831b61ad9624a694e4afd7dc -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.10.7-py313h3dea7bd_0.conda#2847245cb868cdf87bb7fee7b8605d10 -https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.0.1-py313h536fd9c_0.conda#e886bb6a3c24f8b9dd4fcd1d617a1f64 +https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1.2-pyhd8ed1ab_0.conda#e9b05deb91c013e5224672a4ba9cf8d1 +https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_2.conda#55c7804f428719241a90b152016085a1 +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.11.0-py313h3dea7bd_0.conda#bf5f7b7fc409c4993e75362afe312f60 +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.1.0-py313h07c4f96_1.conda#bcca9afd203fe05d9582249ac12762da https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.60.1-py313h3dea7bd_0.conda#904860fc0d57532d28e9c6c4501f19a9 -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda#4afc585cd97ba8a23809406cd8a9eda8 +https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda#ff1966654a6cd1cf06a6e44c13e60b8a https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h6e4c0c1_103.conda#c74d83614aec66227ae5199d98852aaf https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-36_h0358290_openblas.conda#13a3fe5f9812ac8c5710ef8c03105121 -https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a -https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_2.conda#27ac5ae872a21375d980bd4a6f99edf3 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-36_h47877c9_openblas.conda#55daaac7ecf8ebd169cdbe34dc79549e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.0-h26afc86_1.conda#8337b675e0cad517fbcb3daf7588087a +https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.2-gpl_h7be2006_100.conda#9d0eaa26e3c5d7af747b3ddee928327b +https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_2.conda#53e7cbb2beb03d69a478631e23e340e9 +https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda#c01021ae525a76fe62720c7346212d74 +https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.4-hf7376ad_0.conda#da21f286c4466912cc579911068034b6 +https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda#3ccff1066c05a1e6c221356eecc40581 +https://conda.anaconda.org/conda-forge/linux-64/libpq-18.0-h3675c94_0.conda#064887eafa473cbfae9ee8bedd3b7432 +https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda#372a62464d47d9e966b630ffae3abe73 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.12.2-hca5e8e5_0.conda#3c3e5ccbb2d96ac75e1b8b028586db5c +https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.1-h26afc86_0.conda#1b92b7d1b901bd832f8279ef18cac1f4 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda#7ba3f09fceae6a120d664217e58fe686 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.4-py313hf6604e3_0.conda#c47c527e215377958d28c470ce4863e1 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 https://conda.anaconda.org/conda-forge/noarch/pbr-7.0.1-pyhd8ed1ab_0.conda#0d118f1c98cec5ebd0cdfd066e49a446 -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py313ha492abd_3.conda#3354141a95eee5d29000147578dbc13f -https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.0-hb72c0af_0.conda#438e75abf4d8c9c1d9e483b6c3f36282 +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py313h77f6078_2.conda#42d11c7d1ac21ae2085f58353641e71c https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.3-pyhd8ed1ab_0.conda#bf0dc4c8a32e91290e3fae5403798c63 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda#5e2eb9bf77394fc2e5918beefec9f9ab -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f https://conda.anaconda.org/conda-forge/linux-64/yarl-1.20.1-py313h8060acc_0.conda#b3659ec61a97eb6f64aeca04effb999d https://conda.anaconda.org/conda-forge/linux-64/aiohttp-3.13.0-py313h301e4d9_0.conda#7526956f63a3af83dc15b68bb5d1b5c8 https://conda.anaconda.org/conda-forge/noarch/asv_runner-0.2.1-pyhd8ed1ab_0.conda#fdcbeb072c80c805a2ededaa5f91cd79 -https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2 -https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.9.1-pyhcf101f3_0.conda#c49de33395d775a92ea90e0cb34c3577 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee -https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_2.conda#d10d9393680734a8febc4b362a4c94f2 -https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_2.conda#53e7cbb2beb03d69a478631e23e340e9 -https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.12.1-default_h7f8ec31_1002.conda#c01021ae525a76fe62720c7346212d74 -https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.3-hf7376ad_0.conda#5728d01354f55d4f7e6f5e3073919a32 -https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_h11f7409_103.conda#3ccff1066c05a1e6c221356eecc40581 -https://conda.anaconda.org/conda-forge/linux-64/libpq-18.0-h3675c94_0.conda#064887eafa473cbfae9ee8bedd3b7432 -https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda#372a62464d47d9e966b630ffae3abe73 -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.12.0-hca5e8e5_0.conda#38d9cae31d66c1b73ab34e786bb3afe5 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.3-py313hf6604e3_0.conda#3122d20dc438287e125fb5acff1df170 -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.7.2-py313h77f6078_2.conda#42d11c7d1ac21ae2085f58353641e71c -https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda#1f987505580cb972cf28dc5f74a0f81b -https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-9.2.0-pyhd8ed1ab_0.conda#ad8f901272d56cfb6bf22bb89e9be59b -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860 -https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py313h33d0bda_5.conda#5bcffe10a500755da4a71cc0fb62a420 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f -https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py313h54dd161_0.conda#1fe43bd1fc86e22ad3eb0edec637f8a2 https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2#8cb2fc4cd6cc63f1369cfa318f581cc3 +https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.2-pyha770c72_0.conda#749ebebabc2cae99b2e5b3edd04c6ca2 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.4-py313h29aa505_2.conda#1363e8db910e403edc8fd486f8470ec6 https://conda.anaconda.org/conda-forge/noarch/colorspacious-1.1.2-pyhecae5ae_1.conda#04151bb8e351c6209caad045e4b1f4bd https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py313h7037e92_2.conda#6c8b4c12099023fcd85e520af74fd755 -https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda#25f954b7dae6dd7b0dc004dab74f1ce9 -https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_2.conda#bb0230917e2473c77d615104dbe8a49d -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.2-default_h99862b1_3.conda#7076df5c94a595a25d9d55361098ac12 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.2-default_h746c552_3.conda#c26d396fc2d5c800e84e4e81a687bc6a +https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.9.1-pyhcf101f3_0.conda#c49de33395d775a92ea90e0cb34c3577 +https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_2.conda#d10d9393680734a8febc4b362a4c94f2 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.4-default_h99862b1_0.conda#5eb56f7a1892309ba09d1024068714cc +https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.4-default_h746c552_0.conda#bb842304ab95206d6f335861aa4270d8 https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_2.conda#b513eb83b3137eca1192c34bf4f013a7 https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h6f5c62b_11.conda#68fc66282364981589ef36868b1a7c78 +https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_hcb59c51_118.conda#ce07b32efdf860ed996fdedcff3ea96e https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.3.1-py313h29aa505_2.conda#ad53894d278895bf15c8fc324727d224 https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.2-nompi_h90de81b_102.conda#f4c67a50ac3008a578530e7fc32f3d98 https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py313h08cd8bf_1.conda#9e87d4bda0c2711161d765332fa38781 https://conda.anaconda.org/conda-forge/linux-64/pykdtree-1.4.3-py313h29aa505_1.conda#094a1bcea4ac73f86b82f050dbc42c74 -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda#6891acad5e136cb62a8c2ed2679d6528 -https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.15.1-pyhd8ed1ab_0.conda#0511afbe860b1a653125d77c719ece53 -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 -https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.4.0-py313h07c4f96_1.conda#5ccb0566ef58b0d9b3973ff1da006593 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhd8ed1ab_0.conda#1f987505580cb972cf28dc5f74a0f81b https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.9.0-py313h29aa505_1.conda#952d8629c239c33a1a3087aa373f965f https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.2-py313h11c21cd_0.conda#85a80978a04be9c290b8fe6d9bccff1c +https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-9.2.2-pyhd8ed1ab_0.conda#e2e4d7094d0580ccd62e2a41947444f3 https://conda.anaconda.org/conda-forge/linux-64/shapely-2.1.2-py313hfc84eb1_0.conda#63f3a31e1933ebf61a15be43f880fea4 +https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-pyhd8ed1ab_2.conda#9aa358575bbd4be126eaa5e0039f835c +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860 https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.2.0-hb60516a_1.conda#29ed2be4b47b5aa1b07689e12407fbfd -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a -https://conda.anaconda.org/conda-forge/noarch/wslink-2.4.0-pyhd8ed1ab_0.conda#a7c17eeb817efebaf59a48fdeab284a4 +https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py313h33d0bda_5.conda#5bcffe10a500755da4a71cc0fb62a420 +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py313h54dd161_0.conda#1fe43bd1fc86e22ad3eb0edec637f8a2 https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2#6b889f174df1e0f816276ae69281af4d https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.3.0-py313h29aa505_1.conda#36a704169c6a0b4ce8335d160103e218 -https://conda.anaconda.org/conda-forge/noarch/distributed-2025.9.1-pyhcf101f3_0.conda#f140b63da44c9a3fc7ae75cb9cc53c47 https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda#057083b06ccf1c2778344b6dabace38b https://conda.anaconda.org/conda-forge/linux-64/esmf-8.9.0-nompi_h8d4c64c_3.conda#9102d5f5b8d01fcfa81efec370815772 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.1.0-h15599e2_0.conda#7704b1edaa8316b8792424f254c1f586 +https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda#25f954b7dae6dd7b0dc004dab74f1ce9 https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.2-pyhd8ed1ab_0.conda#efbc812363856906a80344b496389d2e -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.6-py313h683a580_1.conda#0483ab1c5b6956442195742a5df64196 +https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_2.conda#bb0230917e2473c77d615104dbe8a49d +https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.10.3-hec83942_22.conda#e9393caea4437bf4958f7df06847329c +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.7-py313h683a580_0.conda#5858a4032f99c89b175f7f5161c7b0cd https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.2-nompi_py313hfae5b86_104.conda#b6ddba788230a41a534cf288d41a1df4 -https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_0.conda#bc6c44af2a9e6067dd7e949ef10cdfba -https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda#6891acad5e136cb62a8c2ed2679d6528 +https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.15.1-pyhd8ed1ab_0.conda#0511afbe860b1a653125d77c719ece53 +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.4.0-py313h07c4f96_1.conda#5ccb0566ef58b0d9b3973ff1da006593 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a +https://conda.anaconda.org/conda-forge/noarch/wslink-2.5.0-pyhd8ed1ab_0.conda#8fa415e696acd9af59ce0a4425fd1b38 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.25.0-py313h08cd8bf_1.conda#a0d8dc5c90850d9f1a79f69c98aef0ff https://conda.anaconda.org/conda-forge/noarch/cmocean-4.0.3-pyhd8ed1ab_1.conda#dd71e4ec2fbffe38c0359976505f816e +https://conda.anaconda.org/conda-forge/noarch/distributed-2025.9.1-pyhcf101f3_0.conda#f140b63da44c9a3fc7ae75cb9cc53c47 https://conda.anaconda.org/conda-forge/noarch/esmpy-8.9.0-pyhc455866_0.conda#37e9756efac1b155daface77417f0463 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_1.conda#9a2be7d0089f5934b550933ca0d9fe85 https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda#79f71230c069a287efe3a8614069ddf1 -https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_3.conda#d2bbbd293097e664ffb01fc4cdaf5729 +https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.3.0-pyha770c72_0.conda#bc6c44af2a9e6067dd7e949ef10cdfba https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.3-h5c1c036_0.conda#cc0bffcaf6410aba9c6581dfdc18012d +https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.4.3-py313h6bd603e_3.conda#28ae3685a33eee815e353a90b880c8b4 +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda#db0c6b99149880c8ba515cf4abe93ee4 https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.43-h021d004_4.conda#a891e341072432fafb853b3762957cbf https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.0-h61e6d4b_0.conda#91e6d4d684e237fba31b9815c4b40edf -https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.1-py313h0eea884_6.conda#084f6aad0a6d251d5e7842418588d9d4 +https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_3.conda#d2bbbd293097e664ffb01fc4cdaf5729 +https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.2-py313h0eea884_0.conda#6df480e4eb9a2afba5f209bfc6d968ec https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-49.0-unix_0.conda#b3f0179590f3c0637b7eb5309898f79e https://conda.anaconda.org/conda-forge/noarch/pyvista-0.46.3-pyhd8ed1ab_1.conda#ff39551518c75398a7a84de513024856 https://conda.anaconda.org/conda-forge/noarch/geovista-0.5.3-pyhd8ed1ab_1.conda#64348d05eedb1b1b5676f63101d004f2 @@ -310,3 +330,4 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda#00534ebcc0375929b45c3039b5ba7636 https://conda.anaconda.org/conda-forge/noarch/sphinx-8.1.3-pyhd8ed1ab_1.conda#1a3281a0dc355c02b5506d87db2d78ac https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda#3bc61f7161d28137797e038263c04c54 + diff --git a/requirements/py311.yml b/requirements/py311.yml index 43191252dc..049d2b8fc4 100644 --- a/requirements/py311.yml +++ b/requirements/py311.yml @@ -34,6 +34,8 @@ dependencies: - pandas - pip - python-stratify + - rasterio + - affine # Test dependencies. - asv_runner diff --git a/requirements/py312.yml b/requirements/py312.yml index d14b9976bc..f5141337d0 100644 --- a/requirements/py312.yml +++ b/requirements/py312.yml @@ -34,6 +34,8 @@ dependencies: - pandas - pip - python-stratify + - rasterio + - affine # Test dependencies. - asv_runner diff --git a/requirements/py313.yml b/requirements/py313.yml index 1b6249b97b..caffd582f7 100644 --- a/requirements/py313.yml +++ b/requirements/py313.yml @@ -34,6 +34,8 @@ dependencies: - pandas - pip - python-stratify + - rasterio + - affine # Test dependencies. - asv_runner