Search code examples
powerbivega-litevegadeneb

Text mark (data label) placement in a layered Vega-Lite chart


I am attempting to exercise greater control in the placement of text marks within a chart that has dual Y-Axis; however, applying an independent scale throws the text mark placement out of kilter.

Here is an example of the intended text mark placement in a stacked chart:

intended output

This is what I am getting at the moment:

unwanted output

This is the specification I am using:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {
        "Month": 1,
        "Category": "Bikes",
        "Sales": 100,
        "Sales LY": 90,
        "Target": 95,
        "Volume": 300
      },
      {
        "Month": 1,
        "Category": "Cars",
        "Sales": 560,
        "Sales LY": 800,
        "Target": 880,
        "Volume": 1200
      },
      {
        "Month": 2,
        "Category": "Bikes",
        "Sales": 130,
        "Sales LY": 170,
        "Target": 190,
        "Volume": 450
      },
      {
        "Month": 2,
        "Category": "Cars",
        "Sales": 600,
        "Sales LY": 665,
        "Target": 680,
        "Volume": 1200
      },
      {
        "Month": 3,
        "Category": "Bikes",
        "Sales": 200,
        "Sales LY": 150,
        "Target": 165,
        "Volume": 400
      },
      {
        "Month": 3,
        "Category": "Cars",
        "Sales": 750,
        "Sales LY": 1000,
        "Target": 1100,
        "Volume": 1200
      },
      {
        "Month": 4,
        "Category": "Bikes",
        "Sales": 80,
        "Sales LY": 105,
        "Target": 120,
        "Volume": 500
      },
      {
        "Month": 4,
        "Category": "Cars",
        "Sales": 800,
        "Sales LY": 600,
        "Target": 660,
        "Volume": 1200
      }
    ]
  },
    "transform": [
      {"calculate": "'Volume'", "as": "Volume Legend"},        
      {"calculate": "'Target'", "as": "Target Legend"},      
      {"calculate": "datum.Category + ' LY'", "as": "Category2"}             
      ] ,
  "height": 300,
  "width": 300,  
  "layer": [
    {
      "name": "SALES",
      "mark": {"type": "bar", "xOffset": 0, "size": 10},
      "encoding": {
        "x": {"field": "Month", "type": "ordinal", "axis": {"labelAngle": 0}},
        "y": {"field": "Sales", "type": "quantitative"},
          "color": {
        "field": "Category",             
          "type" : "nominal",          
            "scale": {
            "domain": ["Bikes","Cars" ],
          "range": ["#4CD964","#39A34B"]}
        }
      }
    },
    {
      "name": "SALES Data Labels",
      "mark": {
        "type": "text", 
        "align": "right", 
        "baseline": "line-bottom",               
        "dx": 10
          },      
      "encoding": {
        "x": {"field": "Month", "type": "ordinal"},        
        "y": {"field": "Sales", "type": "quantitative", "axis": null},
        "text": {"field": "Sales","type": "quantitative"} 
      }
    },
    {
      "name": "VOLUME",
      "mark": {"type": "line"},
      "encoding": {
        "x": {
          "field": "Month",
          "type": "ordinal"
        },
        "y": {
          "aggregate": "sum",
          "field": "Volume",
          "type": "quantitative"
        },
        "stroke": {
          "field": "Volume Legend",
          "scale": {"range": ["black"]},
          "legend": {"title": ""}
        }
      }
    }
  ],
  "resolve": {
    "scale": {"y": "independent"}
  }
}

What modifications to the script can be made to achieve the desired outcome? Any assistance would be greatly appreciated!!


Solution

  • You mean like this? You can have layers inside layers to isolate the resolve attribute.

    enter image description here

    {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "data": {
        "values": [
          {
            "Month": 1,
            "Category": "Bikes",
            "Sales": 100,
            "Sales LY": 90,
            "Target": 95,
            "Volume": 300
          },
          {
            "Month": 1,
            "Category": "Cars",
            "Sales": 560,
            "Sales LY": 800,
            "Target": 880,
            "Volume": 1200
          },
          {
            "Month": 2,
            "Category": "Bikes",
            "Sales": 130,
            "Sales LY": 170,
            "Target": 190,
            "Volume": 450
          },
          {
            "Month": 2,
            "Category": "Cars",
            "Sales": 600,
            "Sales LY": 665,
            "Target": 680,
            "Volume": 1200
          },
          {
            "Month": 3,
            "Category": "Bikes",
            "Sales": 200,
            "Sales LY": 150,
            "Target": 165,
            "Volume": 400
          },
          {
            "Month": 3,
            "Category": "Cars",
            "Sales": 750,
            "Sales LY": 1000,
            "Target": 1100,
            "Volume": 1200
          },
          {
            "Month": 4,
            "Category": "Bikes",
            "Sales": 80,
            "Sales LY": 105,
            "Target": 120,
            "Volume": 500
          },
          {
            "Month": 4,
            "Category": "Cars",
            "Sales": 800,
            "Sales LY": 600,
            "Target": 660,
            "Volume": 1200
          }
        ]
      },
      "transform": [
        {"calculate": "'Volume'", "as": "Volume Legend"},
        {"calculate": "'Target'", "as": "Target Legend"},
        {"calculate": "datum.Category + ' LY'", "as": "Category2"}
      ],
      "height": 300,
      "width": 300,
      "encoding": {
        "x": {"field": "Month", "type": "ordinal", "axis": {"labelAngle": 0}},
        "y": {"field": "Sales", "type": "quantitative"}
      },
      "layer": [
        {
          "layer": [
            {
              "name": "SALES",
              "mark": {"type": "bar", "xOffset": 0, "size": 10},
              "encoding": {
                "color": {
                  "field": "Category",
                  "type": "nominal",
                  "scale": {
                    "domain": ["Bikes", "Cars"],
                    "range": ["#4CD964", "#39A34B"]
                  }
                }
              }
            },
            {
              "name": "SALES Data Labels",
              "mark": {
                "type": "text",
                "align": "center",
                "baseline": "line-bottom"
              },
              "encoding": {"text": {"field": "Sales", "type": "quantitative"}}
            }
          ]
        },
        {
          "name": "VOLUME",
          "mark": {"type": "line"},
          "encoding": {
            "x": {"field": "Month", "type": "ordinal"},
            "y": {"aggregate": "sum", "field": "Volume", "type": "quantitative"},
            "stroke": {
              "field": "Volume Legend",
              "scale": {"range": ["black"]},
              "legend": {"title": ""}
            }
          }
        }
      ],
      "resolve": {"scale": {"y": "independent"}}
    }