Annotate the axis of an Altair / Vega-Lite plot with a phylogenetic tree.
Project description
tree-annotated-plot: annotate the axis of an Altair / Vega-Lite plot with a phylogenetic tree
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.
Notes for developing the package
Installation (development)
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.
Visual verification after code changes
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 --strict
Then 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.
Continuous integration
.github/workflows/ci.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 and publishes them to PyPI when a v* tag
is pushed (see "Releasing a new version" below).
Releasing a new version
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 --tags
The release workflow then builds, verifies tag ⇋ version match, and
publishes to PyPI.
Documentation
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.
Building documentation locally
For day-to-day editing of the docs, use MkDocs's live-reload server (saves go straight to your browser):
mkdocs serve # http://localhost:8000
To 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.sh
This 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/.
Adding a new example
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tree_annotated_plot-0.0.1.tar.gz.
File metadata
- Download URL: tree_annotated_plot-0.0.1.tar.gz
- Upload date:
- Size: 65.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
caefe23ad40ed0f4fb88b24c9b7e8d20e7cf444c7ee4e952a1865209582b39bc
|
|
| MD5 |
e261c881f8345f222c0bbc3a497532ce
|
|
| BLAKE2b-256 |
861930c7390ccd7359374c125329f96609a30a1a5c807c18895498818b104512
|
Provenance
The following attestation bundles were made for tree_annotated_plot-0.0.1.tar.gz:
Publisher:
release.yml on jbloomlab/tree-annotated-plot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tree_annotated_plot-0.0.1.tar.gz -
Subject digest:
caefe23ad40ed0f4fb88b24c9b7e8d20e7cf444c7ee4e952a1865209582b39bc - Sigstore transparency entry: 1438617537
- Sigstore integration time:
-
Permalink:
jbloomlab/tree-annotated-plot@5666521206131c9cde41b6cb682815da4706309f -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/jbloomlab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5666521206131c9cde41b6cb682815da4706309f -
Trigger Event:
push
-
Statement type:
File details
Details for the file tree_annotated_plot-0.0.1-py3-none-any.whl.
File metadata
- Download URL: tree_annotated_plot-0.0.1-py3-none-any.whl
- Upload date:
- Size: 30.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab59d32b7efbe056b402684f0e9d7e884e0f9bcb7bf587a24b002be3b987a398
|
|
| MD5 |
b72e869eb4e82f7c125b2f589572d1c7
|
|
| BLAKE2b-256 |
5ad09b452e3f0d709b15cdbe8edbe123fd0ac87562eae64e63c0cde5df0df370
|
Provenance
The following attestation bundles were made for tree_annotated_plot-0.0.1-py3-none-any.whl:
Publisher:
release.yml on jbloomlab/tree-annotated-plot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tree_annotated_plot-0.0.1-py3-none-any.whl -
Subject digest:
ab59d32b7efbe056b402684f0e9d7e884e0f9bcb7bf587a24b002be3b987a398 - Sigstore transparency entry: 1438617560
- Sigstore integration time:
-
Permalink:
jbloomlab/tree-annotated-plot@5666521206131c9cde41b6cb682815da4706309f -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/jbloomlab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5666521206131c9cde41b6cb682815da4706309f -
Trigger Event:
push
-
Statement type: