Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
0d264bd
Starting the refactor
May 14, 2026
ce66907
Continuing structural refactor, still using legacy to do the work though
May 14, 2026
c5a9abb
Migrated contour related tests into integration and unit tests in the…
May 14, 2026
35d81dc
interim commit while doing real function migration
May 14, 2026
e5e4d49
Moved away from relying on legacy to -notimplementederror- so we can …
May 14, 2026
e3d84c9
Clarity around viewports and hooks for animation
May 14, 2026
e9b4f01
Near complete coverage now of the contouring using new code, includes…
May 14, 2026
6e42c61
Cleaning up project setup and testing environment
May 14, 2026
3ee7805
Fixed orca handling, including adding extra testing paths
May 14, 2026
eb20fd0
Now really trying to extract old cfplot dependencies from contour
May 14, 2026
8136d53
refactor concept
May 14, 2026
a6a86bd
Interim step migrating utility functions out of cfplot
May 14, 2026
009bd6e
Fixing AI getting itself in a pickle
May 14, 2026
cd4e42c
More interim cfplot inclusions
May 14, 2026
6256738
More interim migration
May 14, 2026
860e5cb
Moving plotvars
May 14, 2026
b3b4fc0
Sync reference images and add missing contour datasets
May 15, 2026
529b409
Moved reference image location
May 15, 2026
54fda34
Fix file save path suffix detection in refactored contour renderer
May 15, 2026
6c370e1
Fix field ordering in Examples 6 and 9 using identity-based fixture l…
May 15, 2026
e58af64
Fixed remaining identity risks in tests
May 15, 2026
75af3e8
Ignore and untrack generated test images
May 15, 2026
3d58203
Continuing to decouple cfplot.py from contour.py
May 15, 2026
dbc58f7
Starting to create stateful contouring support modules
May 15, 2026
dee8633
Migrate gopen/gclose to contour runtime and test view flag
May 15, 2026
213080e
Extract map setup into MapRuntime class, remove all cfplot imports fr…
May 15, 2026
757cb80
Addressing the plot mismatch issues
May 15, 2026
0aa7fb8
Interim commit while examining example 4
May 15, 2026
8677eb7
Iterating towards satisfactory stereographic rendering
May 15, 2026
67df810
Acceptable example 4 performance
May 15, 2026
2ea07fd
Steady progress, up to, but not including 19 (of the ones which are c…
May 15, 2026
8db8604
Up to 21 working now, starting to find some mis-labelled reference im…
May 15, 2026
522db38
Now working through test 22
May 15, 2026
759f5e4
All the map tests extracted in first pass now pass, including some wi…
May 16, 2026
42936db
Removing semantic leakage between map and layout runtime
May 16, 2026
3c5fb5a
Making further progress on refactoring, and inconsistent defaults bet…
May 16, 2026
ac2eafc
New code now owns setvars and implements grid=False/True interaction …
May 16, 2026
139c728
All but one skipped test pass. Note that we don't yet have the full t…
May 16, 2026
c946a29
Dealt with another couple of issue with rotated pole via CANARI example
May 16, 2026
c930fd9
refactor(l1): move map title helpers into map_runtime
May 16, 2026
46b926b
refactor(l2-part1): extract map features helper to centralize ocean/l…
May 17, 2026
9a97797
refactor(l2): complete - rotated_runtime module with centralized map …
May 17, 2026
8279b95
refactor(l4): consolidate _add_cyclic into utility.py
May 17, 2026
918d79f
refactor(l3+l7): colourbar_labels as canonical impl; maybe_autosave t…
May 17, 2026
010df52
refactor(l9): remove dead lonlat arg and enforce integration image mi…
May 18, 2026
09291d9
refactor(l6): extract xy tick logic and refresh contour references
May 18, 2026
ec1b0c3
docs: mark L6 tick extraction as completed
May 18, 2026
89f8028
merge: main into integration branch (prefer refactor26 architecture)
May 20, 2026
e27a457
interim commit as we bring things together
May 20, 2026
dba2b72
Tests run now, though many are skipped because the reference images a…
May 21, 2026
12fcba2
Still iterating towards retiring cfplot.py
May 21, 2026
2264890
interim commit
May 21, 2026
154fc01
Interim commit
May 21, 2026
84d78d5
interim commit
May 21, 2026
906b62d
Near complete merge, with no dependency now on cfplot, I think
May 21, 2026
5db09ec
Further iteration towards converged plotvar state
May 22, 2026
2c8e723
Finally renamed cfplot.py out of the way
May 22, 2026
c751a76
Removed depdency on old utils folder
May 23, 2026
70c6694
interim commit
May 23, 2026
e587c0b
Removed internal parameters package
May 23, 2026
285074d
Continuing to rationalise state and colour
May 23, 2026
d0c6f16
more redudant code cleanup, calculate, and graphic gone
May 23, 2026
5ad102a
Carried out a comprehensive clean out of duplicated functionality
May 23, 2026
d9f8293
misisng tests
May 23, 2026
7e83b37
Dev documentation as of today
May 24, 2026
050cd3b
cleaning up pvars, maybe
May 27, 2026
ab533b3
Clarifying version
May 27, 2026
7853e7d
Bug fix for part of https://github.com/NCAS-CMS/xconv2/issues/35
May 27, 2026
162b917
Missing unit tests
May 27, 2026
3eea098
Tests for the bug fix for orthographic projections
May 27, 2026
32a02db
Merge branch 'main-into-refactor26' into main-refactor26-pvars
May 27, 2026
dc805e0
Includes existing lfric plotting functionality
May 27, 2026
fb46a46
clarity in testing
May 28, 2026
0de93ac
Include the planned animation support
May 28, 2026
0e47ec6
Merge branch 'main-refactor26-pvars'
May 28, 2026
094a860
Testing and packaging fixes following copilot review
May 28, 2026
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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# User specfic gubbins
.vscode/
.DS_Store

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down Expand Up @@ -159,3 +163,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# Test-generated plot outputs
/generated-example-images/
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,32 @@ f = cf.read('<dataset name>.nc')[0] # picks out a read-in field of the dataset
cfp.con(f.subspace(time=<chosen time value>)) # creates a contour plot of the field at that time value
```

### Contour Animation Titles

`cfp.con()` supports animation-aware title updates via:

- `animation=True`
- `animation_axis="auto"` (or one of `"T"`, `"Z"`, `"Y"`, `"X"`)
- `animation_title_template="{title} [{frame}]"` (optional)

With `animation_axis="auto"`, axis inference is based on `ptype`:

- for `ptype != 0`, choose a singleton axis not used by that `ptype`;
- for `ptype == 0`, fallback preference is singleton `T`, then `Z`, then `Y`, then `X`.

Example:

```python
cfp.con(
f,
animation=True,
reuse_map_background=True,
animation_axis="auto",
animation_title_template="{title} [{frame}]",
title="Air temperature",
)
```


### Examples Gallery

Expand Down
Binary file added cf-plot-refactor-claude.pdf
Binary file not shown.
Binary file added cfplot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
208 changes: 110 additions & 98 deletions cfplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,114 +4,126 @@
Documentation is hosted and found at: https://ncas-cms.github.io/cf-plot/
"""

__author__ = "Andy Heaps"
__maintainer__ = "Sadie Bartholomew"
__date__ = "28th April 2025"
__version__ = "3.4.0"
from importlib.metadata import PackageNotFoundError, version as pkg_version
from pathlib import Path

__author__ = "Andy Heaps, Sadie Bartholomew, Bryan Lawrence"
__date__ = "16th May, 2026"

import os
from distutils.version import StrictVersion

import cartopy
import cf
import matplotlib
def _version_from_pyproject():
pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
if not pyproject.exists():
return None

# Import module functionality -------------------------------------------
# Imports to export functions: cfp.<module>.<function> -> cfp.<function>
# either as intended going forward or to preserve existing API.
# TODO review what should be available at module level.
in_project = False
for raw_line in pyproject.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#"):
continue
if line.startswith("[") and line.endswith("]"):
in_project = line == "[project]"
continue
if in_project and line.startswith("version"):
_, _, value = line.partition("=")
return value.strip().strip('"').strip("'")

from .calculate import calculate_levels
from .colour import (
# Internal fuctions: don't expose, but leave commented here to track:
# _cscale_get_map,; _process_color_scales,
cbar,
)
return None


def _resolve_version():
# In a source checkout, pyproject.toml is the version source of truth.
pyproject_version = _version_from_pyproject()
if pyproject_version:
return pyproject_version

# In installed-package contexts, use distribution metadata.
try:
return pkg_version("cf-plot")
except PackageNotFoundError:
return "0+unknown"


__version__ = _resolve_version()

from .colorbar import cbar
from .colour import cscale
from .contour import con
from .graphic import (
# Internal fuctions: don't expose, but leave commented here to track:
# _which,
gclose,
gopen,
gpos,
)
from .contour import levs
from .layout_runtime import gclose, gopen, gset
from .layout_runtime import gpos
from .line import lineplot
from .mapping import (
# Internal fuctions: don't expose, but leave commented here to track:
# _mapaxis,; _map_title,; _plot_map_axes,; _set_map,
axes_plot,
map_grid,
)
from .parameters import (
allvars_defaults,
axes,
cscale,
cscale1,
global_blockfill,
global_fill,
global_lines,
gset,
levs,
mapset,
plotvars,
plotvars_defaults,
pvars,
reset,
setvars,
setvars_defaults,
viridis,
)
from .map_runtime import mapset
from .state import plotvars, setvars
from .stipple import stipple
from .stream import stream
from .trajectory import traj
from .utils import (
# Internal fuctions: don't expose, but leave commented here to track:;
# _bfill,; _bfill_ugrid,; _cf_data_assign,; _dim_titles,; _gvals,; _supscr,;
# _timeaxis,
add_cyclic,
cf_var_name,
cf_var_name_titles,
find_dim_names,
find_pos_in_array,
find_z,
fix_floats,
generate_titles,
irregular_window,
max_ndecs_data,
ndecs,
pcon,
polar_regular_grid,
regrid,
rgaxes,
stipple_points,
vloc,
)
from .validate import check_well_formed, orca_check # _check_data internal
from .state import reset_runtime_state
from .utility import gvals as _gvals_impl, mapaxis as _mapaxis_impl, regrid
from .vector import vect
from .rotated_runtime import _render_rotated_grid_axes


def reset():
gset()
cscale()
levs()
mapset()
setvars()
reset_runtime_state()


def _gvals(*args, **kwargs):
return _gvals_impl(*args, **kwargs)


def rgaxes(*, xpole=None, ypole=None, xvec=None, yvec=None, xticks=None, xticklabels=None, yticks=None, yticklabels=None, axes=True, xaxis=True, yaxis=True, xlabel=None, ylabel=None):
return _render_rotated_grid_axes(
xpole=xpole,
ypole=ypole,
xvec=xvec,
yvec=yvec,
xticks=xticks,
xticklabels=xticklabels,
yticks=yticks,
yticklabels=yticklabels,
axes=axes,
xaxis=xaxis,
yaxis=yaxis,
xlabel=xlabel,
ylabel=ylabel,
)


def _mapaxis(min=None, max=None, type=None):
return _mapaxis_impl(
min_val=min,
max_val=max,
axis_type=type,
degsym=bool(plotvars.degsym),
)


# Process versions and display ------------------------------------------

# Check for the minimum cf-python version
cf_version_min = "3.17.0"
errstr = f"cf-python >= {cf_version_min} needs to be installed to use cf-plot"
if StrictVersion(cf.__version__) < StrictVersion(cf_version_min):
raise Warning(errstr)
# TODO add these checks for all other dependencies too?

# Check for a display and use the Agg backing store if none is present
# This is for batch mode processing
try:
disp = os.environ["DISPLAY"]
except Exception:
matplotlib.use("Agg")

# Check for user setting of pre_existing_data_dir pointing to central
# cartopy setup
# This is used in the cfview simple setup process
try:
pre_existing_data_dir = os.environ["pre_existing_data_dir"]
cartopy.config["pre_existing_data_dir"] = pre_existing_data_dir
except KeyError:
pass
__all__ = [
"cbar",
"con",
"cscale",
"gclose",
"gopen",
"gpos",
"gset",
"levs",
"lineplot",
"mapset",
"plotvars",
"regrid",
"reset",
"rgaxes",
"setvars",
"stipple",
"stream",
"traj",
"vect",
"_gvals",
"_mapaxis",
]
Loading