Published
Edited
Jun 11, 2020
Insert cell
Insert cell
chart = {
// Container SVG
const svg = d3
.select(DOM.svg())
.attr("viewBox", [
0,
0,
viewboxWidth + 150,
viewboxHeight + margin.top + margin.bottom
]);

// Wrapper G element
const g = svg
.attr('preserveAspectRatio', 'xMidYMid')
.append('g')
.attr(
'transform',
`translate(${margin.left + margin.right}, ${margin.top})`
);

// Bind the cartogram data to a selection of G elements
const states = g
.selectAll('.states')
.data(cartogram_data)
.enter()
.append('g')
.attr('class', 'state-groups');

// Use california data in the legend
const legend_data = {};
cartogram_data
.filter(d => d.state_postal == legend_state)
.forEach(d => {
Object.keys(d).map(dd => (legend_data[dd] = d[dd]));
});
legend_data.total = "123";
legend_data.state_postal = "State";
legend_data.x_min_label = first_x_label;

// Draw a legend
const legend = svg.selectAll(".legend").data([legend_data]);
legend
.enter()
.append("g")
.attr("transform", `translate(${(viewboxWidth - state_size) / 2}, 70)`)
.call(renderState, { labelTotal: true, labelNewCases: true });

// Render a state, for each state element
states
.append('g')
.attr(
'transform',
d => `translate(${x_column(d)}, ${y_column(d) + state_size})`
)
.call(xAxis);

states
.append("g")
.attr('transform', d => `translate(${x_column(d)}, ${y_column(d)})`)
.call(renderState);

// Title
svg
.append("text")
.text(title)
.attr("x", viewboxWidth / 2)
.attr("y", 50)
.attr("font-weight", "bold")
.attr("font-size", 20)
.attr("text-anchor", "middle");
return svg.node();
}
Insert cell
// Function to render the internal elements of each state rectangle -- assumes data is bound
renderState = function(stateElement, options = {}) {
// Append a background element
stateElement
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", state_size)
.attr("width", x.range()[1])
.style("fill", "#eee")
.style("fill-opacity", .5);

// Show a different background where x > 0
stateElement
.append("rect")
.attr("x", d => x(d.x_min))
.attr("y", 0)
.attr("height", state_size)
.attr("width", d => x.range()[1] - x(d.x_min))
.style("fill", "#fff4c7");

// Show the state name
stateElement
.append('text')
.attr("x", 0)
.attr('y', 12)
.attr('font-size', 12)
.attr('font-family', 'Helvetica')
.attr('fill', "black")
.attr("font-weight", "bold")
.text(d => d.state_postal);

// Show the total
stateElement
.append('text')
.attr("x", 0)
.attr('y', 24)
.attr('font-size', 12)
.attr('font-family', 'Helvetica')
.attr('fill', "red")
.text(d => format(d.total));

// Append rectangles
stateElement
.selectAll('.bar')
.data(d => d.data)
.enter()
.append('rect')
.attr('x', d => x(d.key))
.attr('y', d => state_size - y(d.value))
.attr('width', x.bandwidth())
.attr('height', d => y(d.value))
.attr('fill', "red")
.attr('stroke', 'red')
.attr('stroke-width', 0.5);

// Small triangle with label
stateElement
.append("path")
.attr('transform', d => `translate(${x(d.x_min)}, ${state_size})`)
.attr("d", "M 0,1 L -3,3 3,3 Z")
.style("fill", "#666");

// Label the first date below the x axis
stateElement
.append("text")
.attr('transform', d => {
console.log("d", d);
return `translate(${x(d.x_min)}, ${state_size})`;
})
.text(d => d.x_min_label)
.attr("y", 13)
.style("text-anchor", "middle")
.style("font-size", "9");

// Label along the left side (for the legend)
if (options.labelTotal) {
// Legend total cases (text)
stateElement
.append('text')
.attr("x", -5)
.attr('y', 22)
.attr("text-anchor", "end")
.text(total_label)
.style("font-size", "9")
.attr('font-family', 'Helvetica');

// Tiny triangle
stateElement
.append("path")
.attr("transform", "translate(0, 20) rotate(90)")
.attr("d", "M 0,1 L -3,3 3,3 Z")
.style("fill", "#666");
}

// Label along the right side (for the legend)
if (options.labelNewCases) {
// // Legend new cases label
stateElement
.append('text')
.attr("x", state_size + 5)
.attr('y', d => 2 + state_size - y(d.data[d.data.length - 1].value))
.attr("text-anchor", "start")
.text(y_height_label)
.style("font-size", "9")
.attr('font-family', 'Helvetica');

// Total cases triangle
stateElement
.append("path")
.attr(
"transform",
d =>
`translate(${state_size}, ${state_size -
y(d.data[d.data.length - 1].value)}) rotate(-90)`
)
.attr("d", "M 0,1 L -3,3 3,3 Z")
.style("fill", "#666");
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
title = "Your Cartogram Title Here"
Insert cell
first_x_label = "First X Value Label"
Insert cell
total_label = "Total Label"
Insert cell
y_height_label = "Y Height Label"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
_ = require('lodash')
Insert cell
d3 = require("d3@5", "d3-svg-legend@2", "d3-scale-chromatic@1.3.3")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more