# Synteny visualization (pairwise minimap2)

**TL;DR:** align two assemblies with `minimap2 -c --eqx`, load the PAF as a
synteny track, and read it whole-genome in a dotplot and base-level in the
linear synteny view. `add-track -a` takes `query,target`, the reverse of the
minimap2 argument order.

## Prerequisites

- a JBrowse 2 instance (see the [web quickstart](https://jbrowse.org/jb2/docs/quickstart_web), or the
  [desktop quickstart](https://jbrowse.org/jb2/docs/quickstart_desktop); the steps below are identical
  on both, and on Desktop the FASTAs and alignments are local files)
- [minimap2](https://github.com/lh3/minimap2)
- `samtools`
- htslib (`bgzip`, `tabix`)
- `unzip`
- the NCBI
  [`datasets`](https://www.ncbi.nlm.nih.gov/datasets/docs/v2/download-and-install/)
  CLI, which fetches the three assemblies and their gene annotations
- `node`, for the [JBrowse CLI](https://jbrowse.org/jb2/docs/cli)

On Debian/Ubuntu, `apt install minimap2 samtools tabix unzip` covers most of
these. The NCBI `datasets` CLI is a single-binary download, and `node` comes
from [nodejs.org](https://nodejs.org/).

## Where the data comes from

Three _H. pylori_ RefSeq assemblies, each fetched by accession with the
`datasets` CLI.

- 26695:
  https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/000/307/795/GCF_000307795.1_ASM30779v1/
- CHC155:
  https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/025/998/455/GCF_025998455.1_ASM2599845v1/
- J99:
  https://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/000/982/695/GCF_000982695.1_ASM98269v1/

- the 26695 strain's gene annotation, rehosted so the color-by-attribute figure
  loads without rerunning the pipeline:
  https://jbrowse.org/demos/hpylori/hpylori_26695.gff.gz

## Three strains, stacked

Three _Helicobacter pylori_ strains (26695, CHC155, and J99) go from raw
assemblies to a stacked three-genome synteny view. The steps work the same on
any pair of assemblies.

## Aligning the assemblies

<!-- from: scripts/build_hpylori_synteny.sh -->

```bash
minimap2 -c -x asm20 --eqx hpylori_j99.fa hpylori_26695.fa > 26695_vs_j99.paf
```

- `-x asm20` is the assembly preset for the divergence between the genomes.
  `asm5` covers up to about 5%; these strains diverge well past that.
- `-c` emits the base-level CIGAR the linear synteny view draws from.
- `--eqx` splits CIGAR matches (`=`) from mismatches (`X`), so the same track
  opened in a plain linear genome view draws per-base mismatches.
  [Color by → Identity](https://jbrowse.org/jb2/docs/user_guides/linear_synteny_view#coloring-the-ribbons)
  reads the PAF's divergence tag or match counts.

JBrowse also loads [MUMmer](https://github.com/mummer4/mummer) `.delta` and UCSC
`.chain` files directly, and
[paftools.js](https://github.com/lh3/minimap2/blob/master/misc/paftools.js) has
`delta2paf` and `chain2paf` for converting them.

## Loading the assemblies and the alignment

Both genomes are added as assemblies before the alignment that references them:

<!-- from: scripts/build_hpylori_synteny.sh -->

```bash
jbrowse add-assembly hpylori_26695.fa --load copy
jbrowse add-assembly hpylori_j99.fa --load copy
jbrowse add-track 26695_vs_j99.paf -a hpylori_26695,hpylori_j99 --load copy
```

The `-a` order is `query,target`, the reverse of the minimap2 argument order:
`minimap2 target.fa query.fa` becomes `add-track -a query,target`.

## Reading the whole genome in a dotplot

**Add → Dotplot view** opens the import form in **Quick start**: pick the track
just added and click **Launch**. **Swap** transposes the axes. **Manual** picks
each axis and a synteny file by hand.

<Figure caption="The dotplot import form in Manual mode, where you pick the X-axis and Y-axis assembly by hand, then optionally add a synteny file (.paf, .out, .delta, .chain, .anchors, or .anchors.simple)." src="/img/sv_synteny/dotplot_import.png" />

<Figure caption="The 26695 vs J99 alignment, 26695 on the X-axis and J99 on the Y-axis. The backbone runs anti-diagonal because the two assemblies were deposited in opposite orientations, and the pieces sitting off it are the rearrangements between the strains." src="/img/sv_synteny/dotplot.png" />

To open any of those pieces at base resolution, drag a box across it, then
right-click inside the box and choose **Linear synteny view**.

## Stacking the three strains

A band is drawn between adjacent rows only, so a 26695 / CHC155 / J99 stack
needs the two adjacent alignments:

<!-- from: scripts/build_hpylori_synteny.sh -->

```bash
minimap2 -c -x asm20 --eqx hpylori_chc155.fa hpylori_26695.fa > 26695_vs_chc155.paf
minimap2 -c -x asm20 --eqx hpylori_j99.fa hpylori_chc155.fa > chc155_vs_j99.paf
```

Add the third assembly and both alignments the same way as above, then:

1. **Add → Linear synteny view**, then switch to **Manual**.
2. Pick an assembly per row, with **Add row** for the third.
3. Click the arrow between each adjacent pair to choose that band's synteny
   track: 26695 against CHC155, then CHC155 against J99.
4. Click **Launch**, and all three strains stack in one view.

Open each strain's gene track from its own track selector.

<Video src="/media/synteny/three_strain_import.mp4" caption="The four steps above and the gene tracks after them: Manual, a genome per row with Add row for the third, each connector showing the alignment it resolved for that pair, Launch, and each strain's gene track from that row's own track selector." />

<Figure caption="Three H. pylori strains stacked with a gene track on each genome. Ribbons connect aligned blocks between adjacent genomes, and genes such as fliR, cbf2, efp, and lysS line up across all three strains." src="/img/sv_synteny/linear_synteny_genes.png" />

Each panel is a full linear genome view with its own search box, zoom and track
selector. See [](https://jbrowse.org/jb2/docs/user_guides/linear_synteny_view) for ribbon options and
[URL parameters → linear synteny view](https://jbrowse.org/jb2/docs/urlparams#linear-synteny-view) for
building one from a URL.

## Coloring genes by ortholog

In bacteria the gene symbol is effectively the ortholog id, since NCBI reuses
standardized symbols across strains. On each gene track, pick **Color by... →
Attribute...** from the track menu and enter `gene`. Every distinct value gets
its own deterministic color, so an ortholog carries one color down all three
panels. Features with no value are grey; most genes here carry only a locus tag.

<Figure caption="The click and its result. Left, the Color by attribute dialog on the first strain's gene track with the attribute name set to gene. Right, the same three strains after applying it: a shared symbol holds one color down all three panels." src="/img/sv_synteny/color_by_attribute_steps.png" links="Dialog=sv_synteny/color_by_attribute,Result=sv_synteny/ortholog_colors" />

The dialog writes a display color expression, which is one line of config:

```json addtrack
{
  "type": "FeatureTrack",
  "trackId": "hpylori_26695.gff",
  "name": "H. pylori 26695 genes",
  "assemblyNames": ["hpylori_26695"],
  "adapter": {
    "type": "Gff3TabixAdapter",
    "uri": "https://jbrowse.org/demos/hpylori/hpylori_26695.gff.gz"
  },
  "displayDefaults": {
    "showOnlyGenes": true,
    "color": "jexl:randomColor(get(feature,'gene'))"
  }
}
```

## Using PIF for large genomes

A bacterial PAF is small enough to load whole. For a large whole-genome
alignment, convert it to [](https://jbrowse.org/jb2/docs/developer_guides/pif_format) so JBrowse
fetches only the alignments in the current viewport:

```bash
jbrowse make-pif alignment.paf
jbrowse add-track alignment.pif.gz -a query,target --load copy
```

## Troubleshooting

`assemblyNames` in the wrong order is the common one. JBrowse checks at view
load whether the top row's chromosome names belong to that assembly, and a
warning in the view header names the remedy when they belong to the other row.

<Figure caption="A synteny track whose assemblyNames are reversed. No chromosome name resolves, so the band is empty, and the header warning reports the reversal." src="/img/sv_synteny/assembly_order_warning.png" />

A view that scatters its blocks randomly comes from a preset too tight for the
divergence, which leaves only short spurious anchors. Raise it, and check
`-c --eqx` were passed.

## Reproduce it end to end

One script builds everything above,
[`build_hpylori_synteny.sh`](https://github.com/GMOD/jbrowse-components/blob/main/scripts/build_hpylori_synteny.sh):

```bash
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/build_hpylori_synteny.sh
bash build_hpylori_synteny.sh          # builds ./hpylori_synteny_build/jbrowse2
npx --yes serve hpylori_synteny_build/jbrowse2 # then open the printed URL
```

It downloads the three assemblies, aligns the strain pairs, and writes a
`config.json` with a gene track per strain, the pairwise synteny tracks, and a
default session stacking all three. It needs the tools under
[Prerequisites](#prerequisites).

## See also

- [](https://jbrowse.org/jb2/docs/config_guides/synteny_track)
- [](https://jbrowse.org/jb2/docs/user_guides/dotplot_view)
- [](https://jbrowse.org/jb2/docs/user_guides/linear_synteny_view)
- [](https://jbrowse.org/jb2/docs/tutorials/genomes_synteny)
- [](https://jbrowse.org/jb2/docs/tutorials/hg002_haplotypes)
- [](https://jbrowse.org/jb2/docs/tutorials/allvsall_synteny)
- [](https://jbrowse.org/jb2/docs/tutorials/multiway_synteny_grape_peach_cacao)
- [](https://jbrowse.org/jb2/docs/config_guides/maf_track)

## References

- Diesh et al. (2024).
  [Setting Up the JBrowse 2 Genome Browser](https://doi.org/10.1002/cpz1.1120)
- Diesh et al. (2023).
  [JBrowse 2: A Modular Genome Browser with Views of Synteny and Structural Variation](https://doi.org/10.1186/s13059-023-02914-z)

