Skip to content
Open
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ of being developed elsewhere.
-----


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

#### Version `3.5.1`, released 2026-??-??

* Update code to prevent deprecation-related warnings
(https://github.com/NCAS-CMS/cf-plot/issues/72)
* Prevent striped artefacts on curvilinear longitude/latitude grids such as NEMO.

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

Expand Down
8 changes: 5 additions & 3 deletions cfplot/blockfill.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ def _bfill(
colarr[pts] = np.size(levels) - 2

if isinstance(field, np.ma.MaskedArray):
pts = np.ma.where(field.mask)
if np.size(pts) > 0:
colarr[pts] = -1
# np.ma.where on a scalar False mask raises on newer NumPy.
# Use a normalized boolean mask array instead.
mask = np.ma.getmaskarray(field)
if np.any(mask):
colarr[mask] = -1

norm = matplotlib.colors.BoundaryNorm(levels, cmap.N)

Expand Down
91 changes: 86 additions & 5 deletions cfplot/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ def render_filled(
runtime.image = runtime.mymap.contourf(
lons,
lats,
self.data.field * self.data.fmult,
_mask_curvilinear_contour_seams(self.data) * self.data.fmult,
self.data.levels,
extend=scale.levels_extend,
cmap=cmap,
Expand Down Expand Up @@ -792,10 +792,11 @@ def render_lines(

runtime = plotvars.runtime
dec = plotvars.decoration
field = _mask_curvilinear_contour_seams(self.data) * self.data.fmult
cs = runtime.mymap.contour(
self.data.x,
self.data.y,
self.data.field * self.data.fmult,
field,
self.data.levels,
colors=colors,
linewidths=linewidths,
Expand Down Expand Up @@ -825,7 +826,7 @@ def render_lines(
cs0 = runtime.mymap.contour(
self.data.x,
self.data.y,
self.data.field * self.data.fmult,
field,
[-1e-32, 0],
colors=colors,
linewidths=zero_thick,
Expand Down Expand Up @@ -1697,6 +1698,70 @@ def _window_irregular_map_data(
return field_irregular, lons_irregular, lats_irregular


def _is_curvilinear_map_grid(data: ContourData) -> bool:
"""Return True for map plots using 2D lon/lat coordinates.

Curvilinear grids (for example NEMO/ORCA ocean meshes) are prone to
severe contourf/contour artefacts. These are better rendered through
blockfill/pcolormesh.
"""
if data.ptype != 1 or data.x is None or data.y is None:
return False
if np.ndim(data.field) != 2:
return False
return np.ndim(data.x) == 2 and np.ndim(data.y) == 2


def _mask_curvilinear_contour_seams(data: ContourData) -> np.ndarray:
"""Exclude vertices adjoining longitude jumps from structured contours."""
if not _is_curvilinear_map_grid(data):
return data.field

mask = np.ma.getmaskarray(data.field).copy()
for axis in (0, 1):
jumps = np.abs(np.diff(data.x, axis=axis)) > 180.0
before = [slice(None), slice(None)]
after = before.copy()
before[axis] = slice(None, -1)
after[axis] = slice(1, None)
# Mask both ends so corner masking cannot retain a seam-crossing
# triangle and interpolate a contour across the whole map.
mask[tuple(before)] |= jumps
mask[tuple(after)] |= jumps

return np.ma.array(data.field, mask=mask)


def _resolve_curvilinear_render_defaults(
*,
data: ContourData,
kwargs: dict[str, Any],
fill: bool,
lines: bool,
blockfill: bool,
blockfill_fast: bool | None,
) -> tuple[bool, bool, bool, bool | None]:
"""Adjust renderer defaults for curvilinear lon/lat map grids.

Returns the possibly updated (fill, lines, blockfill, blockfill_fast)
tuple.
"""
if not _is_curvilinear_map_grid(data):
return fill, lines, blockfill, blockfill_fast

# Use robust defaults for 2D map grids unless the caller explicitly
# requested alternative behavior.
if "blockfill" not in kwargs:
blockfill = True
fill = False
if blockfill and blockfill_fast is None:
blockfill_fast = True
if "lines" not in kwargs:
lines = False

return fill, lines, blockfill, blockfill_fast


def _render_with_new_xy(
f: Any, x: Any, y: Any, kwargs: dict[str, Any]
) -> bool:
Expand Down Expand Up @@ -1776,6 +1841,7 @@ def _render_with_new_xy(
fill = kwargs.get("fill", global_fill)
lines = kwargs.get("lines", global_lines)
blockfill = kwargs.get("blockfill", global_blockfill)
blockfill_fast = kwargs.get("blockfill_fast", None)
line_labels = kwargs.get("line_labels", True)
zero_thick = kwargs.get("zero_thick", False)
colors = kwargs.get("colors", "k")
Expand All @@ -1787,6 +1853,17 @@ def _render_with_new_xy(
if blockfill:
fill = False

fill, lines, blockfill, blockfill_fast = (
_resolve_curvilinear_render_defaults(
data=data,
kwargs=kwargs,
fill=fill,
lines=lines,
blockfill=blockfill,
blockfill_fast=blockfill_fast,
)
)

colorbar = kwargs.get("colorbar", True)
if not fill and not blockfill:
colorbar = False
Expand Down Expand Up @@ -2171,7 +2248,7 @@ def _render_with_new_xy(
)
if blockfill:
renderer.render_blockfill(
fast=kwargs.get("blockfill_fast", None), alpha=alpha, zorder=zorder
fast=blockfill_fast, alpha=alpha, zorder=zorder
)
if lines:
renderer.render_lines(
Expand Down Expand Up @@ -2350,7 +2427,11 @@ def con(f=None, x=None, y=None, **kwargs):
| 6 = rotated pole
| negative_linestyle='solid' - set to one of 'solid', 'dashed'
| zero_thick=False - add a thick zero contour line. Set to 3 for example.
| blockfill=False - set to True for a blockfill plot
| blockfill=False - set to True for a blockfill plot. For 2D lon/lat
| grids, defaults to fast blockfill without lines.
| Explicit False selects contours; vertices adjoining
| longitude wrap jumps are excluded to avoid stripes,
| leaving a narrow gap at the seam.
| colorbar_title=colbar_title - title for the colour bar
| colorbar=True - add a colour bar if a filled contour plot
| colorbar_label_skip=None - skip colour bar labels. Set to 2 to skip
Expand Down
2 changes: 1 addition & 1 deletion docs/source/examples/example_1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Example 1: Basic contour plot in default projection
cfp.con(f.subspace(time=15))


.. figure:: /../../tests/reference-example-images/ref_fig_1.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_1.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Example 10: Latitude-time Hovmöller plot
cfp.con(f.subspace(longitude=0), lines=0)


.. figure:: /../../tests/reference-example-images/ref_fig_10.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_10.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Example 11: Latitude-time subset view Hovmöller plot
cfp.con(f.subspace(longitude=0), lines=0)


.. figure:: /../../tests/reference-example-images/ref_fig_11.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_11.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Example 12: Longitude-time Hovmöller plot
cfp.con(f.subspace(latitude=0), lines=0)


.. figure:: /../../tests/reference-example-images/ref_fig_12.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_12.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Example 13: Basic vector plot

cfp.vect(u=u, v=v, key_length=10, scale=100, stride=5)

.. figure:: /../../tests/reference-example-images/ref_fig_13.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_13.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ Example 14: Vector plot overlaid on a contour map
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_14.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_14.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ Example 15: Polar projection vector plot
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_15.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_15.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_16a.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ Example 16a: Zonal vector plot
)


.. figure:: /../../tests/reference-example-images/ref_fig_16a.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_16a.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_16b.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ Example 16b: Basic stream plot
cfp.stream(u=u, v=v, density=2)


.. figure:: /../../tests/reference-example-images/ref_fig_16b.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_16b.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_16c.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ Example 16c: Stream plot in a colour scale
)
cfp.gclose()

.. figure:: /../../tests/reference-example-images/ref_fig_16c.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_16c.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_17.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ Example 17: Basic stipple plot
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_17.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_17.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ Example 18: Polar stipple plot
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_18.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_18.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_19a.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ Example 19a: Multiple plots
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_19a.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_19a.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_19b.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ Example 19b: Multiple plots with user-specified plot positions
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_19b.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_19b.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_19c.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ Example 19c: Accomodating more than one colour bar
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_19c.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_19c.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ Example 2: Basic blockfill plot in default projection
cfp.con(f.subspace(time=15), blockfill=True, lines=False)


.. figure:: /../../tests/reference-example-images/ref_fig_2.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_2.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_20.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ Example 20: Case where user-defined axis labels are required
cfp.con(f.subspace[9])


.. figure:: /../../tests/reference-example-images/ref_fig_20.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_20.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_21a.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ Example 21a: User-defined axes
)


.. figure:: /../../tests/reference-example-images/ref_fig_21a.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_21a.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_21b.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Example 21b: Plot of rotated pole data

cfp.con(f)

.. figure:: /../../tests/reference-example-images/ref_fig_21b.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_21b.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_22.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ Example 22: Plot of rotated pole data on its native grid
cfp.con(f)


.. figure:: /../../tests/reference-example-images/ref_fig_22.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_22.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_23a.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ Example 23a: Rotated pole plot from data which is not CF Compliant
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_23a.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_23a.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_23b.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ Example 23b: Overlaying vectors over a rotated pole data plot
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_23b.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_23b.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_24a.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ Example 24a: UGRID blockfill plot with LFRic cubed sphere mesh output
)


.. figure:: /../../tests/reference-example-images/ref_fig_24a.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_24a.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_24b.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ Example 24b: UGRID plot in the polar stereographic projection view
)


.. figure:: /../../tests/reference-example-images/ref_fig_24b.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_24b.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_24c.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ Example 24c: UGRID contour plot with LFRic cubed sphere mesh output
cfp.con(g, lines=False)


.. figure:: /../../tests/reference-example-images/ref_fig_24c.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_24c.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_25.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ Example 25: UGRID contour plot with ORCA 2 output
cfp.con(f=temp.array, x=lons.array, y=lats.array, ptype=1)


.. figure:: /../../tests/reference-example-images/ref_fig_25.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_25.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_26a.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ Example 26a: Contour plot based on discrete feature values
)


.. figure:: /../../tests/reference-example-images/ref_fig_26a.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_26a.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_26b.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ Example 26b: Contour plot from discrete feature values with labelling
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_26b.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_26b.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_27.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ Example 27: Basic line plot
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_27.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_27.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_28.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ Example 28: Line plot with a legend
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_28.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_28.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_29.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ Example 29: Time series line plot
)


.. figure:: /../../tests/reference-example-images/ref_fig_29.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_29.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ Example 3: Contour plot with altered map limits and levels
cfp.con(f.subspace(time=15))


.. figure:: /../../tests/reference-example-images/ref_fig_3.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_3.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_30.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ Example 30: Line plot with two x axes
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_30.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_30.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_31.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ Example 31: UKCP projection

cfp.con(f, lines=False)

.. figure:: /../../tests/reference-example-images/ref_fig_31.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_31.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_32.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ Example 32: UKCP projection with blockfill
)


.. figure:: /../../tests/reference-example-images/ref_fig_32.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_32.png
2 changes: 1 addition & 1 deletion docs/source/examples/example_33.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Example 33: OSGB and EuroPP projections
cfp.gclose()


.. figure:: /../../tests/reference-example-images/ref_fig_33.png
.. figure:: /../../tests/v350-controlled-env-ref-images/ref_fig_33.png
Loading