Search code examples
powerbipowerbi-desktopvega-litevegadeneb

Add Arbitrary Legend to Vega-Lite Deneb


This is currently my code . I'm displaying two area charts showcasing the evolution of metrics in 2022 and 2023 over the months:

{
  "data": {"name": "dataset"},
  "transform": [
    {
      "calculate": "format(datum['Leaves Count 2022']/1000,'0.1f')+'k'",
      "as": "l1"
    },
    {
      "calculate": "format(datum['Leaves Count 2023']/1000,'0.1f')+'k'",
      "as": "l2"
    },
    {
      "joinaggregate": [
        {
          "op": "max",
          "field": "Leaves Count 2022",
          "as": "l1max"
        },
        {
          "op": "max",
          "field": "Leaves Count 2023",
          "as": "l2max"
        },
        {
          "op": "min",
          "field": "Leaves Count 2022",
          "as": "l1min"
        },
        {
          "op": "min",
          "field": "Leaves Count 2023",
          "as": "l2min"
        }
      ]
    }
  ],
  "layer": [
    {
      "mark": {
        "type": "area",
        "line": {"color": "#063970"},
        "color": {
          "x1": 1,
          "y1": 1,
          "gradient": "linear",
          "stops": [
            {
              "offset": 0,
              "color": "white"
            },
            {
              "offset": 1,
              "color": "#063970"
            }
          ]
        }
      }
    },
    {
      "mark": {
        "type": "area",
        "line": {"color": "#2596be"},
        "color": {
          "x1": 1,
          "y1": 1,
          "gradient": "linear",
          "stops": [
            {
              "offset": 0,
              "color": "white"
            },
            {
              "offset": 1,
              "color": "#2596be"
            }
          ]
        }
      },
      "encoding": {
        "y": {
          "field": "Leaves Count 2022",
          "type": "quantitative"
        }
      }
    },
    {
      "mark": {
        "type": "circle",
        "size": 50,
        "fill": {
          "expr": "datum['Leaves Count 2023'] == datum.l2max ? 'red' : datum['Leaves Count 2023'] == datum.l2min ? 'green' : '#063970'"
        },
        "stroke": "white",
        "strokeWidth": 1
      },
      "encoding": {
        "x": {
          "field": "MONTH",
          "type": "ordinal",
          "sort": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
        },
        "y": {
          "field": "Leaves Count 2023",
          "type": "quantitative"
        }
      }
    },
    {
      "mark": {
        "type": "circle",
        "size": 50,
        "fill": {
          "expr": "datum['Leaves Count 2022'] == datum.l1max ? 'red' : datum['Leaves Count 2022'] == datum.l1min ? 'green' : '#2596be'"
        },
        "stroke": "white",
        "strokeWidth": 1
      },
      "encoding": {
        "x": {
          "field": "MONTH",
          "type": "ordinal",
          "sort": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
        },
        "y": {
          "field": "Leaves Count 2022",
          "type": "quantitative"
        }
      }
    },
    {
      "mark": {
        "type": "text",
        "yOffset": -10,
        "size": 10,
        "color": {
          "expr": "datum['Leaves Count 2023'] == datum.l2max ? 'red' : datum['Leaves Count 2023'] == datum.l2min ? 'green' : '#000000'"
        },
        "fontWeight": {
          "expr": "datum['Leaves Count 2023'] == datum.l2max || datum['Leaves Count 2023'] == datum.l2min ? 'bold' : 'normal'"
        }
      },
      "encoding": {
        "text": {"field": "l2"},
        "opacity": {
          "condition": {
            "test": {
              "field": "MONTH",
              "equal": "off"
            },
            "value": 0.1
          },
          "value": 1
        },
        "y": {
          "field": "Leaves Count 2023",
          "type": "quantitative",
          "axis": null
        }
      }
    },
    {
      "mark": {
        "type": "text",
        "yOffset": -10,
        "size": 10,
        "color": {
          "expr": "datum['Leaves Count 2022'] == datum.l1max ? 'red' : datum['Leaves Count 2022'] == datum.l1min ? 'green' : '#000000'"
        },
        "fontWeight": {
          "expr": "datum['Leaves Count 2022'] == datum.l1max || datum['Leaves Count 2022'] == datum.l1min ? 'bold' : 'normal'"
        }
      },
      "encoding": {
        "text": {"field": "l1"},
        "opacity": {
          "condition": {
            "test": {
              "field": "MONTH",
              "equal": "off"
            },
            "value": 0.1
          },
          "value": 1
        },
        "y": {
          "field": "Leaves Count 2022",
          "type": "quantitative",
          "axis": null
        }
      }
    },
    {
      "mark": {
        "type": "rule",
        "stroke": "red",
        "strokeDash": [3, 3]
      },
      "encoding": {
        "x": {
          "field": "MONTH",
          "type": "ordinal",
          "sort": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
        },
        "y": {
          "field": "Leaves Count 2023",
          "type": "quantitative"
        }
      },
      "transform": [
        {"filter": "datum['Leaves Count 2023'] == datum.l2max"}
      ]
    },
    {
      "mark": {
        "type": "rule",
        "stroke": "red",
        "strokeDash": [3, 3]
      },
      "encoding": {
        "x": {
          "field": "MONTH",
          "type": "ordinal",
          "sort": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
        },
        "y": {
          "field": "Leaves Count 2022",
          "type": "quantitative"
        }
      },
      "transform": [
        {"filter": "datum['Leaves Count 2022'] == datum.l1max"}
      ]
    }
  ],
  "encoding": {
    "x": {
      "field": "MONTH",
      "type": "ordinal",
      "axis": {"labelPadding": 0},
      "sort": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
      "title": "MONTH"
    },
    "y": {
      "field": "Leaves Count 2023",
      "type": "quantitative",
      "axis": null
    }
  }
}

I want to add a legend at the right of my visual with two small circles colored in the same color as my the colors I used for leaves count 2022 and leaves count 2023 , something like this

enter image description here How can my code be edited to fulfill this task?


Solution

  • Legends are supposed to be created automatically from your spec. Your spec is a bit to big to parse and impossible to test without sample data. However, you can create arbitrary legends as follows.

    enter image description here

    {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "description": "A bar chart with negative values. We can hide the axis domain line, and instead use a conditional grid color to draw a zero baseline.",
      "data": {
        "values": [
          {"a": "A", "b": -28},
          {"a": "B", "b": 55},
          {"a": "C", "b": -33},
          {"a": "D", "b": 91},
          {"a": "E", "b": 81},
          {"a": "F", "b": 53},
          {"a": "G", "b": -19},
          {"a": "H", "b": 87},
          {"a": "I", "b": 52}
        ]
      },
      "encoding": {
        "y": {
          "field": "a",
          "type": "nominal",
          "axis": {
            "domain": false,
            "ticks": false,
            "labelAngle": 0,
            "labelPadding": 4
          }
        },
        "x": {
          "field": "b",
          "type": "quantitative",
          "scale": {"padding": 20},
          "axis": {
            "gridColor": {
              "condition": {"test": "datum.value === 0", "value": "black"},
              "value": "#ddd"
            }
          }
        }
      },
      "layer": [
        {"mark": "bar"},
        {
          "mark": {
            "type": "text",
            "align": {"expr": "datum.b < 0 ? 'right' : 'left'"},
            "dx": {"expr": "datum.b < 0 ? -2 : 2"}
          },
          "encoding": {"text": {"field": "b", "type": "quantitative"}}
        },
        {
          "mark": "point",
          "encoding": {
            "strokeWidth":{"value": 0},
            "fill": {
              "field": "x",
              "title":"Total Sales by Channel",
              "scale": {
                "domain": ["Export", "Wholesale"],
                "range": ["red", "blue"]
              }, "legend":{"symbolStrokeWidth":0}
            }
          }
        }
      ]
    }
    

    The important part is this and all the values (including field x) are made up:

    {
          "mark": "point",
          "encoding": {
            "strokeWidth":{"value": 0},
            "fill": {
              "field": "x",
              "title":"Total Sales by Channel",
              "scale": {
                "domain": ["Export", "Wholesale"],
                "range": ["red", "blue"]
              }, "legend":{"symbolStrokeWidth":0}
            }
          }
        }