Transition for map colors

So the next thing I am trying to accomplish is making the ZCTAs transition colors when a new classification selection is made.

This is the notebook.

From doing some reading of other notebooks I thought that the following code in it’s own cell would be what I needed:

d3
  .selectAll('.ZCTA')
  .transition()
  .attr(
    "fill",
    d => prevScale(prev_by_ZCTA.get(d.properties.ZCTA5CE10)) || "white"
  )
  .duration(1000)

However, when any selection changes the ZCTAs go back to being colored black (the default color I suppose).

Pointers on what I am missing / why what I am doing does not work?

Here’s my suggestion:

What I changed:

  1. d3.select(chart).selectAll('.ZCTA') so that your side-effect cell initiating the transition explicitly depends on the chart cell, and thus is guaranteed to run after the chart is initialized (rather than potentially before when the notebook is first loaded), and will automatically run again if the chart cell is ever edited or changes.

  2. Initialize the path fill to white, so that you get a fade-in on first load.

  3. Use the default transition duration of 250ms.

  4. Update the title text in the side-effect cell, too, so that the chart isn’t regenerated when the selected classification changes, which would prevent the transition (since the chart cell would entirely regenerate and you’d always be transitioning from scratch).

1 Like

Also, the notebook visualizer may help you debug these issues in the future. Here’s your notebook visualized (prior to my suggestion):

Note that the classification cell feeds into prev_by_ZCTA into chart, which was causing the chart to regenerate when the classification changed, breaking the transition. In my suggestion, the graph is:

Where 19 is the anonymous side-effect cell initiating the transition.

2 Likes

Thanks for pointing out the notebook visualizer and for explaining the dependency.

I added the ability to change the color scheme, but in doing so the legend (which referenced prevScale) caused chart to regenerate.

In order to avoid this, I added a placeholder scale to the call to add the legend in the chart definition:

.attr("class", "legend")
.append(() =>
  legend({
    color: d3.scaleThreshold(
      [15, 25, 35, 45],
      ['#fef0d9', '#fdcc8a', '#fc8d59', '#e34a33', '#b30000']
    ),
    title: `Adult Prevalence of Obese (%)`,
    width: 200
  })

and then selected and updated the relevant rectangles in the legend in a different cell:

  d3.select(chart)
    .select(".legend")
    .selectAll("rect")
    .transition(750)
    .attr("fill", (d, i) => colorSchemeMap.get(colorScheme)[i]);

Is this the “right” way to perform this transition, or is there something easier that I am missing?

I also had a reference to classification in the initial version of the legend which would update the title with the classification being displayed. However this also caused chart to regenerate and so I added a placeholder legend title and created an update to it outside of chart. However, to select the <text></text> selector(?) that contains the text of the legend title, I had to resort to noticing that it was the last <text> element in the <g class="legend"> element.

Is this the recommended way to do things, or is there a better way to accomplish this?

1 Like

Do you need the transitions here? If you’re looking for a simpler approach, you could ditch the transitions entirely and just rely on Observable’s reactivity to redraw the chart.

Similarly, do you need the transitions when you change the color scheme? If you don’t, and you just want a quick way to see what the chart would look like in a different scheme before you decide which one to use, you could avoid support transitions for a color scheme change and just let Observable re-run the cells reactively instead of using side-effects.

I am just starting to learn D3 and Javascript and trying to understand how to make visualizations.

As such, I don’t need the transitions. It was using this as an exercise to learn how to accomplish transitions, and in particular how to accomplish the transitions seamlessly.

When I make the legend title dynamic using the line:

title: `Adult Prevalence of ${classification} (%)`

in the legend definition, as I have done in this notebook, then each time the classification changes the chart is redrawn and you see a flash of white and no transition effect between the different prevalence states.

Given that, I was wondering if what I had done was a reasonable way to accomplish the seamless transitions.

Correct. You should avoid referencing classification in the chart cell if you don’t want the chart to be re-created when the value of classification changes. If you want transitions, you should only reference classification from the side-effect cell that initiates the transition. Have the chart cell create the “skeleton” chart, and then have the side-effect cell be responsible for anything that can change.

(You can reference viewof classification.value if you want to refer to the selected value without re-running when the selected value changes. But I think it’s cleaner to only reference classification from the side-effect cell.)

I employ a similar technique in this sortable bar chart: