VegaChart

Overview

A VegaChart is one of the many possible visualizations that may be created using the Vega Visualization Grammar, which is a declarative language for creating, saving, and sharing interactive visualization designs. With Vega, you can describe the visual appearance and interactive behavior of a visualization in a JSON format, and generate web-based views using Canvas or SVG.

When drawing a VegaChart, you must include in the options a specification for how to build the chart in the Vega visualization grammar. Several examples of such specifications are included below, and several can be seen on the Vega Examples page.

Data can be passed to a VegaChart in a very similar way as for other Google Charts, using a DataTable (or DataView). The difference is that rather than relying on the order of the columns to determine how they are used, VegaChart relies on the id of each column being the same as what is expected for the particular Vega visualization you have specified. For example, the following DataTable is created with columns that have ids for 'category' and 'amount', and these ids are the same as used within the 'vega' option.

With DataTable
        const dataTable = new google.visualization.DataTable();
        dataTable.addColumn({type: 'string', 'id': 'category'});
        dataTable.addColumn({type: 'number', 'id': 'amount'});
        dataTable.addRows([
          ['A', 28],
          ['B', 55],
          ['C', 43],
        ]);

        const options = {
          'vega': {
            ...
            // Here we create the Vega data object named 'datatable',
            // which will be passed in via the draw() call with a DataTable.
            'data': {'name': 'datatable'},

            'scales': [
              {
                'name': 'yscale',
                // Here is an example of how to use the 'amount' field from 'datatable'.
                'domain': {'data': 'datatable', 'field': 'amount'},
              }
            ]
          }
        };

        const chart = new google.visualization.VegaChart(
          document.getElementById('chart-div'));
        chart.draw(dataTable, options);
    
With Vega inlined data
        // A DataTable is required, but it may be empty.
        const dataTable = new google.visualization.DataTable();
        const options = {
          'vega': {
            // Here the data is specified inline in the Vega specification.
            "data": [
              {
               "name": "table",
                "values": [
                  {"category": "A", "amount": 28},
                  {"category": "B", "amount": 55},
                  {"category": "C", "amount": 43},
                ]
              }
            ],

            'scales': [
              {
                'name': 'yscale',
                // Here is how Vega normally uses the 'amount' field from 'table'.
                "domain": {"data": "table", "field": "category"},
              }
            ]
          }
        };

        const chart = new google.visualization.VegaChart(
          document.getElementById('chart-div'));
        chart.draw(dataTable, options);
    

However, only one such DataTable can be passed in to the VegaChart this way, whereas some Vega charts require more than one data table. We will address this limitation in a future release of Google Charts.

In the meantime, you can specify any additional data you need to use in the 'vega' 'data' option, either by inlining it or by loading it from a URL. Examples of both can be found below.

Note: While the Google Charts VegaChart can draw any Vega chart that you can specify with a Vega JSON Specification (including everything in the Example Gallery), additional features that require calls to the Vega API are not yet available.

A Simple Example, the Bar Chart

Here is a simple example of a VegaChart that draws a Bar Chart. (See the original example, a detailed tutorial, and the Bar Chart in Vega chart editor.)

While this represents yet another way to produce a bar chart in Google Charts, we plan to integrate all of the features of the other Bar and Column charts into a new implementation based on this VegaChart.

In this example, note that the 'data' option is replaced with the following, which uses the 'datatable' provided by the draw call as the 'source' for another data object called 'table', and 'table' is used in the remainder of the Vega spec.

  'data': [{'name': 'table', 'source': 'datatable'}],

<html>
  <head>
    <script src='https://www.gstatic.com/charts/loader.js'></script>
    <script>
      google.charts.load('49', {'packages': ['vegachart']}).then(drawChart);

      function drawChart() {
        const dataTable = new google.visualization.DataTable();
        dataTable.addColumn({type: 'string', 'id': 'category'});
        dataTable.addColumn({type: 'number', 'id': 'amount'});
        dataTable.addRows([
          ['A', 28],
          ['B', 55],
          ['C', 43],
          ['D', 91],
          ['E', 81],
          ['F', 53],
          ['G', 19],
          ['H', 87],
        ]);

        const options = {
          "vega": {
            "$schema": "https://vega.github.io/schema/vega/v4.json",
            "width": 500,
            "height": 200,
            "padding": 5,

            'data': [{'name': 'table', 'source': 'datatable'}],

            "signals": [
              {
                "name": "tooltip",
                "value": {},
                "on": [
                  {"events": "rect:mouseover", "update": "datum"},
                  {"events": "rect:mouseout",  "update": "{}"}
                ]
              }
            ],

            "scales": [
              {
                "name": "xscale",
                "type": "band",
                "domain": {"data": "table", "field": "category"},
                "range": "width",
                "padding": 0.05,
                "round": true
              },
              {
                "name": "yscale",
                "domain": {"data": "table", "field": "amount"},
                "nice": true,
                "range": "height"
              }
            ],

            "axes": [
              { "orient": "bottom", "scale": "xscale" },
              { "orient": "left", "scale": "yscale" }
            ],

            "marks": [
              {
                "type": "rect",
                "from": {"data":"table"},
                "encode": {
                  "enter": {
                    "x": {"scale": "xscale", "field": "category"},
                    "width": {"scale": "xscale", "band": 1},
                    "y": {"scale": "yscale", "field": "amount"},
                    "y2": {"scale": "yscale", "value": 0}
                  },
                  "update": {
                    "fill": {"value": "steelblue"}
                  },
                  "hover": {
                    "fill": {"value": "red"}
                  }
                }
              },
              {
                "type": "text",
                "encode": {
                  "enter": {
                    "align": {"value": "center"},
                    "baseline": {"value": "bottom"},
                    "fill": {"value": "#333"}
                  },
                  "update": {
                    "x": {"scale": "xscale", "signal": "tooltip.category", "band": 0.5},
                    "y": {"scale": "yscale", "signal": "tooltip.amount", "offset": -2},
                    "text": {"signal": "tooltip.amount"},
                    "fillOpacity": [
                      {"test": "datum === tooltip", "value": 0},
                      {"value": 1}
                    ]
                  }
                }
              }
            ]
          }
        };

        const chart = new google.visualization.VegaChart(document.getElementById('chart-div'));
        chart.draw(dataTable, options);
      }
    </script>
  </head>

  <body>
    <div id="chart-div" style="width: 700px; height: 250px;"></div>
  </body>

</html>


Heatmap Example

VegaChart can draw a Heatmap using simple rect marks for each cell. (Original example)

The temperature data is loaded in this example from a csv file, using the following 'data' spec.

  "data": [
    {
      "name": "temperature",
      "url": "https://vega.github.io/editor/data/seattle-temps.csv"
      ...
    }
  ]

<html>
  <head>
    <script src='https://www.gstatic.com/charts/loader.js'></script>
    <script>
      google.charts.load('49', {'packages': ['vegachart']}).then(drawChart);

      function drawChart() {
        // A DataTable is required, but it can be empty.
        const dataTable = new google.visualization.DataTable();

        const options = {
          'vega': {
            "$schema": "https://vega.github.io/schema/vega/v5.json",
            "width": 500,
            "height": 500,
            "padding": 5,

            "title": {
              "text": "Seattle Annual Temperatures",
              "anchor": "middle",
              "fontSize": 16,
              "frame": "group",
              "offset": 4
            },

            "data": [
              {
                "name": "temperature",
                "url": "https://vega.github.io/vega/data/seattle-weather-hourly-normals.csv",
                "format": {"type": "csv", "parse": {"temperature": "number", "date": "date"}},
                "transform": [
                  {
                    "type": "formula", "as": "hour",
                    "expr": "hours(datum.date)"
                  },
                  {
                    "type": "formula", "as": "day",
                    "expr": "datetime(year(datum.date), month(datum.date), date(datum.date))"
                  }
                ]
              }
            ],

            "scales": [
              {
                "name": "x",
                "type": "time",
                "domain": {"data": "temperature", "field": "day"},
                "range": "width"
              },
              {
                "name": "y",
                "type": "band",
                "domain": [
                  6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
                  0, 1, 2, 3, 4, 5
                ],
                "range": "height"
              },
              {
                "name": "color",
                "type": "linear",
                "range": {"scheme": "redyellowblue"},
                "domain": {"data": "temperature", "field": "temperature"},
                "reverse": true,
                "zero": false, "nice": true
              }
            ],

            "axes": [
              {"orient": "bottom", "scale": "x", "domain": false, "title": "Month", "format": "%b"},
              {
                "orient": "left", "scale": "y", "domain": false, "title": "Hour",
                "encode": {
                  "labels": {
                    "update": {
                      "text": {
                        "signal": "datum.value === 0 ? 'Midnight' : datum.value === 12 ? 'Noon' : datum.value < 12 ? datum.value + ':00 am' : (datum.value - 12) + ':00 pm'"
                      }
                    }
                  }
                }
              }
            ],

            "legends": [
              {
                "fill": "color",
                "type": "gradient",
                "title": "Avg. Temp (°F)",
                "titleFontSize": 12,
                "titlePadding": 4,
                "gradientLength": {"signal": "height - 16"}
              }
            ],

            "marks": [
              {
                "type": "rect",
                "from": {"data": "temperature"},
                "encode": {
                  "enter": {
                    "x": {"scale": "x", "field": "day"},
                    "y": {"scale": "y", "field": "hour"},
                    "width": {"value": 5},
                    "height": {"scale": "y", "band": 1},
                    "tooltip": {
                      "signal":
                        "timeFormat(datum.date, '%b %d %I:00 %p') + ': ' + datum.temperature + '°'"
                    }
                  },
                  "update": {
                    "fill": {"scale": "color", "field": "temperature"}
                  }
                }
              }
            ]
          }
        };

        const chart = new google.visualization.VegaChart(document.getElementById('chart-div'));
        chart.draw(dataTable, options);
      }
    </script>
  </head>

  <body>
    <div id="chart-div" style="width: 500px; height: 500px;"></div>
  </body>

</html>


Volcano Contour Example

VegaChart can draw a Volcano Contour chart using the icocontour transform. (Original example)

The volcano data is loaded in this example from a json file, using the following 'data' spec.

  "data": [
    {
      "name": "volcano",
      "url": "https://vega.github.io/editor/data/volcano.json"
    }
  ]

<html>
  <head>
    <script src='https://www.gstatic.com/charts/loader.js'></script>
    <script>
      google.charts.load('49', {'packages': ['vegachart']}).then(drawChart);

      function drawChart() {
        const dataTable = new google.visualization.DataTable();
        const options = {
          'vega': {
            "$schema": "https://vega.github.io/schema/vega/v5.json",
            "width": 600,
            "autosize": "none",

            "signals": [
              {
                "name": "grid",
                "init": "data('volcano')[0]"
              },
              {
                "name": "height",
                "update": "round(grid.height * width / grid.width)"
              },
              {
                "name": "smooth", "value": true,
                "bind": {"input": "radio", "options": [true, false]}
              }
            ],

            "data": [
              {
                "name": "volcano",
                "url": "https://vega.github.io/editor/data/volcano.json"
              },
              {
                "name": "contours",
                "source": "volcano",
                "transform": [
                  {
                    "type": "isocontour",
                    "scale": {"expr": "width / datum.width"},
                    "smooth": {"signal": "smooth"},
                    "thresholds": {"signal": "sequence(90, 195, 5)"}
                  }
                ]
              }
            ],

            "scales": [
              {
                "name": "color",
                "type": "linear",
                "domain": [90, 190],
                "range": {"scheme": "blueorange"}
              }
            ],

            "marks": [
              {
                "type": "path",
                "from": {"data": "contours"},
                "encode": {
                  "enter": {
                    "stroke": {"value": "#ccc"},
                    "strokeWidth": {"value": 1},
                    "fill": {"scale": "color", "field": "contour.value"}
                  }
                },
                "transform": [
                  {
                    "type": "geopath",
                    "field": "datum.contour"
                  }
                ]
              }
            ]
          }
      };

        const chart = new google.visualization.VegaChart(document.getElementById('chart-div'));
        chart.draw(dataTable, options);
      }
    </script>
  </head>

  <body>
    <div id="chart-div" style="width: 500px; height: 500px;"></div>
  </body>

</html>


BeeSwarm Example

VegaChart can draw a BeeSwarm chart using the force transform. (Original example)


<html>
  <head>
    <script src='https://www.gstatic.com/charts/loader.js'></script>
    <script>
      google.charts.load('49', {'packages': ['vegachart']}).then(drawChart);

      function drawChart() {
        const dataTable = new google.visualization.DataTable();
        const options = {
          'vega': {
            "$schema": "https://vega.github.io/schema/vega/v5.json",
            "width": 600,
            "height": 100,
            "padding": {"left": 5, "right": 5, "top": 0, "bottom": 20},
            "autosize": "none",

            "signals": [
              { "name": "cx", "update": "width / 2" },
              { "name": "cy", "update": "height / 2" },
              { "name": "radius", "value": 8, "bind": {"input": "range", "min": 2, "max": 15, "step": 1} },
              { "name": "collide", "value": 1, "bind": {"input": "range", "min": 1, "max": 10, "step": 1} },
              { "name": "gravityX", "value": 0.2, "bind": {"input": "range", "min": 0, "max": 1} },
              { "name": "gravityY", "value": 0.1, "bind": {"input": "range", "min": 0, "max": 1} },
              { "name": "static", "value": true, "bind": {"input": "checkbox"} }
            ],

            "data": [
              {
                "name": "people",
                "url": "https://vega.github.io/editor/data/miserables.json",
                "format": {"type": "json", "property": "nodes"}
              }
            ],

            "scales": [
              {
                "name": "xscale",
                "type": "band",
                "domain": {
                  "data": "people",
                  "field": "group",
                  "sort": true
                },
                "range": "width"
              },
              {
                "name": "color",
                "type": "ordinal",
                "domain": {"data": "people", "field": "group"},
                "range": {"scheme": "category20c"}
              }
            ],

            "axes": [
              { "orient": "bottom", "scale": "xscale" }
            ],

            "marks": [
              {
                "name": "nodes",
                "type": "symbol",
                "from": {"data": "people"},
                "encode": {
                  "enter": {
                    "fill": {"scale": "color", "field": "group"},
                    "xfocus": {"scale": "xscale", "field": "group", "band": 0.5},
                    "yfocus": {"signal": "cy"}
                  },
                  "update": {
                    "size": {"signal": "pow(2 * radius, 2)"},
                    "stroke": {"value": "white"},
                    "strokeWidth": {"value": 1},
                    "zindex": {"value": 0}
                  },
                  "hover": {
                    "stroke": {"value": "purple"},
                    "strokeWidth": {"value": 3},
                    "zindex": {"value": 1}
                  }
                },
                "transform": [
                  {
                    "type": "force",
                    "iterations": 300,
                    "static": {"signal": "static"},
                    "forces": [
                      {"force": "collide", "iterations": {"signal": "collide"}, "radius": {"signal": "radius"}},
                      {"force": "x", "x": "xfocus", "strength": {"signal": "gravityX"}},
                      {"force": "y", "y": "yfocus", "strength": {"signal": "gravityY"}}
                    ]
                  }
                ]
              }
            ]
          }
        };

        const chart = new google.visualization.VegaChart(document.getElementById('chart-div'));
        chart.draw(dataTable, options);
      }
    </script>
  </head>

  <body>
    <div id="chart-div" style="width: 700px; height: 250px;"></div>
  </body>

</html>

Radar Chart Example

VegaChart can be used to draw three small Radar Charts. (Original example of a similar radar chart)


<html>
  <head>
    <title>Radar Chart Small Multiples</title>
    <script src='https://www.gstatic.com/charts/loader.js'></script>
    <script>
      google.charts.load('49', {'packages': ['vegachart']}).then(loadCharts);

      const lasagna = [
        ["Protein",0.1308,"Lasagna, cheese, frozen, prepared"],
        ["Carbohydrates",0.05032727272727273,"Lasagna, cheese, frozen, prepared"],
        ["Vitamin C",0.228,"Lasagna, cheese, frozen, prepared"],
        ["Calcium",0.08538461538461538,"Lasagna, cheese, frozen, prepared"],
        ["Zinc",0.11375,"Lasagna, cheese, frozen, prepared"],
        ["Sodium",0.18933333333333333,"Lasagna, cheese, frozen, prepared"]
      ];

      const pork = [
        ["Protein",0.2638,"Pulled pork in barbecue sauce"],
        ["Carbohydrates",0.06814545454545454,"Pulled pork in barbecue sauce"],
        ["Vitamin C",0.002666666666666667,"Pulled pork in barbecue sauce"],
        ["Calcium",0.033846153846153845,"Pulled pork in barbecue sauce"],
        ["Zinc",0.23125,"Pulled pork in barbecue sauce"],
        ["Sodium",0.444,"Pulled pork in barbecue sauce"]
      ];

      const melon = [
        ["Protein",0.0168,"Melons, cantaloupe, raw"],
        ["Carbohydrates",0.029672727272727274,"Melons, cantaloupe, raw"],
        ["Vitamin C",0.4893333333333334,"Melons, cantaloupe, raw"],
        ["Calcium",0.006923076923076923,"Melons, cantaloupe, raw"],
        ["Zinc",0.0225,"Melons, cantaloupe, raw"],
        ["Sodium",0.010666666666666666,"Melons, cantaloupe, raw"]
      ];

      function loadCharts() {
        addChart(lasagna[0][2], lasagna, "#B82E2E");
        addChart(pork[0][2], pork, "#6633CC");
        addChart(melon[0][2], melon, "#109618");
      };

      function addChart(title, data, color) {
        const dataTable = new google.visualization.DataTable();
        dataTable.addColumn({type: 'string', 'id': 'key'});
        dataTable.addColumn({type: 'number', 'id': 'value'});
        dataTable.addColumn({type: 'string', 'id': 'category'});
        dataTable.addRows(data);

        const options = {
          'vega': {
            "$schema": "https://vega.github.io/schema/vega/v5.json",
            "width": 250,
            "height": 300,
            "autosize": "none",
            "title": {
              "text": title,
              "anchor": "middle",
              "fontSize": 14,
              "dy": -8,
              "dx": {"signal": "-width/4"},
              "subtitle": "RDI per 100g"
            },
            "signals": [
              {"name": "radius", "update": "90"}
            ],
            "data": [
              {
                "name": "table",
                "source": "datatable",
              },
              {
                "name": "keys",
                "source": "table",
                "transform": [
                  {
                    "type": "aggregate",
                    "groupby": ["key"]
                  }
                ]
              }
            ],
            "scales": [
              {
                "name": "angular",
                "type": "point",
                "range": {"signal": "[-PI, PI]"},
                "padding": 0.5,
                "domain": {"data": "table", "field": "key"}
              },
              {
                "name": "radial",
                "type": "linear",
                "range": {"signal": "[0, radius]"},
                "zero": true,
                "nice": false,
                "domain": [0,0.5],
              }
            ],
            "encode": {
              "enter": {
                "x": {"signal": "width/2"},
                "y": {"signal": "height/2 + 20"}
              }
            },
            "marks": [
              {
                "type": "group",
                "name": "categories",
                "zindex": 1,
                "from": {
                  "facet": {"data": "table", "name": "facet", "groupby": ["category"]}
                },
                "marks": [
                  {
                    "type": "line",
                    "name": "category-line",
                    "from": {"data": "facet"},
                    "encode": {
                      "enter": {
                        "interpolate": {"value": "linear-closed"},
                        "x": {"signal": "scale('radial', datum.value) * cos(scale('angular', datum.key))"},
                        "y": {"signal": "scale('radial', datum.value) * sin(scale('angular', datum.key))"},
                        "stroke": {"value": color},
                        "strokeWidth": {"value": 1.5},
                        "fill": {"value": color},
                        "fillOpacity": {"value": 0.1}
                      }
                    }
                  },
                  {
                    "type": "text",
                    "name": "value-text",
                    "from": {"data": "category-line"},
                    "encode": {
                      "enter": {
                        "x": {"signal": "datum.x + 14 * cos(scale('angular', datum.datum.key))"},
                        "y": {"signal": "datum.y + 14 * sin(scale('angular', datum.datum.key))"},
                        "text": {"signal": "format(datum.datum.value,'.1%')"},
                        "opacity": {"signal": "datum.datum.value > 0.01 ? 1 : 0"},
                        "align": {"value": "center"},
                        "baseline": {"value": "middle"},
                        "fontWeight": {"value": "bold"},
                        "fill": {"value": color},
                      }
                    }
                  }
                ]
              },
              {
                "type": "rule",
                "name": "radial-grid",
                "from": {"data": "keys"},
                "zindex": 0,
                "encode": {
                  "enter": {
                    "x": {"value": 0},
                    "y": {"value": 0},
                    "x2": {"signal": "radius * cos(scale('angular', datum.key))"},
                    "y2": {"signal": "radius * sin(scale('angular', datum.key))"},
                    "stroke": {"value": "lightgray"},
                    "strokeWidth": {"value": 1}
                  }
                }
              },
              {
                "type": "text",
                "name": "key-label",
                "from": {"data": "keys"},
                "zindex": 1,
                "encode": {
                  "enter": {
                    "x": {"signal": "(radius + 11) * cos(scale('angular', datum.key))"},
                    "y": [
                      {
                        "test": "sin(scale('angular', datum.key)) > 0",
                        "signal": "5 + (radius + 11) * sin(scale('angular', datum.key))"
                      },
                      {
                        "test": "sin(scale('angular', datum.key)) < 0",
                        "signal": "-5 + (radius + 11) * sin(scale('angular', datum.key))"
                      },
                      {
                        "signal": "(radius + 11) * sin(scale('angular', datum.key))"
                      }
                    ],
                    "text": {"field": "key"},
                    "align":
                      {
                        "value": "center"
                      },
                    "baseline": [
                      {
                        "test": "scale('angular', datum.key) > 0", "value": "top"
                      },
                      {
                        "test": "scale('angular', datum.key) == 0", "value": "middle"
                      },
                      {
                        "value": "bottom"
                      }
                    ],
                    "fill": {"value": "black"},
                    "fontSize": {"value": 12}
                  }
                }
              },
              {
                "type": "line",
                "name": "twenty-line",
                "from": {"data": "keys"},
                "encode": {
                  "enter": {
                    "interpolate": {"value": "linear-closed"},
                    "x": {"signal": "0.2 * radius * cos(scale('angular', datum.key))"},
                    "y": {"signal": "0.2 * radius * sin(scale('angular', datum.key))"},
                    "stroke": {"value": "lightgray"},
                    "strokeWidth": {"value": 1}
                  }
                }
              },
              {
                "type": "line",
                "name": "fourty-line",
                "from": {"data": "keys"},
                "encode": {
                  "enter": {
                    "interpolate": {"value": "linear-closed"},
                    "x": {"signal": "0.4 * radius * cos(scale('angular', datum.key))"},
                    "y": {"signal": "0.4 * radius * sin(scale('angular', datum.key))"},
                    "stroke": {"value": "lightgray"},
                    "strokeWidth": {"value": 1}
                  }
                }
              },
              {
                "type": "line",
                "name": "sixty-line",
                "from": {"data": "keys"},
                "encode": {
                  "enter": {
                    "interpolate": {"value": "linear-closed"},
                    "x": {"signal": "0.6 * radius * cos(scale('angular', datum.key))"},
                    "y": {"signal": "0.6 * radius * sin(scale('angular', datum.key))"},
                    "stroke": {"value": "lightgray"},
                    "strokeWidth": {"value": 1}
                  }
                }
              },
              {
                "type": "line",
                "name": "eighty-line",
                "from": {"data": "keys"},
                "encode": {
                  "enter": {
                    "interpolate": {"value": "linear-closed"},
                    "x": {"signal": "0.8 * radius * cos(scale('angular', datum.key))"},
                    "y": {"signal": "0.8 * radius * sin(scale('angular', datum.key))"},
                    "stroke": {"value": "lightgray"},
                    "strokeWidth": {"value": 1}
                  }
                }
              },
              {
                "type": "line",
                "name": "outer-line",
                "from": {"data": "radial-grid"},
                "encode": {
                  "enter": {
                    "interpolate": {"value": "linear-closed"},
                    "x": {"field": "x2"},
                    "y": {"field": "y2"},
                    "stroke": {"value": "lightgray"},
                    "strokeWidth": {"value": 1}
                  }
                }
              }
            ]
          }
        };

        const elem = document.createElement("div");
        elem.setAttribute("style", "display: inline-block; width: 250px; height: 300px; padding: 20px;");

        const chart = new google.visualization.VegaChart(elem);
        chart.draw(dataTable, options);

        document.getElementById("chart-area").appendChild(elem);
      }
    </script>
  </head>

  <body>
    <div id="chart-area"></div>
  </body>
</html>