Skip to content
 
 

Repository files navigation

gramene-anatomogram

Gramene's fork of @ebi-gene-expression-group/anatomogram 2.4.0, published on npm as gramene-anatomogram. It draws the Expression Atlas tissue SVGs next to the heatmaps of gramene-atlas-heatmap in Gramene sites (SorghumBase first).

What is different from 2.4.0:

  • React 18, as a peer dependency, with no React deprecation warnings: recompose, react-svg, styled-components, ReactDOM.render and function-component defaultProps are gone.
  • The SVGs and icons are bundled. Each SVG is its own lazily imported chunk, so only the species/view on screen is fetched, from wherever the host serves its bundle. There is no atlasUrl, publicPath or webpack-only require(), so it builds with Parcel, Vite and webpack alike.
  • Each SVG is injected once per species/view. Changing showIds/highlightIds/selectIds repaints the tissues in place instead of re-injecting the SVG.
  • ESM only. The code keeps upstream's module layout; see Fork maintenance.

Install

npm install gramene-anatomogram react@^18.2 react-dom@^18.2

Usage

import Anatomogram, { isSupportedSpecies } from 'gramene-anatomogram'

{isSupportedSpecies(species) &&                // e.g. 'sorghum_bicolor' or 'Sorghum bicolor'
  <Anatomogram
    species={species}
    showIds={['PO_0025034', 'PO_0009005']}
    highlightIds={highlighted}
    onMouseOver={(ids) => setHighlighted(ids)}  // ids: string[]
    onMouseOut={() => setHighlighted([])}
  />}

Without React in the host page:

import { render } from 'gramene-anatomogram'

const root = render({ species: 'sorghum_bicolor', highlightIds: ['PO_0025034'] }, 'target') // element id or element
render({ species: 'sorghum_bicolor', highlightIds: [] }, 'target')  // updates the same root
root.unmount()

Props

Prop Type Default Notes
species string required An id such as sorghum_bicolor, or a name such as Sorghum bicolor (see normaliseSpecies). Unsupported species render nothing
showIds, highlightIds, selectIds string[] [] Ontology ids to paint. Compared by value, so new arrays with the same ids do not repaint. Select > highlight > show
showColour, highlightColour, selectColour string grey, red, purple
showOpacity, highlightOpacity, selectOpacity number 0.4 +0.2 while the mouse is over a tissue; shown tissues hover in the highlight colour
onMouseOver, onMouseOut, onClick (ids: string[]) => void no-op All the ids of the tissue under the mouse (several ids can share a shape through <use>). The latest function is always called; a new identity does not re-register listeners
onInjected (error, svg) => void no-op (null, svgElement) once per injected SVG, after painting; (error) when the SVG cannot be loaded
linkTarget string | null '_blank' target of the links in the SVG (the EBI licence attribution); _blank adds rel="noopener noreferrer". null leaves them untouched
loadSvg (species, view) => Promise<string> bundled SVGs SVG text from somewhere else, e.g. hosted or trimmed files. view is null for single-view species. Results are cached per function, so pass a stable one. Trusted sources only: <script>, <foreignObject>, on* attributes and javascript: URLs are removed, but this is not a sanitiser
atlasUrl string Deprecated, ignored. Accepted so that 2.x callers keep working

The ids of each SVG are listed in src/json/svgsMetadata.json (also exported as svgsMetadata).

Exports

Export
default The component
render(options, target) Renders into target (an element id or an element) with createRoot, one root per container; returns the root
anatomogramSpecies, supportedSpecies The 20 species with an SVG (the same array)
normaliseSpecies(name) Lower-cases and turns each run of whitespace into _: 'Sorghum bicolor''sorghum_bicolor'
isSupportedSpecies(name) supportedSpecies.includes(normaliseSpecies(name))
getAnatomogramViews(species), getDefaultView(species) Views of a species (['whole_plant', 'flower_parts']), []/null for single-view species, undefined for unsupported ones
svgsMetadata [{filename, species, view, ids}] for the 31 SVGs

How the assets load

  • dist/gramene-anatomogram.js (~75 kB) is the entry. Each of the 31 SVGs is a chunk in dist/svg/ that exports the file's text; the entry reaches it with a plain import(). The host's bundler emits the chunks next to its own bundle and loads them relative to it, so nothing is fetched from EBI or from an atlasUrl. Only the species/view on screen is loaded, so the animal SVGs cost nothing at runtime. The SVGs total 13.5 MB (5 MB gzipped as a tarball).
  • The view switcher icons are data: URLs inside the entry: a host Content-Security-Policy needs img-src data:.
  • ESM only: a CommonJS copy would duplicate the SVG strings. Node ≥20.19 or ≥22.12 can require() it (require(esm)); Jest's module loader cannot.
  • The package deliberately has no source or browser field: Parcel would then compile src/, whose import.meta.glob only Vite understands.
  • <use> caveat (animal SVGs only, as upstream): tissues such as UBERON_0000178 are <use xlink:href="#CL_0000738"> elements. Two instances showing the same animal SVG put duplicate ids in the page. Plant SVGs have no <use>.

Migrating from @ebi-gene-expression-group/anatomogram 2.x

  • Change the package name. React 18 and an ESM-aware bundler are required.
  • Drop atlasUrl and any hosting of the SVG/PNG files.
  • render() now uses createRoot, returns the root and accepts an element as well as an id.
  • Callbacks receive an array of ids. They always did; the upstream README said a single string.
  • DOM: div.gxa-anatomogram > (div.gxa-anatomogram-switcher > img[role=button], div.gxa-anatomogram-svg > svg.injected-svg). The svg is one wrapper shallower than react-svg's div > div > svg, and there are no styled-components classes: all styles are inline and no global CSS is injected.
  • normaliseSpecies replaces every run of whitespace, not only the first run of spaces.

Development

npm install
npm test               # vitest + jsdom; any console.error/warn fails the test unless it calls allowConsole()
npm run build          # dist/, then scripts/check-dist.mjs
npm run lint:pkg       # publint
npm run pack:local     # build, then gramene-anatomogram-3.0.0.tgz
npm run dev            # playground (html/) on http://localhost:5176

The playground starts on sorghum and lists plants first. It has an event log (also written to the console as [anatomogram] …), a two-instance switch, a remount button, a linkTarget selector and a switch that passes the display name (Sorghum bicolor). URL parameters: ?species=<id> and ?strict=1 (StrictMode). From a workstation: ssh -L 5176:localhost:5176 <host>.

To try an unpublished build in a consumer, install the tarball rather than npm link (a symlink would load a second React):

npm run pack:local
cd ../atlas-heatmap && npm install --no-save ../anatomogram/gramene-anatomogram-3.0.0.tgz
# gramene-search: npm install --no-save ../anatomogram/gramene-anatomogram-3.0.0.tgz ../atlas-heatmap/gramene-atlas-heatmap-6.0.0.tgz && rm -rf .parcel-cache*

The consumer's next npm install puts the registry version back.

After editing an SVG (see the authoring guidelines in src/svg/README.md), run npm run parseSvgs to regenerate src/json/svgsMetadata.json. The build does not run it, but a test fails when the JSON is stale. fast-xml-parser stays pinned to 3.14.0 as upstream had it (3.15 broke its validation). npm audit flags that version; it is a development dependency only.

Fork maintenance

  • Remotes: origin is warelab/anatomogram, upstream is ebi-gene-expression-group/anatomogram.

  • Branches: master mirrors upstream and is never committed to. gramene is the default branch, cut from upstream's v2.4.0 tag; releases are tagged vX.Y.Z on it.

  • Edit upstream files in place without reformatting them. Leave src/svg/, src/img/, src/json/, src/Assets.js and wiki/ byte-identical to upstream, apart from SVG changes made on purpose. Make focused commits.

  • Review what the fork changes:

    git fetch upstream --tags
    git diff -M --stat upstream/master...gramene -- . ':(exclude)package-lock.json'
    git diff -M upstream/master...gramene -- src html scripts __test__
    git diff --stat upstream/master -- src/svg src/img src/json src/Assets.js wiki   # empty
  • Taking upstream changes: git merge upstream/master (or cherry-pick) into gramene, then npm test and npm run build.

  • Releasing: bump version, add a CHANGELOG.md entry, npm run pack:local and try the tarball in the consumers, then tag and npm publish (prepublishOnly runs the tests, the build with check-dist, and publint).

Upstream README (2.4.0)

Expression Atlas Anatomogram

Build Status Coverage Status License License: CC BY 4.0

This is an anatomogram for Expression Atlas that we use to illustrate the experiments.

See demo here.

The ontology IDs are sourced from Uberon, EFO and Plant Ontology.

To install:

npm install --save @ebi-gene-expression-group/anatomogram

You can use it as a React component:

import Anatomogram from '@ebi-gene-expression-group/anatomogram'

...

<Anatomogram .../>

Alternatively, if you don’t use React we’re providing a convenience render method:

import {render} from '@ebi-gene-expression-group/anatomogram'

...

render(options, target)

Where options are the props passed as an object, and target is an ID of the DOM element that will contain the anatomogram.

For example code, have a look at the demo component and how we use it in our Expression Atlas Heatmap. If you want to see it in action, go to an organism part experiment in Atlas or a search that returns organism part experiments.

Props

Name Type Default value Description
species string - This is the only required attribute of the anatomogram
showIds array []
highlightIds array []
selectIds array []
showColour string grey
highlightColour string red
selectColour string purple
showOpacity number 0.4
highlightOpacity number 0.4
selectOpacity number 0.4
onMouseOver function () => {} Callback invoked when the mouse is hovered on a tissue
onMouseOut function () => {} Callback invoked when the mouse is hovered off a tissue
onClick function () => {} Callback invoked when a tissue is clicked
onInjected function () => {} Callback invoked when SVG is injected

For a list of available species and IDs have a look at src/json/svgsMetadata.json. The file is generated with npm run parseSvgs (Gramene fork: no longer on every publish; a test checks it is up to date).

All three callbacks take a single argument, the array of IDs of the tissue affected by the mouse event (several IDs can share a shape through <use> elements). (Gramene fork: corrected; this README used to say a single string.)

The state of a tissue is either not shown, shown, highlighted or selected. On mouseover the opacity is increased by 0.2. You can read a more technically inclined description of the anatomogram behaviour in the repository wiki.

Building

(Gramene fork: the webpack build and atlasUrl/--output-public-path setup are gone. The SVGs and icons are bundled with the code; see How the assets load.)

Contribute

Read carefully our authoring guidelines before adding new tissues or updating IDs in an existing anatomogram or adding a new SVG.

Run npm run parseSvgs to parse the updated IDs. When you are finished run a local copy of the demo page:

npm run dev

Go to localhost:5176 and see that the tissues show up like you want them to.

Licence

Code is distributed under the Apache License version 2.0. All images, anatomograms and switcher icons, are distributed under the Creative Commons Attribution 4.0 International license. See LICENSE and NOTICE.

About

Anatomogram illustrating Expression Atlas experiments

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages