Search code examples
powerbivega-litevegadeneb

Can I partially color a bar based on percentage complete on a temporal axis?


I am trying to create a Gantt chart and I want to color a single task with two colors, based on a percentage complete. Say, make the complete part green and the remaining part orange.

How can I achieve this?

Below is a sample code, also available in the editor here.

{
  "data": {
    "values": [
      {"Description": "Task 1", "Start": "2023-01-05", "End": "2023-01-10", "Percentage complete": 0},
      {"Description": "Task 2", "Start": "2023-01-01", "End": "2023-01-15", "Percentage complete": 75},
      {"Description": "Task 3", "Start": "2023-01-01", "End": "2023-01-03", "Percentage complete": 100}
    ]
  },
  "layer": [
    {
      "mark": "bar",
      "encoding": {
        "y": {
          "field": "Description",
          "type": "ordinal",
          "stack": null
        },
        "x": {
          "field": "Start",
          "type": "temporal"
        },
        "x2": {
          "field": "End",
          "type": "temporal"
        }
      }
    }
  ]
}

The expected result should look like this.

I tried looking at folding, transforming, and scale. But as I am new to Vega-lite, to no avail.


Solution

  • You have two options.

    1. Reshape your data upstream. Your partially coloured bars should be rendered as two bars stacked - one for incomplete and one for complete.

    2. Use Reactive Geometry as described here. This may need Vega rather than VL.

    Here it is using reactive geometry.

    enter image description here

    {
      "$schema": "https://vega.github.io/schema/vega/v5.json",
      "background": "white",
      "padding": 5,
      "width": 200,
      "style": "cell",
      "data": [
        {
          "name": "source_0",
          "values": [
            {
              "Description": "Task 1",
              "Start": "2023-01-05",
              "End": "2023-01-10",
              "Percentatecomplete": 0
            },
            {
              "Description": "Task 2",
              "Start": "2023-01-01",
              "End": "2023-01-15",
              "Percentatecomplete": 0.75
            },
            {
              "Description": "Task 3",
              "Start": "2023-01-01",
              "End": "2023-01-03",
              "Percentatecomplete": 1
            }
          ]
        },
        {
          "name": "data_0",
          "source": "source_0",
          "transform": [
            {"type": "formula", "expr": "toDate(datum[\"Start\"])", "as": "Start"},
            {"type": "formula", "expr": "toDate(datum[\"End\"])", "as": "End"},
            {
              "type": "filter",
              "expr": "(isDate(datum[\"Start\"]) || (isValid(datum[\"Start\"]) && isFinite(+datum[\"Start\"])))"
            }
          ]
        }
      ],
      "signals": [
        {"name": "y_step", "value": 20},
        {
          "name": "height",
          "update": "bandspace(domain('y').length, 0.1, 0.05) * y_step"
        }
      ],
      "marks": [
        {
          "name": "layer_0_marks",
          "type": "rect",
          "style": ["bar"],
          "from": {"data": "data_0"},
          "encode": {
            "update": {
              "fill": {"value": "#4c78a8"},
              "x": {"scale": "x", "field": "Start"},
              "x2": {"scale": "x", "field": "End"},
              "y": {"scale": "y", "field": "Description"},
              "height": {"signal": "max(0.25, bandwidth('y'))"}
            }
          }
        },
        {
          "type": "rect",
          "from": {"data": "layer_0_marks"},
          "encode": {
            "update": {
              "x": {"field": "x"},
              "y": {"field": "y"},
              "fill": {"value": "red"},
              
              "width": {"signal": "(datum.x2 - datum.x) * datum.datum.Percentatecomplete"},
              "height": {"signal": "max(0.25, bandwidth('y'))"}
            }
          }
        }
      ],
      "scales": [
        {
          "name": "x",
          "type": "time",
          "domain": {"data": "data_0", "fields": ["Start", "End"]},
          "range": [0, {"signal": "width"}]
        },
        {
          "name": "y",
          "type": "band",
          "domain": {"data": "data_0", "field": "Description", "sort": true},
          "range": {"step": {"signal": "y_step"}},
          "paddingInner": 0.1,
          "paddingOuter": 0.05
        }
      ],
      "axes": [
        {
          "scale": "x",
          "orient": "bottom",
          "gridScale": "y",
          "grid": true,
          "tickCount": {"signal": "ceil(width/40)"},
          "domain": false,
          "labels": false,
          "aria": false,
          "maxExtent": 0,
          "minExtent": 0,
          "ticks": false,
          "zindex": 0
        },
        {
          "scale": "x",
          "orient": "bottom",
          "grid": false,
          "title": "Start, End",
          "labelFlush": true,
          "labelOverlap": true,
          "tickCount": {"signal": "ceil(width/40)"},
          "zindex": 0
        },
        {
          "scale": "y",
          "orient": "left",
          "grid": false,
          "title": "Description",
          "zindex": 0
        }
      ]
    }