Published
Edited
Nov 13, 2020
4 stars
Also listed in…
hex maps
Insert cell
Insert cell
electoral_tile_map = {
let div = d3
.create('div')
.style("width", `${map_width}px`)
.style("height", `${height}px`)
.style('overflow', 'hidden');

let svg = div
.append('svg')
.style('overflow', 'hidden')
.attr("viewBox", [0, 0, map_width, height]);

let path = d3.geoPath().projection(proj);

let map = svg.append('g');
map
.selectAll("path.tile")
.data(electoral_tiles.tiles.features)
.join("path")
.attr('class', 'tile')
.attr('d', path)
.style("fill", function(d) {
let result = result_map.get(d.properties.name);
if (result == 'R') {
return '#ff3333';
} else if (result == 'D') {
return '#3333ff';
} else {
return '#ccc';
}
})
.attr("stroke-width", '0.8px')
.attr("stroke", function(d) {
return '#000';
})
.attr("stroke-linejoin", "round");
map
.selectAll("path.subtile")
.data(electoral_tiles.maine_nebraska_1)
.join("path")
.attr('class', 'subtile')
.attr('d', path)
.style("fill", function(d) {
let name = d.properties.name;
if (name == 'MAINE') {
return '#ff3333';
} else if (name == "NEBRASKA") {
return '#3333ff';
} else {
return '#aaa';
}
})
.attr("stroke-width", '1px')
.attr("stroke", function(d) {
return '#000';
})
.attr("stroke-linejoin", "round");

map
.selectAll("path.boundary")
.data(electoral_tiles.boundaries.features)
.join("path")
.attr('class', 'boundary')
.attr('d', path)
.style('fill', 'white')
.style("fill-opacity", 0)
.attr("stroke-width", '2px')
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr('title', function(d) {
if (d.properties.name == "MAINE") {
return "MAINE: 3 votes for Biden and 1 vote for Trump";
} else if (d.properties.name == "NEBRASKA") {
return "NEBRASKA: 4 votes for Trump and 1 vote for Biden";
}
let result = result_map.get(d.properties.name);
let winner;
if (result == "D") {
winner = "Biden";
} else if (result == "R") {
winner = "Trump";
} else {
winner = "no one in particular, just yet.";
}
return `${d.properties.name}: ${d.properties.tilegramValue} votes for ${winner}`;
})
.on('mouseenter', function(evt) {
let copy = this.cloneNode(true);
copy.setAttribute('stroke', 'black');
copy.setAttribute('stroke-width', '6px');
copy.setAttribute('class', 'temp');
copy.addEventListener('mouseleave', function() {
map.selectAll('.temp').remove();
});
tippy(copy, {
content: copy.getAttribute("title"),
followCursor: true,
theme: 'light'
});
map.append(() => copy);
});

let biden_votes = d3.sum(
electoral_tiles.boundaries.features
.filter(o => result_map.get(o.properties.name) == 'D')
.map(o => o.properties.tilegramValue)
);
let trump_votes = d3.sum(
electoral_tiles.boundaries.features
.filter(o => result_map.get(o.properties.name) == 'R')
.map(o => o.properties.tilegramValue)
);
let undecided_votes = d3.sum(
electoral_tiles.boundaries.features
.filter(o => result_map.get(o.properties.name) == '?')
.map(o => o.properties.tilegramValue)
);
map
.append('text')
.attr('x', `${0.2 * map_width}`)
.attr('y', `${0.05 * height}`)
.attr('font-family', 'sans-serif')
.text(`Biden: ${biden_votes}`);
map
.append('text')
.attr('x', `${0.4 * map_width}`)
.attr('y', `${0.05 * height}`)
.attr('font-family', 'sans-serif')
.text(`Trump: ${trump_votes}`);
map
.append('text')
.attr('x', `${0.6 * map_width}`)
.attr('y', `${0.05 * height}`)
.attr('font-family', 'sans-serif')
.text(`Undecided: ${undecided_votes}`);

return div.node();
}
Insert cell
proj = d3
.geoIdentity()
.reflectY(true)
.fitSize([map_width, height], electoral_tiles.boundaries)
Insert cell
map_width = width
Insert cell
height = 0.8 * map_width
Insert cell
result_map = {
let results = await FileAttachment("results2020.csv").csv();
let result_map = new Map();
results.forEach(function(o) {
result_map.set(o.state, o.result);
});
return result_map;
}
Insert cell
electoral_tiles = {
let map_file = await FileAttachment("ElectoralCollege@2.json").json();
let tiles = topojson.feature(map_file, map_file.objects.tiles);
let maine_nebraska_1 = tiles.features
.filter(
o => o.properties.name == "MAINE" || o.properties.name == "NEBRASKA"
)
.map(o => ({
type: "Feature",
properties: {
name: o.properties.name,
tilegramValue: o.properties.tilegramValue
},
geometry: { type: "Polygon", coordinates: o.geometry.coordinates[0] }
}));
let boundaries = topojson.feature(map_file, map_file.objects.boundaries);
return {
tiles: tiles,
boundaries: boundaries,
maine_nebraska_1: maine_nebraska_1
};
}
Insert cell
tippy = require("tippy.js@6")
Insert cell
style = html`<link rel="stylesheet" href="${await require.resolve(
`tippy.js/themes/light.css`
)}">`
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require('d3@6')
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