From 972fa2fe4eafba5252c8609ff4d9bf5cf4e7d757 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Mon, 22 Jul 2024 21:02:17 +0200 Subject: [PATCH] Add Vega graphic as an example Vega is a data format, though one could say configuration format, for declaratively defining data visualizations. It can be generated from Python using Altair, but how about generating it with RCL? I don't know if this is a good idea, but it is at least possible. And it's already way more convenient to write than writing json in the online editor, because in RCL you don't have to quote the keys. --- examples/vega.rcl | 122 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 examples/vega.rcl diff --git a/examples/vega.rcl b/examples/vega.rcl new file mode 100644 index 0000000..5cd7fb2 --- /dev/null +++ b/examples/vega.rcl @@ -0,0 +1,122 @@ +// This example contains a Vega data visualization, see also [1]. You can +// evaluate this example to json using `rcl je examples/vega.rcl`, and then +// paste it into [2] to see the chart. The example is based on the default +// bar chart example at [3]. +// [1]: https://vega.github.io/vega/ +// [2]: https://vega.github.io/editor/#/edited +// [3]: https://vega.github.io/editor/#/examples/vega/bar-chart + +// There is no simple way to change the font in one place in Vega, we have to +// set all these properties. So write a function, so we can write it once and +// import it into all our graphics. For the purpose of the example, it is +// inlined here though. +let config_font = font => { + title = { font = font }, + axis = { labelFont = font, titleFont = font }, + legend = { labelFont = font, titleFont = font }, + header = { labelFont = font, titleFont = font }, + mark = { font = font }, + title = { font = font, subtitleFont = font }, +}; + +let head = { + "$schema": "https://vega.github.io/schema/vega/v5.json", + description = "A basic bar chart example.", + width = 400, + height = 200, + padding = 5, + config = config_font("Cantarell"), +}; + +let data_table = { + name = "table", + // Note, we inline the data here, but if you have a different json data set, + // we could just write `values = import "data.json"` here to include it. + values = [ + { "category": "A", "amount": 28 }, + { "category": "B", "amount": 55 }, + { "category": "C", "amount": 43 }, + { "category": "D", "amount": 91 }, + { "category": "E", "amount": 81 }, + { "category": "F", "amount": 53 }, + { "category": "G", "amount": 19 }, + { "category": "H", "amount": 87 }, + ], +}; + +let signal_tooltip = { + name = "tooltip", + value = {}, + // In the original example, this is a bit verbose. We can distill the data + // into its essence here, and generate the expanded form with repeating field + // names using a list comperhension below. + let on_events = { "rect:pointerover": "datum", "rect:pointerout": "{}" }; + on = [for event, update in on_events: { events = event, update = update }], +}; + +let scales = [ + { + name = "xscale", + type = "band", + domain = { data = "table", field = "category" }, + range = "width", + // TODO: This should be a float, but RCL does not support those yet. + padding = "0.05", + round = true, + }, + { + name = "yscale", + domain = { data = "table", field = "amount" }, + nice = true, + range = "height", + }, +]; + +{ + for k, v in head: k: v, + data = [data_table], + signals = [signal_tooltip], + scales = scales, + + axes = [ + { orient = "bottom", scale = "xscale" }, + { orient = "left", scale = "yscale" }, + ], + + marks = [ + { + type = "rect", + from = { data = "table" }, + encode = { + enter = { + width = { band = 1, scale = "xscale" }, + x = { field = "category", scale = "xscale" }, + y = { field = "amount", scale = "yscale" }, + y2 = { scale = "yscale", value = 0 }, + }, + hover = { fill = { value = "red" } }, + update = { fill = { value = "steelblue" } }, + }, + }, + { + type = "text", + encode = { + enter = { + align = { value = "center" }, + baseline = { value = "bottom" }, + fill = { value = "#333" }, + }, + update = { + fillOpacity = [ + { test = "datum === tooltip", value = 0 }, + { value = 1 }, + ], + text = { signal = "tooltip.amount" }, + // TODO: Band should be a float here as well. + x = { band = "0.5", scale = "xscale", signal = "tooltip.category" }, + y = { offset = -2, scale = "yscale", signal = "tooltip.amount" }, + }, + }, + }, + ], +}