vega-lite icon indicating copy to clipboard operation
vega-lite copied to clipboard

Erratic/broken field-based opacity range behavior if only a single value is used

Open dechamps opened this issue 1 year ago • 1 comments

Consider this:

{
  "mark": {"type": "point"},
  "encoding": {
    "opacity": {
      "field": "x",
      "scale": {"range": {"field": "o"}},
      "type": "nominal"
    }
  },
  "transform": [{"calculate": "if(datum.x,0.5,1)", "as": "o"}],
  "data": {"values": [{"x": false}, {"x": true}]}
}

This works perfectly fine:

visualization(144)

So far so good.

But then watch what happens if I change true for false (or vice-versa):

{
  "mark": {"type": "point"},
  "encoding": {
    "opacity": {
      "field": "x",
      "scale": {"range": {"field": "o"}},
      "type": "nominal"
    }
  },
  "transform": [{"calculate": "if(datum.x,0.5,1)", "as": "o"}],
  "data": {"values": [{"x": false}, {"x": false}]}
}

visualization(145)

Oops.

A similar problem occurs if I change the expression to just return a constant:

{
  "mark": {"type": "point"},
  "encoding": {
    "opacity": {
      "field": "x",
      "scale": {"range": {"field": "o"}},
      "type": "nominal"
    }
  },
  "transform": [{"calculate": "1", "as": "o"}],
  "data": {"values": [{"x": false}, {"x": true}]}
}

visualization(146)

The issue can be seen in-browser and in the PNG exports above. However, somewhat mindbogglingly, SVG exports don't show the problem?!

visualization(4)

It's also worth noting that while opacity is affected, color isn't:

{
  "mark": {"type": "point"},
  "encoding": {
    "color": {
      "field": "x",
      "scale": {"range": {"field": "c"}},
      "type": "nominal"
    }
  },
  "transform": [{"calculate": "if(datum.x,'green','red')", "as": "c"}],
  "data": {"values": [{"x": false}, {"x": false}]}
}

visualization(147)

dechamps avatar Oct 22 '23 18:10 dechamps

One workaround is to stop using a field range and use domain instead:

{
  "mark": {"type": "point"},
  "encoding": {
    "opacity": {
      "field": "x",
      "scale": {"domain": [false, true], "range": [1, 0.5]},
      "type": "nominal"
    }
  },
  "data": {"values": [{"x": false}, {"x": true}]}
}

However this may get messy as one will need to match the legend labels by hand, and more importantly this might not be an option if the domain is not known in advance.

dechamps avatar Oct 22 '23 18:10 dechamps