How to follow make a dot and text follow a mouse? - vega

To get to know how events work in VEGA, I gave myself the simple exercice to render a dot and a text positioned on the cursors coordinates in a given view.
I managed to make a symbol follow the mouse coordinates, yet when I try to make two types of marks ( text and symbol) follow the mouse , it doesn't render them both but the first one appearing in the marks [ ] array.
Any ideas ?
Following specification is used in the exercice :
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"width": 600,
"height": 600,
"signals": [
{
"name": "position",
"on": [
{
"events": {
"type": "mousemove"
},
"update": "xy()"
}
]
},
{
"name": "positionDot",
"on": [
{
"events": {
"type": "mousemove"
},
"update": "xy()"
}
]
}
],
"marks": [
{
"type": "text",
"encode": {
"enter": {
"fill": {
"value": "#000"
},
"text": {
"value": "Text Label"
}
},
"update": {
"opacity": {
"value": 1
},
"x": {
"signal": "position[0]+20"
},
"y": {
"signal": "position[1]+20"
},
"fontSize": {
"value": 50
}
}
}
},
{
"name": "dot",
"type": "symbol",
"encode": {
"enter": {
"fill": {
"value": "#939597"
},
"stroke": {
"value": "#652c90"
},
"x": {
"value": 0
},
"y": {
"value": 0
}
},
"update": {
"x": {
"signal": "positionDot[0]"
},
"y": {
"signal": "position[1]"
},
"size": {
"value": 550
},
"opacity": {
"value": 1
}
},
"hover": {
"opacity": {
"value": 0.5
}
}
}
}
]
}
https://vega.github.io/editor/#/gist/1dcd1034a1d8139b3de49f041ed1ed4b/spec.json

Your example works as expected if an initial value (using "init" or "value") is included in signal position:
"signals": [
{
"name": "position",
"init": [0, 0],
"on": [
{
"events": {
"type": "mousemove"
},
"update": "xy()"
}
]
},
View in Vega online editor

Related

Vega Zoomable USA Map

I want to use this map https://vega.github.io/vega/examples/zoomable-world-map/ but only for USA.
I used this spec.
private specVega = {
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "An interactive world map supporting pan and zoom.",
"width": 900,
"height": 500,
"autosize": "none",
"signals": [
{ "name": "tx", "update": "width / 2" },
{ "name": "ty", "update": "height / 2" },
{
"name": "scale",
"value": 150,
"on": [{
"events": {"type": "wheel", "consume": true},
"update": "clamp(scale * pow(1.0005, -event.deltaY * pow(16, event.deltaMode)), 150, 3000)"
}]
},
{
"name": "angles",
"value": [0, 0],
"on": [{
"events": "mousedown",
"update": "[rotateX, centerY]"
}]
},
{
"name": "cloned",
"value": null,
"on": [{
"events": "mousedown",
"update": "copy('projection')"
}]
},
{
"name": "start",
"value": null,
"on": [{
"events": "mousedown",
"update": "invert(cloned, xy())"
}]
},
{
"name": "drag", "value": null,
"on": [{
"events": "[mousedown, window:mouseup] > window:mousemove",
"update": "invert(cloned, xy())"
}]
},
{
"name": "delta", "value": null,
"on": [{
"events": {"signal": "drag"},
"update": "[drag[0] - start[0], start[1] - drag[1]]"
}]
},
{
"name": "rotateX", "value": 0,
"on": [{
"events": {"signal": "delta"},
"update": "angles[0] + delta[0]"
}]
},
{
"name": "centerY", "value": 0,
"on": [{
"events": {"signal": "delta"},
"update": "clamp(angles[1] + delta[1], -60, 60)"
}]
}
],
"projections": [
{
"name": "projection",
"type": "mercator",
"scale": {"signal": "scale"},
"rotate": [{"signal": "rotateX"}, 0, 0],
"center": [0, {"signal": "centerY"}],
"translate": [{"signal": "tx"}, {"signal": "ty"}]
}
],
"data": [
{
"name": "counties",
values: null,
"format": {"type": "topojson", "feature": "states"},
},
{
"name": "graticule",
"transform": [
{ "type": "graticule", "step": [15, 15] }
]
}
],
"marks": [
{
"type": "shape",
"from": {"data": "graticule"},
"encode": {
"enter": {
"strokeWidth": {"value": 1},
"stroke": {"value": "#ddd"},
"fill": {"value": null}
}
},
"transform": [
{ "type": "geoshape", "projection": "projection" }
]
},
{
"type": "shape",
"from": {"data": "states"},
"encode": {
"enter": {
"strokeWidth": {"value": 0.5},
"stroke": {"value": "#bbb"},
"fill": {"value": "#e5e8d3"}
}
},
"transform": [
{ "type": "geoshape", "projection": "projection" }
]
}
]
}
this.specVega["data"][0]["values"] = "data/us-10m.json" (this is just for understanding which data i used)
So i put data for only US here, but it didn't work. Got an error in console:
Undefined data set name: "states"
In general i just need a zoomable map for USA only, which i am going to use as a bubble map.
If this map has a zoom i would definitely use it https://vega.github.io/vega-lite/examples/geo_layer.html
Here is an working example of USA map by states or counties in "albersUsa" projection. Zooming is by mouse wheel and panning by mouse click-drag. For better performance, zooming and panning are best done with states instead of counties.
View in Vega online editor
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "An interactive USA map supporting pan and zoom.",
"width": 900,
"height": 500,
"autosize": "none",
"signals": [
{
"name": "signal_show_map_graticule",
"value": true,
"bind": {
"input": "checkbox",
"name": "Map graticule: "}
},
{
"name": "signal_states_or_counties",
"value": "states",
"bind": { "input": "select",
"options": ["states", "counties"],
"name": "Map areas: "
}
},
{
"name": "signal_map_scale",
"value": 1000,
"bind": {"input": "range", "min": 1000, "max": 4000, "step": 50, "name": "Map scale: "},
"on": [{
"events": {"type": "wheel", "consume": true},
"update": "round(clamp(signal_map_scale * pow(1.0005, -event.deltaY * pow(16, event.deltaMode)), 1000, 4000))"
}]
},
{ "name": "signal_translate_xy",
"value": [450, 250],
"update": "[clamp(signal_translate_last_xy[0] + signal_mouse_delta_xy[0], -300, 1200), clamp(signal_translate_last_xy[1] + signal_mouse_delta_xy[1], -200, 800)]"
},
{ "name": "signal_translate_last_xy",
"value": [450, 250],
"on": [{
"events": [
{"type": "mousedown"}
],
"update": "signal_translate_xy"
}]
},
{
"name": "signal_mouse_start_xy",
"value": [0, 0],
"on": [{
"events": [
{"type": "mousedown"}
],
"update": "xy()"
}]
},
{
"name": "signal_mouse_drag_xy",
"value": [0, 0],
"on": [{
"events": [
{
"type": "mousemove",
"between": [
{"type": "mousedown"},
{"type": "mouseup"}
]
}
],
"update": "xy()"
}]
},
{
"name": "signal_mouse_delta_xy",
"value": [0, 0],
"update": "[signal_mouse_drag_xy[0] - signal_mouse_start_xy[0], signal_mouse_drag_xy[1] - signal_mouse_start_xy[1]]"
}
],
"projections": [
{
"name": "map_projection",
"type": "albersUsa",
"scale": {"signal": "signal_map_scale"},
"translate": {"signal": "signal_translate_xy"}
}
],
"data": [
{
"name": "data_geo_usa",
"url": "data/us-10m.json",
"format": {
"type": "topojson",
"feature": {"signal": "signal_states_or_counties"}
}
},
{
"name": "data_geo_graticule",
"transform": [
{ "type": "graticule", "step": [5, 5] }
]
}
],
"marks": [
{
"type": "shape",
"from": {"data": "data_geo_graticule"},
"encode": {
"enter": {
"strokeWidth": {"value": 1},
"stroke": {"value": "#ddd"}
},
"update": {
"strokeOpacity": {"signal": "signal_show_map_graticule ? 1 : 0"}
}
},
"transform": [
{ "type": "geoshape", "projection": "map_projection" }
]
},
{
"type": "shape",
"from": {"data": "data_geo_usa"},
"encode": {
"enter": {
"strokeWidth": {"value": 0.5},
"stroke": {"value": "#bbb"},
"fill": {"value": "#e5e8d3"}
}
},
"transform": [
{ "type": "geoshape", "projection": "map_projection" }
]
}
]
}
Change "data" to:
"data": [
{
"name": "states",
"url": "data/us-10m.json",
"format": {"type": "topojson", "feature": "states"}
},
View in Vega online editor
Apologies but I'm not a mapping expert. I have the following where zoom works fine but the drag is a little janky but definitely useable. It would probably take me quite a while to get this any better.
Roy's answer is another option for you.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"autosize": "none",
"background": "white",
"padding": 5,
"width": 500,
"height": 300,
"signals": [
{"name": "tx", "update": "width / 2"},
{"name": "ty", "update": "height / 2"},
{
"name": "scale",
"value": 685,
"on": [
{
"events": {"type": "wheel", "consume": true},
"update": "clamp(scale * pow(1.0005, -event.deltaY * pow(16, event.deltaMode)), 150, 3000)"
}
]
},
{
"name": "angles",
"value": [222, 146],
"on": [{"events": "mousedown", "update": "[rotateX, centerY]"}]
},
{
"name": "cloned",
"value": null,
"on": [{"events": "mousedown", "update": "copy('projection')"}]
},
{
"name": "start",
"value": null,
"on": [{"events": "mousedown", "update": "invert(cloned, xy())"}]
},
{
"name": "drag",
"value": null,
"on": [
{
"events": "[mousedown, window:mouseup] > window:mousemove",
"update": "invert(cloned, xy())"
}
]
},
{
"name": "delta",
"value": null,
"on": [
{
"events": {"signal": "drag"},
"update": "[(drag[0] - start[0])*4, (start[1] - drag[1])*4]"
}
]
},
{
"name": "rotateX",
"value": 250,
"on": [{"events": {"signal": "delta"}, "update": "angles[0] + delta[0]"}]
},
{
"name": "centerY",
"value": 140,
"on": [{"events": {"signal": "delta"}, "update": "angles[1] + delta[1]"}]
}
],
"data": [
{
"name": "source_0",
"url": "data/us-10m.json",
"format": {"type": "topojson", "feature": "states"}
}
],
"projections": [
{
"name": "projection",
"scale": {"signal": "scale"},
"translate": [{"signal": "rotateX"}, {"signal": "centerY"}],
"type": "albersUsa"
}
],
"marks": [
{
"name": "marks",
"type": "shape",
"style": ["geoshape"],
"from": {"data": "source_0"},
"encode": {
"update": {"fill": {"value": "lightgray"}, "stroke": {"value": "white"}}
},
"transform": [{"type": "geoshape", "projection": "projection"}]
}
]
}

Why column facet in Vega Lite not working properly with layer?

I'm trying to create 3 column plot and it works when there's no layer.
But when I add the layer - 3 columns get merged into one plot (open in editor).
How to make it to be separated into 3 columns by the duration field?
CODE
For the plot with the full data please use editor link above.
{
"encoding": {
"column": { "field": "duration", "type": "nominal" },
"x": { "field": "bin_i", "type": "ordinal" }
},
"layer": [
{
"mark": { "type": "bar", "size": 2 },
"encoding": {
"y": { "field": "min", "type": "quantitative" },
"y2": { "field": "max", "type": "quantitative" }
}
},
{
"mark": { "type": "tick" },
"encoding": {
"y": { "field": "mean", "type": "quantitative" }
}
}
],
"data": {
"values": [
{
"bin_i": 1,
"duration": 1,
"max": 1.9642835793718165,
"mean": 1.0781367168962268,
"min": 0.3111818864927448
},
...
]
}
}
A layered chart does not accept a faceted encoding. If you want to facet a layered chart, you should use the facet operator rather than a facet encoding.
For your example, it would look like this (Vega Editor):
{
"facet": {"column": {"field": "duration", "type": "nominal"}},
"spec": {
"encoding": {
"x": {"field": "bin_i", "type": "ordinal"}
},
"layer": [
{
"mark": {"type": "bar", "size": 2},
"encoding": {
"y": {"field": "min", "type": "quantitative"},
"y2": {"field": "max"}
}
},
{
"mark": {"type": "tick"},
"encoding": {
"y": {"field": "mean", "type": "quantitative"}
}
}
]
},
"data": {
"values": [
{
"bin_i": 1,
"duration": 1,
"max": 1.9642835793718165,
"mean": 1.0781367168962268,
"min": 0.3111818864927448
},
...
]
}
}

Incorrect axis position for grouped marks

I'd like to group axes and marks together within a group mark, as I'd like a bar chart to be one of several onto the data. However, when I do so the x axis moved from the bottom to the top of the bar chart. Here's an example:
{
"$schema": "https://vega.github.io/schema/vega/v4.json",
"width": 400,
"height": 200,
"padding": 5,
"data": [
{
"name": "cars",
"format": {
"type": "json",
"parse": {
"year": "date"
}
},
"url": "https://vega.github.io/vega-datasets/data/cars.json",
"transform": [
{
"type": "aggregate",
"groupby": [
"Origin"
],
"as": [
"num_records"
]
}
]
}
],
"scales": [
{
"name": "x",
"type": "band",
"domain": {
"data": "cars",
"field": "Origin"
},
"range": "width",
"padding": 0.05
},
{
"name": "y",
"type": "linear",
"domain": {
"data": "cars",
"field": "num_records"
},
"range": "height",
"nice": true
}
],
"marks": [
{
"type": "group",
"axes": [
{
"orient": "bottom",
"scale": "x"
},
{
"orient": "left",
"scale": "y"
}
],
"marks": [
{
"type": "rect",
"from": {
"data": "cars"
},
"encode": {
"enter": {
"x": {
"scale": "x",
"field": "Origin"
},
"width": {
"scale": "x",
"band": 1
},
"y": {
"scale": "y",
"field": "num_records"
},
"y2": {
"scale": "y",
"value": 0
}
}
}
}
]
}
]
}
The Group Mark documentation suggests that groups support such nested visualisation specifications. What am I doing wrong?
What I was doing wrong was not encoding the width and height of the group mark. Here's my revised example:
{
"$schema": "https://vega.github.io/schema/vega/v4.json",
"width": 400,
"height": 200,
"padding": 5,
"data": [
{
"name": "cars",
"format": {
"type": "json",
"parse": {
"year": "date"
}
},
"url": "https://vega.github.io/vega-datasets/data/cars.json",
"transform": [
{
"type": "aggregate",
"groupby": [
"Origin"
],
"as": [
"num_records"
]
}
]
}
],
"scales": [
{
"name": "x",
"type": "band",
"domain": {
"data": "cars",
"field": "Origin"
},
"range": "width",
"padding": 0.05
},
{
"name": "y",
"type": "linear",
"domain": {
"data": "cars",
"field": "num_records"
},
"range": "height",
"nice": true
}
],
"marks": [
{
"type": "group",
"axes": [
{
"orient": "bottom",
"scale": "x"
},
{
"orient": "left",
"scale": "y"
}
],
"encode": {
"enter": {
"width": {
"signal": "width"
},
"height": {
"signal": "height"
}
}
},
"marks": [
{
"type": "rect",
"from": {
"data": "cars"
},
"encode": {
"enter": {
"x": {
"scale": "x",
"field": "Origin"
},
"width": {
"scale": "x",
"band": 1
},
"y": {
"scale": "y",
"field": "num_records"
},
"y2": {
"scale": "y",
"value": 0
}
}
}
}
]
}
]
}

How to add legend for single or multi series chart in Vega Lite?

How do I add a legend to a basic chart in Vega?
I'm using Vega in the web app where I want all my charts to include a legend even if its a single series.
i.e in Google Sheets it looks like
Since Datum hasn't been implemented yet I added an extra layer as a workaround (This also works for Multi Series Charts by adding additional values into data.values for the rule.)
{
"mark": {
"type": "rule"
},
"data": {
"values": [
{
"color": "Total Units"
}
]
},
"encoding": {
"color": {
"field": "color",
// If you want to update the color of the legend...
"scale": {"range": ["blue", "#000"]},
"sort": false,
"type": "nominal",
"legend": { "title": "" }
}
}
}
Also for those that want to view an example in VegaLite Editor https://vega.github.io/editor/#/
{
"layer": [
{
"mark": "bar",
"data": {
"values": [
{
"goal": 25,
"project": "a",
"score": 25
},
{
"goal": 47,
"project": "b",
"score": 57
},
{
"goal": 30,
"project": "c",
"score": 23
},
{
"goal": 27,
"project": "d",
"score": 19
}
]
},
"encoding": {
"x": {
"type": "nominal",
"field": "project"
},
"y": {
"type": "quantitative",
"field": "score"
}
},
"height": 300,
"width": 400
},
{
"mark": {
"type": "rule"
},
"data": {
"values": [
{
"color": "Goal"
}
]
},
"encoding": {
"color": {
"field": "color",
"sort": false,
"type": "nominal",
"legend": { "title": "" }
}
}
}
]
}

How to align line chart with bar chart

I'd like to align the line vertices (and bullets) of the following chart to the center of the corresponding bars:
{
"data": [
{
"name": "table",
"transform": [
{
"type": "formula",
"field": "predicate0",
"expr": "d.data.x"
}
],
"values": [
{
"x": 1,
"series": 0,
"y": 9
},
{
"x": 1,
"series": 1,
"y": 9
},
{
"x": 3,
"series": 0,
"y": 7
},
{
"x": 3,
"series": 1,
"y": 7
},
{
"x": 5,
"series": 0,
"y": 1
},
{
"x": 5,
"series": 1,
"y": 3
},
{
"x": 2,
"series": 0,
"y": 9
},
{
"x": 2,
"series": 1,
"y": 9
},
{
"x": 4,
"series": 0,
"y": 2
},
{
"x": 4,
"series": 1,
"y": 3
}
]
}
],
"scales": [
{
"name": "x-axis",
"range": "width",
"type": "ordinal",
"padding": 0.2,
"domain": {
"data": "table",
"field": "predicate0"
}
},
{
"name": "y-axis",
"round": true,
"nice": true,
"range": "height",
"domain": {
"data": "table",
"field": "data.y"
}
},
{
"name": "color",
"type": "ordinal",
"domain": [
0,
1
],
"range": [
"#4682b4",
"#8a8f99"
]
},
{
"name": "shortLabels",
"type": "ordinal",
"domain": {
"data": "table",
"field": "data.x"
}
}
],
"axes": [
{
"properties": {
"axis": {
"stroke": {
"value": "#55606e"
}
},
"labels": {
"fill": {
"value": "#55606e"
},
"font": {
"value": "Open Sans"
},
"fontSize": {
"value": 12
}
},
"ticks": {
"stroke": {
"value": "#55606e"
}
},
"title": {
"fill": {
"value": "#55606e"
},
"font": {
"value": "Open Sans"
},
"fontSize": {
"value": 14
},
"fontWeight": {
"value": "normal"
}
}
},
"scale": "x-axis",
"tickPadding": 8,
"tickSize": 0,
"title": "Series",
"type": "x"
},
{
"grid": "true",
"layer": "back",
"scale": "y-axis",
"ticks": 5,
"title": "Count",
"type": "y",
"properties": {
"grid": {
"strokeDash": {
"value": [
2,
2
]
}
},
"ticks": {
"stroke": {
"scale": "color",
"value": 0
}
},
"axis": {
"stroke": {
"scale": "color",
"value": 0
}
},
"title": {
"font": {
"value": "Open Sans"
},
"fontSize": {
"value": 14
},
"fontWeight": {
"value": "normal"
},
"fill": {
"scale": "color",
"value": 0
}
},
"labels": {
"font": {
"value": "Open Sans"
},
"fontSize": {
"value": 12
},
"fill": {
"scale": "color",
"value": 0
}
}
}
}
],
"marks": [
{
"type": "rect",
"properties": {
"enter": {
"x": {
"scale": "x-axis",
"field": "predicate0"
},
"width": {
"scale": "x-axis",
"band": true
},
"y": {
"scale": "y-axis",
"field": "data.y"
},
"y2": {
"scale": "y-axis",
"value": 0
},
"fill": {
"scale": "color",
"field": "data.series"
}
}
},
"from": {
"transform": [
{
"type": "filter",
"test": "d.data.series === 0"
}
],
"data": "table"
}
},
{
"type": "line",
"properties": {
"enter": {
"x": {
"scale": "x-axis",
"field": "data.x"
},
"y": {
"scale": "y-axis",
"field": "data.y"
},
"stroke": {
"scale": "color",
"field": "data.series"
},
"strokeWidth": {
"value": 2
}
}
},
"from": {
"transform": [
{
"type": "filter",
"test": "d.data.series === 1"
}
],
"data": "table"
}
},
{
"type": "symbol",
"properties": {
"update": {
"fill": {
"scale": "color",
"field": "data.series"
}
},
"enter": {
"strokeWidth": {
"value": 3
},
"fill": {
"scale": "color",
"field": "data.series"
},
"x": {
"scale": "x-axis",
"field": "data.x"
},
"y": {
"scale": "y-axis",
"field": "data.y"
}
}
},
"from": {
"transform": [
{
"type": "filter",
"test": "d.data.series === 1"
}
],
"data": "table"
}
}
]
}
Unfortunately, the line marks doesn't support the dx property. I tried using a group but wasn't able to get it to work.