Search code examples
jsonchartsvisualizationdata-analysisvega-lite

How to position text at the top edge of a VegaLite chart?


I am trying to create a VegaLite chart with labelled highlighted areas which are specified in a separate dataset using [min_x, max_x] coordinates.

I've managed to highlight the area itself using a rect mark, but I'm struggling to properly position the label at the top edge of the chart.

This is the end result I'm trying to achieve. In this example I'm using the dy property of the text mark to position the label. Unfortunately this only works when the chart height is known in advance, which doesn't work for my use case.

Desired result

If there is a better way to achieve the result I want, please let me know. This is the Vegalite specification for the chart above, without previously mentioned dy property:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "datasets": {
    "test_data": [
      {"x": 0, "y": 1.5},
      {"x": 5, "y": 2},
      {"x": 9, "y": 4},
      {"x": 14, "y": 0}
    ],
    "highlight_data": [
      {"from_x": 2.3, "to_x": 3, "label": "AAA"},
      {"from_x": 6.3, "to_x": 8, "label": "BBB"}
    ]
  },
  "data": {"name": "test_data"},
  "layer": [
    {
      "data": {"name": "highlight_data"},
      "transform": [
        {"calculate": "(datum.from_x + datum.to_x) / 2", "as": "mean_x"}
      ],
      "layer": [
        {
          "mark": {"type": "rect", "opacity": 0.3},
          "encoding": {
            "x": {"field": "from_x", "type": "quantitative"},
            "x2": {"field": "to_x"},
            "color": {"value": "#fcfc00"}
          }
        },
        {
          "mark": {"type": "text"},
          "encoding": {
            "text": {"field": "label"},
            "x": {"field": "mean_x", "type": "quantitative"}
          }
        }
      ]
    },
    {
      "mark": {"type": "line"},
      "encoding": {
        "x": {"field": "x", "type": "quantitative", "title": "X Label"},
        "y": {"field": "y", "type": "quantitative", "title": "Y Label"}
      }
    }
  ],
  "title": "Title",
  "width": 800,
  "height": 500
}

Solution

  • You can reference height inside an expression to make it dynamic.

    "dy": { "expr": "-height + (height/2) -10"}

    {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "datasets": {
        "test_data": [
          {"x": 0, "y": 1.5},
          {"x": 5, "y": 2},
          {"x": 9, "y": 4},
          {"x": 14, "y": 0}
        ],
        "highlight_data": [
          {"from_x": 2.3, "to_x": 3, "label": "AAA"},
          {"from_x": 6.3, "to_x": 8, "label": "BBB"}
        ]
      },
      "data": {"name": "test_data"},
      "layer": [
        {
          "data": {"name": "highlight_data"},
          "transform": [
            {"calculate": "(datum.from_x + datum.to_x) / 2", "as": "mean_x"}
          ],
          "layer": [
            {
              "mark": {"type": "rect", "opacity": 0.3},
              "encoding": {
                "x": {"field": "from_x", "type": "quantitative"},
                "x2": {"field": "to_x"},
                "color": {"value": "#fcfc00"}
              }
            },
            {
              "mark": {"type": "text", "dy": { "expr": "-height + (height/2) -10"}},
              "encoding": {
                "text": {"field": "label"},
                "x": {"field": "mean_x", "type": "quantitative"}
                
              }
            }
          ]
        },
        {
          "mark": {"type": "line"},
          "encoding": {
            "x": {"field": "x", "type": "quantitative", "title": "X Label"},
            "y": {"field": "y", "type": "quantitative", "title": "Y Label"}
          }
        }
      ],
      "title": "Title",
      "width": 800,
      "height": 500
    }