From dee760ab12297fc5abea901f4d3d26bca54a045b Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Wed, 27 Sep 2023 12:26:54 +1000 Subject: [PATCH] Make coasts and gridlines optional on plots There are currently a number of problems with gridlines on plots in Jupyter notebooks. There are different solutions that depend on whether you want an interactive plot, a static plot in a notebook, or are saving the plot to a file. Unfortunately there is no single solution that works in all cases currently. Until a proper solution is found, users can disable gridlines and then re-enable them in the way that works for their current environment. https://github.com/SciTools/cartopy/issues/2245 https://github.com/SciTools/cartopy/issues/2246 https://github.com/SciTools/cartopy/issues/2247 --- docs/releases/development.rst | 8 ++++++ src/emsarray/plot.py | 51 ++++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/docs/releases/development.rst b/docs/releases/development.rst index ab24975..927df15 100644 --- a/docs/releases/development.rst +++ b/docs/releases/development.rst @@ -5,3 +5,11 @@ Next release (in development) * Fix transect plot title and units. All attributes were being dropped accidentally in `prepare_data_array_for_transect()`. (:pr:`114`). +* Add `coast` and `gridlines` parameters to :func:`emsarray.plot.plot_on_figure()`, + allowing users to disable these components of a plot. + Currently gridlines can cause issues in interactive Jupyter notebooks + and some other environments. + There is no one solution to every situation. + Allowing users to disable gridlines is a temporary work around + while other solutions are being sought. + (:pr:`115`, :issue:`SciTools/cartopy#2245`, :issue:`SciTools/cartopy#2246`, :issue:`SciTools/cartopy#2247`). diff --git a/src/emsarray/plot.py b/src/emsarray/plot.py index 8ee6dd0..71eedbc 100644 --- a/src/emsarray/plot.py +++ b/src/emsarray/plot.py @@ -62,7 +62,12 @@ def add_coast(axes: Axes, **kwargs: Any) -> None: axes.add_feature(coast, **kwargs) -def add_gridlines(axes: Axes) -> gridliner.Gridliner: +def add_gridlines( + axes: Axes, + draw_labels={'left', 'bottom'}, + auto_update=True, + **kwargs: Any, +) -> gridliner.Gridliner: """ Add some gridlines to the axes. @@ -75,9 +80,11 @@ def add_gridlines(axes: Axes) -> gridliner.Gridliner: ------- cartopy.mpl.gridliner.Gridliner """ - gridlines = axes.gridlines(draw_labels=True, auto_update=True) - gridlines.top_labels = False - gridlines.right_labels = False + gridlines = axes.gridlines( + draw_labels=draw_labels, + auto_update=auto_update, + **kwargs, + ) return gridlines @@ -270,6 +277,8 @@ def plot_on_figure( title: Optional[str] = None, projection: Optional[cartopy.crs.Projection] = None, landmarks: Optional[Iterable[Landmark]] = None, + gridlines: bool = True, + coast: bool = True, ) -> None: """ Plot a :class:`~xarray.DataArray` @@ -298,6 +307,10 @@ def plot_on_figure( which is defined in :attr:`.Convention.data_crs`. landmarks : list of :data:`landmarks `, optional Landmarks to add to the plot. These are tuples of (name, point). + gridlines : bool, default True + Whether to add gridlines to the plot using :func:`add_gridlines()`. + coast : bool, default True + Whether to add coastlines to the plot using :func:`add_coast()`. """ if projection is None: projection = cartopy.crs.PlateCarree() @@ -331,10 +344,19 @@ def plot_on_figure( if landmarks: add_landmarks(axes, landmarks) - add_coast(axes) - add_gridlines(axes) + if coast: + add_coast(axes) + if gridlines: + add_gridlines(axes) + axes.autoscale() + # Work around for gridline positioning issues + # https://github.com/SciTools/cartopy/issues/2245#issuecomment-1732313921 + layout_engine = figure.get_layout_engine() + if layout_engine is not None: + layout_engine.execute(figure) + @_requires_plot def animate_on_figure( @@ -347,6 +369,8 @@ def animate_on_figure( title: Optional[Union[str, Callable[[Any], str]]] = None, projection: Optional[cartopy.crs.Projection] = None, landmarks: Optional[Iterable[Landmark]] = None, + gridlines: bool = True, + coast: bool = True, interval: int = 1000, repeat: Union[bool, Literal['cycle', 'bounce']] = True, ) -> animation.FuncAnimation: @@ -392,6 +416,10 @@ def animate_on_figure( which is defined in :attr:`.Convention.data_crs`. landmarks : list of :data:`landmarks `, optional Landmarks to add to the plot. These are tuples of (name, point). + gridlines : bool, default True + Whether to add gridlines to the plot using :func:`add_gridlines()`. + coast : bool, default True + Whether to add coastlines to the plot using :func:`add_coast()`. interval : int The interval between frames of animation repeat : {True, False, 'cycle', 'bounce'} @@ -442,8 +470,10 @@ def animate_on_figure( axes.add_collection(quiver) # Draw a coast overlay - add_coast(axes) - gridlines = add_gridlines(axes) + if coast: + add_coast(axes) + if gridlines: + gridliner = add_gridlines(axes) if landmarks: add_landmarks(axes, landmarks) axes.autoscale() @@ -470,8 +500,9 @@ def animate(index: int) -> Iterable[Artist]: coordinate_value = coordinate.values[index] axes.title.set_text(coordinate_callable(coordinate_value)) changes.append(axes.title) - changes.extend(gridlines.xline_artists) - changes.extend(gridlines.yline_artists) + if gridlines: + changes.extend(gridliner.xline_artists) + changes.extend(gridliner.yline_artists) if collection is not None: collection.set_array(scalar_values[index])