Vega Zoomable USA Map - vega

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"}]
}
]
}

Related

Modifying the Width of a Symbol Mark in Vega

I want to label the symbols in my Vega graph. Some of the labels are long, extending beyond the boundaries of my symbols. How do I extend the width of a symbol to handle long labels?
Update: Is there a way of increasing the width of a symbol mark?
This is a link to my code.
N.B: I tried using rect marks, whose widths are easier to modify. But the aesthetics of the rect mark do not work for my use case. My use case is a Force Transform force-directed graph.
Here is a working example using your Vega spec with Force transform and text box using reactive geometry as suggested by David.
View in Vega online editor
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A node-link diagram with force-directed layout, depicting character co-occurrence in the novel Les Misérables.",
"width": 500,
"height": 200,
"autosize": "pad",
"signals": [
{
"name": "nodeRadius",
"value": 50,
"bind": {"input": "range", "min": 1, "max": 100, "step": 1}
},
{
"name": "nodeCharge",
"value": -100,
"bind": {"input": "range", "min": -100, "max": 10, "step": 1}
},
{
"name": "linkDistance",
"value": 66,
"bind": {"input": "range", "min": 5, "max": 100, "step": 1}
},
{
"name": "textPadding",
"value": 5,
"bind": {"input": "range", "min": 0, "max": 20, "step": 1}
},
{
"name": "cornerRadius",
"value": 10,
"bind": {"input": "range", "min": 0, "max": 20, "step": 1}
},
{ "name": "showSymbol", "value": false, "bind": {"input": "checkbox"} },
],
"data": [
{
"name": "node_data",
"values": [
{"name": "A Name", "type": "node", "1d": 0},
{"name": "A Very Long Name", "type": "node", "1d": 1}
]
},
{"name": "edge_data", "values": [{"source": 0, "target": 1}]}
],
"marks": [
{
"type": "symbol",
"name": "nodes",
"from": {"data": "node_data"},
"encode": {
"update": {
"fill": {"value": "grey"},
"opacity": {"signal": "showSymbol ? 0.5 : 0"},
"size": {"signal": "PI * nodeRadius * nodeRadius"},
"shape": {"value": "circle"}
}
},
"transform": [
{
"type": "force",
"iterations": 300,
"static": {"signal": "false"},
"signal": "force",
"forces": [
{
"force": "center",
"x": {"signal": "width/2"},
"y": {"signal": "height/2"}
},
{"force": "collide", "radius": {"signal": "nodeRadius"}},
{"force": "nbody", "strength": {"signal": "nodeCharge * 10"}},
{
"force": "link",
"links": "edge_data",
"distance": {"signal": "linkDistance"}
}
]
}
]
},
{
"type": "path",
"from": {"data": "edge_data"},
"interactive": false,
"encode": {
"update": {"stroke": {"value": "#ccc"}, "strokeWidth": {"value": 0.5}}
},
"transform": [
{
"type": "linkpath",
"require": {"signal": "force"},
"shape": "line",
"sourceX": "datum.source.x",
"sourceY": "datum.source.y",
"targetX": "datum.target.x",
"targetY": "datum.target.y"
}
]
},
{
"type": "text",
"name": "textmark",
"from": {"data": "nodes"},
"interactive": false,
"encode": {
"enter": {"fill": {"value": "black"}, "fontSize": {"value": 16}},
"update": {
"y": {"field": "y", "offset": {"signal": "nodeRadius * -0.1"}},
"x": {"field": "x"},
"text": {"field": "datum.name"},
"align": {"value": "center"},
"baseline": {"value": "middle"}
}
},
"zindex": 1
},
{
"name": "rectmark",
"type": "rect",
"from": {"data": "textmark"},
"encode": {
"update": {
"x": {"field": "bounds.x1", "round": true, "offset": {"signal": "-textPadding"}},
"x2": {"field": "bounds.x2", "round": true, "offset": {"signal": "textPadding"}},
"y": {"field": "bounds.y1", "round": true, "offset": {"signal": "-textPadding"}},
"y2": {"field": "bounds.y2", "round": true, "offset": {"signal": "textPadding"}},
"cornerRadius": {"signal": "cornerRadius"},
"fill": {"value": "aliceblue"},
"stroke": {"value": "steelblue"}
}
}
}
]
}
It isn't clear what you're asking. Do you mean just increase the size of the symbol statically like this?
If so, just put a value in the size property.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A node-link diagram with force-directed layout, depicting character co-occurrence in the novel Les Misérables.",
"width": 700,
"height": 500,
"autosize": "pad",
"signals": [
{
"name": "nodeRadius",
"value": 50,
"bind": {"input": "range", "min": 1, "max": 50, "step": 1}
},
{
"name": "nodeCharge",
"value": -100,
"bind": {"input": "range", "min": -100, "max": 10, "step": 1}
},
{
"name": "linkDistance",
"value": 66,
"bind": {"input": "range", "min": 5, "max": 100, "step": 1}
}
],
"data": [
{
"name": "node_data",
"values": [
{"name": "A Name", "type": "node", "1d": 0},
{"name": "A Very Long Name", "type": "node", "1d": 1}
]
},
{"name": "edge_data", "values": [{"source": 0, "target": 1}]}
],
"marks": [
{
"type": "symbol",
"name": "nodes",
"from": {"data": "node_data"},
"encode": {
"enter": {
"fill": {"value": "steelblue"},
"size": {"value": 10000},
"tooltip": {"field": "name"}
}
},
"transform": [
{
"type": "force",
"iterations": 300,
"static": {"signal": "false"},
"signal": "force",
"forces": [
{
"force": "center",
"x": {"signal": "width/2"},
"y": {"signal": "height/2"}
},
{"force": "collide", "radius": {"signal": "nodeRadius"}},
{"force": "nbody", "strength": {"signal": "nodeCharge * 10"}},
{
"force": "link",
"links": "edge_data",
"distance": {"signal": "linkDistance"}
}
]
}
]
},
{
"type": "path",
"from": {"data": "edge_data"},
"interactive": false,
"encode": {
"update": {"stroke": {"value": "#ccc"}, "strokeWidth": {"value": 0.5}}
},
"transform": [
{
"type": "linkpath",
"require": {"signal": "force"},
"shape": "line",
"sourceX": "datum.source.x",
"sourceY": "datum.source.y",
"targetX": "datum.target.x",
"targetY": "datum.target.y"
}
]
},
{
"type": "text",
"from": {"data": "nodes"},
"interactive": false,
"enter": {"fill": {"value": "black"}, "fontSize": {"value": 10}},
"encode": {
"update": {
"y": {"field": "y", "offset": {"signal": "nodeRadius * -0.1"}},
"x": {"field": "x"},
"text": {"field": "datum.name"},
"align": {"value": "center"}
}
}
}
]
}
Or you can split the word like this.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A node-link diagram with force-directed layout, depicting character co-occurrence in the novel Les Misérables.",
"width": 700,
"height": 500,
"autosize": "pad",
"signals": [
{
"name": "nodeRadius",
"value": 50,
"bind": {"input": "range", "min": 1, "max": 50, "step": 1}
},
{
"name": "nodeCharge",
"value": -100,
"bind": {"input": "range", "min": -100, "max": 10, "step": 1}
},
{
"name": "linkDistance",
"value": 66,
"bind": {"input": "range", "min": 5, "max": 100, "step": 1}
}
],
"data": [
{
"name": "node_data",
"values": [
{"name": "A Name", "type": "node", "1d": 0},
{"name": ["A Very", "Long Name"], "type": "node", "1d": 1}
]
},
{"name": "edge_data", "values": [{"source": 0, "target": 1}]}
],
"marks": [
{
"type": "symbol",
"name": "nodes",
"from": {"data": "node_data"},
"encode": {
"enter": {
"fill": {"value": "steelblue"},
"size": {"value": 10000},
"tooltip": {"field": "name"}
}
},
"transform": [
{
"type": "force",
"iterations": 300,
"static": {"signal": "false"},
"signal": "force",
"forces": [
{
"force": "center",
"x": {"signal": "width/2"},
"y": {"signal": "height/2"}
},
{"force": "collide", "radius": {"signal": "nodeRadius"}},
{"force": "nbody", "strength": {"signal": "nodeCharge * 10"}},
{
"force": "link",
"links": "edge_data",
"distance": {"signal": "linkDistance"}
}
]
}
]
},
{
"type": "path",
"from": {"data": "edge_data"},
"interactive": false,
"encode": {
"update": {"stroke": {"value": "#ccc"}, "strokeWidth": {"value": 0.5}}
},
"transform": [
{
"type": "linkpath",
"require": {"signal": "force"},
"shape": "line",
"sourceX": "datum.source.x",
"sourceY": "datum.source.y",
"targetX": "datum.target.x",
"targetY": "datum.target.y"
}
]
},
{
"type": "text",
"from": {"data": "nodes"},
"interactive": false,
"enter": {"fill": {"value": "black"}, "fontSize": {"value": 10}},
"encode": {
"update": {
"y": {"field": "y", "offset": {"signal": "nodeRadius * -0.1"}},
"x": {"field": "x"},
"text": {"field": "datum.name"},
"align": {"value": "center"}
}
}
}
]
}
If you want something dynamic to size the node to the label extent, you have to use reactive geometry.
Using reactive geometry for you. This example is from jheer on GitHub.
This sets the rectangles width and height according to the text mark.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"width": 100,
"height": 100,
"data": [
{"name": "labels", "values": [{"label": "Text"}]}
],
"marks": [
{
"type": "group",
"signals": [
{"name": "bgoffset", "value": 3}
],
"marks": [
{
"name": "textmark",
"type": "text",
"from": {"data": "labels"},
"encode": {
"enter": {
"text": {"field": "label"},
"x": {"value": 50},
"y": {"value": 50},
"fontSize": {"value": 14},
"align": {"value": "center"},
"baseline": {"value": "middle"}
}
},
"zindex": 1
},
{
"name": "rectmark",
"type": "rect",
"from": {"data": "textmark"},
"encode": {
"enter": {
"x": {"field": "bounds.x1", "round": true, "offset": {"signal": "-bgoffset"}},
"x2": {"field": "bounds.x2", "round": true, "offset": {"signal": "bgoffset"}},
"y": {"field": "bounds.y1", "round": true, "offset": {"signal": "-bgoffset"}},
"y2": {"field": "bounds.y2", "round": true, "offset": {"signal": "bgoffset"}},
"fill": {"value": "aliceblue"},
"stroke": {"value": "steelblue"}
}
}
}
]
}
]
}

Vega How to SUM all descendants value of every node on a treemap

I am trying to access the sum of values for a node in VEGA. In other words, I want to display sum of "percentage" values of all leaves for each parent node.
Got the following Vega specs (https://gist.github.com/omerakko/655674f9f37e9361fe5378b6d440e411)
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "An example of treemap layout for hierarchical data.",
"width": 960,
"height": 500,
"padding": 2.5,
"autosize": "none",
"data": [
{
"name": "tree",
"url": "https://raw.githubusercontent.com/omerakko/VEGA/main/vegaTreemapData.json",
"transform": [
{
"type": "stratify",
"key": "id",
"parentKey": "parent"
},
{
"type": "treemap",
"field": "percentage",
"sort": {"field": "value", "order":"descending"},
"round": true,
"method": "resquarify",
"ratio": 1,
"size": [{"signal": "width"}, {"signal": "height"}],
"paddingOuter": 2,
"paddingInner":2
}
]
},
{
"name": "nodes",
"source": "tree",
"transform": [{ "type": "filter", "expr": "datum.children" }]
},
{
"name": "leaves",
"source": "tree",
"transform": [{ "type": "filter", "expr": "!datum.children" },
{"type": "filter", "expr": "datum.percentage > 0"}]
}
],
"scales": [
{
"name": "color",
"type": "ordinal",
"domain": {"data": "nodes", "field": "name"},
"range": [
"transparent", "#dd96ba", "#dea84e", "#c83836", "#dfde9b",
"#5eafb9", "#adc35d"]
},
{
"name": "size",
"type": "ordinal",
"domain": [0, 1, 2, 3],
"range": [256, 28, 20, 14]
},
{
"name": "opacity",
"type": "ordinal",
"domain": [0, 1, 2, 3],
"range": [0.15, 0.5, 0.8, 1.0]
}
],
"marks": [
{
"type": "rect",
"from": {"data": "nodes"},
"interactive": false,
"encode": {
"enter": {
"fill": {"value":"#333238"},
"stroke": {"scale": "color", "field": "name"},
"strokeWidth":{"value": 5}
},
"update": {
"x": {"field": "x0"},
"y": {"field": "y0"},
"x2": {"field": "x1"},
"y2": {"field": "y1"},
"stroke": {"scale": "color", "field": "name"}
}
}
},
{
"type": "rect",
"from": {"data": "leaves"},
"encode": {
"enter": {
"stroke": {"value": "#fff"}
},
"update": {
"x": {"field": "x0"},
"y": {"field": "y0"},
"x2": {"field": "x1"},
"y2": {"field": "y1"},
"fill": {"value": "transparent"}
},
"hover": {
"fill": {"value": "red"}
}
}
},
{
"type": "text",
"from": {"data": "nodes"},
"interactive": false,
"encode": {
"enter": {
"font": {"value": "Helvetica Neue, Arial"},
"align": {"value": "center"},
"baseline": {"value": "middle"},
"fill": {"scale": "color", "field": "name"},
"text": {"field": "name"},
"fontSize": {"scale": "size", "field": "depth"}
},
"update": {
"x": {"signal": "0.5 * (datum.x0 + datum.x1)"},
"y": {"signal": "0.5 * (datum.y0 + datum.y1)"}
}
}
}
]
}
There is doc available https://vega.github.io/vega/docs/transforms/treemap/ here saying that I can access to what I want, but I couldnt manage to apply it to the specs.
It seems like it's not currently possible in Vega. There's this PR that hasn't been merged yet.
But you can work around that by manually aggregating the values and then looking them up. Here a gist in which there's now a total field for each node, the relevant part being:
{
"name": "leaves",
"source": "tree",
"transform": [
{"type": "filter", "expr": "!datum.children"},
{"type": "filter", "expr": "datum.percentage > 0.3"}
]
},
{
"name": "totals",
"source": "leaves",
"transform": [
{
"type": "aggregate",
"groupby": ["parent"],
"fields": ["percentage"],
"as": ["total"],
"ops": ["sum"]
}
]
},
{
"name": "nodes",
"source": "tree",
"transform": [
{"type": "filter", "expr": "datum.children"},
{
"type": "lookup",
"from": "totals",
"key": "parent",
"fields": ["id"],
"values": ["total"],
"as": ["total"]
}
]
},
Basically you just get sums of all the leaves by parent in totals and then, after constructing the base nodes dataset, look the total up in totals.
Note that this will only work for this particular example, where there are exactly two levels in the hierarchy.

Vertical Violin Plot in Vega

I'm trying to swap the axes on the violin plot example provided by the Vega documentation here. When attempting this myself (link here) I seem successful in swapping the axes, and the rectangles seem to render fine. However the area chart (the actual violin) is completely absent. I'm completely at a loss on this. Any help would be much appriciated.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A violin plot example showing distributions for pengiun body mass.",
"width": 800,
"height": 600,
"padding": 5,
"config": {
"axisBand": {"bandPosition": 1, "tickExtra": true, "tickOffset": 0}
},
"signals": [
{"name": "plotWidth", "update": "(width - 30)/3"},
{"name": "height", "update": "height * 1"},
{"name": "trim", "value": true, "bind": {"input": "checkbox"}},
{
"name": "bandwidth",
"value": 0,
"bind": {"input": "range", "min": 0, "max": 200, "step": 1}
}
],
"data": [
{
"name": "penguins",
"url": "data/penguins.json",
"transform": [
{
"type": "filter",
"expr": "datum.Species != null && datum['Body Mass (g)'] != null"
}
]
},
{
"name": "density",
"source": "penguins",
"transform": [
{
"type": "kde",
"field": "Body Mass (g)",
"groupby": ["Species"],
"bandwidth": {"signal": "bandwidth"},
"extent": {"signal": "trim ? null : [2000, 6500]"}
}
]
},
{
"name": "stats",
"source": "penguins",
"transform": [
{
"type": "aggregate",
"groupby": ["Species"],
"fields": ["Body Mass (g)", "Body Mass (g)", "Body Mass (g)"],
"ops": ["q1", "median", "q3"],
"as": ["q1", "median", "q3"]
}
]
}
],
"scales": [
{
"name": "layout",
"type": "band",
"range": "width",
"domain": {"data": "penguins", "field": "Species"}
},
{
"name": "yscale",
"type": "linear",
"range": "height",
"round": true,
"domain": {"data": "penguins", "field": "Body Mass (g)"},
"domainMin": 0,
"zero": false,
"nice": true
},
{
"name": "hscale",
"type": "linear",
"range": [0, {"signal": "plotWidth"}],
"domain": {"data": "density", "field": "density"}
},
{
"name": "color",
"type": "ordinal",
"domain": {"data": "penguins", "field": "Species"},
"range": "category"
}
],
"axes": [
{"orient": "bottom", "scale": "layout", "zindex": 1},
{"orient": "left", "scale": "yscale", "zindex": 1}
],
"marks": [
{
"type": "group",
"from": {
"facet": {"data": "density", "name": "violin", "groupby": "Species"}
},
"encode": {
"enter": {
"xc": {"scale": "layout", "field": "Species", "band": 0.5},
"width": {"signal": "plotWidth"},
"height": {"signal": "height"}
}
},
"data": [
{
"name": "summary",
"source": "stats",
"transform": [
{"type": "filter", "expr": "datum.Species === parent.Species"}
]
}
],
"marks": [
{
"type": "area",
"from": {"data": "violin"},
"encode": {
"enter": {
"fill": {"scale": "color", "field": {"parent": "Species"}}
},
"update": {
"y": {"scale": "yscale", "field": "value"},
"xc": {"signal": "plotWidth / 2"},
"width": {"scale": "hscale", "field": "density"}
}
}
},
{
"type": "rect",
"from": {"data": "summary"},
"encode": {
"enter": {"fill": {"value": "black"}, "width": {"value": 2}},
"update": {
"y": {"scale": "yscale", "field": "q1"},
"y2": {"scale": "yscale", "field": "q3"},
"xc": {"signal": "plotWidth / 2"}
}
}
},
{
"type": "rect",
"from": {"data": "summary"},
"encode": {
"enter": {
"fill": {"value": "black"},
"height": {"value": 2},
"width": {"value": 8}
},
"update": {
"y": {"scale": "yscale", "field": "median"},
"xc": {"signal": "plotWidth / 2"}
}
}
}
]
}
]
}
Chart Image
Was able to figure this out, required an "orient" parameter in the encoding of the area mark.
"marks": [
{
"type": "area",
"from": {"data": "violin"},
"encode": {
"enter": {
"orient": {"value":"horizontal"},
"fill": {"scale": "color", "field": {"parent": "Species"}}
},
"update": {
"y": {"field":"value", "scale":"yscale"},
"xc": {"signal": "plotWidth / 2"},
"width": {"scale": "hscale", "field": "density"}
}
}
},
Example Here

Brushing/linking in vega (not vega-lite)

I'm trying to brush/link two plots in vega, more specifically a node-link diagram and a couple of scatterplots. Based on how dragging works with signals in the node-link diagram I did get quite far, but not far enough...
For the sake of simplicity, I'll provide a little test code here using just two scatterplots:
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"padding": 0,
"autosize": "none",
"width": 800,
"height": 400,
"signals": [
{ "description": "Any datapoint is activated",
"name": "datapoint_is_activated", "value": false,
"on": [
{
"events": "symbol:mouseover",
"update": "true"
},
{
"events": "symbol:mouseout",
"update": "false"
}
]
},
{ "description": "Active datapoint",
"name": "activated_datapoint", "value": null,
"on": [
{
"events": "symbol:mouseover",
"update": "item()"
},
{
"events": "symbol:mouseout",
"update": "null"
}
]
}
],
"data": [
{
"name": "table",
"values": [
{"name": "point A", "a": 1, "b": 2, "c": 3},
{"name": "point B", "a": 4, "b": 5, "c": 6},
{"name": "point C", "a": 9, "b": 8, "c": 7}
]
}
],
"scales": [
{ "name": "xscale",
"type": "linear",
"domain": [0,10],
"range": [0,200]
},
{ "name": "yscale",
"type": "linear",
"domain": [0,10],
"range": [0,200]
}
],
"layout": {"padding": 20},
"marks": [
{ "name": "plot1",
"type": "group",
"axes": [
{"orient": "bottom", "scale": "xscale"},
{"orient": "right", "scale": "yscale"}
],
"marks": [
{
"type": "symbol",
"from": {"data": "table"},
"encode": {
"enter": {
"x": {"field": "a", "scale": "xscale"},
"y": {"field": "b", "scale": "yscale"},
"tooltip": {"field": "name"}
},
"update": {
"size": {"value": 100},
"fill": {"value": "grey"}
}
}
}
]
},
{ "name": "plot2",
"type": "group",
"axes": [
{"orient": "bottom", "scale": "xscale"},
{"orient": "right", "scale": "yscale"}
],
"marks": [
{
"type": "symbol",
"from": {"data": "table"},
"on": [
{
"trigger": "datapoint_is_activated",
"modify": "activated_datapoint",
"values": "{fill: \"red\"}"
},
{
"trigger": "!datapoint_is_activated",
"modify": "activated_datapoint",
"values": "{fill: \"grey\"}"
}
],
"encode": {
"enter": {
"x": {"field": "a", "scale": "xscale"},
"y": {"field": "c", "scale": "yscale"},
"size": {"value": 100},
"tooltip": {"field": "name"}
},
"update": {
"fill": {"value": "grey"}
}
}
}
]
}
]
}
The resulting image looks like this:
The idea is that hovering over a datapoint in the left plot will highlight the corresponding datapoint in the right plot. I know this is straightforward in vega-lite, but that does not (yet) support node-link diagrams. Hence: vega.
My approach in the code is to:
create a signal in the outer scope that tracks (a) if there is a point activated, and (b) which point this is
in plot 2 to have a trigger that takes the activated datapoint and changes its colour to 'red'.
I have an inkling feeling that it has to do with my definition of the modify and values part, but I can't figure it out.
Any help very much appreciated!
Thank you,
jan.
After a lot of trial and error, I did find a way to do this. Instead of using a trigger (which I believe would be the canonical way of doing this), I merely use a test in the fill section: datapoint_is_activated && datum === activated_datapoint.datum. See code and image below.
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"padding": 0,
"autosize": "none",
"width": 800,
"height": 400,
"signals": [
{ "description": "Any datapoint is activated",
"name": "datapoint_is_activated", "value": false,
"on": [
{
"events": "symbol:mouseover",
"update": "true"
},
{
"events": "symbol:mouseout",
"update": "false"
}
]
},
{ "description": "Active datapoint",
"name": "activated_datapoint", "value": null,
"on": [
{
"events": "symbol:mouseover",
"update": "item()"
},
{
"events": "symbol:mouseout",
"update": "null"
}
]
}
],
"data": [
{
"name": "table",
"values": [
{"name": "point A", "a": 2, "b": 2, "c": 4},
{"name": "point B", "a": 4, "b": 5, "c": 6},
{"name": "point C", "a": 5, "b": 3, "c": 5}
]
}
],
"scales": [
{ "name": "xscale",
"type": "linear",
"domain": [0,10],
"range": [0,200]
},
{ "name": "yscale",
"type": "linear",
"domain": [0,10],
"range": [0,200]
}
],
"layout": {"padding": 20},
"marks": [
{ "name": "plot1",
"type": "group",
"axes": [
{"orient": "bottom", "scale": "xscale"},
{"orient": "right", "scale": "yscale"}
],
"marks": [
{
"type": "symbol",
"from": {"data": "table"},
"encode": {
"enter": {
"x": {"field": "a", "scale": "xscale"},
"y": {"field": "b", "scale": "yscale"},
"tooltip": {"field": "name"},
"size": {"value": 200}
},
"update": {
"fill": [
{"test": "datapoint_is_activated && datum === activated_datapoint.datum",
"value": "red"},
{"value": "lightgrey"}
]
}
}
}
]
},
{ "name": "plot2",
"type": "group",
"axes": [
{"orient": "bottom", "scale": "xscale"},
{"orient": "right", "scale": "yscale"}
],
"marks": [
{
"type": "symbol",
"from": {"data": "table"},
"encode": {
"enter": {
"x": {"field": "a", "scale": "xscale"},
"y": {"field": "c", "scale": "yscale"},
"size": {"value": 200},
"tooltip": {"field": "name"}
},
"update": {
"fill": [
{"test": "datapoint_is_activated && datum === activated_datapoint.datum",
"value": "red"},
{"value": "lightgrey"}
]
}
}
}
]
}
]
}

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.