-
Notifications
You must be signed in to change notification settings - Fork 51
Adding a new track type
Data tracks take input data tiles and display it within a browser. To create a new track type, it is necessary to go through a number of steps. For this tutorial, we'll create a new track which displays a box-plot.
In order for HiGlass to display a new track, it needs to know that it should display the new track. As with all other tracks, this is part of the viewconfig. In production, viewconfigs can be generated by exporting the view. During development, they are either loaded from app/index.html
(when viewing http://127.0.0.1:8080
after running npm start
) or from app/scripts/testViewConfs.js
when running the tests (npm run tests
).
When creating a new track we recommend adding a test case to test/HiGlassComponentTest.jsx
to ensure that it is created and functions properly. If this proves too troublesome, it's also possible to add the config for the new track in `app/index.html.
We'll be creating a new type of track called a horizontal-boxplot
, so add the following section to the "top" view of the testViewConfig
in app/index.html
. The uid here should be unique to this instance of the track so we just give it some random string (xxyxx
). The height specifies how high this track should be. The tilesetUid
specifies the uid of the data source on the server
. In this case we'll use data that exists on our public server (higlass.io).
{
'uid': 'xxyxx',
type:'horizontal-boxplot',
height: 40,
tilesetUid: 'F2vbUeqhS86XkxuO1j2rPA',
server: "http://higlass.io/api/v1"
}
When index.html
is loaded, it will create a HiGlass component using that viewconfig. When it gets to the "top" section and sees the definition of that track, it will try to render it. Since it doesn't yet exist, it won't be able to. To tell it how to render a horizontal-boxplot
track, we have to create a class that can render it and associate it with the horizontal-boxplot
track type.
To create the new track type, we'll use the horizontal-line
track as a template. This track is defined in the app/scripts/HorizontalLine1DPixiTrack.js
file. To begin, we'll copy this file:
cp app/scripts/HorizontalLine1DPixiTrack.js app/scripts/HorizontalBoxplotTrack.js
There's a lot of boilerplate in the track code but the important parts are in drawTile
. In particular, the loop which iterates of tileValues
does the actual drawing using the graphics.lineTo
and graphics.moveTo
function calls. These need to be changed to draw rectangles instead of lines. See the PIXI.js documentation to find the documentation for the drawRect
function which needs to be called.
When polishing the track, the exportSVG
method should also be implemented so that the view can be exported to SVG. This can be done after the new track is created and tested.
Now that we have a class which renders this track type, we need to associate it with the track name (horizontal-boxplot
) which was used in the viewconfig. This resolution is done app/scripts/TrackRenderer.jsx
. The easiest thing to do is to copy the example and plug in the newly created track names:
case 'horizontal-boxplot':
return new HorizontalBoxplotTrack(this.currentProps.pixiStage,
track.server,
track.tilesetUid,
handleTilesetInfoReceived,
track.options,
() => this.currentProps.onNewTilesLoaded(track.uid));
We also need to import HorizontalBoxplotTrack
from its javascript file at the top of TrackRenderer.jsx
:
import {HorizontalBoxplotTrack} from './HorizontalBoxplotTrack.js';
This should be enough to get the track to display if it's already specified in the viewconf. To make it discoverable and configurable, we need to add it to the list of known track types in app/scripts/config.js
.
To be able to add a track using the "Add Track" dialog (accessed using the plus sign icon in HiGlass), HiGlass needs to know what types of data it is capable of displaying. This is specified in app/scripts/config.js
. For our new box-plot track, we'll just copy the config for horizontal-line and change it slightly:
{
type: 'horizontal-boxplot',
datatype: ['vector'],
local: false,
orientation: '1d-horizontal',
thumbnail: null,
availableOptions: [ 'labelPosition', 'labelColor', 'labelTextOpacity', 'labelBackgroundOpacity', 'axisPositionHorizontal', 'valueScaling' ],
defaultOptions: {
labelColor: 'black',
labelPosition: 'topLeft',
axisPositionHorizontal: 'right',
valueScaling: 'linear'
}
}
This tells HiGlass, that whenever it encounters a tileset containing data of the type vector
it can display it using horizontal-boxplot
track. It tells it that it can be placed in horizontal
orientation, which means it can only be added as a top or bottom track. The thumbnail
option is null because we haven't specified a thumbnail for this track. The available options mean that we can set the position of the label (dataset name) position, color, opacity as well as the axis position and type of scaling (e.g. log or linear) of data as options. We also provide some default values for these options in case they're not specified.
That's it. The horizontal-boxplot
track should now be ready to use. What follows in this page are some scattered thoughts on the nitty gritty topics of scales and tiles. They can be ignored for unless a more thorough understanding of the track operations is desired.
Zoomed scales:
Horizontal tracks: this._xScale()
Vertical tracks: this._yScale()
2D tracks: this._xScale()
and this._yScale()
Original scales:
this._refXScale()
this._refYScale()
To draw the data, it needs various scales. The HorizontalLine1DPixiTrack
, for
example, requires a valueScale
that maps tile values to y positions within
the track. This scale can be calculated in a number of different ways, but the
simplest is to just use the maxVisibleValue()
function of the track. This
returns the maximum value present in the dense
fields of all the visible
tiles.
Other scaling methods may include... quantile scaling, log scaling, etc...
Custom tracks may require bespoke scaling methods. When drawing intervals, we may want to calculate what the maximum number of intervals that will be drawn on top of each other at any point will be. Then for each interval, we will want to calculate its y position.
If the track will rely on translations and zooms to move and rescale the content,
it needs to set pMain = this.pMobile
in its constructor and draw using the
reference scales (this._refXScale
and this._refYScale
).
This function is called when the tile is initially created.
It is especially useful for tracks that require heavy initial rendering and
lighter transformations for zooming and panning. The HeatmapTiledPixiTrack
,
for example, creates the heatmap sprite and renders it in the initTile
function. It omits the drawTile
function because it wouldn't do anything and
relies on the zoomed
function to alter the graphic's translate and scale
factor to change the view.
Within the tile
structure there is the tileData
member which contains the
data retrieved from the server. The tile
object itself contains the following
fields. The following is an example of a tile:
tile = {
graphics: <Pixi.Graphics>,
remoteId: "uid.4.3",
tileId: "uid.4.3",
tileData: {
discrete: [[0,1,0],[0,3,0]],
tileId: "uuid.0.0",
tilePos: [3],
zoomLevel: 4
}
The tile
object can also contain information that is relevant to its
rendering. If it is meant to be displayed as text, then it can contain
additional PIXI.Text objects which are simply rescaled when the tile is
redrawn.
There are two ways to draw the visible data:
- Draw each individual tile:
Example: HeatmapTiledPlot: Each tile can be drawn completely independently of every other one.
- Adjacent tiles required:
Example: HorizontalLine1DPixiTrack.js: To connect the lines between adjacent tiles, we need a draw method that looks at the adjacent tiles.
- Draw all the tiles at once
Example: CNVIntervalTrack: We need to have all of the intervals that are visible ready so that we can create a layout where all the elements are considered and there's no overlaps.
- LeftTrackModifier switches out
pBase
so removing it requires removing its pBase from the stage, rather than the original track's
- Adding a new track
- Resizing a view
- Adding and removing views
- Replacing tracks
- Changing a heatmap's colormap
- Adding track labels
- Adding new views
- Closing views
- View synchronization
- View linking
- Syncing and linking
- Exporting and sharing