JBrowse 2 · Build Your Own examples

Comparing two genomes

Human and mouse at BRCA1, joined by synteny ribbons.

Two linear views and a ribbon band

The engine comes from @jbrowse/react-app2, because the linear view package’s session holds exactly one view and a synteny view holds two. Each row is an ordinary linear genome view, so TrackStack draws it, and the synteny view fans its one width out to both rows.

View source — 150 lines
import { Suspense } from 'react'

import { SessionPaletteProvider } from '@jbrowse/core/ui/PaletteContext'
import { useWidthSetter } from '@jbrowse/core/util/hooks'
import { DisplayUIProvider } from '@jbrowse/display-ui'
import { TrackStack, ViewStatus } from '@jbrowse/display-ui/embed'
import { LevelSyntenyCanvas } from '@jbrowse/plugin-linear-comparative-view'
import { useCreateViewState } from '@jbrowse/react-app2'
import { observer } from 'mobx-react'

import type {
  LinearSyntenyViewHelperModel,
  LinearSyntenyViewModel,
} from '@jbrowse/plugin-linear-comparative-view'

const Ribbons = observer(function Ribbons({
  level,
}: {
  level: LinearSyntenyViewHelperModel
}) {
  return (
    <div style={{ position: 'relative', height: level.height }}>
      <LevelSyntenyCanvas model={level} />
      {level.linearSyntenyDisplays.map(display => (
        <div
          key={display.id}
          style={{
            position: 'absolute',
            inset: 0,
            zIndex: 1,
            pointerEvents: 'none',
            overflow: 'hidden',
          }}
        >
          <Suspense fallback={null}>
            <display.RenderingComponent model={display} />
          </Suspense>
        </div>
      ))}
    </div>
  )
})

const Rows = observer(function Rows({
  view,
}: {
  view: LinearSyntenyViewModel
}) {
  const ref = useWidthSetter(view)
  return (
    <div ref={ref}>
      {view.status.type === 'ready' ? (
        view.views.map((row, i) => {
          const level = view.levels[i - 1]
          return (
            <div key={row.id}>
              {level ? <Ribbons level={level} /> : null}
              <TrackStack view={row}>
                <div style={{ fontSize: '0.7rem', opacity: 0.7, padding: 2 }}>
                  {row.assemblyNames[0]}
                </div>
              </TrackStack>
            </div>
          )
        })
      ) : (
        <ViewStatus view={view} />
      )}
    </div>
  )
})

const geneTrack = (assembly: string) => ({
  trackId: `${assembly}_genes`,
  name: `RefSeq curated (${assembly})`,
  uri: `https://jbrowse.org/ucsc/${assembly}/ncbiRefSeqCurated.gff.gz`,
  index: `https://jbrowse.org/ucsc/${assembly}/ncbiRefSeqCurated.gff.gz.csi`,
  assemblyNames: [assembly],
  displayDefaults: { height: 110, geneGlyphMode: 'longestCoding' },
})

const chromSizes = (assembly: string) => ({
  name: assembly,
  sequence: {
    adapter: {
      type: 'ChromSizesAdapter',
      uri: `https://jbrowse.org/ucsc/${assembly}/${assembly}.chrom.sizes`,
    },
  },
})

const SyntenyRibbons = observer(function SyntenyRibbons() {
  const state = useCreateViewState({
    config: {
      assemblies: [chromSizes('hg38'), chromSizes('mm39')],
      tracks: [
        geneTrack('hg38'),
        geneTrack('mm39'),
        {
          type: 'SyntenyTrack',
          trackId: 'hg38_mm39',
          name: 'Human vs mouse (UCSC liftOver)',
          assemblyNames: ['hg38', 'mm39'],
          adapter: {
            type: 'PairwiseIndexedPAFAdapter',
            uri: 'https://jbrowse.org/ucsc/hg38/liftOver/hg38ToMm39.over.pif.gz',
            csi: true,
            assemblyNames: ['mm39', 'hg38'],
          },
        },
      ],
      defaultSession: {
        name: 'synteny',
        views: [
          {
            type: 'LinearSyntenyView',
            views: [
              {
                assembly: 'hg38',
                loc: 'chr17:43,040,000..43,130,000',
                tracks: ['hg38_genes'],
              },
              {
                assembly: 'mm39',
                loc: 'chr11:101,375,000..101,447,000',
                tracks: ['mm39_genes'],
              },
            ],
            tracks: ['hg38_mm39'],
            drawCurves: true,
            cigarMode: 'matches',
          },
        ],
      },
    },
  })
  if (!state) {
    return null
  }
  const { session } = state
  return (
    <SessionPaletteProvider session={session}>
      <DisplayUIProvider>
        <Rows view={session.views[0] as LinearSyntenyViewModel} />
      </DisplayUIProvider>
    </SessionPaletteProvider>
  )
})

export default SyntenyRibbons