Skip to content

Commit 91d3bda

Browse files
authored
[semver:patch] Compatibility fixes for matplotlib 3.5 (#32)
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)
1 parent ac305c0 commit 91d3bda

File tree

8 files changed

+81
-62
lines changed

8 files changed

+81
-62
lines changed

.appveyor.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ install:
1818
- pip install -i https://pypi.anaconda.org/psyplot/simple --no-deps psyplot-ci-orb
1919
- conda config --add channels conda-forge
2020
- conda config --add channels psyplot
21+
- conda config --add channels psyplot/label/develop
2122
- conda info -a
2223
- conda list
2324
# windows config
@@ -43,7 +44,7 @@ after_test:
4344
deploy_script:
4445
- cmd: "
4546
IF NOT DEFINED APPVEYOR_REPO_TAG_NAME (
46-
deploy-conda-recipe -l %APPVEYOR_REPO_BRANCH% -py %PYTHON_VERSION% ci/conda-recipe
47+
deploy-conda-recipe -l %APPVEYOR_REPO_BRANCH:/=-% -py %PYTHON_VERSION% ci/conda-recipe
4748
) ELSE (
4849
deploy-conda-recipe -py %PYTHON_VERSION% ci/conda-recipe
4950
)"

.circleci/config.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: 2.1
22

33
orbs:
4-
psyplot: psyplot/[email protected].25
4+
psyplot: psyplot/[email protected].29
55
mattermost-plugin-notify: nathanaelhoun/[email protected]
66

77
executors:
@@ -28,7 +28,7 @@ parameters:
2828
parallelism:
2929
description: How many parallel jobs to execute
3030
type: integer
31-
default: 2
31+
default: 4
3232
build_docs:
3333
description: Build the documentation
3434
type: boolean
@@ -46,6 +46,7 @@ workflows:
4646
build_args: "--no-test"
4747
build_docs: << pipeline.parameters.build_docs >>
4848
env_packages: pytest-cov dask psyplot-gui statsmodels netcdf4 seaborn
49+
default_branch: develop
4950
- psyplot/test-parallel:
5051
name: test-matplotlib-latest
5152
parallelism: << pipeline.parameters.parallelism >>

CHANGELOG.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
v1.4.1
2+
======
3+
Compatibility fixes
4+
5+
Changed
6+
-------
7+
- We now use the new ``convert_coordinate`` method that has been introduced
8+
with psyplot v1.4.1 (see
9+
`psyplot/psyplot#39 <https://github.com/psyplot/psyplot/pull/39>`__ and
10+
`#30 <https://github.com/psyplot/psy-simple/pull/30>`__)
11+
12+
Fixed
13+
-----
14+
- psy-simple is now compatible with matplotlib 3.5 (see
15+
`#31 <https://github.com/psyplot/psy-simple/pull/31>`__)
16+
117
v1.4.0
218
======
319
Compatibility fixes and LGPL license

docs/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: psyplot_docs
22
channels:
33
- local
4+
- psyplot/label/__CURRENTBRANCH__
45
- psyplot/label/master
56
- conda-forge
67
dependencies:

psy_simple/colors.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
# You should have received a copy of the GNU LGPL-3.0 license
2929
# along with this program. If not, see <https://www.gnu.org/licenses/>.
3030

31+
from psyplot.compat.pycompat import isstring
3132
import six
3233
import matplotlib as mpl
3334
from matplotlib.colors import Colormap, LinearSegmentedColormap, BoundaryNorm
@@ -203,11 +204,11 @@ def get_cmap(name, lut=None):
203204
Different from the :func::`matpltolib.pyplot.get_cmap` function, this
204205
function changes the number of colors if `name` is a
205206
:class:`matplotlib.colors.Colormap` instance to match the given `lut`."""
206-
if name in rcParams['colors.cmaps']:
207+
if isstring(name) and name in rcParams['colors.cmaps']:
207208
colors = rcParams['colors.cmaps'][name]
208209
lut = lut or len(colors)
209210
return FixedColorMap.from_list(name=name, colors=colors, N=lut)
210-
elif name in _cmapnames:
211+
elif isstring(name) and name in _cmapnames:
211212
colors = _cmapnames[name]
212213
lut = lut or len(colors)
213214
return FixedColorMap.from_list(name=name, colors=colors, N=lut)

psy_simple/plotters.py

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ def convert_radian(coord, *variables):
138138
xr.Variable
139139
The transformed variable if one of the given `variables` has units in
140140
radian"""
141+
warn(
142+
"The psy_simple.plotters.convert_radian method has been deprecated."
143+
"Please use the `plotter.convert_coordinate` instead.",
144+
DeprecationWarning
145+
)
141146
if any(v.attrs.get('units', '').startswith('radian') for v in variables):
142147
return coord * 180. / np.pi
143148
return coord
@@ -2807,8 +2812,7 @@ def array(self):
28072812
data, axis='x', coords=data.coords)
28082813
if bounds is None:
28092814
bounds = xcoord
2810-
if self.plotter.convert_radian:
2811-
bounds = convert_radian(bounds, xcoord, bounds)
2815+
bounds = self.convert_coordinate(bounds, xcoord)
28122816
return bounds.values.ravel()
28132817
return self.decoder.get_plotbounds(xcoord)
28142818

@@ -2826,8 +2830,7 @@ def array(self):
28262830
data, axis='y', coords=data.coords)
28272831
if bounds is None:
28282832
bounds = ycoord
2829-
if self.plotter.convert_radian:
2830-
bounds = convert_radian(bounds, ycoord, bounds)
2833+
bounds = self.convert_coordinate(bounds, ycoord)
28312834
return bounds.values.ravel()
28322835
return self.decoder.get_plotbounds(self.transpose.get_y(self.data))
28332836

@@ -3128,6 +3131,9 @@ def update(self, value):
31283131
else:
31293132
if isinstance(value[0], six.string_types):
31303133
value = self.calc_funcs[value[0]](*value[1:])
3134+
if value[0] == value[-1]:
3135+
# make sure we have a small difference between the values
3136+
value[-1] += value[-1] * 0.5
31313137
self.bounds = value
31323138
self.norm = mpl.colors.BoundaryNorm(
31333139
value, len(value) - 1)
@@ -3216,8 +3222,7 @@ class Plot2D(Formatoption):
32163222
dataset, we will interpolate them
32173223
'contourf'
32183224
Make a filled contour plot using the :func:`matplotlib.pyplot.contourf`
3219-
function or the :func:`matplotlib.pyplot.tricontourf` for unstructured
3220-
data. The levels for the contour plot are controlled by the
3225+
function. The levels for the contour plot are controlled by the
32213226
:attr:`levels` formatoption
32223227
'contour'
32233228
Same a ``'contourf'``, but does not make a filled contour plot, only
@@ -3351,19 +3356,15 @@ def _contourf(self):
33513356
self.remove()
33523357
arr = self.array
33533358
cmap = self.cmap.get_cmap(arr)
3354-
filled = self.value not in ['contour', 'tricontour']
3359+
filled = self.value != 'contour'
33553360
if hasattr(self, '_plot'):
33563361
self._plot.set_cmap(cmap)
33573362
self._plot.set_norm(self.bounds.norm)
33583363
else:
33593364
levels = self.levels.norm.boundaries
3360-
xcoord = self.xcoord
3361-
ycoord = self.ycoord
3362-
if self.plotter.convert_radian:
3363-
xcoord = convert_radian(xcoord, xcoord)
3364-
ycoord = convert_radian(ycoord, ycoord)
3365-
if (self.value in ['tricontourf', 'tricontour'] or
3366-
self.decoder.is_unstructured(self.raw_data)):
3365+
xcoord = self.convert_coordinate(self.xcoord)
3366+
ycoord = self.convert_coordinate(self.ycoord)
3367+
if self.decoder.is_unstructured(self.raw_data):
33673368
pm = self.ax.tricontourf if filled else self.ax.tricontour
33683369
mask = ~np.isnan(arr)
33693370
x = xcoord.values[mask]
@@ -3385,8 +3386,7 @@ def cell_nodes_x(self):
33853386
data = self.data
33863387
xbounds = decoder.get_cell_node_coord(
33873388
data, coords=data.coords, axis='x')
3388-
if self.plotter.convert_radian:
3389-
xbounds = convert_radian(xbounds, xcoord, xbounds)
3389+
xbounds = self.convert_coordinate(xbounds, xcoord)
33903390
return xbounds.values
33913391

33923392
@property
@@ -3397,8 +3397,7 @@ def cell_nodes_y(self):
33973397
data = self.data
33983398
ybounds = decoder.get_cell_node_coord(
33993399
data, coords=data.coords, axis='y')
3400-
if self.plotter.convert_radian:
3401-
ybounds = convert_radian(ybounds, ycoord, ybounds)
3400+
ybounds = self.convert_coordinate(ybounds, ycoord)
34023401
return ybounds.values
34033402

34043403
def _polycolor(self):
@@ -3631,8 +3630,7 @@ def cell_nodes_x(self):
36313630
xbounds = decoder.get_cell_node_coord(
36323631
data, coords=data.coords, axis='x',
36333632
nans='skip' if self.mask_datagrid.value else None)
3634-
if self.plotter.convert_radian:
3635-
xbounds = convert_radian(xbounds, xcoord, xbounds)
3633+
xbounds = self.convert_coordinate(xbounds, xcoord)
36363634
return xbounds.values
36373635

36383636
@property
@@ -3644,8 +3642,7 @@ def cell_nodes_y(self):
36443642
ybounds = decoder.get_cell_node_coord(
36453643
data, coords=data.coords, axis='y',
36463644
nans='skip' if self.mask_datagrid.value else None)
3647-
if self.plotter.convert_radian:
3648-
ybounds = convert_radian(ybounds, ycoord, ybounds)
3645+
ybounds = self.convert_coordinate(ybounds, ycoord, ybounds)
36493646
return ybounds.values
36503647

36513648
def __init__(self, *args, **kwargs):
@@ -3993,8 +3990,14 @@ def update_colorbar(self, pos):
39933990
old.callbacksSM.disconnect(old.colorbar_cid)
39943991
old.colorbar = None
39953992
old.colorbar_cid = None
3996-
cid = mappable.callbacksSM.connect(
3997-
'changed', cbar.on_mappable_changed)
3993+
if mpl.__version__ < "3.3":
3994+
cid = mappable.callbacksSM.connect(
3995+
'changed', cbar.on_mappable_changed
3996+
)
3997+
else:
3998+
cid = mappable.callbacksSM.connect(
3999+
'changed', cbar.update_normal
4000+
)
39984001
mappable.colorbar = cbar
39994002
mappable.colorbar_cid = cid
40004003
cbar.update_normal(cbar.mappable)
@@ -4092,6 +4095,18 @@ def draw_colorbar(self, pos):
40924095
kwargs['extend'] = self.extend.value
40934096
if 'location' not in kwargs:
40944097
kwargs['orientation'] = orientation
4098+
if mpl.__version__.startswith("3.5.0"):
4099+
from matplotlib.contour import ContourSet
4100+
if (
4101+
kwargs.get("orientation") == "horizontal" and
4102+
isinstance(self.plot.mappable, ContourSet)
4103+
):
4104+
warn(
4105+
"Horizontal colorbars are not possible for contour plots "
4106+
"with matplotlib 3.5.0, see "
4107+
"https://github.com/matplotlib/matplotlib/issues/21683"
4108+
)
4109+
kwargs.pop("orientation")
40954110
self.cbars[pos] = cbar = fig.colorbar(self.plot.mappable, **kwargs)
40964111
self._just_drawn.add(cbar)
40974112
self.set_label_pos(pos)
@@ -4897,8 +4912,9 @@ def keep(x):
48974912
except ValueError:
48984913
pass
48994914
# remove arrows
4900-
self.ax.patches = [patch for patch in self.ax.patches
4901-
if keep(patch)]
4915+
for patch in list(self.ax.patches):
4916+
if not keep(patch):
4917+
self.ax.patches.remove(patch)
49024918
else:
49034919
try:
49044920
self._plot.remove()
@@ -5650,10 +5666,6 @@ class Base2D(Plotter):
56505666
"""Base plotter for 2-dimensional plots
56515667
"""
56525668

5653-
#: Boolean that is True if coordinates with units in radian should be
5654-
#: converted to degrees
5655-
convert_radian = False
5656-
56575669
_rcparams_string = ['plotter.plot2d.']
56585670

56595671
cmap = CMap('cmap')

psy_simple/plugin.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040
validate_stringset)
4141
from matplotlib.rcsetup import (
4242
validate_bool, validate_color, validate_fontsize,
43-
ValidateInStrings, validate_int, validate_legend_loc,
44-
validate_colorlist)
43+
ValidateInStrings, validate_int, validate_colorlist
44+
)
4545
from psy_simple import __version__ as plugin_version
4646
import xarray as xr
4747

@@ -437,6 +437,16 @@ def validate_sym_lims(val):
437437
return list(map(validator, val))
438438

439439

440+
valid_legend_locs = [
441+
"best",
442+
"upper right", "upper left", "lower left", "lower right", "right",
443+
"center left", "center right", "lower center", "upper center",
444+
"center"
445+
]
446+
447+
validate_legend_loc = ValidateInStrings("legend_loc", valid_legend_locs, True)
448+
449+
440450
def validate_legend(value):
441451
if isinstance(value, dict):
442452
return value
@@ -713,19 +723,9 @@ def __call__(self, val):
713723

714724
def validate_plot(val):
715725
validator = ValidateInStrings(
716-
'2d plot', ['mesh', 'contourf', 'contour', 'poly',
717-
'tri', 'tricontourf', 'tricontour'], True)
726+
'2d plot', ['mesh', 'contourf', 'contour', 'poly'], True)
718727

719728
val = validator(val)
720-
depr_map = {
721-
"tri": "poly", "tricontourf": "contourf", "tricontour": "contour"
722-
}
723-
if val in depr_map:
724-
warn("plot=%r is depreceated for the plot formatoption and will be "
725-
"removed in psy-simple 1.4.0. Please use plot=%r instead." % (
726-
val, depr_map[val]),
727-
DeprecationWarning)
728-
return depr_map[val]
729729
return val
730730

731731

tests/test_plot2d.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -402,20 +402,7 @@ def test_single_level(self):
402402
ds = xr.Dataset()
403403
ds["test"] = (("y", "x"), np.ones((4, 5)))
404404
sp = ds.psy.plot.plot2d(cmap="Reds", bounds=["rounded", 3])
405-
self.assertEqual(list(sp.plotters[0].bounds.bounds), [1.0, 1.0, 1.0])
406-
407-
408-
@pytest.mark.parametrize(
409-
"old,new",
410-
(("tri", "poly"), ("tricontour", "contour"), ("tricontourf", "contourf")),
411-
)
412-
def test_plot_deprecation(old, new):
413-
"""Test if tri is deprecated correctly"""
414-
with psy.open_dataset(os.path.join(bt.test_dir, "icon_test.nc")) as ds:
415-
with pytest.warns(DeprecationWarning, match="plot=[\"']%s[\"']" % old):
416-
sp = ds.psy.plot.plot2d(name="t2m", plot=old)
417-
plotter = sp.plotters[0]
418-
assert plotter.plot.value == new
405+
self.assertEqual(list(sp.plotters[0].bounds.bounds), [1.0, 1.0, 1.5])
419406

420407

421408
def test_plot_poly_3D_bounds():

0 commit comments

Comments
 (0)