Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility fixes for matplotlib 3.5 #32

Merged
merged 4 commits into from
Feb 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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