Parameters¶
Parameters enable various dynamic behaviors in GenomeSpy visualizations, such as interactive selections, conditional encoding, and data filtering with expressions. They also enable parameterization when importing specification fragments from external files or named templates. Parameters in GenomeSpy are heavily inspired by the parameters concept of Vega-Lite.
Using Input Bindings¶
Parameters can be bound to input elements, such as sliders, dropdowns, and checkboxes. The GenomeSpy Core library shows the input elements below the visualization. In the GenomeSpy App, the input elements are shown in the View visibility menu, allowing the visualization author to provide configuration options to the end user.
Parameters with input bindings should have a unique name within the import
scope. While not enforced in core,
this is necessary for bookmarkable state in the GenomeSpy App.
By default, selection parameters and parameters with input bindings are
persisted in the GenomeSpy App's bookmarks and provenance history. Use
persist: false to opt out of persistence for ephemeral params (such as hover
selections) or when encoding.key is not defined for point selections.
For point selections, encoding.key should uniquely identify data objects.
You can use either a single field or a composite key (an array of field
definitions). When using a composite key, keep the field order stable across
bookmark creation and restore.
The following example shows how to bind parameters to input elements and use them to control the size, angle, and text of a text mark.
{
"description": "Parameter bindings example.",
"padding": 0,
"view": { "fill": "#cbeef3" },
"params": [
{
"name": "size",
"value": 80,
"bind": { "input": "range", "min": 1, "max": 300 }
},
{
"name": "angle",
"value": 0,
"bind": { "input": "range", "min": 0, "max": 360 }
},
{
"name": "text",
"value": "Params are cool!",
"bind": {
"input": "select",
"options": ["Params are cool!", "GenomeSpy", "Hello", "World"]
}
}
],
"data": {
"values": [{}]
},
"mark": {
"type": "text",
"font": "Lobster",
"text": { "expr": "text" },
"size": { "expr": "size" },
"angle": { "expr": "angle" }
}
}
Expressions¶
Parameters can be based on expressions, which can depend on other parameters. They are automatically re-evaluated when the dependent parameters change.
{
"description": "Expression parameter example.",
"view": { "stroke": "lightgray" },
"params": [
{
"name": "A",
"value": 2,
"bind": { "input": "range", "min": 0, "max": 10, "step": 1 }
},
{
"name": "B",
"value": 3,
"bind": { "input": "range", "min": 0, "max": 10, "step": 1 }
},
{ "name": "C", "expr": "A * B" }
],
"data": {
"values": [{}]
},
"mark": {
"type": "text",
"size": 30,
"text": { "expr": "'' + A + ' * ' + B + ' = ' + C" }
}
}
Selection Parameters¶
Parameters allow for defining interactive selections, which can be used in
conditional encodings and
"filter" transforms. GenomeSpy compiles the
conditional encoding rules into efficient GPU shader code, enabling fast
interactions in very large data sets.
Point Selection¶
The following example has been adapted from Vega-Lite's example
gallery
with slight modifications (GenomeSpy provides no "bar" mark). The
specification below is fully compatible with Vega-Lite. You can select multiple
bars by holding down the Shift key.
{
"description": "A bar chart with highlighting on hover and selecting on click. (Inspired by Tableau's interaction style.)",
"data": {
"values": [
{ "a": "A", "b": 28 },
{ "a": "B", "b": 55 },
{ "a": "C", "b": 43 },
{ "a": "D", "b": 91 },
{ "a": "E", "b": 81 },
{ "a": "F", "b": 53 },
{ "a": "G", "b": 19 },
{ "a": "H", "b": 87 },
{ "a": "I", "b": 52 }
]
},
"params": [
{
"name": "highlight",
"select": { "type": "point", "on": "pointerover" }
},
{ "name": "select", "select": "point" }
],
"mark": { "type": "rect", "fill": "#4C78A8", "stroke": "black" },
"encoding": {
"x": {
"field": "a",
"type": "ordinal",
"scale": { "type": "band", "padding": 0.2 }
},
"y": { "field": "b", "type": "quantitative" },
"fillOpacity": {
"value": 0.3,
"condition": { "param": "select", "value": 1 }
},
"strokeWidth": {
"value": 0,
"condition": [
{ "param": "select", "value": 2, "empty": false },
{ "param": "highlight", "value": 1, "empty": false }
]
}
}
}
Interval Selection¶
Interval selections allow for selecting a range of data points along one or two axes. By default, the start gesture depends on whether the brushed channels are zoomable:
- if any brushed channel is zoomable, start brushing with
Shift+ drag - otherwise, start brushing with plain drag
You can override this behavior with select.on, for example "on": "mousedown"
to always start brushing on plain drag. The selection can be cleared by clicking
outside the selected area.
Active interval selections can also be resized with the mouse wheel when the
pointer is over the selection rectangle. This is controlled by select.zoom.
By default, select.zoom is:
falsewhen any brushed channel uses a zoomable scale (to avoid wheel-gesture conflicts)trueotherwise
You can override the behavior with select.zoom: true/false or an explicit
wheel event definition such as "zoom": "wheel[event.altKey]".
In concatenated views, select.extent controls where the selection rectangle is
drawn. The default "auto" draws one rectangle across the concat container when
the selected channel uses aligned projections in the child views. Otherwise, it
draws a rectangle in each participating view. "container" requires the
spanning rectangle, and "view" always draws per-view rectangles.
{
"description": "Interval selection example.",
"params": [
{
"name": "brush",
"value": { "x": [2, 4] },
"select": { "type": "interval", "encodings": ["x"] }
}
],
"data": { "url": "data/sincos.csv" },
"mark": { "type": "point", "size": 100 },
"encoding": {
"x": {
"field": "x",
"type": "quantitative",
"scale": { "zoom": true }
},
"y": { "field": "sin", "type": "quantitative" },
"color": {
"condition": { "param": "brush", "value": "#38c" },
"value": "#ddd"
}
}
}
Selections can also drive "filter" transforms, allowing for aggregating or otherwise transforming only the selected data points. The example below shows how to aggregate only the brushed penguins from the Palmer Penguins dataset.
{
"description": [
"Aggregating brushed penguins",
"Data: Gorman KB, Williams TD, Fraser WR (2014) Ecological Sexual Dimorphism and Environmental Variability within a Community of Antarctic Penguins (Genus Pygoscelis). PLoS ONE 9(3): e90081. doi:10.1371/journal.pone.0090081"
],
"height": 350,
"padding": 10,
"data": {
"url": "vega-datasets/penguins.json"
},
"transform": [{ "type": "collect" }],
"params": [{ "name": "brush" }],
"resolve": {
"scale": { "color": "shared", "y": "independent" },
"legend": { "color": "independent" }
},
"spacing": 30,
"hconcat": [
{
"title": { "text": "Palmer Penguins", "style": "overlay-title" },
"name": "scatterPlot",
"params": [
{
"name": "brush",
"select": { "type": "interval", "encodings": ["x", "y"] },
"push": "outer"
}
],
"width": 350,
"mark": { "type": "point", "filled": false, "size": 40, "opacity": 0.7 },
"encoding": {
"x": {
"field": "Beak Length (mm)",
"type": "quantitative",
"scale": { "zero": false, "padding": 0.1 }
},
"y": {
"field": "Beak Depth (mm)",
"type": "quantitative",
"scale": { "zero": false, "padding": 0.1 }
},
"color": {
"condition": {
"param": "brush",
"field": "Species",
"type": "nominal",
"scale": {
"domain": ["Chinstrap", "Adelie", "Gentoo"],
"range": ["#BF5CCA", "#FF6C02", "#0F7574"]
},
"legend": { "orient": "top-right", "offset": 0 }
},
"value": "lightgrey"
}
}
},
{
"name": "barCharts",
"width": 200,
"transform": [
{
"type": "filter",
"param": "brush",
"fields": { "x": "Beak Length (mm)", "y": "Beak Depth (mm)" }
}
],
"resolve": {
"scale": { "x": "independent", "color": "shared" }
},
"encoding": {
"y": { "field": "count", "type": "quantitative" }
},
"vconcat": [
{
"name": "speciesChart",
"transform": [{ "type": "aggregate", "groupby": ["Species"] }],
"mark": "rect",
"encoding": {
"x": {
"field": "Species",
"type": "nominal",
"scale": {
"domain": ["Chinstrap", "Adelie", "Gentoo"],
"padding": 0.3
},
"axis": { "labelAngle": 0 }
},
"color": { "field": "Species", "type": "nominal", "legend": null }
}
},
{
"name": "sexChart",
"transform": [
{
"type": "filter",
"expr": "datum['Sex'] == 'MALE' || datum['Sex'] == 'FEMALE'"
},
{ "type": "aggregate", "groupby": ["Sex"] }
],
"mark": "rect",
"encoding": {
"x": {
"field": "Sex",
"type": "nominal",
"scale": { "domain": ["MALE", "FEMALE"], "padding": 0.3 },
"axis": { "labelAngle": 0 }
},
"color": { "value": "gray" }
}
}
]
}
]
}
Linking Scale Domains Across Views¶
An interval selection can drive scale domains in sibling views. To make this work with GenomeSpy's hierarchical parameter scopes:
- Define an empty parameter in a common ancestor.
- Define the brushing selection in a child view with the same
name. - Add
"push": "outer"so selection updates are written to the ancestor parameter. - Reference the parameter in a linked scale domain, for example:
{ "param": "brush" }. The domain can be placed inencoding.<channel>.scale.domainor, for composed views, in a view-levelscales.<channel>.domain.
If the linked scale is zoomable, GenomeSpy automatically keeps the domain and selection synchronized in both directions. For non-zoomable linked scales, the selection only drives the domain.
Use initial on the linked domain object to provide the configured starting
domain while the selection is empty. initial is only supported on zoomable
linked scales:
{
"scale": {
"zoom": true,
"domain": {
"param": "brush",
"initial": [10, 20]
}
}
}
initial participates in the configured domain of the linked scale. If the
linked interval selection is later cleared, the scale returns to its
normal default or data-derived domain instead of restoring initial.
GenomeSpy App persistence
In the GenomeSpy App, selection parameters are persisted in bookmarks,
URL hash state, and provenance history by default. In overview+detail
setups, this means the linked domain is restored through the selection
state. Use persist: false when the brush is only auxiliary UI and should
not affect saved state.
For app-specific state sharing and persistence, see Visualizing Sample Collections.
Zoomable Linking Example¶
{
"description": "Two-way interval linking example.",
"params": [{ "name": "brush" }],
"resolve": {
"scale": { "x": "independent" }
},
"data": {
"sequence": { "start": 0, "stop": 101, "step": 1, "as": "x" }
},
"transform": [{ "type": "formula", "expr": "sin(datum.x / 7)", "as": "y" }],
"vconcat": [
{
"height": 80,
"params": [
{
"name": "brush",
"select": { "type": "interval", "encodings": ["x"] },
"push": "outer"
}
],
"mark": { "type": "point", "size": 20, "opacity": 0.35 },
"encoding": {
"x": { "field": "x", "type": "quantitative" },
"y": { "field": "y", "type": "quantitative" }
}
},
{
"mark": { "type": "point", "size": 55, "opacity": 0.75 },
"encoding": {
"x": {
"field": "x",
"type": "quantitative",
"scale": {
"zoom": true,
"domain": { "param": "brush" }
}
},
"y": { "field": "y", "type": "quantitative" }
}
}
]
}
Overview+detail Example¶
The example below shows an overview+detail view of a genome. The top view shows
the whole genome, while the bottom view shows a zoomed-in region. The linked
domain is configured at the root using scales.x.domain, and its initial
value sets the detail view's starting domain. The overview template uses
resolve.scale.x: "excluded" so its own x scales are not affected by the
detail domain. A "link" mark with a "diagonal" shape
visually connects the selected region in the overview to the detail view.
{
"description": [
"Genome overview + detail example.",
"The top view provides an overview of the genome, and the bottom view shows details for the brushed region in the overview.",
"The overview is implemented as a template that can be reused across different specs.",
"The overview's x scale is excluded from the root scale domain, which is linked to the brush parameter and initializes the detail view."
],
"assembly": "hg38",
"params": [{ "name": "brush" }],
"scales": {
"x": {
"domain": {
"param": "brush",
"initial": [
{ "chrom": "chr6", "pos": 20000000 },
{ "chrom": "chr11", "pos": 40000000 }
]
}
}
},
"spacing": 0,
"vconcat": [
{ "import": { "template": "overview" } },
{
"name": "detail",
"data": {
"values": [
{ "chrom": "chr1", "start": 10000000, "end": 50000000 },
{ "chrom": "chr7", "start": 20000000, "end": 60000000 },
{ "chrom": "chr17", "start": 30000000, "end": 70000000 }
]
},
"encoding": {
"x": {
"chrom": "chrom",
"pos": "start",
"type": "locus",
"axis": {
"chromGrid": true
}
},
"x2": { "chrom": "chrom", "pos": "end" }
},
"mark": "rect",
"view": { "stroke": "lightgray" }
}
],
"templates": {
"overview": {
"resolve": { "scale": { "x": "excluded" } },
"spacing": 0,
"vconcat": [
{
"name": "chromosomes",
"height": 20,
"view": { "stroke": "#d0d0d0", "strokeZindex": 10 },
"cursor": "text",
"data": { "lazy": { "type": "axisGenome", "channel": "x" } },
"encoding": {
"x": {
"field": "continuousStart",
"type": "locus",
"axis": null,
"scale": { "zoom": false }
},
"x2": { "field": "continuousEnd" },
"text": { "field": "name" }
},
"layer": [
{
"encoding": {
"fill": {
"field": "odd",
"type": "nominal",
"legend": null,
"scale": {
"domain": [true, false],
"range": ["#e8e8e8", "white"]
}
}
},
"mark": {
"type": "rect",
"tooltip": null
}
},
{
"mark": {
"type": "text",
"paddingX": 3,
"paddingY": 5,
"tooltip": null
}
}
],
"params": [
{
"name": "brush",
"persist": false,
"select": {
"type": "interval",
"encodings": ["x"],
"mark": {
"clip": false,
"zindex": 11,
"stroke": "#048",
"strokeOpacity": 0.6,
"fillOpacity": 0.02,
"fill": "#08F",
"shadowBlur": 5,
"shadowColor": "#08F",
"shadowOpacity": 0.8
}
},
"push": "outer"
}
]
},
{
"name": "link-decoration",
"height": 30,
"data": { "lazy": { "type": "axisGenome", "channel": "x" } },
"transform": [
{
"type": "aggregate",
"fields": ["continuousEnd"],
"ops": ["max"],
"as": ["genomeEnd"]
},
{ "type": "formula", "expr": "[0, datum.genomeEnd]", "as": "x2" },
{ "type": "flatten", "fields": ["x2"], "index": "side" },
{ "type": "collect" },
{
"type": "formula",
"expr": "brush.intervals.x ? brush.intervals.x[datum.side] : datum.x2",
"as": "x"
}
],
"encoding": {
"x": {
"field": "x",
"type": "locus",
"scale": { "zoom": false },
"axis": null
},
"x2": { "field": "x2" }
},
"mark": {
"type": "link",
"linkShape": "diagonal",
"y": 1,
"y2": 0,
"color": "#8CF"
}
}
]
}
}
}
Ruler Parameters¶
Ruler parameters track one domain coordinate per configured positional channel and display it as a guide in compatible views. They are useful for cursor readouts, genome-browser-style center coordinates, and sibling views that compute from a shared cursor coordinate.
Rulers are not selections. They store a tagged parameter value such as:
{
type: "ruler",
values: {
x: 12.5
}
}
Rulers can be pointer-driven, following the mouse pointer, or viewport-driven, following the center of the current scale viewport.
Pointer-driven rulers use ruler.on:
"mousemove"follows the pointer and clears on mouse leave by default."mousedown"updates on press and continues while dragging. Use an event filter such as"mousedown[event.shiftKey]"when plain drag is used for zoom or pan gestures.
Viewport-driven rulers use "source": "viewport" and track the center of the
current scale viewport. A viewport ruler must not define on and does not clear
on mouse leave.
Ruler Guides in Descendant Views¶
Rulers can be shown separately in each view with a compatible scale or drawn
across a container (hconcat or vconcat) when the participating views share
the relevant scale and layout alignment.
The example below shows a pointer-driven ruler drawn inside two views with independent x scales.
{
"description": "Ruler parameter example.",
"params": [
{
"name": "cursor",
"persist": false,
"ruler": {
"encodings": ["x"],
"mark": { "stroke": "red" }
}
}
],
"data": {
"values": [
{ "x": 10, "x2": 40, "y": 1 },
{ "x": 50, "x2": 90, "y": 1 },
{ "x": 30, "x2": 70, "y": 2 }
]
},
"encoding": {
"x": { "field": "x", "type": "index", "scale": { "domain": [0, 100] } },
"x2": { "field": "x2" },
"y": { "field": "y", "type": "ordinal" }
},
"resolve": { "scale": { "x": "independent" } },
"vconcat": [
{ "mark": "rect", "title": "Move your mouse over the plot!" },
{ "mark": "rect" }
],
"config": {
"view": { "stroke": "lightgray" },
"scale": { "ordinal": { "padding": 0.2 } }
}
}
Pushing Ruler Values to Ancestor Scopes¶
Like selection parameters, ruler parameters can use push: "outer" to update a
parameter declared in an ancestor scope. This allows one view to provide the
coordinate while a sibling view reads the same parameter in expressions,
transforms, or encodings.
{
"description": "A child ruler pushes a coordinate to an outer parameter used by a sibling view.",
"params": [{ "name": "cursor" }],
"vconcat": [
{
"height": 25,
"data": { "values": [{}] },
"mark": {
"type": "text",
"text": { "expr": "'x = ' + cursor.values.x" }
}
},
{
"title": {
"text": "Move your mouse here!",
"style": "overlay-title"
},
"data": {
"values": [
{ "x": 1, "y": 2 },
{ "x": 2, "y": 4 },
{ "x": 3, "y": 3 },
{ "x": 4, "y": 7 },
{ "x": 5, "y": 5 }
]
},
"params": [
{
"name": "cursor",
"push": "outer",
"ruler": {
"encodings": ["x"],
"mark": { "stroke": "gray", "strokeDash": [4, 4] }
}
}
],
"mark": "point",
"encoding": {
"x": { "field": "x", "type": "quantitative" },
"y": { "field": "y", "type": "quantitative" }
}
}
],
"config": { "view": { "stroke": "lightgray" } }
}
Properties¶
ruler supports the following properties:
clear-
Type:
mouseleave|mouseup|FalseEvent that clears the ruler, or
falseto keep the current value.Default value:
"mouseleave"foron: "mousemove", otherwisefalse. display-
Type:
"line"|"center"|"band"|"none"How the ruler is drawn for snapped index or locus coordinates.
"line"draws at the coordinate."center"draws at the center of the coordinate band."band"draws a rectangle covering the coordinate band."none"tracks the ruler value without drawing a guide.Default value:
"center"for snapped index and locus scales, otherwise"line". encodings-
Type: array
Positional channels whose domain coordinates are tracked by the ruler.
Default value:
["x"] extent-
Type:
"auto"|"view"|"container"Visual extent of the ruler.
"view"draws one guide per participating view."container"draws one spanning guide when participating projections align."auto"chooses a spanning guide only when it is safe.Default value:
"auto" mark-
Type: RulerMarkConfig
Rule or band appearance. Has no effect when
displayis"none". on-
Type:
"mousemove"|"mousedown"| RulerEventConfig | stringEvent that updates a pointer-driven ruler.
"mousemove"follows the pointer."mousedown"updates on press and continues while dragging. Event filters can require modifier keys.Default value:
"mousemove" snap-
Type:
auto|integer|FalseQuantization applied before writing the ruler value.
"auto"snaps index and locus scales to integer coordinates."integer"snaps all numeric coordinates.falsekeeps the original coordinate.Default value:
"auto"for index and locus scales, otherwisefalse. source-
Type:
"pointer"|"viewport"Source of the ruler coordinate.
"pointer"uses pointer events configured byon."viewport"tracks the center of the current viewport.Default value:
"pointer"