Skip to main content

Gauge Chart

Gauge charts are effective in visualizing metrics in comparison to a threshold to monitor progress or benchmark against a target.

Simple Gauge Chart

reporting-custom-chart/simple-gauge-chart
Legacy syntax
CustomChart {
fields {
field value {
type: "measure"
label: "Value"
}
field max_value {
type: "measure"
label: "Max Value"
}
}

options {
option background_color {
type: "color-picker"
label: "Background Color"
default_value: "#cbd1d6"
}
option value_color {
type: "color-picker"
label: "Value Color"
default_value: "black"
}
option fillColor {
type: "color-picker"
label: "Fill Color"
default_value: "#3b60d2"
}
option needle_color {
type: "color-picker"
label: "Needle Color"
default_value: "black"
}
option needleScale {
label: 'Needle Scaling Factor'
type: 'number-input'
default_value: 0.8
}
option showLabels {
label: 'Show Labels'
type: 'toggle'
default_value: true
}
option showTicks {
label: 'Show Ticks'
type: 'toggle'
default_value: true
}
option tickGaps {
label: 'Tick Gaps'
type: 'number-input'
default_value: 0.25
}
}
template: @vgl {
"config": {
"padding": {"left": 0, "top": 0, "right": 0, "bottom": 0},
"autosize": "fit",
"view": {
"stroke": "transparent"
},
"font": "Inter"
},
"params": [
{"name": "centerX", "expr": "width/2"},
{"name": "centerY", "expr": "height/2 + outerRadius/2"},
{"name": "outerRadius", "expr": "radiusRef"},
{"name": "radiusRef", "expr": "min(width/2, height/2)"},
{"name": "innerRadius", "expr": "outerRadius - outerRadius * 0.25"},
{"name": "fontFactor", "expr": "radiusRef/5"},
{"name": "backgroundColor", "value": @{options.background_color.value}},
{"name": "fillColor", "value": @{options.fillColor.value}},
{"name": "needleColor", "value": @{options.needle_color.value}},
{"name": "needleSize", "expr": "innerRadius * @{options.needleScale.value}"}
],
"data": {
"values": @{values}
},
"transform": [
{"calculate": "datum['@{fields.value.name}'] / datum['@{fields.max_value.name}']", "as": "percentage"},
{"calculate": "(datum.percentage) * (PI) + (-PI/2)", "as": "arcValue"}
],
"layer": [
// Base Gauge
{
"mark": {
"type": "arc",
"name": "gauge",
"theta": {"expr": "-PI/2"},
"theta2": {"expr": "PI/2"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"innerRadius": {"expr": "innerRadius"},
"outerRadius": {"expr": "outerRadius"},
"fill": {"expr": "backgroundColor"},
"stroke": {"expr": "backgroundColor"},
"strokeWidth": 1,
}
},
// Value Gauge
{
"mark": {
"type": "arc",
"name": "gauge",
"theta": {"expr": "-PI/2"},
"theta2": {"expr": "datum.arcValue"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"innerRadius": {"expr": "innerRadius"},
"outerRadius": {"expr": "outerRadius"},
"fill": {"expr": "fillColor"}
}
},
// Label
{
"transform": [
{"calculate": "[0, datum['@{fields.max_value.name}']]", "as": "label"},
{"flatten": ["label"]},
{"calculate": "datum.label === 0 ? 0 : 1", "as": "angle"}
],
"encoding": {
"text": {
"field": "label",
"format": @{fields.max_value.format},
"formatType": "holisticsFormat"
},
"theta": {
"field": "angle",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
}
},
"mark": {
"type": "text",
"fontSize": {"expr": "fontFactor/2"},
"fontWeight": 600,
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"radius": {"expr": "outerRadius*1.05"},
"align": {"expr": "datum.angle === 0 ? 'right' : 'left'"},
"baseline": "alphabetic",
"opacity": {"expr": "@{options.showLabels.value} ? 1 : 0"}
}
},
// Main Value
{
"transform": [
{"calculate": "datum['@{fields.value.name}'] + ' (' + format(datum.percentage, '.0%') + ')'", "as": "mainValue"}
],
"encoding": {
"text": {
"field": "mainValue"
}
},
"mark": {
"type": "text",
"fontSize": {"expr": "fontFactor"},
"fontWeight": 600,
"x": {"expr": "centerX"},
"y": {"expr": "centerY + fontFactor*1.2"},
"baseline": "alphabetic",
"align": "center",
"color": @{options.value_color.value}
}
},
// Needle
{
"encoding": {
"angle": {
"field": "percentage",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [-90, 90]}
}
},
"mark": {
"type": "point",
"shape": {
"expr": "'M 5 -2 A 7 7 0 0 1 -10 -7 L 0 -' + toString(needleSize) + ' Z'"
},
"size": 4,
"opacity": 1,
"fill": {"expr": "needleColor"},
"stroke": {"expr": "needleColor"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"}
}
},
// Ticks
{
"data": {
"sequence": {
"as": "ticks",
"start": 0,
"stop": 1.01,
"step": @{options.tickGaps.value}
}
},
"encoding": {
"theta": {
"field": "ticks",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
},
"theta2": {
"field": "ticks",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
}
},
"mark": {
"type": "arc",
"outerRadius": {"expr": "outerRadius"},
"innerRadius": {"expr": "innerRadius"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"stroke": {"expr": "needleColor"},
"opacity": {"expr": "@{options.showTicks.value} ? 1 : 0"}
}
}
]
};;
}

Gauge Chart with Target

reporting-custom-chart/gauge-chart-with-target
Legacy syntax
CustomChart {
fields {
field value {
type: "measure"
label: "Value"
}
field max_value {
type: "measure"
label: "Max Value"
}
field target {
type: "measure"
label: "Target"
}
}

options {
option background_color {
type: "color-picker"
label: "Background Color"
default_value: "#cbd1d6"
}
option fillColor {
type: "color-picker"
label: "Fill Color"
default_value: "#3b60d2"
}
option value_color {
type: "color-picker"
label: "Value Color"
default_value: "black"
}
option needle_color {
type: "color-picker"
label: "Needle Color"
default_value: "black"
}
option needleScale {
label: 'Needle Scaling Factor'
type: 'number-input'
default_value: 0.8
}
option showLabels {
label: 'Show Labels'
type: 'toggle'
default_value: false
}
option showTicks {
label: 'Show Ticks'
type: 'toggle'
default_value: false
}
option tickGaps {
label: 'Tick Gaps'
type: 'number-input'
default_value: 0.25
}
option targetColor {
type: "color-picker"
label: "Target Color"
default_value: "#5fc93c"
}
}
template: @vgl {
"config": {
"padding": {"left": 0, "top": 0, "right": 0, "bottom": 0},
"autosize": "fit",
"view": {
"stroke": "transparent"
},
"font": "Inter"
},
"params": [
{"name": "centerX", "expr": "width/2"},
{"name": "centerY", "expr": "height/2 + outerRadius/2"},
{"name": "outerRadius", "expr": "radiusRef"},
{"name": "radiusRef", "expr": "min(width/2, height/2)"},
{"name": "innerRadius", "expr": "outerRadius - outerRadius * 0.25"},
{"name": "fontFactor", "expr": "radiusRef/5"},
{"name": "backgroundColor", "value": @{options.background_color.value}},
{"name": "fillColor", "value": @{options.fillColor.value}},
{"name": "needleColor", "value": @{options.needle_color.value}},
{"name": "needleSize", "expr": "innerRadius * @{options.needleScale.value}"},
{"name": "targetColor", "value": @{options.targetColor.value}}
],
"data": {
"values": @{values}
},
"transform": [
{"calculate": "datum['@{fields.value.name}'] / datum['@{fields.max_value.name}']", "as": "percentage"},
{"calculate": "(datum.percentage) * (PI) + (-PI/2)", "as": "arcValue"},
{"calculate": "datum['@{fields.target.name}'] / datum['@{fields.max_value.name}']", "as": "targetPercentage"}
],
"encoding": {
"tooltip": [
{"field": @{fields.value.name}, "format": @{fields.value.format}, "formatType": "holisticsFormat"},
{"field": @{fields.target.name}, "format": @{fields.target.format}, "formatType": "holisticsFormat"},
{"field": @{fields.max_value.name}, "format": @{fields.max_value.format}, "formatType": "holisticsFormat"}
]
},
"layer": [
// Base Gauge
{
"mark": {
"type": "arc",
"name": "gauge",
"theta": {"expr": "-PI/2"},
"theta2": {"expr": "PI/2"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"innerRadius": {"expr": "innerRadius"},
"outerRadius": {"expr": "outerRadius"},
"fill": {"expr": "backgroundColor"},
"stroke": {"expr": "backgroundColor"},
"strokeWidth": 1,
}
},
// Value Gauge
{
"mark": {
"type": "arc",
"name": "gauge",
"theta": {"expr": "-PI/2"},
"theta2": {"expr": "datum.arcValue"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"innerRadius": {"expr": "innerRadius"},
"outerRadius": {"expr": "outerRadius"},
"fill": {"expr": "fillColor"},
}
},
// Label
{
"transform": [
{"calculate": "[0, datum['@{fields.max_value.name}']]", "as": "label"},
{"flatten": ["label"]},
{"calculate": "datum.label === 0 ? 0 : 1", "as": "angle"}
],
"encoding": {
"text": {
"field": "label",
"format": @{fields.max_value.format},
"formatType": "holisticsFormat"
},
"theta": {
"field": "angle",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
}
},
"mark": {
"type": "text",
"fontSize": {"expr": "fontFactor/2"},
"fontWeight": 600,
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"radius": {"expr": "outerRadius*1.05"},
"align": {"expr": "datum.angle === 0 ? 'right' : 'left'"},
"baseline": "alphabetic",
"opacity": {"expr": "@{options.showLabels.value} ? 1 : 0"}
}
},
// Main Value
{
"transform": [
{"calculate": "datum['@{fields.value.name}'] + ' (' + format(datum.percentage, '.0%') + ')'", "as": "mainValue"},
{
"calculate": "format(datum['@{fields.value.name}'] - datum['@{fields.target.name}'], ',.0f') + ' (' + format(datum['@{fields.value.name}']/datum['@{fields.target.name}'] - 1, '.0%') + ')'",
"as": "reference"
}
],
"encoding": {
"text": {
"field": "mainValue"
}
},
"mark": {
"type": "text",
"fontSize": {"expr": "fontFactor"},
"fontWeight": 600,
"x": {"expr": "centerX"},
"y": {"expr": "centerY + fontFactor*1.2"},
"baseline": "alphabetic",
"align": "center",
"color": @{options.value_color.value}
}
},
// Needle
{
"encoding": {
"angle": {
"field": "percentage",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [-90, 90]}
}
},
"mark": {
"type": "point",
"shape": {
"expr": "'M 5 -2 A 7 7 0 0 1 -10 -7 L 0 -' + toString(needleSize) + ' Z'"
},
"size": 4,
"opacity": 1,
"fill": {"expr": "needleColor"},
"stroke": {"expr": "needleColor"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"}
}
},
// Ticks
{
"data": {
"sequence": {
"as": "ticks",
"start": 0,
"stop": 1.01,
"step": @{options.tickGaps.value}
}
},
"encoding": {
"theta": {
"field": "ticks",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
},
"theta2": {
"field": "ticks",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
}
},
"mark": {
"type": "arc",
"outerRadius": {"expr": "outerRadius"},
"innerRadius": {"expr": "innerRadius"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"stroke": {"expr": "needleColor"},
"opacity": {"expr": "@{options.showTicks.value} ? 1 : 0"}
}
},
// Target Gauge
{
"encoding": {
"theta": {
"field": "targetPercentage",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
},
"theta2": {
"field": "targetPercentage",
"type": "quantitative",
"scale": {"domain": [0, 1], "range": [{"expr": "-PI/2"}, {"expr": "PI/2"}]}
}
},
"mark": {
"type": "arc",
"outerRadius": {"expr": "outerRadius*1.05"},
"innerRadius": {"expr": "innerRadius*0.95"},
"x": {"expr": "centerX"},
"y": {"expr": "centerY"},
"stroke": {"expr": "targetColor"},
"strokeWidth": 2
}
}
]
};;
}

Open Markdown
Let us know what you think about this document :)