Search code examples
vega-lite

Identical Selections


I have several vconcat plots that all share an x-axis. As I have a helpful label assigned to each selection, I would like the selection to be shared by all plots. That is, if I create a selection on any plot, that selection should appear on all plots. If I remove a selection from any plot, it should be removed from all plots.

At no time should the selections differ.

This is similar to my previous question Synchronized Selections except that now I would like for the selections to be identical across all vconcat plots. The selection of each plot is tied to the x encoding; no selection is bound to the plot scales.

For example:

identical selections


Solution

  • As with the answer to the linked question, I am starting from Overview and Detail example on vega-lite's site, using the v5 schema. The steps:

    1. For every layer, patch the generated Vega to push the s1_x signal to the outer scope.

    2. Patch the brush visuals to use the appropriate row from the now multi-row s1_store table.

    vlSpec = {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "data": {"url": "https://raw.githubusercontent.com/vega/vega-datasets/main/data/sp500.csv"},
      "vconcat": [
        { 
          "params": [
            { "name": "s1",
              "select":
                { "type": "interval",
                  "encodings": ["x"]
                }
            }
          ],
          "width": 480,
          "mark": "area",
          "encoding": {
            "x": {
              "field": "date",
              "type": "temporal",
              "axis": {"title": ""}
            },
            "y": {"field": "price", "type": "quantitative"}
          }
        },
        {
          "params": [
            { "name": "s1",
              "select":
                { "type": "interval",
                  "encodings": ["x"]
                }
            }
          ],
          "width": 480,
          "height": 60,
          "mark": "area",
          "encoding": {
            "x": {"field": "date", "type": "temporal"},
            "y": {
              "field": "price",
              "type": "quantitative",
              "axis": {"tickCount": 3, "grid": false}
            }
          }
        }
      ]
    }
    vegaEmbed( '#vis', vlSpec, {"patch": spec => {
    spec.signals.push({"name": "s1_x", "value": []});
      for (const [idx, group] of spec.marks.entries()) {
        let s1_x = group.signals.find(i => i.name == "s1_x")
        delete s1_x.value
        s1_x.push = "outer"
        for (const mark of group.marks) {
          if (!mark.name.startsWith("s1_brush")) {
            continue
          }
          console.log("mark: ", mark)
          for (const set of Object.values(mark.encode.update)) {
            for (const prop of set) {
              if (!Object.hasOwn(prop, "test")) {
                continue
              }
              console.log("found one", prop)
              prop.test = prop.test.replace("[0].unit ", `[${idx}].unit `)
            }
          }
        }
      }
      return spec
    }}).then(function(result) {
    }).catch(console.error);