-
| More details in this notebook. Is there a good way to get the transformed data value(s) from a Plot? I want to append an element onto a chart following a click event. Because a Plot exposes the data value ( A simple click event like this works well enough: clickEvent = (plotObject, value, columns) => {
  // Get the scales
  const xScale = plotObject.scale("x");
  const yScale = plotObject.scale("y");
  // Create circle
  const circle = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "circle"
  );
  // Position it using the scales
  circle.setAttribute(
    "cx",
    xScale.apply(value[columns.x]) + xScale.bandwidth / 2
  );
  circle.setAttribute("cy", yScale.apply(value[columns.y]));
  circle.setAttribute("r", 5);
  circle.setAttribute("fill", "red");
  // Append
  plotObject.appendChild(circle);
}But for a stacked chart, the transformed values aren't exposed, so the  // Renamed the `spot` function from https://observablehq.com/@enjalot/plot-spot
getTransformed = (
  data = [],
  { transform, ...channels },
  facets = [Uint32Array.from(data, (d, i) => i)]
) => {
  if (transform !== undefined) {
    ({ data, facets } = transform(data, facets));
  }
  return {
    data,
    facets,
    ...Object.fromEntries(
      Object.entries(channels).map(([name, value]) => [
        name,
        Plot.valueof(data, value)
      ])
    )
  };
}However, we then need to identify the hovered value in the transformed data, as the Plot's  // For finding the element in the transformed data
findTransformedIndex = (value, transformedData) =>
  transformedData.findIndex((d) =>
    Object.keys(value).every((key) => d[key] === value[key])
  )Putting that all together looks something like this: rect.addEventListener("pointerdown", (event) => {
      // Explicitly transform the data
      const transformed = getTransformed(
        tidy,
        Plot.stackY({
          x: "state",
          y: "population",
          fill: "age"
        })
      );
      // Find the value
      const index = findTransformedIndex(chart.value, transformed.data);
      const hoverValue = { ...chart.value, y: transformed.y2[index] };
      // Call the custom click event handler
      clickEvent(chart, hoverValue, { x: "state", y: "y" });
    });So, all of this is to say, is there a simpler/clearer/more-reliable way to retrieve the stacked values from a Plot? | 
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 8 replies
-
| Here's what I'd try: Plot.plot({
  y: { tickFormat: "s" },
  marks: [
    Plot.barY(tidy, {
      x: "state",
      y: "population",
      fill: "age",
      sort: { x: "y", reverse: true },
      render(index, scales, values, dimensions, context, next) {
        const g = next(index, scales, values, dimensions, context);
        g.addEventListener("pointerdown", (event) => {
          const i = event.target.__data__;
          d3.select(context.ownerSVGElement)
            .append("circle")
            .attr("r", 5)
            .attr("fill", "red")
            .attr("cx", values.x[i] + scales.scales.x.bandwidth / 2)
            .attr("cy", values.y2[i]);
        });
        return g;
      }
      //tip: true
    })
  ],
  width
}) | 
Beta Was this translation helpful? Give feedback.
-
| @Fil any chance you know why you need to click twice on a pointer-transformed element to get the event listener to fire 😅 ? https://observablehq.com/d/2b8dd9674553809d | 
Beta Was this translation helpful? Give feedback.
Here's what I'd try: