1

I'm currently working on creating an interactive chart in Vega-Lite that will change opacity based on filtering by months, similar to the behavior seen in Power BI's built-in charts.

I came across a useful workaround that seems to do the job and can be found here: https://vega.github.io/vega-lite/examples/interactive_bar_select_highlight.html

However, I'm struggling to apply this workaround to my own visual. I keep getting the error message Duplicate signal name: "highlight_tuple" and I cannot seem to figure out the exact reason behind it.

My visual is slightly different from the example provided in the link, as it contains multiple marks, including a bar chart, line chart, and text labels. I wonder if this difference might be causing the issue? Any insights or ideas on what might be causing this problem would be greatly appreciated.

Below is the code I am currently using:

 {
  "data": {"name": "dataset"},
  "layer": [
    {
      "mark": {
        "type": "line",
        "interpolate": "natural",
        "size": 4,
        "color": "#f2c80f",
        "tooltip": true
      },
      "encoding": {
        "x": {
          "field": "Month",
          "type": "nominal",
          "title": null,
          "axis": {
            "labelAngle": 0,
            "labelColor": "black",
            "labelFont": "Segoe UI Semibold",
            "labelFontSize": 14
          }
        },
        "y": {
          "field": "Hour",
          "type": "quantitative",
          "title": null,
          "axis": {
            "tickCount": 5,
            "grid": true
          }
        }
      }
    },
    {
      "mark": {
        "type": "bar",
        "tooltip": true,
        "size": 25,
        "cornerRadius": 3,
        "stroke": "#f65038",
        "strokeWidth": 1,
        "fill": {
          "expr": "pbiPatternSVG('diagonal-stripe-1', 'lightgray', 'white')"
        }
      },
      "encoding": {
        "x": {
          "field": "Month",
          "type": "nominal",
          "title": null,
          "axis": {
            "labelAngle": 0,
            "labelColor": "black",
            "labelFont": "Segoe UI Semibold"
          }
        },
        "y": {
          "field": "Loss",
          "type": "quantitative",
          "scale": {
            "domain": [0, 20000]
          },
          "title": null,
          "axis": null
        }
      }
    },
    {
      "mark": {
        "type": "point",
        "filled": false,
        "size": 80,
        "shape": "circle",
        "tooltip": true
      },
      "encoding": {
        "x": {
          "field": "Month",
          "type": "nominal"
        },
        "y": {
          "field": "Hour",
          "type": "quantitative",
          "title": null,
          "axis": null
        }
      }
    },
    {
      "mark": {
        "type": "text",
        "baseline": "bottom",
        "align": "center",
        "dx": 0,
        "dy": -10,
        "font": "Segoe UI Semibold",
        "fontSize": 14,
        "color": "black"
      },
      "encoding": {
        "x": {
          "field": "Month",
          "type": "nominal"
        },
        "y": {
          "field": "Hour",
          "type": "quantitative",
          "axis": null
        },
        "text": {
          "field": "Hour",
          "type": "quantitative",
          "format": "0 h",
          "formatType": "pbiFormat"
        }
      }
    },
    {
      "mark": {
        "type": "text",
        "cornerRadius": 2,
        "width": 18,
        "height": 18,
        "opacity": 1,
        "tooltip": true,
        "yOffset": 30,
        "xOffset": 0
      },
      "encoding": {
        "x": {"field": "Month"},
        "color": {
          "condition": [
            {
              "test": "datum['Hour']<0",
              "value": "#F78272"
            },
            {
              "test": "datum['Hour']===null",
              "value": "transparent"
            }
          ],
          "value": "#5BDA7D"
        }
      }
    },
    {
      "mark": {
        "type": "text",
        "baseline": "bottom",
        "align": "center",
        "dx": 0,
        "dy": 52,
        "font": "Segoe UI Semibold",
        "fontSize": 14,
        "color": "#f65038"
      },
      "encoding": {
        "color": {
          "condition": [
            {
              "test": "datum['Hour'] === null",
              "value": "transparent"
            }
          ],
          "value": "#f65038"
        },
        "x": {
          "field": "Month",
          "type": "nominal"
        },
        "y": {
          "field": "Value",
          "type": "quantitative",
          "axis": null,
          "scale": {
            "domain": [0, 20000]
          }
        },
        "text": {
          "field": "Loss",
          "type": "quantitative",
          "format": "-,0 €",
          "formatType": "pbiFormat"
        }
      }
    },
    {
      "mark": {
        "type": "text",
        "baseline": "bottom",
        "align": "center",
        "dx": 0,
        "dy": 35,
        "font": "Segoe UI",
        "fontSize": 12,
        "color": "black"
      },
      "encoding": {
        "color": {
          "condition": [
            {
              "test": "datum['Hour'] === null",
              "value": "transparent"
            }
          ],
          "value": "black"
        },
        "x": {
          "field": "Month",
          "type": "nominal"
        },
        "y": {
          "field": "Value",
          "type": "quantitative",
          "axis": null,
          "scale": {
            "domain": [0, 20000]
          }
        },
        "text": {
          "value": "Loss:",
          "type": "quantitative",
          "format": "-,0 €",
          "formatType": "pbiFormat"
        }
      }
    }
  ],
  "resolve": {
    "scale": {"y": "independent"}
  }
}

sample data:

{
  "data": {
    "values": [
      {
        "Month": 1,
        "Hour": 100,
        "Loss": 6000,
        "Value": 0
      },
      {
        "Month": 2,
        "Hour": 150,
        "Loss": 7500,
        "Value": 0
      },
      {
        "Month": 3,
        "Hour": 160,
        "Loss": 8500,
        "Value": 0
      }
    ]
  },
  "layer": [
    {
      "mark": {
        "type": "line",
        "interpolate": "natural",
        "size": 4,
        "color": "#f2c80f",
        "tooltip": true
      },
      "encoding": {
        "opacity": {
          "condition": {
            "test": {
              "field": "__selected__",
              "equal": "off"
            },
            "value": 0.1
          },
          "value": 1
        },
        "x": {
          "field": "Month",
          "type": "nominal",
          "title": null,
          "axis": {
            "labelAngle": 0,
            "labelColor": "black",
            "labelFont": "Segoe UI Semibold",
            "labelFontSize": 14
          }
        },
        "y": {
          "field": "Hour",
          "type": "quantitative",
          "title": null,
          "axis": {
            "tickCount": 5,
            "grid": true
          }
        }
      }
    },
    {
      "mark": {
        "type": "bar",
        "tooltip": true,
        "size": 25,
        "cornerRadius": 3,
        "stroke": "#f65038",
        "fill": {
          "expr": "pbiPatternSVG('diagonal-stripe-1', 'lightgray', 'white')"
        }
      },
      "encoding": {
        "opacity": {
          "condition": {
            "test": {
              "field": "__selected__",
              "equal": "off"
            },
            "value": 0.1
          },
          "value": 1
        },
        "x": {
          "field": "Month",
          "type": "nominal",
          "title": null,
          "axis": {
            "labelAngle": 0,
            "labelColor": "black",
            "labelFont": "Segoe UI Semibold"
          }
        },
        "y": {
          "field": "Loss",
          "type": "quantitative",
          "scale": {
            "domain": [0, 20000]
          },
          "title": null,
          "axis": null
        }
      }
    }
  ],
  "resolve": {
    "scale": {"y": "independent"}
  }
}

Many thanks guys!

Davide Bacci
  • 16,647
  • 3
  • 10
  • 36
tomecsek
  • 107
  • 6
  • Can you upload a sample .pbix including data? – Davide Bacci Jul 21 '23 at 12:01
  • 1
    Have you read the following? https://deneb-viz.github.io/interactivity-highlight – Davide Bacci Jul 21 '23 at 12:02
  • @DavideBacci - thank you so much for the article! I have found the answer there! basically this line of code was the solution for my problem: "opacity": { "condition": { "test": { "field": "__selected__", "equal": "off" }, "value": 0 }, "value": 1 } thanks mate! – tomecsek Jul 21 '23 at 12:13
  • just an additional question.... why is the above code not working with "strokeWidth"? Do you know by chance? many thanks! "strokeWidth": { "condition": { "test": {"field": "__selected__", "equal": "off"}, "value": 0.1 }, "value": 1 } – tomecsek Jul 21 '23 at 12:36
  • No idea without looking at a working example. – Davide Bacci Jul 21 '23 at 12:46
  • @DavideBacci unfortunately the original file is too heave any contains sensitive info however I created a Vega Lite snippet with sample data and added it to my original post, is it OK like this to check for you? basically the "opacity" in encoding works fine just when I add "strokeOpacity" or "strokeWidth" it doesn´t do anything at all...thanks! – tomecsek Jul 21 '23 at 18:28

1 Answers1

1

This looks like a bug to me. This works:

{
  "data": {"name": "dataset"},
  "mark": {
    "type": "bar",
    "tooltip": true,
    "size": 25,

    "stroke": "#f65038",
    "fill": {
      "expr": "pbiPatternSVG('diagonal-stripe-1', 'lightgray', 'white')"
    }
  },
  "encoding": {
    "fillOpacity": {
      "value": {
        "expr": "datum['__selected__']=='off'?0.2:1"
      }
    },
    "strokeWidth": {
      "value": {
        "expr": "datum['__selected__']=='off'?0.2:2"
      }
    },
    "x": {
      "field": "Month",
      "type": "nominal",
      "title": null,
      "axis": {
        "labelAngle": 0,
        "labelColor": "black",
        "labelFont": "Segoe UI Semibold"
      }
    },
    "y": {
      "field": "Loss",
      "type": "quantitative",
      "scale": {"domain": [0, 20000]},
      "title": null,
      "axis": null
    }
  }
}

enter image description here

But if you put "cornerRadius": 3 back in, then it fails. I've seen other corner radius bugs so this may be related.

Davide Bacci
  • 16,647
  • 3
  • 10
  • 36
  • 1
    you´re brilliant and indeed correct ofc...thank you so much for this! definitely seems to be a bug! – tomecsek Jul 21 '23 at 19:54
  • one last thing and sorry for bothering so much...but if I´d like to change labelOpacity on x-axis based on that logic it is not working...is it a bug or I´m doing something wrong again? "x": { "field": "Month", "type": "nominal", "title": null, "axis": { "labelAngle": 0, "labelColor": "black", "labelFont": "Segoe UI Semibold", "labelOpacity": { "value": { "expr": "datum['__selected__']==='off'?0.2:1" } } }} – tomecsek Jul 22 '23 at 07:45
  • Not 100% sure as I would normally do this in Vega rather than VL. – Davide Bacci Jul 22 '23 at 07:51