Skip to content

Conditional Encoding

Conditional encoding changes a visual channel when a selection parameter matches a row. The channel's own definition is the fallback:

{
  "encoding": {
    "color": {
      "condition": { "param": "brush", "value": "#3a86ff" },
      "value": "#d9d9d9"
    }
  }
}

Conditional encoding works with color, opacity, size, shape, and other visual channels. Point selections suit clicks and hovering:

{
  "description": [
    "Hover and click bar selection",
    "Highlights bars on hover and selects them on click. Inspired by Tableau's interaction style."
  ],

  "data": {
    "values": [
      { "a": "A", "b": 28 },
      { "a": "B", "b": 55 },
      { "a": "C", "b": 43 },
      { "a": "D", "b": 91 },
      { "a": "E", "b": 81 },
      { "a": "F", "b": 53 },
      { "a": "G", "b": 19 },
      { "a": "H", "b": 87 },
      { "a": "I", "b": 52 }
    ]
  },

  "params": [
    {
      "name": "highlight",
      "select": { "type": "point", "on": "pointerover" }
    },
    { "name": "select", "select": "point" }
  ],

  "mark": { "type": "rect", "fill": "#4C78A8", "stroke": "black" },

  "encoding": {
    "x": {
      "field": "a",
      "type": "ordinal",
      "scale": { "type": "band", "padding": 0.2 }
    },
    "y": { "field": "b", "type": "quantitative" },
    "fillOpacity": {
      "value": 0.3,
      "condition": { "param": "select", "value": 1 }
    },
    "strokeWidth": {
      "value": 0,
      "condition": [
        { "param": "select", "value": 2, "empty": false },
        { "param": "highlight", "value": 1, "empty": false }
      ]
    }
  }
}

Empty Selections

An empty selection matches every row by default. Add "empty": false to a condition when its style should apply only after a selection, for example:

{ "param": "select", "empty": false, "value": 2 }

Combining Selections

Use test to combine selections with nested and, or, and not. Each leaf can set its own empty policy:

{
  "test": {
    "or": [
      { "param": "hover", "empty": false },
      { "and": [{ "param": "sourceBrush" }, { "param": "targetBrush" }] }
    ]
  },
  "value": "#3a86ff"
}

A leaf's empty defaults to true and is evaluated before the Boolean operators. A single "test": { "param": "brush" } is equivalent to the direct "param": "brush" condition.

A two-axis brush is empty until both intervals are active. Declare a one-axis brush to select on one axis.

For a simple union, use param.or inside test:

{
  "test": { "param": { "or": ["select", "brush"] }, "empty": true },
  "value": "#3a86ff"
}

The union matches rows in any active selection. With empty: true (the default), it also matches every row while all selections are empty. Set empty: false inside test to keep the fallback in that case.

Reusing Tests in a Unit View

When several channels use the same test, define it once in the unit view's predicates and refer to it from each condition, including order:

{
  "mark": "point",
  "predicates": {
    "highlighted": {
      "or": [
        { "param": "hover", "empty": false },
        { "param": "brush", "empty": false }
      ]
    }
  },
  "encoding": {
    "fillOpacity": {
      "condition": { "test": { "ref": "highlighted" }, "value": 1 },
      "value": 0.2
    },
    "order": {
      "condition": { "test": { "ref": "highlighted" }, "value": 1 },
      "value": 0
    }
  }
}

Each consuming unit must define names referenced by its encodings, including inherited encodings. Selection parameters and projected channels resolve in that unit. Named predicates cannot refer to other named predicates.

An interval normally tests the matching positional channel and, on ranged marks, its second endpoint according to the mark's hit-test mode. Use "project": { "x": "x2" } to test the selected x interval against x2 alone. Predicates may test the same selection against different endpoints.

project is a GenomeSpy extension. Map every component declared by the selection to an unconditional field encoding on the same axis and of the same type. Quantitative, index, and locus types are supported.

In this example, the upper brush tests each link's x2 (target), and the lower brush tests x (source). Links keep their color when they match both; an empty brush leaves its endpoint unconstrained.

{
  "description": "Brush both ends of a link",

  "data": {
    "values": [
      { "source": 80, "target": 730 },
      { "source": 230, "target": 420 },
      { "source": 380, "target": 880 },
      { "source": 530, "target": 230 },
      { "source": 680, "target": 580 },
      { "source": 830, "target": 100 }
    ]
  },

  "params": [{ "name": "targetBrush" }, { "name": "sourceBrush" }],

  "scales": { "x": { "domain": [0, 1000] } },

  "resolve": { "scale": { "x": "shared" }, "axis": { "x": "shared" } },

  "spacing": 0,

  "vconcat": [
    {
      "height": 30,
      "cursor": "text",
      "params": [
        {
          "name": "targetBrush",
          "push": "outer",
          "select": {
            "type": "interval",
            "encodings": ["x"],
            "on": "mousedown"
          }
        }
      ],
      "mark": { "type": "point", "size": 120 },
      "encoding": { "x": { "field": "target", "type": "index" } }
    },
    {
      "name": "links",
      "mark": { "type": "link", "linkShape": "diagonal" },
      "encoding": {
        "x": { "field": "source", "type": "index" },
        "x2": { "field": "target" },
        "y": { "value": 0 },
        "y2": { "value": 1 },
        "color": {
          "condition": {
            "test": {
              "and": [
                { "param": "targetBrush", "project": { "x": "x2" } },
                { "param": "sourceBrush", "project": { "x": "x" } }
              ]
            },
            "value": "#2962a3"
          },
          "value": "#d8dbe0"
        }
      }
    },
    {
      "height": 30,
      "cursor": "text",
      "params": [
        {
          "name": "sourceBrush",
          "push": "outer",
          "select": {
            "type": "interval",
            "encodings": ["x"],
            "on": "mousedown"
          }
        }
      ],
      "mark": { "type": "point", "size": 120 },
      "encoding": { "x": { "field": "source", "type": "index" } }
    }
  ]
}

For endpoint brushes, hover, and several conditional channels, see the PISA Squid Plot.

Multiple Conditions

Conditions in an array are tested in order. The channel's main definition is the final fallback:

{
  "condition": [
    { "param": "select", "empty": false, "value": 2 },
    { "param": "highlight", "empty": false, "value": 1 }
  ],
  "value": 0
}

See Also

  • Marks for the general encoding model
  • Parameters for defining selection and input-bound params