# Synteny track

A `SyntenyTrack` pairs two assemblies from an alignment file and powers both the
dotplot and linear synteny views. Pick the adapter by alignment format (PAF for
minimap2). The one thing to get right is that `assemblyNames` is
`[query, target]`, the reverse of the order minimap2 takes its inputs.

## Choosing an adapter

<!-- FILE_TYPES synteny START -->

<!-- prettier-ignore -->
| Format | Adapter | Track type | Notes |
| --- | --- | --- | --- |
| BLAST tabular | [](https://jbrowse.org/jb2/docs/config/blasttabularadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) |  |
| Chain (UCSC liftOver / lastz) | [](https://jbrowse.org/jb2/docs/config/chainadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) |  |
| Delta (MUMmer / nucmer) | [](https://jbrowse.org/jb2/docs/config/deltaadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) |  |
| Indexed PAF (PIF) | [](https://jbrowse.org/jb2/docs/config/pairwiseindexedpafadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | Built by `jbrowse make-pif`; fetches only the visible region |
| MashMap | [](https://jbrowse.org/jb2/docs/config/mashmapadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) |  |
| MCScan anchors | [](https://jbrowse.org/jb2/docs/config/mcscananchorsadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | Gene-level synteny; also needs one BED per assembly |
| MCScan blocks | [](https://jbrowse.org/jb2/docs/config/mcscanblocksadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | Multi-genome, reference-anchored; also needs one BED per assembly |
| MCScan simple anchors | [](https://jbrowse.org/jb2/docs/config/mcscansimpleanchorsadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | Gene-level synteny; also needs one BED per assembly |
| Multi-genome indexed PAF (PIF) | [](https://jbrowse.org/jb2/docs/config/multigenomeindexedpafadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | The tabix-indexed form of multi-genome PAF |
| Multi-genome PAF | [](https://jbrowse.org/jb2/docs/config/multigenomepafadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | PanSN-prefixed; all-vs-all or a star against one reference, in one file |
| PAF | [](https://jbrowse.org/jb2/docs/config/pafadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | Loaded entirely into memory; convert to PIF for large alignments |
| Several pairwise alignments sharing one genome | [](https://jbrowse.org/jb2/docs/config/multipairwisesyntenyadapter) | [](https://jbrowse.org/jb2/docs/config/syntenytrack) | One anchor genome aligned to each of N others, as N pairwise files |

<!-- FILE_TYPES synteny END -->

## Alignment format glossary

Every format says the same thing in its own dialect: this stretch of one genome
matches that stretch of the other. The **query** and **target** roles and the
**CIGAR** are shared across them, and
[query, target, and CIGAR](https://jbrowse.org/jb2/docs/user_guides/linear_synteny_view#query-target-and-cigar)
explains them.

- **PAF** a plain text table, one row per matching region, from minimap2, wfmash
  and most modern aligners
- **PIF** JBrowse's indexed PAF, made with
  [`jbrowse make-pif`](https://jbrowse.org/jb2/docs/cli#jbrowse-make-pif). The browser fetches only the
  region on screen, which makes a whole-genome alignment usable
- **pairwise vs multi-genome** a pairwise file compares two genomes. A
  multi-genome file holds many, aligned all-vs-all or each against one
  reference, which lets a linear synteny view stack more than two rows. Its
  sequence names carry a [PanSN](https://github.com/pangenome/PanSN-spec) prefix
  (`sample#haplotype#contig`, e.g. `K12#1#chr`) so the adapter can tell which
  genome each row belongs to; `make-pif` passes names through unchanged, so the
  naming comes first ([all-vs-all tutorial](https://jbrowse.org/jb2/docs/tutorials/allvsall_synteny))
- **chain** UCSC's format, from lastz and liftOver. Prefer `*.over.chain.gz` or
  `*.rbest.chain.gz` over a raw `*.all.chain.gz`, which also holds every match
  driven by repeats and gene copies and fills the view with clutter
- **delta** MUMmer and nucmer's format
- **BLAST tabular** BLAST's `-outfmt 6`, one row per high-scoring pair
- **MashMap** the approximate mapper's output: where long segments correspond,
  with no base-by-base alignment
- **anchors** MCScan's gene-level format, which pairs matching genes by name.
  Small files that still find synteny between distant species, with no
  base-by-base detail; the adapters need a BED per genome to place the genes.
  `.anchors.simple` collapses each run of anchors into one block, and **blocks**
  is MCScan's multi-genome table, one column per genome anchored to a reference
  column

## Quick start: PAF from minimap2

```bash
minimap2 -cx asm20 target.fa query.fa > alignment.paf
```

`asm5`, `asm10` and `asm20` suit about 0.1%, 1% and 5% divergence. Measure it
before choosing: align one chromosome with `asm20` and read the `de:f` tag,
minimap2's per-alignment divergence. D. simulans against D. mauritiana, sister
species, reads 2.4%, and `asm5` splits their largest chromosome into 6,805
records where `asm20` gives 278.

```json addtrack
{
  "type": "SyntenyTrack",
  "trackId": "alignment",
  "assemblyNames": ["query", "target"],
  "name": "alignment",
  "adapter": {
    "type": "PAFAdapter",
    "uri": "alignment.paf",
    "queryAssembly": "query",
    "targetAssembly": "target"
  }
}
```

The query draws on the dotplot's horizontal axis and the top row in linear
synteny; the target on the vertical axis and the bottom row.

<!-- GOTCHA PAFAdapter START -->

:::caution Gotcha

`assemblyNames` is `[query, target]`, which is the **reverse** of the order
minimap2 and nucmer take their inputs (`minimap2 target.fa query.fa`). Getting
it backwards draws every alignment against the wrong assembly, and the adapter
raises no error. Set the named `queryAssembly` and `targetAssembly` fields
instead and the ordering can't be misread.

:::

<!-- GOTCHA PAFAdapter END -->

## Adapter reference

Each adapter's config page lists its slots. Across all of them:

- **`assemblyNames` on a pairwise adapter** is exactly `[query, target]`. Most
  also take `queryAssembly`/`targetAssembly`, which cannot be read in the wrong
  order; the MCScan anchors adapters take only `assemblyNames`. On a
  multi-genome adapter it is the full list of genomes in the file, in any order,
  and on `MCScanBlocksAdapter` it is an optional narrowing of `blockAssemblies`
  rather than an ordered pair
- **Every adapter except the two indexed PAF adapters reads the whole file into
  memory.** Convert a large alignment to PIF and use `PairwiseIndexedPAFAdapter`
  or `MultiGenomeIndexedPAFAdapter`; `MultiPairwiseSyntenyAdapter` reads no file
  itself and inherits whichever behaviour its children have
- **The two compressions are not interchangeable.** A whole-file adapter takes
  plain `gzip`, so `.paf.gz` works with no index. The two indexed adapters need
  bgzip plus a tabix index, as `jbrowse make-pif` writes; a plain `gzip` file
  has no blocks to seek into and fails outright. `csi: true` selects a `.csi`
  index over `.tbi`
- **The MCScan anchors adapters take one BED per assembly** (`bed1`, `bed2`),
  intermediate outputs of the
  [MCScan workflow](<https://github.com/tanghaibao/jcvi/wiki/MCscan-(Python-version)>).
  `MCScanBlocksAdapter` describes N genomes, so its BEDs are `bedLocations`, an
  array positional against the table's columns

### Gene id matching in the MCScan adapters {#gene-ids-are-the-join-in-the-mcscan-adapters}

The MCScan adapters place a feature by looking its gene id up in a BED, so the
two files have to agree on those ids byte for byte.

<!-- GOTCHA MCScanAnchorsAdapter START -->

:::caution Gotcha

The anchors file carries no coordinates: a gene is placed by matching its id
against column 4 of a BED, byte for byte. A row naming a gene neither BED has is
dropped, so a partial mismatch draws fewer ribbons than the file holds rather
than erroring, and only a file where no row resolves fails the track. Ids get
mangled by isoform suffixes, by BLAST truncating a FASTA header at the first
space, and by jcvi stripping suffixes unless run with `--no_strip_names`. BED
column 1 has to match the assembly's reference sequence names too, and a name
the assembly does not have draws nothing at all.

:::

<!-- GOTCHA MCScanAnchorsAdapter END -->

<!-- GOTCHA MCScanSimpleAnchorsAdapter START -->

:::caution Gotcha

A block row names four genes, the first and last on each side, and all four are
placed by matching column 4 of a BED byte for byte. A row with any of the four
missing is dropped, so a partial mismatch draws fewer blocks than the file holds
rather than erroring, and only a file where no row resolves fails the track. Ids
get mangled by isoform suffixes and by jcvi stripping suffixes unless run with
`--no_strip_names`. BED column 1 has to match the assembly's reference sequence
names too, and a name the assembly does not have draws nothing at all.

:::

<!-- GOTCHA MCScanSimpleAnchorsAdapter END -->

<!-- GOTCHA MCScanBlocksAdapter START -->

:::caution Gotcha

`blockAssemblies` and `bedLocations` are positional against the table's own
columns, which is not necessarily the order `assemblyNames` lists or the order
the genomes were given to whatever wrote the table. Get it wrong and every gene
is looked up in another genome's BED; the track fails with the column order
named, rather than drawing empty. The table carries no coordinates: a gene is
placed by matching its id against column 4 of its column's BED, byte for byte.
One column whose BED places none of its ids fails the track naming that column,
since the rest still resolve and only the bands touching that genome would have
been empty. BED column 1 has to match the assembly's reference sequence names,
which is the one mismatch that still draws nothing rather than erroring.

:::

<!-- GOTCHA MCScanBlocksAdapter END -->

Both tutorials that build these files check the ids before loading anything:
[MCScan anchors](https://jbrowse.org/jb2/docs/tutorials/mcscan_synteny_grape_peach) and
[OrthoFinder orthogroups](https://jbrowse.org/jb2/docs/tutorials/orthofinder_synteny).

### PanSN depth: sample or haplotype

The two multi-genome adapters match a JBrowse assembly to PAF records by the
PanSN prefix on each sequence name, assuming the assembly name is the sample
name. Where the two differ, `assemblyNameToPanSN` maps one to the other, and the
prefix may name a whole sample (`grape`) or a single haplotype (`grape#1`):

```json addtrack
{
  "type": "SyntenyTrack",
  "trackId": "grape_peach_haps_ava",
  "name": "Grape/peach haplotypes all-vs-all",
  "assemblyNames": ["grape_hap1", "grape_hap2", "peach_hap1", "peach_hap2"],
  "adapter": {
    "type": "MultiGenomePAFAdapter",
    "uri": "all_vs_all.paf",
    "assemblyNames": ["grape_hap1", "grape_hap2", "peach_hap1", "peach_hap2"],
    "assemblyNameToPanSN": {
      "grape_hap1": "grape#1",
      "grape_hap2": "grape#2",
      "peach_hap1": "peach#1",
      "peach_hap2": "peach#2"
    }
  }
}
```

- **Mapping to `grape`** makes the sample one assembly, and an alignment between
  its haplotypes then reads as paralogy
- **Mapping to `grape#1`** gives each haplotype its own row, so hap1 against
  hap2 becomes a synteny band, kept even where the two are identical. Only a
  true self-diagonal, one PanSN sequence against itself at the same coordinates,
  is dropped
- **A prefix matches only at a `#` boundary**, so `grape` cannot pick up
  `grapefruit#1#chr1`, and mates are labelled at the depth you listed

## In the circular view

A `SyntenyTrack` opens in a [circular view](https://jbrowse.org/jb2/docs/user_guides/circular_view)
too, as [](https://jbrowse.org/jb2/docs/config/chordsyntenydisplay): each alignment is a ribbon between
the span it covers on one side and the span its mate covers on the other, with
the block's own extent on both arcs and an inversion drawn as a twist — a
Circos-style picture of where a genome's blocks land. Clicking one opens the
same synteny feature details panel the linear views open.

The circle has to carry both ends of the alignment, so what the view is opened
on decides what can be drawn.

**A self-alignment** — a genome against itself, segmental duplications, a
whole-genome-duplication PAF — names one assembly on both sides and needs
nothing special: open the circular view on that assembly and turn the track on.

```json addtrack
{
  "type": "SyntenyTrack",
  "trackId": "volvox_self",
  "name": "Volvox self-alignment",
  "assemblyNames": ["volvox", "volvox"],
  "adapter": {
    "type": "PAFAdapter",
    "uri": "volvox_self.paf",
    "queryAssembly": "volvox",
    "targetAssembly": "volvox"
  },
  "displays": [
    {
      "type": "ChordSyntenyDisplay",
      "displayId": "volvox_self-ChordSyntenyDisplay",
      "color": "jexl:get(feature,'score')>1000?'rgba(0,0,0,0.4)':'rgba(0,0,0,0.1)'"
    }
  ]
}
```

**Two assemblies** need both on the circle, which is a list in the view's
`assembly`: each one lays its contigs out in turn, so hg38 takes one arc and
mm39 the next and every ribbon crosses between them. The import form's Quick
start opens that circle from the track, and as a view it is:

```json
{
  "type": "CircularView",
  "assembly": ["hg38", "mm39"],
  "displayedRegionNames": ["chr1", "chr2", "chr3"],
  "tracks": ["hg38_vs_mm39"],
  "autoDiagonalize": true
}
```

A `displayedRegionNames` list is resolved against each assembly in turn, which
keeps a few named chromosomes of both genomes on the circle instead of every
unplaced contig of either; `{ "mm39": ["chr1", "chr2"] }` restricts one genome
and leaves the other whole.

## See also

- [](https://jbrowse.org/jb2/docs/user_guides/linear_synteny_view)
- [](https://jbrowse.org/jb2/docs/user_guides/dotplot_view)
- [Synteny visualization tutorial](https://jbrowse.org/jb2/docs/tutorials/synteny_visualization)
- [ORTHOLOG_TABLES.md](https://github.com/GMOD/jbrowse-components/blob/main/agent-docs/reference/ORTHOLOG_TABLES.md)
  — what a `.blocks` table can and cannot express, and why all-vs-all is a
  question about the producer rather than about the format

