From 7e8fab3bdd17660ffb52fa80e53352b516fac512 Mon Sep 17 00:00:00 2001 From: Marco Braun Date: Sun, 3 Dec 2023 23:42:16 -0500 Subject: [PATCH 1/9] Implement list for levels of colorbar in gridmap --- figanos/matplotlib/plot.py | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 8901e453..c6ef0422 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -20,6 +20,7 @@ import seaborn as sns import xarray as xr from cartopy import crs as ccrs +from collections.abc import Iterable from matplotlib.cm import ScalarMappable from matplotlib.lines import Line2D from matplotlib.projections import PolarAxes @@ -395,7 +396,7 @@ def gridmap( geometries_kw: dict[str, Any] | None = None, contourf: bool = False, cmap: str | matplotlib.colors.Colormap | None = None, - levels: int | None = None, + levels: int | list | np.ndarray | None = None, divergent: bool | int | float = False, show_time: bool | str | int | tuple[float, float] = False, frame: bool = False, @@ -434,8 +435,8 @@ def gridmap( If None, look for common variables (from data/ipcc_colors/varaibles_groups.json) in the name of the DataArray or its 'history' attribute and use corresponding colormap, aligned with the IPCC visual style guide 2022 (https://www.ipcc.ch/site/assets/uploads/2022/09/IPCC_AR6_WGI_VisualStyleGuide_2022.pdf). - levels : int, optional - Number of levels to divide the colormap into. + levels : int, list, np.ndarray, optional + Number of levels to divide the colormap into or list of level boundaries (in data units). divergent : bool or int or float If int or float, becomes center of cmap. Default center is 0. show_time : bool, tuple or {'top left', 'top right', 'bottom left', 'bottom right'} @@ -543,15 +544,17 @@ def gridmap( divergent=divergent, ) - if levels: - lin = custom_cmap_norm( - cmap, - np.nanmin(plot_data.values), - np.nanmax(plot_data.values), - levels=levels, - divergent=divergent, - linspace_out=True, - ) + if levels is not None: + lin = levels + if not isinstance(lin, Iterable): + lin = custom_cmap_norm( + cmap, + np.nanmin(plot_data.values), + np.nanmax(plot_data.values), + levels=levels, + divergent=divergent, + linspace_out=True, + ) plot_kw.setdefault("levels", lin) elif divergent and "levels" not in plot_kw: @@ -595,6 +598,16 @@ def gridmap( plotting.setdefault("levels", levels) im = plot_data.plot.contourf(**plotting) + # just a few notes for the subsequent if block: + # if we have a facetgrid + xr.plot.facetgrid.FacetGrid + # facets of facetgrid are + cartopy.mpl.geoaxes.GeoAxes + # if we don't have a facetgrid, depending on contourf we have either + cartopy.mpl.contour.GeoContourSet # or + cartopy.mpl.geoaxes.GeoAxes + # use im.axs to access the axes instead of im.axes + if ax: ax = add_features_map( data, From 3a1602f4378dfc0f960b08b28af8889bb6b10562 Mon Sep 17 00:00:00 2001 From: Marco Braun Date: Thu, 21 Mar 2024 10:22:42 -0400 Subject: [PATCH 2/9] samll fixes with comments --- figanos/matplotlib/plot.py | 33 ++++++++++++++++++++++++--------- figanos/matplotlib/utils.py | 3 ++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index ecc29b46..cf3c1535 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -552,6 +552,14 @@ def gridmap( ) plot_kw.setdefault("cmap", cmap) + # if levels: + # lin = custom_cmap_norm( + # cmap, + # np.nanmin(plot_data.values), + # np.nanmax(plot_data.values), + # divergent=divergent, + # linspace_out=True, + # ) if levels is not None: lin = levels if not isinstance(lin, Iterable): @@ -587,7 +595,7 @@ def gridmap( plot_kw["cbar_kwargs"].setdefault("label", wrap_text(cbar_label)) if transform and ("xlim" in plot_kw and "ylim" in plot_kw): - extend = [ + extent = [ plot_kw["xlim"][0], plot_kw["xlim"][1], plot_kw["ylim"][0], @@ -596,7 +604,7 @@ def gridmap( plot_kw.pop("xlim") plot_kw.pop("ylim") elif transform and ("xlim" in plot_kw or "ylim" in plot_kw): - extend = None + extent = None warnings.warn( "Requires both xlim and ylim with 'transform'. Xlim or ylim was dropped" ) @@ -605,8 +613,8 @@ def gridmap( if "ylim" in plot_kw.keys(): plot_kw.pop("ylim") else: - extend = None - # extend = None + extent = None + # extent = None # plot if ax: @@ -630,8 +638,8 @@ def gridmap( # use im.axs to access the axes instead of im.axes if ax: - if extend: - ax.set_extend(extend) + if extent: + ax.set_extent(extent) ax = add_features_map( data, ax, @@ -691,9 +699,9 @@ def gridmap( else: raise TypeError("show_time must be a bool, string,or tuple") - if extend: + if extent: for i, fax in enumerate(im.axs.flat): - fax.set_extent(extend) + fax.set_extent(extent) use_attrs.setdefault("suptitle", "long_name") return set_plot_attrs(use_attrs, data, facetgrid=im) @@ -1428,9 +1436,10 @@ def scattermap( else: raise ValueError("If `data` is a dict, it must be of length 1.") - # select data to plot + # select data to plot and its xr.Dataset if isinstance(data, xr.DataArray): plot_data = data + data = xr.Dataset({plot_data.name: plot_data}) elif isinstance(data, xr.Dataset): if len(data.data_vars) > 1: warnings.warn( @@ -1569,6 +1578,12 @@ def scattermap( if ax: plot_kw_pop.setdefault("ax", ax) im = data.plot.scatter(**plot_kw_pop) + # im = (data + # .where(xr.DataArray(mask, dims=plot_data.dims), drop=True) + # .plot.scatter(**plot_kw_pop) + # ) + # alternative (not sure if this would always work - does plot_data always have only one dimension?) + # data.sel({plot_data.dims[0]: mask}) # add features if ax: diff --git a/figanos/matplotlib/utils.py b/figanos/matplotlib/utils.py index f069bc1f..9fdb1bf9 100644 --- a/figanos/matplotlib/utils.py +++ b/figanos/matplotlib/utils.py @@ -8,6 +8,7 @@ import warnings from tempfile import NamedTemporaryFile from typing import Any, Callable +from copy import deepcopy import cairosvg import cartopy.crs as ccrs @@ -80,7 +81,7 @@ def empty_dict(param): """Return empty dict if input is None.""" if param is None: param = {} - return param + return deepcopy(param) # avoid modifying original dict when popping items def check_timeindex( From 2b8720e07891799f64f6edb8c685b68331614c54 Mon Sep 17 00:00:00 2001 From: Marco Braun Date: Thu, 21 Mar 2024 10:27:30 -0400 Subject: [PATCH 3/9] small fixes: make deepcopy of input dict (util.py); convert DataArray to Dataset, rename extend->extent (plot.py) --- figanos/matplotlib/plot.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index cf3c1535..3f731b0b 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -552,14 +552,6 @@ def gridmap( ) plot_kw.setdefault("cmap", cmap) - # if levels: - # lin = custom_cmap_norm( - # cmap, - # np.nanmin(plot_data.values), - # np.nanmax(plot_data.values), - # divergent=divergent, - # linspace_out=True, - # ) if levels is not None: lin = levels if not isinstance(lin, Iterable): @@ -614,7 +606,6 @@ def gridmap( plot_kw.pop("ylim") else: extent = None - # extent = None # plot if ax: @@ -1578,12 +1569,6 @@ def scattermap( if ax: plot_kw_pop.setdefault("ax", ax) im = data.plot.scatter(**plot_kw_pop) - # im = (data - # .where(xr.DataArray(mask, dims=plot_data.dims), drop=True) - # .plot.scatter(**plot_kw_pop) - # ) - # alternative (not sure if this would always work - does plot_data always have only one dimension?) - # data.sel({plot_data.dims[0]: mask}) # add features if ax: From 4c6cdf604df13fdb8f77db1d85a93ebaa594c6c7 Mon Sep 17 00:00:00 2001 From: Marco Braun Date: Thu, 21 Mar 2024 11:36:48 -0400 Subject: [PATCH 4/9] add AUTHORS.rst, CHANGES.rst --- AUTHORS.rst | 1 + CHANGES.rst | 3 ++- figanos/matplotlib/plot.py | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index a862c749..1900c4e3 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -19,3 +19,4 @@ Contributors ------------ * Gabriel Rondeau-Genesse `@RondeauG `_ +* Marco Braun `@vindelico `_ diff --git a/CHANGES.rst b/CHANGES.rst index fcc1f0c9..3a98c4c7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,7 @@ Changelog 0.4.0 (unreleased) ------------------ -Contributors to this version: Trevor James Smith (:user:`Zeitsperre`). +Contributors to this version: Trevor James Smith (:user:`Zeitsperre`), Marco Braun (:user:`vindelico`) Internal changes ^^^^^^^^^^^^^^^^ @@ -19,6 +19,7 @@ Contributors to this version: Sarah-Claude Bourdeau-Goulet (:user:`Sarahclaude`) New features and enhancements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Use list or ndarray as levels for colorbar and small bug fixes (:pull:`???`). * New function ``fg.matplotlib.hatchmap`` (:pull:`107`). * Support for translating figures. Activating a locale through `xclim`'s ``metadata_locales`` option will try to use metadata saved by `xclim` or `xscen` in this locale and to translate common terms appearing in the figures. `figanos` currently ships with French translations of those terms. (:pull:`109`, :issue:`64`). * New ``figanos.Logos`` class added to manage and install logos stored in user's Home configuration directory. The ``figanos.utils.plot_logo`` function call signature has changed to support the new system. (:issue:`115`, :pull:`119`). diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index b23bc79b..9d22af0d 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -684,8 +684,9 @@ def gridmap( plot_kw.setdefault("cmap", cmap) if levels is not None: - lin = levels - if not isinstance(lin, Iterable): + if isinstance(lin, Iterable): + lin = levels + else: lin = custom_cmap_norm( cmap, np.nanmin(plot_data.values), From 36d05662d52f7fb749d8e27c3351cf3eabfaf1d1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:38:29 +0000 Subject: [PATCH 5/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- figanos/matplotlib/plot.py | 5 +---- figanos/matplotlib/utils.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 9d22af0d..48f0fcd2 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -4,6 +4,7 @@ import copy import math import warnings +from collections.abc import Iterable from pathlib import Path from typing import Any @@ -20,7 +21,6 @@ import seaborn as sns import xarray as xr from cartopy import crs as ccrs -from collections.abc import Iterable from matplotlib.cm import ScalarMappable from matplotlib.lines import Line2D from matplotlib.projections import PolarAxes @@ -741,7 +741,6 @@ def gridmap( else: extent = None - # plot if ax: plot_kw.setdefault("ax", ax) @@ -753,8 +752,6 @@ def gridmap( else: im = plot_data.plot.contourf(**plot_kw) - - if ax: if extent: ax.set_extent(extent) diff --git a/figanos/matplotlib/utils.py b/figanos/matplotlib/utils.py index 794a7cc2..984f00db 100644 --- a/figanos/matplotlib/utils.py +++ b/figanos/matplotlib/utils.py @@ -7,9 +7,9 @@ import pathlib import re import warnings +from copy import deepcopy from tempfile import NamedTemporaryFile from typing import Any, Callable -from copy import deepcopy import cairosvg import cartopy.crs as ccrs From fc1ad562d24c979acf2670072f1e526bb8ec5461 Mon Sep 17 00:00:00 2001 From: Marco Braun Date: Thu, 21 Mar 2024 11:43:25 -0400 Subject: [PATCH 6/9] add .zenodo.json --- .zenodo.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.zenodo.json b/.zenodo.json index f554cf31..e86d2547 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -23,6 +23,11 @@ "name": "Rondeau-Genesse, Gabriel", "affiliation": "Ouranos", "orcid": "0000-0003-3389-9406" + }, + { + "name": "Braun, Marco", + "affiliation": "Ouranos", + "orcid": "0000-0001-5061-3217" } ], "keywords": [ From 3dc24fc790a789803361596272d9b5a923111304 Mon Sep 17 00:00:00 2001 From: Marco Braun Date: Thu, 21 Mar 2024 11:53:09 -0400 Subject: [PATCH 7/9] fix merging glitches --- figanos/matplotlib/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 48f0fcd2..84caa48d 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -684,7 +684,7 @@ def gridmap( plot_kw.setdefault("cmap", cmap) if levels is not None: - if isinstance(lin, Iterable): + if isinstance(levels, Iterable): lin = levels else: lin = custom_cmap_norm( @@ -794,8 +794,8 @@ def gridmap( geometries_kw, frame, ) - if extend: - fax.set_extent(extend) + if extent: + fax.set_extent(extent ) # when im is an ax, it has a colorbar attribute. If it is a facetgrid, it has a cbar attribute. if (frame is False) and ( From 5ee9539c17c49886629f87275944459bbd98b654 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:53:35 +0000 Subject: [PATCH 8/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- figanos/matplotlib/plot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/figanos/matplotlib/plot.py b/figanos/matplotlib/plot.py index 84caa48d..0d20c178 100644 --- a/figanos/matplotlib/plot.py +++ b/figanos/matplotlib/plot.py @@ -795,7 +795,7 @@ def gridmap( frame, ) if extent: - fax.set_extent(extent ) + fax.set_extent(extent) # when im is an ax, it has a colorbar attribute. If it is a facetgrid, it has a cbar attribute. if (frame is False) and ( From 13d5061f664624a897bb93647c60006b2ca263c5 Mon Sep 17 00:00:00 2001 From: Marco Braun <43412203+vindelico@users.noreply.github.com> Date: Thu, 21 Mar 2024 11:58:59 -0400 Subject: [PATCH 9/9] Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3a98c4c7..7555cd75 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,7 +19,7 @@ Contributors to this version: Sarah-Claude Bourdeau-Goulet (:user:`Sarahclaude`) New features and enhancements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Use list or ndarray as levels for colorbar and small bug fixes (:pull:`???`). +* Use list or ndarray as levels for colorbar in gridmap and small bug fixes (:pull:`???`). * New function ``fg.matplotlib.hatchmap`` (:pull:`107`). * Support for translating figures. Activating a locale through `xclim`'s ``metadata_locales`` option will try to use metadata saved by `xclim` or `xscen` in this locale and to translate common terms appearing in the figures. `figanos` currently ships with French translations of those terms. (:pull:`109`, :issue:`64`). * New ``figanos.Logos`` class added to manage and install logos stored in user's Home configuration directory. The ``figanos.utils.plot_logo`` function call signature has changed to support the new system. (:issue:`115`, :pull:`119`).