Pointer transforms with px, py on stacked bar charts

Hi,

I’m trying to mimic the rollover in the Observable Plot Pointer doc:


But with a stacked bar chart. I’ve been able to get the Plot.tip working but the closest I’ve gotten is something like in this notebook:

Any suggestions?
Thank you,
Ezra

2 Likes

If I am understanding correctly, you are able to use the Plot tip to show information for specific segments in the tooltip, but the top-level title appears to somewhat arbitrarily return information for only a single segment?

I may be mistaken, but I believe that Plot.text() does not by interact with the pointer events in the same manner as Plot.tip(). The text does appear to be able to access specific segments (based on the text that is currently being returned), but I am also having trouble to control it. I’ll keep at it for a minute and see what I can do (no promises!)

Thank you!
Yes - the top-level title seems to be showing information for a different chart (ie, where the y scale and the stack ordering is different?). And yes, not sure if this is possible but any guidance appreciated!

We may need help from those more adroit than me. (I can ask internally tomorrow if no one reads this and jumps in before then). Everything is possible, but you may need to reach outside of “core” Plot and add some extras. Here’s what I think is happening (and again, I may be mistaken)…

Observable’s Plot library may not support dynamic text updates in response to pointer movements in the same way it does for tooltips. I believe this limitation is because the content shown in the text mark for each bar determined when it the plot is initially drawn and does not update in response to subsequent user interactions (which for a line chart would work fine, but for the bar chart it looks like it’s determined according to the ‘center’ value for each bar).

To achieve dynamic text updating based on mouse position, my guess is that you would need to set up an event listener for mouse movements, updating a variable based on the mouse position, and then using this variable to determine the content of text to be displayed. That being said, creating an event listener inside Plot is not straightforward. You would need to create a new text mark with the updated content and replace the old text mark with it. I am not well versed in this. When I tried, I ended up with this mess (somehow making the tip sticky and still not affecting the text mark) [included mostly for laughs]:

:frowning: […or frowns :stuck_out_tongue: ]

1 Like

Got it – thank you! Yes, I haven’t gone outside of core Plot but ready and willing :slight_smile:

:slight_smile: Give me a day and I’ll see what I we can do. I’ll ask around (so far I am not getting anywhere). :crossed_fingers:

1 Like

This question is a bit tricky (thanks for asking it!). What we want here is to stack the pointer position channels (px, py), instead of the usual x, y channels. But the stackY transform does not care about px and py, and only stacks y on x.

Here’s a way to stack x and y then rename to px and py:

Plot.text(
  tidy,
  Plot.pointerX(
    (({ x, y, ...options }) => ({ px: x, py: y, ...options }))(
      Plot.stackY({
        x: "state",
        y: "population",
        frameAnchor: "top-left",
        dy: -20,
        text: (d) =>
          `${d["population"]} people ${d["age"]} years old in ${d["state"]}`
      })
    )
  )
)

which might be clearer if written as a function:

function renameXYPxPy({x, y, ...options}) {
  return {...options, px: x, py: y};
}
Plot.text(
  tidy,
  Plot.pointerX(
    renameXYPxPy(
      Plot.stackY({
        x: "state",
        y: "population",
        frameAnchor: "top-left",
        dy: -20,
        text: (d) =>
          `${d["population"]} people ${d["age"]} years old in ${d["state"]}`
      })
    )
  )
)

maybe there ought to be an easier method (a helper to rename channels? · Issue #1867 · observablehq/plot · GitHub)

2 Likes

Ooh. Amazing. So simple in hindsight. Thank you!