- Choropleth
- This is county-level choropleth
- Note about tooltips: This choropleth has a built-in SVG tooltip
- implemented with SVG
<title>
element - Observable Plot implements tooltips the same way
- each mark has an optional "title" channel -- if you provide a value, it'll show up as an SVG
<title>
- the other channels: "fill", "fillOpacity", "stroke", "strokeOpacity", "strokeWidth"
- each mark has an optional "title" channel -- if you provide a value, it'll show up as an SVG
- Note: SVG
<title>
is not the same as the HTML document<title>
- implemented with SVG
- My Zoom to Bounding Box
- Adds pan and zoom capabilities to the county-level choropleth
- also adds clickable d3-transitions for zooming in/out on a state
- I created this because zooming a county-level SVG can have performance issues
- Adds pan and zoom capabilities to the county-level choropleth
- State Choropleth
- we're going to customize this below
- topojson has 2-character ID and state name
- TODO: discuss color scale
Add a circle that follows the mouse to State Choropleth
- Hints:
- Use d3.pointer
d3.pointer(event)
is part of d3-selection
- For more hints, look at the implementation of Plot Tooltip
- Use d3.pointer
- SOLUTION
- State Choropleth (w/tooltip)
- First: add a circle that will follow the mouse
const circle = svg.append("circle").attr("r", 10)
- That line should appear after the code that draws the states and state boundaries
- Otherwise it will appear underneath those elements
- When you add the line, you should see 1/4 of the circle in the upper left corner, where
x = y = 0
- That's because we have yet to set the values for
cx
andcy
- That's because we have yet to set the values for
- Second, declare
function mousemoved(event) {}
, which will determine the circle position based on the mouse eventfunction mousemoved(event) { const pointer = d3.pointer(event, svg.node()); circle.attr("cx", pointer[0]) .attr('cy', pointer[1]) // mutable z = pointer // use this to inspect the mouse location (add a `mutable z` cell to the notebook) }
- You can place this anywhere inside the cell.
- Arbitrary placement in the cell is due to Hoisting in JavaScript.
- You can place this anywhere inside the cell.
- Third, add an event listener to the SVG
- Add
svg.on("mousemove", mousemoved)
somewhere in the cell. - You can do it by changing
toconst svg = d3.create("svg") .attr("viewBox", [0, 0, 975, 610]);
const svg = d3.create("svg") .attr("viewBox", [0, 0, 975, 610]) .on('mousemove', mousemoved);
- Note: the technique for getting the circle to follow the mouse on the map
in Introduction to Views
is significantly more subtle
- That example uses layerX and layerY, which is non-standard!!
- Add
- When you add an event listener for
mousemove
- If you add it to the states, then you can use it below for data-dependent styling or a tooltip
- To put it on states, move the line:
.on("mousemove", mousemoved)
after:.join("path")
- To put it on states, move the line:
- If you add it to the states, then you can use it below for data-dependent styling or a tooltip
- TODO Show what happens when you change the location of
.on("mousemove", mousemoved)
- If you put it on the main SVG, then the circle goes anywhere
- If you put it on the "g" with the legend, it only moves if you mouse over the legend
- If you put it on the states, then it won't move outside the states
Update the styling to highlight the state that's being moused over
- Again, get hints from here: Plot Tooltip
- d3.select() -- tells you about
this
- You'll use
this
to select a state - You can also use
this
to get the data value that's bound to the state
- You'll use
- If you change the implementation to
const states = svg.append("g")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.join("path")
.attr("fill", d => color(data.get(d.properties.name)))
.attr("d", path)
.on("mousemove", mousemoved)
.on("mouseout", mousedout)
.append("title")
.text(d => `${d.properties.name}
${format(data.get(d.properties.name))}`);
- Then update
function mousemoved()
to include the following...
d3.select(this).attr('fill', 'crimson')
- And add
function mousedout()
as follows (usingthis
to get the bound datum)
function mousedout(event) {
const d = d3.select(this).data()[0];
d3.select(this).attr('fill', color(data.get(d.properties.name)));
}
- If the circle is still there, then you'll have to add
.attr("pointer-events", "none")
- Otherwise the circle will be underneath the mouse, and the states'
<path>
elements will never detect mouse events
- Otherwise the circle will be underneath the mouse, and the states'
Add a tooltip that shows the data value as it follows the mouse