This is a Python package from the Bloom lab that allows you to combine an Altair / Vega-Lite plot with a Nextstrain JSON of a phylogenetic tree so that the tree is aligned to annotate strains on the axis of the plot. See https://jbloomlab.github.io/tree-annotated-plot/ for detailed documentation.
Note: charts must be saved from altair 6+ (Vega-Lite v6). Older specs raise by default; pass
--no-strict-version(CLI) orstrict_version=False(Python) to override at your own risk. See the docs for details.
Released on PyPI. Requires Python 3.13+.
pip install tree-annotated-plotTo install the bleeding edge directly from this repository:
pip install git+https://github.com/jbloomlab/tree-annotated-plot.gitFor a development checkout, see Installation (development) below.
See CHANGELOG.md for the version history.
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,docs]"pyproject.toml is the canonical source for the supported Python version and runtime dependencies.
This package has automated tests for chart structure (encodings,
sort order, tip reconciliation, scale-bar arithmetic, etc.), but
not for chart appearance — there are no image-snapshot
regression tests, because rendering is sensitive to fonts,
vl-convert-python versions, and platform differences in ways that
make pixel-level comparison flaky in practice.
So the discipline is manual: any time you change code that affects
rendering (src/tree_annotated_plot/_plot.py,
src/tree_annotated_plot/_tree.py, the example scripts under
examples/, or anything they touch), run the full check below and
eyeball every example before considering the change done.
bash scripts/check.sh # lint + format + pytest (all 100+ tests)
bash scripts/build_docs.sh # regenerates docs/images + docs/charts and runs mkdocs --strictThen open site/examples.html in a browser and, for each example:
- Look at the embedded SVG screenshot. Does it match what the feature you changed should produce? Are tip rows still aligned with the chart rows? Is the tree's tip-end facing the chart? Is the scale bar (if enabled) centered and labeled correctly?
- Click "Open the interactive chart →". Hover tips to confirm tooltips fire; if there's a cohort selector (Kikawa examples), toggle it; pan/zoom if relevant.
- Compare against the
deployed site
(which still reflects
mainbefore your change) for what "good" looks like.
If something visibly regresses, fix it before pushing — there's no CI gate that will catch a visual regression for you.
.github/workflows/tests.yml runs on every
push to main and every pull request: it installs the package, fetches
the upstream Kikawa Auspice JSONs (so the real-data tests actually
exercise rather than skip), builds the Kikawa titer chart specs, and
runs ruff check, black --check, and pytest tests/. This mirrors
scripts/check.sh — green locally should mean
green in CI.
A separate workflow,
.github/workflows/docs.yml, builds the
docs site on every push to main and every PR (catching broken
mkdocstrings refs / missing images / unresolved cross-links before
merge), and additionally deploys site/ to GitHub Pages on push to
main.
A third workflow,
.github/workflows/release.yml,
builds the sdist + wheel when a v* tag is pushed, publishes them
to PyPI, and creates a matching
GitHub Release
with auto-generated notes and the wheel/sdist attached (see
"Releasing a new version" below).
Releases are fully automated by tag push. Trusted publishing (OIDC) means no API token or password is stored in the repo — PyPI authenticates the workflow via its repo + workflow filename + environment.
One-time setup (only needed before the first release):
- Reserve
tree-annotated-ploton PyPI (first successful publish creates it; or claim it manually viapip install build && python -m build && twine upload dist/*from a maintainer machine). - On PyPI, go to the project → "Publishing" tab → "Add a new pending
publisher". Fill in: owner
jbloomlab, repositorytree-annotated-plot, workflow filenamerelease.yml, environmentpypi. - (Optional but recommended) On GitHub, repo Settings → Environments
→ New environment → name
pypi. Add yourself as a required reviewer if you want a manual approval gate before each release lands on PyPI.
Per-release recipe (every time):
# 1. Edit pyproject.toml and bump `version = "X.Y.Z"`.
# 2. Commit and tag (the workflow verifies they match before publishing).
git commit -am "release vX.Y.Z"
git tag vX.Y.Z
git push && git push --tagsThe release workflow then builds, verifies tag ⇋ version match, and
publishes to PyPI.
The docs are at
https://jbloomlab.github.io/tree-annotated-plot/.
They are auto-deployed by GitHub Actions on every push to main —
.github/workflows/docs.yml installs
the package + docs extras, runs
scripts/generate_docs_assets.py
to render images and standalone interactive HTML for each
example, then mkdocs build --strict, and uploads site/ as a
GitHub Pages artifact.
For day-to-day editing of the docs, use MkDocs's live-reload server (saves go straight to your browser):
mkdocs serve # http://localhost:8000To do this on the Fred Hutch remote server, do:
mkdocs serve -a $(hostname -i | awk '{print $1}'):$(fhfreeport)For the strict build that matches CI (catches broken
mkdocstrings refs, missing
images, unresolved cross-links):
bash scripts/build_docs.shThis first runs scripts/generate_docs_assets.py (so the example
SVGs and interactive HTMLs exist before MkDocs reads docs/), then
mkdocs build --strict. Output lands in site/.
The docs follow a single template per example: a short motivation,
an embedded SVG screenshot, a link to the fully-interactive
standalone HTML, and the commands to reproduce (CLI form first,
Python API form second). docs/examples.md
shows the existing shape. To add another:
- Drop a runnable script into
examples/. Keep helpers at module level (callable from outside) so the asset script can import them. - Add a clause to
scripts/generate_docs_assets.pythat imports the script and callstree_annotated_plot.plot(...)followed by saving both<name>.svg(intodocs/images/) and<name>.html(intodocs/charts/). Both directories are gitignored — only the asset script writes there. - Add a section to
docs/examples.mdmatching the template. - Run
bash scripts/build_docs.shand opensite/examples.htmlto verify. Push when happy; the deployed site updates on the next CI run.