From b5b57410fea189cb9c3191684f1a936b5a2473ee Mon Sep 17 00:00:00 2001 From: anissa111 Date: Thu, 5 Feb 2026 13:14:33 -0700 Subject: [PATCH 01/11] base bugs resolved by re-adding removed functionality and removing degenerate polygon block --- uxarray/core/dataarray.py | 68 +++++++++++++-- uxarray/grid/geometry.py | 15 ++-- uxarray/grid/grid.py | 169 +++++++++++++++++++++++++++----------- 3 files changed, 191 insertions(+), 61 deletions(-) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 2c52710d1..fcb6ea158 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -275,6 +275,11 @@ def to_geodataframe( def to_polycollection( self, + periodic_elements: Optional[str] = "exclude", + projection: Optional[ccrs.Projection] = None, + return_indices: Optional[bool] = False, + cache: Optional[bool] = True, + override: Optional[bool] = False, **kwargs, ): """Constructs a ``matplotlib.collections.PolyCollection``` consisting @@ -283,8 +288,19 @@ def to_polycollection( Parameters ---------- - **kwargs: dict - Key word arguments to pass into the construction of a PolyCollection + periodic_elements : str, optional + Method for handling periodic elements. One of ['exclude', 'split', or 'ignore']: + - 'exclude': Periodic elements will be identified and excluded from the GeoDataFrame + - 'split': Periodic elements will be identified and split using the ``antimeridian`` package + - 'ignore': No processing will be applied to periodic elements. + projection: ccrs.Projection + Cartopy geographic projection to use + return_indices: bool + Flag to indicate whether to return the indices of corrected polygons, if any exist + cache: bool + Flag to indicate whether to cache the computed PolyCollection + override: bool + Flag to indicate whether to override a cached PolyCollection, if it exists """ # data is multidimensional, must be a 1D slice if self.values.ndim > 1: @@ -293,11 +309,53 @@ def to_polycollection( f"for face-centered data." ) - poly_collection = self.uxgrid.to_polycollection(**kwargs) + if self._face_centered(): + poly_collection, corrected_to_original_faces = ( + self.uxgrid.to_polycollection( + override=override, + cache=cache, + periodic_elements=periodic_elements, + return_indices=True, + projection=projection, + **kwargs, + ) + ) - poly_collection.set_array(self.data) + if periodic_elements == "exclude": + # index data to ignore data mapped to periodic elements + _data = np.delete( + self.values, + self.uxgrid._poly_collection_cached_parameters[ + "antimeridian_face_indices" + ], + axis=0, + ) + elif periodic_elements == "split": + _data = self.values[corrected_to_original_faces] + else: + _data = self.values - return poly_collection + if ( + self.uxgrid._poly_collection_cached_parameters[ + "non_nan_polygon_indices" + ] + is not None + ): + # index data to ignore NaN polygons + _data = _data[ + self.uxgrid._poly_collection_cached_parameters[ + "non_nan_polygon_indices" + ] + ] + + poly_collection.set_array(_data) + + if return_indices: + return poly_collection, corrected_to_original_faces + else: + return poly_collection + else: + raise ValueError("Data variable must be face centered.") def to_raster( self, diff --git a/uxarray/grid/geometry.py b/uxarray/grid/geometry.py index dd3899b2c..e474a7d09 100644 --- a/uxarray/grid/geometry.py +++ b/uxarray/grid/geometry.py @@ -475,14 +475,6 @@ def _grid_to_matplotlib_polycollection( central_longitude=central_longitude, ) - # Filter out degenerate polygons, which cause issues with Cartopy 0.25 - # fmt: off - degenerate = ( - (polygon_shells[:,0,0][:,np.newaxis] == polygon_shells[:,1:,0]).all(axis=1) - | (polygon_shells[:,0,1][:,np.newaxis] == polygon_shells[:,1:,1]).all(axis=1) - ) - # fmt: on - polygon_shells = polygon_shells[~degenerate, ...] # Projected polygon shells if a projection is specified if projection is not None: @@ -516,6 +508,13 @@ def _grid_to_matplotlib_polycollection( does_not_contain_nan = ~np.isnan(shells_d).any(axis=(1, 2)) non_nan_polygon_indices = np.where(does_not_contain_nan)[0] + grid._poly_collection_cached_parameters["non_nan_polygon_indices"] = ( + non_nan_polygon_indices + ) + grid._poly_collection_cached_parameters["antimeridian_face_indices"] = ( + antimeridian_face_indices + ) + # Select which shells to use: projected or original if projected_polygon_shells is not None: shells_to_use = projected_polygon_shells diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 16c012552..5a0885f2f 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -1,11 +1,13 @@ import copy import os from html import escape -from typing import Sequence +from typing import Optional, Sequence from warnings import warn +import cartopy.crs as ccrs import numpy as np import xarray as xr +from spatialpandas import GeoDataFrame from xarray.core.options import OPTIONS from xarray.core.utils import UncachedAccessor @@ -207,9 +209,22 @@ def __init__( "antimeridian_face_indices": None, } - # Cached matplotlib data structures - self._cached_poly_collection = None - self._cached_line_collection = None + # cached parameters for PolyCollection conversions + self._poly_collection_cached_parameters = { + "poly_collection": None, + "periodic_elements": None, + "projection": None, + "corrected_to_original_faces": None, + "non_nan_polygon_indices": None, + "antimeridian_face_indices": None, + } + + # cached parameters for LineCollection conversions + self._line_collection_cached_parameters = { + "line_collection": None, + "periodic_elements": None, + "projection": None, + } self._raster_data_id = None @@ -2285,89 +2300,147 @@ def to_geodataframe( def to_polycollection( self, + periodic_elements: Optional[str] = "exclude", + projection: Optional[ccrs.Projection] = None, + return_indices: Optional[bool] = False, + cache: Optional[bool] = True, + override: Optional[bool] = False, + return_non_nan_polygon_indices: Optional[bool] = False, **kwargs, ): """Constructs a ``matplotlib.collections.PolyCollection``` consisting - of polygons representing the faces of the unstructured grid. + of polygons representing the faces of the current ``Grid`` Parameters ---------- + periodic_elements : str, optional + Method for handling periodic elements. One of ['exclude', 'split', or 'ignore']: + - 'exclude': Periodic elements will be identified and excluded from the GeoDataFrame + - 'split': Periodic elements will be identified and split using the ``antimeridian`` package + - 'ignore': No processing will be applied to periodic elements. + projection: ccrs.Projection + Cartopy geographic projection to use + return_indices: bool + Flag to indicate whether to return the indices of corrected polygons, if any exist + cache: bool + Flag to indicate whether to cache the computed PolyCollection + override: bool + Flag to indicate whether to override a cached PolyCollection, if it exists **kwargs: dict Key word arguments to pass into the construction of a PolyCollection """ - import cartopy.crs as ccrs - if "projection" in kwargs: - proj = kwargs.pop("projection") - warn( - ( - "'projection' is not a supported argument and will be ignored. " - "Define the desired projection on the GeoAxes that this collection will be added to. " - "Example:\n" - " fig, ax = plt.subplots(subplot_kw={'projection': ccrs.Robinson()})\n" - " ax.add_collection(poly_collection)\n" - f"(received projection={proj!r})" - ), - category=FutureWarning, - stacklevel=2, + if periodic_elements not in ["ignore", "exclude", "split"]: + raise ValueError( + f"Invalid value for 'periodic_elements'. Expected one of ['include', 'exclude', 'split'] but received: {periodic_elements}" ) - if self._cached_poly_collection: - return copy.deepcopy(self._cached_poly_collection) + if self._poly_collection_cached_parameters["poly_collection"] is not None: + if ( + self._poly_collection_cached_parameters["periodic_elements"] + != periodic_elements + or self._poly_collection_cached_parameters["projection"] != projection + ): + # cached PolyCollection has a different projection or periodic element handling method + override = True + + if ( + self._poly_collection_cached_parameters["poly_collection"] is not None + and not override + ): + # use cached PolyCollection + if return_indices: + return copy.deepcopy( + self._poly_collection_cached_parameters["poly_collection"] + ), self._poly_collection_cached_parameters[ + "corrected_to_original_faces" + ] + else: + return copy.deepcopy( + self._poly_collection_cached_parameters["poly_collection"] + ) ( poly_collection, corrected_to_original_faces, ) = _grid_to_matplotlib_polycollection( - self, periodic_elements="ignore", projection=None, **kwargs + self, periodic_elements, projection, **kwargs ) - poly_collection.set_transform(ccrs.Geodetic()) - self._cached_poly_collection = poly_collection + if cache: + # cache PolyCollection, indices, and state + self._poly_collection_cached_parameters["poly_collection"] = poly_collection + self._poly_collection_cached_parameters["corrected_to_original_faces"] = ( + corrected_to_original_faces + ) + self._poly_collection_cached_parameters["periodic_elements"] = ( + periodic_elements + ) + self._poly_collection_cached_parameters["projection"] = projection - return copy.deepcopy(poly_collection) + if return_indices: + return copy.deepcopy(poly_collection), corrected_to_original_faces + else: + return copy.deepcopy(poly_collection) def to_linecollection( self, + periodic_elements: Optional[str] = "exclude", + projection: Optional[ccrs.Projection] = None, + cache: Optional[bool] = True, + override: Optional[bool] = False, **kwargs, ): """Constructs a ``matplotlib.collections.LineCollection``` consisting - of lines representing the edges of the unstructured grid. + of lines representing the edges of the current ``Grid`` Parameters ---------- + periodic_elements : str, optional + Method for handling periodic elements. One of ['exclude', 'split', or 'ignore']: + - 'exclude': Periodic elements will be identified and excluded from the GeoDataFrame + - 'split': Periodic elements will be identified and split using the ``antimeridian`` package + - 'ignore': No processing will be applied to periodic elements. + projection: ccrs.Projection + Cartopy geographic projection to use + cache: bool + Flag to indicate whether to cache the computed LineCollection + override: bool + Flag to indicate whether to override a cached LineCollection, if it exists **kwargs: dict Key word arguments to pass into the construction of a LineCollection """ - import cartopy.crs as ccrs - - if "projection" in kwargs: - proj = kwargs.pop("projection") - warn( - ( - "'projection' is not a supported argument and will be ignored. " - "Define the desired projection on the GeoAxes that this collection will be added to. " - "Example:\n" - " fig, ax = plt.subplots(subplot_kw={'projection': ccrs.Robinson()})\n" - " ax.add_collection(line_collection)\n" - f"(received projection={proj!r})" - ), - category=FutureWarning, - stacklevel=2, + if periodic_elements not in ["ignore", "exclude", "split"]: + raise ValueError( + f"Invalid value for 'periodic_elements'. Expected one of ['ignore', 'exclude', 'split'] but received: {periodic_elements}" ) - if self._cached_line_collection: - return copy.deepcopy(self._cached_line_collection) + + if self._line_collection_cached_parameters["line_collection"] is not None: + if ( + self._line_collection_cached_parameters["periodic_elements"] + != periodic_elements + or self._line_collection_cached_parameters["projection"] != projection + ): + override = True + + if not override: + return self._line_collection_cached_parameters["line_collection"] line_collection = _grid_to_matplotlib_linecollection( grid=self, - periodic_elements="ingore", - projection=None, + periodic_elements=periodic_elements, + projection=projection, **kwargs, ) - line_collection.set_transform(ccrs.Geodetic()) - - self._cached_line_collection = line_collection + if cache: + self._line_collection_cached_parameters["line_collection"] = line_collection + self._line_collection_cached_parameters["periodic_elements"] = ( + periodic_elements + ) + self._line_collection_cached_parameters["periodic_elements"] = ( + periodic_elements + ) return copy.deepcopy(line_collection) From 768b6ceaed786d1e4882814409086b5e0bab331d Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 13:45:10 -0700 Subject: [PATCH 02/11] add edge color, update docs notebook --- docs/user-guide/mpl.ipynb | 81 ++++++++++++++++++++++++++++++--------- uxarray/core/dataarray.py | 7 ++-- uxarray/grid/geometry.py | 2 +- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/docs/user-guide/mpl.ipynb b/docs/user-guide/mpl.ipynb index c61d436a3..59b525ae9 100644 --- a/docs/user-guide/mpl.ipynb +++ b/docs/user-guide/mpl.ipynb @@ -12,7 +12,7 @@ "This guide covers:\n", "1. Rasterizing Data onto a Cartopy {class}`~cartopy.mpl.geoaxes.GeoAxes`\n", "2. Visualizing Data with {class}`~matplotlib.collections.PolyCollection`\n", - "3. Visualizing Grid Topology with {class}`~matplotlib.collections.LineCollection`" + "3. Visualizing Grid Topology" ] }, { @@ -353,7 +353,8 @@ "metadata": {}, "outputs": [], "source": [ - "poly_collection = uxds[\"bottomDepth\"].to_polycollection()" + "projection = ccrs.Robinson()\n", + "poly_collection = uxds[\"bottomDepth\"].to_polycollection(projection=projection)" ] }, { @@ -363,9 +364,6 @@ "metadata": {}, "outputs": [], "source": [ - "# disables grid lines\n", - "poly_collection.set_antialiased(False)\n", - "\n", "poly_collection.set_cmap(\"Blues\")\n", "\n", "fig, ax = plt.subplots(\n", @@ -373,7 +371,7 @@ " 1,\n", " facecolor=\"w\",\n", " constrained_layout=True,\n", - " subplot_kw=dict(projection=ccrs.Robinson()),\n", + " subplot_kw=dict(projection=projection),\n", ")\n", "\n", "ax.add_feature(cfeature.COASTLINE)\n", @@ -389,6 +387,10 @@ "id": "22", "metadata": {}, "source": [ + "```{important}\n", + "By default, `periodic_elements` is set to `\"exclude\"`. \n", + "```\n", + "\n", "To reduce the number of polygons in the collection, you can [subset](./subset) before converting." ] }, @@ -401,7 +403,7 @@ "source": [ "lon_bounds = (-50, 50)\n", "lat_bounds = (-20, 20)\n", - "b = 5 # buffer for the selection so we can fill the plot area\n", + "b = 15 # buffer for the selection so we can fill the plot area\n", "\n", "poly_collection = (\n", " uxds[\"bottomDepth\"]\n", @@ -409,7 +411,7 @@ " lon_bounds=(lon_bounds[0] - b, lon_bounds[1] + b),\n", " lat_bounds=(lat_bounds[0] - b, lat_bounds[1] + b),\n", " )\n", - " .to_polycollection()\n", + " .to_polycollection(projection=projection)\n", ")\n", "\n", "poly_collection.set_cmap(\"Blues\")\n", @@ -420,16 +422,16 @@ " figsize=(7, 3.5),\n", " facecolor=\"w\",\n", " constrained_layout=True,\n", - " subplot_kw=dict(projection=ccrs.Robinson()),\n", + " subplot_kw=dict(projection=projection),\n", ")\n", "\n", - "ax.set_extent(lon_bounds + lat_bounds, crs=PlateCarree())\n", + "ax.set_extent(lon_bounds + lat_bounds)\n", "\n", "ax.add_feature(cfeature.COASTLINE)\n", "ax.add_feature(cfeature.BORDERS)\n", "\n", "ax.add_collection(poly_collection)\n", - "plt.title(\"PolyCollection\")" + "plt.title(\"subset PolyCollection\")" ] }, { @@ -437,13 +439,54 @@ "id": "24", "metadata": {}, "source": [ - "### Visualize Grid Topology with `LineCollection`\n", + "### Visualize Grid Topology\n" + ] + }, + { + "cell_type": "markdown", + "id": "36ddaa74", + "metadata": {}, + "source": [ + "#### Using `PolyCollection`\n", + "To visualize the unstructured grid geometry, you can set the edge color on an existing `PolyCollection`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e1970f9", + "metadata": {}, + "outputs": [], + "source": [ + "projection = ccrs.Robinson()\n", + "poly_collection = uxds[\"bottomDepth\"].to_polycollection(projection=projection)\n", "\n", - "To visualize the unstructured grid geometry, you can convert a {class}`~uxarray.Grid` into a {class}`~matplotlib.collections.LineCollection`, which stores the edges of the unstructured grid.\n", + "poly_collection.set_cmap(\"Blues\")\n", + "poly_collection.set_edgecolor('black')\n", "\n", - "```{important}\n", - "Since the transform for the {class}`~matplotlib.collections.LineCollection` and {class}`~matplotlib.collections.PolyCollection` are set to `ccrs.Geodetic()`, the edges and polygons are drawn correctly on the surface of a sphere and properly at the antimeridian.\n", - "```" + "fig, ax = plt.subplots(\n", + " 1,\n", + " 1,\n", + " facecolor=\"w\",\n", + " constrained_layout=True,\n", + " subplot_kw=dict(projection=projection),\n", + ")\n", + "\n", + "ax.add_feature(cfeature.COASTLINE)\n", + "ax.add_feature(cfeature.BORDERS)\n", + "\n", + "ax.add_collection(poly_collection)\n", + "ax.set_global()\n", + "plt.title(\"PolyCollection with set_edgecolor\")" + ] + }, + { + "cell_type": "markdown", + "id": "f45dbc72", + "metadata": {}, + "source": [ + "#### Using `LineCollection`\n", + "You can also convert a {class}`~uxarray.Grid` into a {class}`~matplotlib.collections.LineCollection`, which stores the edges of the unstructured grid." ] }, { @@ -453,7 +496,7 @@ "metadata": {}, "outputs": [], "source": [ - "line_collection = uxds.uxgrid.to_linecollection(colors=\"black\", linewidths=0.5)" + "line_collection = uxds.uxgrid.to_linecollection(colors=\"black\", linewidths=0.5, periodic_elements='split')" ] }, { @@ -481,7 +524,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "uxarray-viz-testing", "language": "python", "name": "python3" }, @@ -495,7 +538,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.13.11" } }, "nbformat": 4, diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index fcb6ea158..4c0b99a83 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -325,9 +325,7 @@ def to_polycollection( # index data to ignore data mapped to periodic elements _data = np.delete( self.values, - self.uxgrid._poly_collection_cached_parameters[ - "antimeridian_face_indices" - ], + self.uxgrid.antimeridian_face_indices, axis=0, ) elif periodic_elements == "split": @@ -350,6 +348,9 @@ def to_polycollection( poly_collection.set_array(_data) + # by default, set edge color to face color + poly_collection.set_edgecolor('face') + if return_indices: return poly_collection, corrected_to_original_faces else: diff --git a/uxarray/grid/geometry.py b/uxarray/grid/geometry.py index e474a7d09..cc81a9861 100644 --- a/uxarray/grid/geometry.py +++ b/uxarray/grid/geometry.py @@ -449,7 +449,7 @@ def _grid_to_matplotlib_polycollection( # Handle unsupported configuration: splitting periodic elements with projection if periodic_elements == "split" and projection is not None: raise ValueError( - "Explicitly projecting lines is not supported. Please pass in your projection" + "Explicitly projecting lines is not supported. Please pass in your projection " "using the 'transform' parameter" ) From 43905317d02c4059fa0c177aed1eaca86bc80aae Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 16:33:44 -0700 Subject: [PATCH 03/11] move set_edgecolor to mpl.ipynb --- docs/user-guide/mpl.ipynb | 10 ++++++++++ uxarray/core/dataarray.py | 5 +---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/user-guide/mpl.ipynb b/docs/user-guide/mpl.ipynb index 59b525ae9..27cc27b32 100644 --- a/docs/user-guide/mpl.ipynb +++ b/docs/user-guide/mpl.ipynb @@ -434,6 +434,16 @@ "plt.title(\"subset PolyCollection\")" ] }, + { + "cell_type": "markdown", + "id": "206c73f6", + "metadata": {}, + "source": [ + "```{tip}\n", + "If the rendered polygons not perfectly aligning produces weird visual bugs, try using `poly_collection.set_edgecolor('face)` and adjusting with `poly_collection.set_linewidth()` if needed.\n", + "```" + ] + }, { "cell_type": "markdown", "id": "24", diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 4c0b99a83..d4eb9caaa 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -325,7 +325,7 @@ def to_polycollection( # index data to ignore data mapped to periodic elements _data = np.delete( self.values, - self.uxgrid.antimeridian_face_indices, + self.uxgrid._poly_collection_cached_parameters["antimeridian_face_indices"], axis=0, ) elif periodic_elements == "split": @@ -348,9 +348,6 @@ def to_polycollection( poly_collection.set_array(_data) - # by default, set edge color to face color - poly_collection.set_edgecolor('face') - if return_indices: return poly_collection, corrected_to_original_faces else: From fe85ee7cf015b60c6bc59c4cfc5b1fe48d8477c0 Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 16:37:26 -0700 Subject: [PATCH 04/11] precommits --- docs/user-guide/mpl.ipynb | 8 +++++--- uxarray/core/dataarray.py | 4 +++- uxarray/grid/geometry.py | 1 - 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/user-guide/mpl.ipynb b/docs/user-guide/mpl.ipynb index 27cc27b32..29010acbc 100644 --- a/docs/user-guide/mpl.ipynb +++ b/docs/user-guide/mpl.ipynb @@ -403,7 +403,7 @@ "source": [ "lon_bounds = (-50, 50)\n", "lat_bounds = (-20, 20)\n", - "b = 15 # buffer for the selection so we can fill the plot area\n", + "b = 15 # buffer for the selection so we can fill the plot area\n", "\n", "poly_collection = (\n", " uxds[\"bottomDepth\"]\n", @@ -472,7 +472,7 @@ "poly_collection = uxds[\"bottomDepth\"].to_polycollection(projection=projection)\n", "\n", "poly_collection.set_cmap(\"Blues\")\n", - "poly_collection.set_edgecolor('black')\n", + "poly_collection.set_edgecolor(\"black\")\n", "\n", "fig, ax = plt.subplots(\n", " 1,\n", @@ -506,7 +506,9 @@ "metadata": {}, "outputs": [], "source": [ - "line_collection = uxds.uxgrid.to_linecollection(colors=\"black\", linewidths=0.5, periodic_elements='split')" + "line_collection = uxds.uxgrid.to_linecollection(\n", + " colors=\"black\", linewidths=0.5, periodic_elements=\"split\"\n", + ")" ] }, { diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index d4eb9caaa..fcb6ea158 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -325,7 +325,9 @@ def to_polycollection( # index data to ignore data mapped to periodic elements _data = np.delete( self.values, - self.uxgrid._poly_collection_cached_parameters["antimeridian_face_indices"], + self.uxgrid._poly_collection_cached_parameters[ + "antimeridian_face_indices" + ], axis=0, ) elif periodic_elements == "split": diff --git a/uxarray/grid/geometry.py b/uxarray/grid/geometry.py index cc81a9861..b7a0c5c51 100644 --- a/uxarray/grid/geometry.py +++ b/uxarray/grid/geometry.py @@ -475,7 +475,6 @@ def _grid_to_matplotlib_polycollection( central_longitude=central_longitude, ) - # Projected polygon shells if a projection is specified if projection is not None: projected_polygon_shells = _build_polygon_shells( From d6438eb773d7e35e184a10d31fe8c2c7c251bbe1 Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 16:50:42 -0700 Subject: [PATCH 05/11] precommits, typing --- uxarray/core/dataarray.py | 3 ++- uxarray/grid/grid.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index fcb6ea158..64bbeacb4 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -2,9 +2,10 @@ import warnings from html import escape -from typing import TYPE_CHECKING, Any, Hashable, Literal, Mapping +from typing import TYPE_CHECKING, Any, Hashable, Literal, Mapping, Optional from warnings import warn +import cartopy.crs as ccrs import numpy as np import xarray as xr from cartopy.mpl.geoaxes import GeoAxes diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 5a0885f2f..1b53a4811 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -7,7 +7,6 @@ import cartopy.crs as ccrs import numpy as np import xarray as xr -from spatialpandas import GeoDataFrame from xarray.core.options import OPTIONS from xarray.core.utils import UncachedAccessor From 377d5ae9a0d34c3f56b51330bb4667a722594051 Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 17:10:11 -0700 Subject: [PATCH 06/11] pull in small docstring change after revert --- uxarray/grid/grid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 1b53a4811..1559e3df1 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -2391,7 +2391,7 @@ def to_linecollection( **kwargs, ): """Constructs a ``matplotlib.collections.LineCollection``` consisting - of lines representing the edges of the current ``Grid`` + of lines representing the edges of the current unstructured grid. Parameters ---------- From 77e934a6660383dffaba880b862ad349600cef93 Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 17:11:29 -0700 Subject: [PATCH 07/11] remove projection warn test --- test/test_plot.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/test_plot.py b/test/test_plot.py index 90e568483..6231f84ae 100644 --- a/test/test_plot.py +++ b/test/test_plot.py @@ -229,10 +229,3 @@ def test_to_raster_pixel_ratio(gridpath, r1, r2): assert (d >= 0).all() and (d <= f - 1).all() -def test_collections_projection_kwarg(gridpath): - import cartopy.crs as ccrs - uxgrid = ux.open_grid(gridpath("ugrid", "outCSne30", "outCSne30.ug")) - - with pytest.warns(FutureWarning): - pc = uxgrid.to_polycollection(projection=ccrs.PlateCarree()) - lc = uxgrid.to_linecollection(projection=ccrs.PlateCarree()) From 00dbb593d754ad564a7499a7f02c09803d77af91 Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 17:16:37 -0700 Subject: [PATCH 08/11] update test --- test/core/test_dataarray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/test_dataarray.py b/test/core/test_dataarray.py index 49061748f..b53bc74ef 100644 --- a/test/core/test_dataarray.py +++ b/test/core/test_dataarray.py @@ -71,7 +71,7 @@ def test_to_polycollection(gridpath, datasetpath): uxds_geoflow['v1'].to_polycollection() # grid conversion - pc_geoflow_grid = uxds_geoflow.uxgrid.to_polycollection() + pc_geoflow_grid = uxds_geoflow.uxgrid.to_polycollection(periodic_elements="ignore") # number of elements assert len(pc_geoflow_grid._paths) == uxds_geoflow.uxgrid.n_face From 3d9eaf20182d10bcb8151ac6433f9c6fcd51dedc Mon Sep 17 00:00:00 2001 From: anissa111 Date: Mon, 9 Feb 2026 17:17:54 -0700 Subject: [PATCH 09/11] precommits --- test/test_plot.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_plot.py b/test/test_plot.py index 6231f84ae..f7cf29561 100644 --- a/test/test_plot.py +++ b/test/test_plot.py @@ -227,5 +227,3 @@ def test_to_raster_pixel_ratio(gridpath, r1, r2): f = r2 / r1 d = np.array(raster2.shape) - f * np.array(raster1.shape) assert (d >= 0).all() and (d <= f - 1).all() - - From 36f861d2a1f21d16718e0b84ddf286bab048812b Mon Sep 17 00:00:00 2001 From: anissa111 Date: Tue, 10 Feb 2026 11:28:24 -0700 Subject: [PATCH 10/11] reword hazing admonition --- docs/user-guide/mpl.ipynb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/user-guide/mpl.ipynb b/docs/user-guide/mpl.ipynb index 29010acbc..ffc9ef3d6 100644 --- a/docs/user-guide/mpl.ipynb +++ b/docs/user-guide/mpl.ipynb @@ -439,8 +439,7 @@ "id": "206c73f6", "metadata": {}, "source": [ - "```{tip}\n", - "If the rendered polygons not perfectly aligning produces weird visual bugs, try using `poly_collection.set_edgecolor('face)` and adjusting with `poly_collection.set_linewidth()` if needed.\n", + "When working with a fine grid, matplotlib's anti-alias algorithm can sometimes produce unexpected visualization effects that look like missing patches or a white \"hazing\" from the fine slivers between the polygons. Try using `poly_collection.set_edgecolor('face)` and adjusting with `poly_collection.set_linewidth()` if needed.\n", "```" ] }, From 2945c21152e0456bf864d86274054a9462f1ff82 Mon Sep 17 00:00:00 2001 From: anissa111 Date: Tue, 10 Feb 2026 11:49:54 -0700 Subject: [PATCH 11/11] fix admonition formatting --- docs/user-guide/mpl.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/user-guide/mpl.ipynb b/docs/user-guide/mpl.ipynb index ffc9ef3d6..2dfd8c1ed 100644 --- a/docs/user-guide/mpl.ipynb +++ b/docs/user-guide/mpl.ipynb @@ -439,6 +439,7 @@ "id": "206c73f6", "metadata": {}, "source": [ + "```{tip}\n", "When working with a fine grid, matplotlib's anti-alias algorithm can sometimes produce unexpected visualization effects that look like missing patches or a white \"hazing\" from the fine slivers between the polygons. Try using `poly_collection.set_edgecolor('face)` and adjusting with `poly_collection.set_linewidth()` if needed.\n", "```" ]