charts

stroke width causes values to go over the zero line


I have made a bar chart which have two columns per category. These columns can have positive or negative values. My data values would sometimes have extremely large and small range or gap which results to small values not being visible enough. I have added a strokeWidth that extremely small values will have a minimum pixel and would still be visible in the graph.

After adding the strokeWidth, it messes with the columns axis starting position from the zero line. Negative items now displays above the zero line when it should be starting from there.


Solution

  • I think it is related to this: https://github.com/vega/vega/pull/3487

    Try setting an offset on your y2:

    enter image description here

    {
      "$schema": "https://vega.github.io/schema/vega/v5.json",
      "width": 700,
      "height": 370,
      "autosize": {"type": "fit", "contains": "padding"},
      "padding": {"bottom": 2, "right": 9, "left": 5},
      "config": {
        "axis": {
          "labelFont": "HelveticaNeueLTW01-55Roman",
          "titleFont": "HelveticaNeueLTW01-55Roman"
        }
      },
      "data": [
        {
          "name": "capital_flows",
          "values": [
            {"label": "2012", "capital_inflow": 208.91, "capital_outflow": -500},
            {"label": "2013", "capital_inflow": 115.61, "capital_outflow": -458.76},
            {"label": "2014", "capital_inflow": 135.7, "capital_outflow": -335.23},
            {"label": "2015", "capital_inflow": 148.75, "capital_outflow": -83.61},
            {"label": "2016", "capital_inflow": 152.57, "capital_outflow": 0.36},
            {"label": "2017", "capital_inflow": 198.23, "capital_outflow": 0.29},
            {"label": "2018", "capital_inflow": 911.81, "capital_outflow": 0.58},
            {"label": "2019", "capital_inflow": 5342.44, "capital_outflow": 14.48},
            {"label": "2020", "capital_inflow": 4664.46, "capital_outflow": 11.51},
            {"label": "2021", "capital_inflow": 3800.92, "capital_outflow": 7.54},
            {"label": "2022", "capital_inflow": 6633, "capital_outflow": 12.04},
            {"label": "2023", "capital_inflow": 7381.93, "capital_outflow": 5.31},
            {"label": "2024", "capital_inflow": 3612.31, "capital_outflow": 3.7}
          ],
          "transform": [
            {"type": "formula", "expr": "split(datum.label, ' ')", "as": "label"}
          ]
        },
        {
          "name": "parsed",
          "source": ["capital_flows"],
          "transform": [
            {
              "type": "aggregate",
              "fields": [
                "capital_inflow",
                "capital_inflow",
                "capital_outflow",
                "capital_outflow"
              ],
              "ops": ["min", "max", "min", "max"],
              "as": ["min_value1", "max_value1", "min_value2", "max_value2"]
            },
            {
              "type": "formula",
              "expr": "datum.max_value1 == 0 && datum.max_value2 == 0 ? 1 : datum.max_value1 > datum.max_value2 ? abs(datum.max_value1) : abs(datum.max_value2)",
              "as": "mergedMax"
            },
            {
              "type": "formula",
              "expr": "datum.min_value1 == 0 && datum.min_value2 == 0 ? -1 : datum.min_value1 < datum.min_value2 ? abs(datum.min_value1) : abs(datum.min_value2)",
              "as": "mergedMin"
            },
            {
              "type": "formula",
              "expr": "datum.mergedMax > datum.mergedMin ? datum.mergedMax : datum.mergedMin",
              "as": "max"
            },
            {"type": "formula", "expr": "-(datum.max)", "as": "min"}
          ]
        }
      ],
      "scales": [
        {
          "name": "x",
          "type": "band",
          "range": "width",
          "domain": {"data": "capital_flows", "field": "label"}
        },
        {
          "name": "y",
          "type": "linear",
          "range": "height",
          "nice": true,
          "zero": false,
          "domain": {"data": "parsed", "field": "limit"},
          "domainMin": {"signal": "pluck(data('parsed'), 'min')"},
          "domainMax": {"signal": "pluck(data('parsed'), 'max')"}
        }
      ],
      "axes": [
        {
          "orient": "bottom",
          "scale": "x",
          "zindex": 1,
          "labelFontSize": 12,
          "labelColor": "#85868C",
          "labelAlign": "center",
          "labelLineHeight": 16.8,
          "labelFontWeight": "normal",
          "labelPadding": 8,
          "labelOpacity": 1,
          "domain": false,
          "domainColor": "#e5e5e5",
          "domainWidth": 1,
          "ticks": false
        },
        {
          "orient": "left",
          "scale": "y",
          "zindex": 0,
          "labelFontSize": 12,
          "labelColor": "#85868C",
          "labelLineHeight": 16.8,
          "labelFontWeight": "normal",
          "labelPadding": 8,
          "labelOpacity": 1,
          "domain": false,
          "domainColor": "#e5e5e5",
          "grid": true,
          "gridOpacity": 0.5,
          "ticks": false,
          "tickCount": {"signal": "pluck(data('parsed'), 'max') < 1000 ? 4 : 6"},
          "gridDash": {"signal": "datum.value == 0 ? [0,0] : [4,4]"},
          "encode": {
            "labels": {
              "update": {
                "text": {
                  "signal": "if(abs(datum.value) >= 1e9, format(datum.value/1e9, '$,.1~f') + 'B', if(abs(datum.value) >= 1e6, format(datum.value/1e6, '$,.1~f') + 'M', if(abs(datum.value) >= 1e3, format(datum.value/1e3, '$,.1~f') + 'K', format(datum.value, '$,.2~f'))))"
                }
              }
            }
          }
        }
      ],
      "marks": [
        {
          "name": "inflow",
          "type": "rect",
          "from": {"data": "capital_flows"},
          "encode": {
            "enter": {
              "xc": {"scale": "x", "field": "label", "band": 0.5},
              "width": {"value": 16},
              "y": {"scale": "y", "field": "capital_inflow"},
              "y2": {"scale": "y", "value": 0},
              "fill": {"value": "#68c487"},
              "cornerRadiusTopLeft": {
                "signal": "datum.capital_inflow > 0 ? '1' : '0'"
              },
              "cornerRadiusTopRight": {
                "signal": "datum.capital_inflow > 0 ? '1' : '0'"
              },
              "cornerRadiusBottomLeft": {
                "signal": "datum.capital_inflow > 0 ? '0' : '1'"
              },
              "cornerRadiusBottomRight": {
                "signal": "datum.capital_inflow > 0 ? '0' : '1'"
              },
              "stroke": {"signal": "datum.capital_inflow !== 0 ? '#68c487': null"}
            }
          }
        },
        {
          "name": "outflow",
          "type": "rect",
          "from": {"data": "capital_flows"},
          "encode": {
            "enter": {
              "xc": {"scale": "x", "field": "label", "band": 0.5, "offset": 19},
              "width": {"value": 16},
              "y": {"scale": "y", "field": "capital_outflow"},
              "y2": {"scale": "y", "value": 0, "offset":1.5},
              "fill": {"value": "#FFB066"},
              "cornerRadiusTopLeft": {
                "signal": "datum.capital_outflow > 0 ? '1' : '0'"
              },
              "cornerRadiusTopRight": {
                "signal": "datum.capital_outflow > 0 ? '1' : '0'"
              },
              "cornerRadiusBottomLeft": {
                "signal": "datum.capital_outflow > 0 ? '0' : '1'"
              },
              "cornerRadiusBottomRight": {
                "signal": "datum.capital_outflow > 0 ? '0' : '1'"
              },
              "stroke": {"signal": "datum.capital_outflow !== 0 ? '#FFB066': null"},
              "strokeWidth": {"value": 1.5}
            }
          }
        }
      ]
    }