Skip to content

Commit

Permalink
[semver:patch] Compatibility fixes for matplotlib 3.5 (#32)
Browse files Browse the repository at this point in the history
Compatibility fixes

Changed
=======

- We now use the new `convert_coordinate` method that has been introduced with
  psyplot v1.4.1 (see psyplot/psyplot#39 and #30)

Fixed
=====
- psy-simple is now compatible with matplotlib 3.5 (see #31)
  • Loading branch information
Chilipp authored Feb 14, 2022
1 parent ac305c0 commit 91d3bda
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 62 deletions.
3 changes: 2 additions & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ install:
- pip install -i https://pypi.anaconda.org/psyplot/simple --no-deps psyplot-ci-orb
- conda config --add channels conda-forge
- conda config --add channels psyplot
- conda config --add channels psyplot/label/develop
- conda info -a
- conda list
# windows config
Expand All @@ -43,7 +44,7 @@ after_test:
deploy_script:
- cmd: "
IF NOT DEFINED APPVEYOR_REPO_TAG_NAME (
deploy-conda-recipe -l %APPVEYOR_REPO_BRANCH% -py %PYTHON_VERSION% ci/conda-recipe
deploy-conda-recipe -l %APPVEYOR_REPO_BRANCH:/=-% -py %PYTHON_VERSION% ci/conda-recipe
) ELSE (
deploy-conda-recipe -py %PYTHON_VERSION% ci/conda-recipe
)"
Expand Down
5 changes: 3 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: 2.1

orbs:
psyplot: psyplot/psyplot-ci-orb@1.5.25
psyplot: psyplot/psyplot-ci-orb@1.5.29
mattermost-plugin-notify: nathanaelhoun/mattermost-plugin-notify@1.2.0

executors:
Expand All @@ -28,7 +28,7 @@ parameters:
parallelism:
description: How many parallel jobs to execute
type: integer
default: 2
default: 4
build_docs:
description: Build the documentation
type: boolean
Expand All @@ -46,6 +46,7 @@ workflows:
build_args: "--no-test"
build_docs: << pipeline.parameters.build_docs >>
env_packages: pytest-cov dask psyplot-gui statsmodels netcdf4 seaborn
default_branch: develop
- psyplot/test-parallel:
name: test-matplotlib-latest
parallelism: << pipeline.parameters.parallelism >>
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
v1.4.1
======
Compatibility fixes

Changed
-------
- We now use the new ``convert_coordinate`` method that has been introduced
with psyplot v1.4.1 (see
`psyplot/psyplot#39 <https://github.com/psyplot/psyplot/pull/39>`__ and
`#30 <https://github.com/psyplot/psy-simple/pull/30>`__)

Fixed
-----
- psy-simple is now compatible with matplotlib 3.5 (see
`#31 <https://github.com/psyplot/psy-simple/pull/31>`__)

v1.4.0
======
Compatibility fixes and LGPL license
Expand Down
1 change: 1 addition & 0 deletions docs/environment.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: psyplot_docs
channels:
- local
- psyplot/label/__CURRENTBRANCH__
- psyplot/label/master
- conda-forge
dependencies:
Expand Down
5 changes: 3 additions & 2 deletions psy_simple/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
# You should have received a copy of the GNU LGPL-3.0 license
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from psyplot.compat.pycompat import isstring
import six
import matplotlib as mpl
from matplotlib.colors import Colormap, LinearSegmentedColormap, BoundaryNorm
Expand Down Expand Up @@ -203,11 +204,11 @@ def get_cmap(name, lut=None):
Different from the :func::`matpltolib.pyplot.get_cmap` function, this
function changes the number of colors if `name` is a
:class:`matplotlib.colors.Colormap` instance to match the given `lut`."""
if name in rcParams['colors.cmaps']:
if isstring(name) and name in rcParams['colors.cmaps']:
colors = rcParams['colors.cmaps'][name]
lut = lut or len(colors)
return FixedColorMap.from_list(name=name, colors=colors, N=lut)
elif name in _cmapnames:
elif isstring(name) and name in _cmapnames:
colors = _cmapnames[name]
lut = lut or len(colors)
return FixedColorMap.from_list(name=name, colors=colors, N=lut)
Expand Down
72 changes: 42 additions & 30 deletions psy_simple/plotters.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ def convert_radian(coord, *variables):
xr.Variable
The transformed variable if one of the given `variables` has units in
radian"""
warn(
"The psy_simple.plotters.convert_radian method has been deprecated."
"Please use the `plotter.convert_coordinate` instead.",
DeprecationWarning
)
if any(v.attrs.get('units', '').startswith('radian') for v in variables):
return coord * 180. / np.pi
return coord
Expand Down Expand Up @@ -2807,8 +2812,7 @@ def array(self):
data, axis='x', coords=data.coords)
if bounds is None:
bounds = xcoord
if self.plotter.convert_radian:
bounds = convert_radian(bounds, xcoord, bounds)
bounds = self.convert_coordinate(bounds, xcoord)
return bounds.values.ravel()
return self.decoder.get_plotbounds(xcoord)

Expand All @@ -2826,8 +2830,7 @@ def array(self):
data, axis='y', coords=data.coords)
if bounds is None:
bounds = ycoord
if self.plotter.convert_radian:
bounds = convert_radian(bounds, ycoord, bounds)
bounds = self.convert_coordinate(bounds, ycoord)
return bounds.values.ravel()
return self.decoder.get_plotbounds(self.transpose.get_y(self.data))

Expand Down Expand Up @@ -3128,6 +3131,9 @@ def update(self, value):
else:
if isinstance(value[0], six.string_types):
value = self.calc_funcs[value[0]](*value[1:])
if value[0] == value[-1]:
# make sure we have a small difference between the values
value[-1] += value[-1] * 0.5
self.bounds = value
self.norm = mpl.colors.BoundaryNorm(
value, len(value) - 1)
Expand Down Expand Up @@ -3216,8 +3222,7 @@ class Plot2D(Formatoption):
dataset, we will interpolate them
'contourf'
Make a filled contour plot using the :func:`matplotlib.pyplot.contourf`
function or the :func:`matplotlib.pyplot.tricontourf` for unstructured
data. The levels for the contour plot are controlled by the
function. The levels for the contour plot are controlled by the
:attr:`levels` formatoption
'contour'
Same a ``'contourf'``, but does not make a filled contour plot, only
Expand Down Expand Up @@ -3351,19 +3356,15 @@ def _contourf(self):
self.remove()
arr = self.array
cmap = self.cmap.get_cmap(arr)
filled = self.value not in ['contour', 'tricontour']
filled = self.value != 'contour'
if hasattr(self, '_plot'):
self._plot.set_cmap(cmap)
self._plot.set_norm(self.bounds.norm)
else:
levels = self.levels.norm.boundaries
xcoord = self.xcoord
ycoord = self.ycoord
if self.plotter.convert_radian:
xcoord = convert_radian(xcoord, xcoord)
ycoord = convert_radian(ycoord, ycoord)
if (self.value in ['tricontourf', 'tricontour'] or
self.decoder.is_unstructured(self.raw_data)):
xcoord = self.convert_coordinate(self.xcoord)
ycoord = self.convert_coordinate(self.ycoord)
if self.decoder.is_unstructured(self.raw_data):
pm = self.ax.tricontourf if filled else self.ax.tricontour
mask = ~np.isnan(arr)
x = xcoord.values[mask]
Expand All @@ -3385,8 +3386,7 @@ def cell_nodes_x(self):
data = self.data
xbounds = decoder.get_cell_node_coord(
data, coords=data.coords, axis='x')
if self.plotter.convert_radian:
xbounds = convert_radian(xbounds, xcoord, xbounds)
xbounds = self.convert_coordinate(xbounds, xcoord)
return xbounds.values

@property
Expand All @@ -3397,8 +3397,7 @@ def cell_nodes_y(self):
data = self.data
ybounds = decoder.get_cell_node_coord(
data, coords=data.coords, axis='y')
if self.plotter.convert_radian:
ybounds = convert_radian(ybounds, ycoord, ybounds)
ybounds = self.convert_coordinate(ybounds, ycoord)
return ybounds.values

def _polycolor(self):
Expand Down Expand Up @@ -3631,8 +3630,7 @@ def cell_nodes_x(self):
xbounds = decoder.get_cell_node_coord(
data, coords=data.coords, axis='x',
nans='skip' if self.mask_datagrid.value else None)
if self.plotter.convert_radian:
xbounds = convert_radian(xbounds, xcoord, xbounds)
xbounds = self.convert_coordinate(xbounds, xcoord)
return xbounds.values

@property
Expand All @@ -3644,8 +3642,7 @@ def cell_nodes_y(self):
ybounds = decoder.get_cell_node_coord(
data, coords=data.coords, axis='y',
nans='skip' if self.mask_datagrid.value else None)
if self.plotter.convert_radian:
ybounds = convert_radian(ybounds, ycoord, ybounds)
ybounds = self.convert_coordinate(ybounds, ycoord, ybounds)
return ybounds.values

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -3993,8 +3990,14 @@ def update_colorbar(self, pos):
old.callbacksSM.disconnect(old.colorbar_cid)
old.colorbar = None
old.colorbar_cid = None
cid = mappable.callbacksSM.connect(
'changed', cbar.on_mappable_changed)
if mpl.__version__ < "3.3":
cid = mappable.callbacksSM.connect(
'changed', cbar.on_mappable_changed
)
else:
cid = mappable.callbacksSM.connect(
'changed', cbar.update_normal
)
mappable.colorbar = cbar
mappable.colorbar_cid = cid
cbar.update_normal(cbar.mappable)
Expand Down Expand Up @@ -4092,6 +4095,18 @@ def draw_colorbar(self, pos):
kwargs['extend'] = self.extend.value
if 'location' not in kwargs:
kwargs['orientation'] = orientation
if mpl.__version__.startswith("3.5.0"):
from matplotlib.contour import ContourSet
if (
kwargs.get("orientation") == "horizontal" and
isinstance(self.plot.mappable, ContourSet)
):
warn(
"Horizontal colorbars are not possible for contour plots "
"with matplotlib 3.5.0, see "
"https://github.com/matplotlib/matplotlib/issues/21683"
)
kwargs.pop("orientation")
self.cbars[pos] = cbar = fig.colorbar(self.plot.mappable, **kwargs)
self._just_drawn.add(cbar)
self.set_label_pos(pos)
Expand Down Expand Up @@ -4897,8 +4912,9 @@ def keep(x):
except ValueError:
pass
# remove arrows
self.ax.patches = [patch for patch in self.ax.patches
if keep(patch)]
for patch in list(self.ax.patches):
if not keep(patch):
self.ax.patches.remove(patch)
else:
try:
self._plot.remove()
Expand Down Expand Up @@ -5650,10 +5666,6 @@ class Base2D(Plotter):
"""Base plotter for 2-dimensional plots
"""

#: Boolean that is True if coordinates with units in radian should be
#: converted to degrees
convert_radian = False

_rcparams_string = ['plotter.plot2d.']

cmap = CMap('cmap')
Expand Down
26 changes: 13 additions & 13 deletions psy_simple/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
validate_stringset)
from matplotlib.rcsetup import (
validate_bool, validate_color, validate_fontsize,
ValidateInStrings, validate_int, validate_legend_loc,
validate_colorlist)
ValidateInStrings, validate_int, validate_colorlist
)
from psy_simple import __version__ as plugin_version
import xarray as xr

Expand Down Expand Up @@ -437,6 +437,16 @@ def validate_sym_lims(val):
return list(map(validator, val))


valid_legend_locs = [
"best",
"upper right", "upper left", "lower left", "lower right", "right",
"center left", "center right", "lower center", "upper center",
"center"
]

validate_legend_loc = ValidateInStrings("legend_loc", valid_legend_locs, True)


def validate_legend(value):
if isinstance(value, dict):
return value
Expand Down Expand Up @@ -713,19 +723,9 @@ def __call__(self, val):

def validate_plot(val):
validator = ValidateInStrings(
'2d plot', ['mesh', 'contourf', 'contour', 'poly',
'tri', 'tricontourf', 'tricontour'], True)
'2d plot', ['mesh', 'contourf', 'contour', 'poly'], True)

val = validator(val)
depr_map = {
"tri": "poly", "tricontourf": "contourf", "tricontour": "contour"
}
if val in depr_map:
warn("plot=%r is depreceated for the plot formatoption and will be "
"removed in psy-simple 1.4.0. Please use plot=%r instead." % (
val, depr_map[val]),
DeprecationWarning)
return depr_map[val]
return val


Expand Down
15 changes: 1 addition & 14 deletions tests/test_plot2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,20 +402,7 @@ def test_single_level(self):
ds = xr.Dataset()
ds["test"] = (("y", "x"), np.ones((4, 5)))
sp = ds.psy.plot.plot2d(cmap="Reds", bounds=["rounded", 3])
self.assertEqual(list(sp.plotters[0].bounds.bounds), [1.0, 1.0, 1.0])


@pytest.mark.parametrize(
"old,new",
(("tri", "poly"), ("tricontour", "contour"), ("tricontourf", "contourf")),
)
def test_plot_deprecation(old, new):
"""Test if tri is deprecated correctly"""
with psy.open_dataset(os.path.join(bt.test_dir, "icon_test.nc")) as ds:
with pytest.warns(DeprecationWarning, match="plot=[\"']%s[\"']" % old):
sp = ds.psy.plot.plot2d(name="t2m", plot=old)
plotter = sp.plotters[0]
assert plotter.plot.value == new
self.assertEqual(list(sp.plotters[0].bounds.bounds), [1.0, 1.0, 1.5])


def test_plot_poly_3D_bounds():
Expand Down

0 comments on commit 91d3bda

Please sign in to comment.