Metadata-Version: 2.4
Name: metroplot
Version: 0.1.0
Summary: Subway-style pipeline diagrams for matplotlib
License-Expression: MIT
Keywords: matplotlib,visualization,bioinformatics,pipeline,diagram
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: matplotlib
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

<p align="center"><img src="graphics/metroplot_logo.png" alt="metroplot" width="400"/></p>

[![tests](https://github.com/s-weissbach/metroplot/actions/workflows/test.yml/badge.svg)](https://github.com/s-weissbach/metroplot/actions/workflows/test.yml)
[![python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
[![license](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![code style: matplotlib](https://img.shields.io/badge/built%20on-matplotlib-11557c.svg)](https://matplotlib.org/)

Subway-style pipeline diagrams for matplotlib. Define stations on a grid and lines that connect them; the renderer handles right-angle routing, parallel-track offsets where lines share segments, and station labels.

![example](graphics/example.png)


## Install

```sh
pip install metroplot
```

Or from source:

```sh
git clone https://github.com/s-weissbach/metroplot.git
cd metroplot
pip install -e .
```

Only hard dependency is `matplotlib`.

## Usage

```python
import matplotlib.pyplot as plt
from metroplot import Diagram

NAVY, SALMON = "#1f2a44", "#e07a6b"

d = Diagram()
(d.station("sra",    0.0,  0.0, "SRA tool", "DATA DOWNLOAD", "below")
  .station("fastqc", 2.0,  0.0, "fastqc",   "QUALITY CONTROL")
  .station("star",   6.0,  0.0, "STAR",     "ALIGNMENT")
  .station("rmats",  9.0,  0.0, "rMATS",    "ALT SPLICING")
  .station("bam",    8.0, -1.5, "bamCoverage", "COVERAGE", "below")
  .station("fc",     6.0, -2.5, "featureCounts", "QUANTIFICATION", "below"))

# A line is a list of routes. Multiple routes that share a station encode a split.
d.line("splicing", SALMON, [
    ["sra", "fastqc", "star", "rmats"],
    ["rmats", "bam"],          # branch off rmats
])
d.line("quant", NAVY, [
    ["sra", "fastqc", "star", "fc"],
])

d.render()
plt.savefig("pipeline.png", dpi=150, bbox_inches="tight")
```

Run [examples/example.py](examples/example.py) to regenerate the figure at the top.

## CLI

After `pip install metroplot` the `metroplot` command is available:

```sh
# From a Snakemake workflow directory
metroplot snakemake path/to/workflow -o pipeline.png

# From a Nextflow DSL2 directory
metroplot nextflow path/to/workflow -o pipeline.png

# Common options
metroplot snakemake path/to/workflow \
  --line-name "My pipeline" \
  --color "#1f2a44" \
  --label-overrides '{"star_align":"STAR"}' \
  --sub-overrides '{"star_align":"ALIGNMENT"}' \
  --column-spacing 2.5 \
  --branch-spacing 2.5 \
  --no-legend \
  --dpi 300 \
  --figsize 18 6
```

## Pipeline workflows

metroplot ships with parsers that turn a workflow definition into a Diagram automatically. Two formats are supported:

- **Snakemake** — `from metroplot.snakemake_io import from_snakemake` parses every `Snakefile` / `*.smk` under a directory, matches each rule's `output:` paths to downstream `input:` paths to derive the DAG.
- **Nextflow (DSL2)** — `from metroplot.nextflow_io import from_nextflow` parses every `*.nf` file, finds `process NAME { … }` blocks, and extracts edges by reading `workflow { … }` blocks for invocations like `STAR(TRIM.out)`.

Both share the same downstream layout/Diagram-builder (`metroplot._pipeline_layout`): the longest source-to-sink path becomes the `y = 0` spine, off-spine rules drop below at `branch_spacing`, and every parameter (label overrides, sub-labels, lanes, colors, column spacing) is exposed as a kwarg.

```python
from metroplot.snakemake_io import from_snakemake   # or: from metroplot.nextflow_io import from_nextflow

d = from_snakemake(
    "path/to/workflow",
    line_name="My pipeline",
    label_overrides={"run_method": "scanpy/Seurat"},     # snake_case rule name → display label
    sub_overrides={"run_method": "ANNOTATION"},          # small uppercase line under each label
    lanes={                                              # optional: split into multiple parallel lines
        "QC":   {"color": "#aaaaaa", "rules": ["FASTQC", "MULTIQC"]},
        "Main": {"color": "#1f2a44", "rules": ["FASTP", "STAR", "FEATURECOUNTS", "DESEQ2"]},
    },
)
d.render()
```

End-to-end demos:

- Snakemake on a real cell-type annotation benchmarking workflow → [examples/example_snakemake.py](examples/example_snakemake.py) → [graphics/snakemake_example.png](graphics/snakemake_example.png)
- Nextflow on an nf-core-style RNA-seq pipeline → [examples/example_nextflow.py](examples/example_nextflow.py) → [graphics/nextflow_example.png](graphics/nextflow_example.png)

Both parsers are best-effort regex-based and do not understand dynamic rules / checkpoint outputs (Snakemake) or subworkflow imports / channel operators like `map`, `branch`, `combine` (Nextflow). For anything they miss, override at the kwarg layer or pass extra rules/edges into `metroplot._pipeline_layout.build_diagram_from_dag` directly.

## Concepts

- **Station** — a node at `(x, y)` on a grid. You control layout fully; there's no auto-layout.
- **Line** — a colored route, owning one or more `routes` (lists of station names). Splits are implicit: two routes that share a station diverge there.
- **Shared segments** — when multiple lines traverse the same `(a, b)` segment, they're auto-offset perpendicular to the segment so they render as parallel tracks.
- **Bends** — non-collinear hops use an L-shape. Per-line `bend="hv"` (horizontal then vertical, default) or `"vh"`.

## Tuning

`Diagram(track_spacing=..., station_radius=..., line_width=..., label_font=..., sub_font=...)` exposes the visual knobs.

## Testing

```sh
pip install -e ".[dev]"
pytest
```

CI runs the suite on Python 3.10, 3.11, and 3.12 (see [`.github/workflows/test.yml`](.github/workflows/test.yml)).

## License

[MIT](LICENSE).
