Skip to content
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
11 changes: 11 additions & 0 deletions .github/workflows/run-image-comparison.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,14 @@ jobs:
- name: Notify about a completed run
if: success()
run: echo "Image comparison testing completed successfully on ${{ matrix.os }}."

- name: Upload generated images and diffs
if: always()
uses: actions/upload-artifact@v4
with:
name: image-comparison-results-${{ runner.os }}
path: |
main/generated-example-images-adv/
main/generated-example-images-cont/
if-no-files-found: warn
retention-days: 7
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ of being developed elsewhere.

### Version `3.5.X`, first released 2026-10-09

#### Version `3.5.0`, release 2026-10-09
#### Version `3.5.1`, released 2026-??-??

* Update code to prevent deprecation-related warnings
(https://github.com/NCAS-CMS/cf-plot/issues/72)

#### Version `3.5.0`, released 2026-10-09

* Modularise and refactor the codebase, which has no user-facing implications
except that functionality is available via sub-modules as well as at the top-level
Expand Down
2 changes: 1 addition & 1 deletion cfplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from .vector import vect

__author__ = "Andy Heaps, Sadie Bartholomew, Bryan Lawrence"
__maintainer__ ="Sadie Bartholomew"
__maintainer__ = "Sadie Bartholomew"
__date__ = "2026-10-09"


Expand Down
17 changes: 9 additions & 8 deletions cfplot/blockfill.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,20 @@ def _bfill(
colmap = get_colour_scale_map()
cmap = matplotlib.colors.ListedColormap(colmap)
fill_colors = list(colmap)
if plotvars.levels_extend in ["min", "both"]:
cmap.set_under(plotvars.cs[0])
if plotvars.levels_extend in ["max", "both"]:
cmap.set_over(plotvars.cs[-1])

under_index = None
over_index = None
extremes = {}
if plotvars.levels_extend in ["min", "both"]:
extremes["under"] = plotvars.cs[0]
under_index = len(fill_colors)
fill_colors.append(plotvars.cs[0])
if plotvars.levels_extend in ["max", "both"]:
extremes["over"] = plotvars.cs[-1]
over_index = len(fill_colors)
fill_colors.append(plotvars.cs[-1])

if extremes:
cmap = cmap.with_extremes(**extremes)

else:
cols = single_fill_color
cmap = matplotlib.colors.ListedColormap(cols)
Expand Down Expand Up @@ -447,10 +448,10 @@ def _bfill_ugrid(

levs = deepcopy(np.array(clevs))

if plotvars.levels_extend == "min" or plotvars.levels_extend == "both":
if plotvars.levels_extend in ("min", "both"):
levs = np.concatenate([[-1e20], levs])
ilevs_max = np.size(levs)
if plotvars.levels_extend == "max" or plotvars.levels_extend == "both":
if plotvars.levels_extend in ("max", "both"):
levs = np.concatenate([levs, [1e20]])
else:
ilevs_max = ilevs_max - 1
Expand Down
8 changes: 6 additions & 2 deletions cfplot/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,19 @@ def cbar(
)

boundaries = levs.astype(float)
extremes = {}
if extend in ("both", "min"):
cmap.set_under(plotvars.cs[0])
extremes["under"] = plotvars.cs[0]
boundaries = np.insert(boundaries, 0, boundaries[0] - 1)
if extend in ("both", "max"):
cmap.set_over(plotvars.cs[-1])
extremes["over"] = plotvars.cs[-1]
boundaries = np.insert(
boundaries, len(boundaries), boundaries[-1] + 1
)

if extremes:
cmap = cmap.with_extremes(**extremes)

if not isinstance(levs, list):
lbot = None

Expand Down
8 changes: 6 additions & 2 deletions cfplot/colour/colour.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,15 +668,19 @@ def cbar(
# Add colorbar extensions if definded by levs. Using boundaries[0]-1
# for the lower and boundaries[-1]+1 is just for the colorbar and
# has no meaning for the plot.
extremes = {}
if extend in ("both", "min"):
cmap.set_under(plotvars.cs[0])
extremes["under"] = plotvars.cs[0]
boundaries = np.insert(boundaries, 0, boundaries[0] - 1)
if extend in ("both", "max"):
cmap.set_over(plotvars.cs[-1])
extremes["over"] = plotvars.cs[-1]
boundaries = np.insert(
boundaries, len(boundaries), boundaries[-1] + 1
)

if extremes:
cmap = cmap.with_extremes(**extremes)

if not isinstance(levs, list):
lbot = None

Expand Down
8 changes: 6 additions & 2 deletions cfplot/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,10 +469,14 @@ def get_cmap(self) -> matplotlib.colors.ListedColormap:
colmap = get_colour_scale_map()
cmap = matplotlib.colors.ListedColormap(colmap)

extremes = {}
if scale.levels_extend == "min" or scale.levels_extend == "both":
cmap.set_under(scale.cs[0])
extremes["under"] = plotvars.cs[0]
if scale.levels_extend == "max" or scale.levels_extend == "both":
cmap.set_over(scale.cs[-1])
extremes["over"] = plotvars.cs[-1]

if extremes:
cmap = cmap.with_extremes(**extremes)

return cmap

Expand Down
68 changes: 52 additions & 16 deletions cfplot/trajectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@
from .state import plotvars


def _trajectory_segments(x, y):
"""Yield contiguous segments of finite, unmasked coordinates."""
x = np.ma.asarray(x, dtype=float)
y = np.ma.asarray(y, dtype=float)

x_data = x.filled(np.nan)
y_data = y.filled(np.nan)

valid = (
~np.ma.getmaskarray(x)
& ~np.ma.getmaskarray(y)
& np.isfinite(x_data)
& np.isfinite(y_data)
)

edges = np.diff(np.r_[False, valid, False].astype(int))
starts = np.flatnonzero(edges == 1)
stops = np.flatnonzero(edges == -1)

for start, stop in zip(starts, stops):
yield start, stop, x_data[start:stop], y_data[start:stop]


def traj(
f=None,
title=None,
Expand Down Expand Up @@ -305,22 +328,35 @@ def traj(
print("plotting markers")

if legend_lines is False:
mymap.plot(
xpts,
ypts,
color=linecolor,
linewidth=plot_linewidth,
linestyle=linestyle,
marker=marker,
markevery=markevery,
markersize=plot_markersize,
markerfacecolor=markerfacecolor,
markeredgecolor=markeredgecolor,
markeredgewidth=markeredgewidth,
zorder=zorder,
clip_on=True,
transform=ccrs.PlateCarree(),
)
for start, stop, x_segment, y_segment in _trajectory_segments(
xpts, ypts
):
segment_markevery = markevery
if (
isinstance(markevery, (int, np.integer))
and markevery > 0
):
segment_markevery = np.flatnonzero(
np.arange(start, stop) % markevery == 0
)
segment_markevery -= start

mymap.plot(
x_segment,
y_segment,
color=linecolor,
linewidth=plot_linewidth,
linestyle=linestyle,
marker=marker,
markevery=segment_markevery,
markersize=plot_markersize,
markerfacecolor=markerfacecolor,
markeredgecolor=markeredgecolor,
markeredgewidth=markeredgewidth,
zorder=zorder,
clip_on=True,
transform=ccrs.PlateCarree(),
)
else:
line_xpts = xpts.compressed()
line_ypts = ypts.compressed()
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_contour_plot_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def _assert_reference_match_strict(generated: Path, reference: Path) -> None:
result = mpl_compare.compare_images(
str(reference),
str(generated),
tol=0.,
tol=0.0,
in_decorator=True,
)
if result is not None:
Expand Down
Loading