Link separate vega charts together?

Viewed 364

Is it possible to link multiple vega charts on a web page without making them into one combined chart?

I'm trying to do this in a dashboard to allow me more control of styling, particularly by putting the graphs on different tiles and making those responsive. As per the documentation, combined charts cannot be made responsive with "width": "container".

I created the following two example charts and embedded them into one HTML file, but I'm getting the following exception: Uncaught (in promise) Error: Cannot find a selection named "selector002".

Is there any way to make this work with Vega? Alternatively, I'd be happy with a vconcat chart if I can somehow get it to adjust to the size of its parent container.

{
    "config": {
        "view": {
            "continuousWidth": 400,
            "continuousHeight": 300
        }
    },
    "data": {
        "name": "data-07d1121d4bb402da807ced7f89269752"
    },
    "mark": "point",
    "encoding": {
        "x": {
            "type": "quantitative",
            "field": "A"
        },
        "y": {
            "type": "quantitative",
            "field": "B"
        }
    },
    "selection": {
        "selector002": {
            "type": "interval",
            "encodings": [
                "x"
            ]
        }
    },
    "width": "container",
    "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json",
    "datasets": {
        "data-07d1121d4bb402da807ced7f89269752": [
            {
                "A": 0.1,
                "B": 0.5,
                "C": 1.0
            },
            {
                "A": 0.1,
                "B": 0.8,
                "C": 0.0
            },
            {
                "A": 0.0,
                "B": 1.0,
                "C": 1.0
            },
            {
                "A": 0.2,
                "B": 0.8,
                "C": 1.0
            },
            {
                "A": 0.2,
                "B": 0.9,
                "C": 0.0
            }
        ]
    }
}
{
    "config": {
        "view": {
            "continuousWidth": 400,
            "continuousHeight": 300
        }
    },
    "data": {
        "name": "data-07d1121d4bb402da807ced7f89269752"
    },
    "mark": "bar",
    "encoding": {
        "color": {
            "type": "ordinal",
            "field": "B"
        },
        "x": {
            "type": "quantitative",
            "field": "A"
        },
        "y": {
            "type": "ordinal",
            "field": "C"
        }
    },
    "transform": [
        {
            "filter": {
                "selection": "selector002"
            }
        }
    ],
    "width": "container",
    "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json",
    "datasets": {
        "data-07d1121d4bb402da807ced7f89269752": [
            {
                "A": 0.1,
                "B": 0.5,
                "C": 1.0
            },
            {
                "A": 0.1,
                "B": 0.8,
                "C": 0.0
            },
            {
                "A": 0.0,
                "B": 1.0,
                "C": 1.0
            },
            {
                "A": 0.2,
                "B": 0.8,
                "C": 1.0
            },
            {
                "A": 0.2,
                "B": 0.9,
                "C": 0.0
            }
        ]
    }
}
1 Answers

There's an open issue related to this: https://github.com/vega/vega-lite/issues/1830

My latest reply on this issue can help you:

Here's a hack using the current version of VegaLite (vega@5.17.0, vega-lite@4.17.0, vega-embed@6.12.2), assuming that you have a brush selection in vegalite_spec_for_minimap:

vegaEmbed(`#minimap`, vegalite_spec_for_minimap)
    .then(function(minimapVe){
        minimapVe.view.addSignalListener('brush', function(signalName, e) {
            console.log("updated");
        });
     });

Here is a full example:

timestampBounds = [1610580331317, 1610845273208];
chart_data.data.name = 'datatable';
vegaEmbed(`#main-chart`, chart_data).then(function(mainChartVe){
    // create a copy of data for the mini-map
    var minimap_data = $.extend(true, {}, chart_data);

    // set the height for minimap
    minimap_data.height = '50';

    // if you have an initial selection, apply it on the minimap
    if(timestampBounds) {
        minimap_data.selection = {"brush": {"type": "interval", "encodings": ["x"], "init": {"x": timestampBounds}}};
    } else {
        minimap_data.selection = {"brush": {"type": "interval", "encodings": ["x"]}};
    }

    function updateChartDataBasedOnSelection(timestampArr) {
        var filteredValues = minimap_data.data.values.filter(x => x.timestamp >= timestampArr[0] && x.timestamp <= timestampArr[1]);
        var changeset = vega.changeset().remove(vega.truthy).insert(filteredValues);
        mainChartVe.view.change('datatable', changeset).run();
    }

    // Have an initial selection
    if(timestampBounds) updateChartDataBasedOnSelection(timestampBounds);

    vegaEmbed(`#minimap`, minimap_data)
        .then(function(minimapVe){
            minimapVe.view.addSignalListener('brush', function(signalName, e) {
                if(e && e.timestamp) {
                    // update the main chart based on the selected data
                    updateChartDataBasedOnSelection(e.timestamp);
                }
            });
        });
});
Related