Public
Edited
Jun 30, 2023
1 fork
1 star
Also listed in…
Political Issues
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function make_boundaries(boundaries) {
let svg = d3.create("svg").attr("width", w).attr("height", h);
let map = svg.append("g");
map
.append("g")
.selectAll("path.boundary")
.data(boundaries.features)
.join("path")
.attr("class", "boundary")
.attr("data-district_votes", function (o) {
return `${o.properties.district}-${o.properties.dem_vote}-${o.properties.rep_vote}`;
})
.attr("fill", "white")
.attr("fill-opacity", 0)
.attr("stroke", "#333")
.attr("stroke-width", 2)
.attr("stroke-linejoin", "round")
.attr("d", path)
.on("pointerenter", function () {
d3.selectAll("path.boundary").attr("fill-opacity", 0.4);
d3.select(this).attr("fill-opacity", 0).attr("stroke-width", 5).raise();
})
.on("pointerleave", function () {
d3.selectAll("path.boundary")
.attr("fill-opacity", 0)
.attr("stroke-width", 2);
});
map
.append("g")
.selectAll("circle.city")
.data(cities)
.join("circle")
.attr("class", "city")
.attr("cx", (o) => o.x)
.attr("cy", (o) => o.y)
.attr("r", 4)
.attr("fill", "black")
.attr("stroke", "white")
.attr("stroke-width", 2)
.attr("pointer-events", "none");
let city_background_container = map
.append("g")
.attr("id", "city_background_container");
let city_name_display = map.append("g");
city_name_display
.selectAll("text.city")
.data(cities)
.join("text")
.attr("class", "city")
.attr("x", (o) => o.x)
.attr("y", (o) => o.y)
.attr("dx", (o) => (o.city == "Winston-Salem" ? (-90 * width) / 1100 : 3))
.attr("dy", (o) => (o.city == "Winston-Salem" ? (-15 * width) / 1100 : -3))
.attr("opacity", show_cities ? 1 : 0)
.text((o) => o.city)
.style("font-family", "sans-serif")
.attr("font-size", (14 * width) / 1100)
.attr("pointer-events", "none");
map
.selectAll("path.boundary")
.nodes()
.forEach(function (d, i) {
let [district, dem, rep] = d
.getAttribute("data-district_votes")
.split("-")
.map((x) => parseInt(x));
let total = dem + rep;
let dprop = dem / total;
let rprop = rep / total;
let dpct = d3.format("0.1%")(dem / total);
let rpct = d3.format("0.1%")(rep / total);
let lean;
if (dprop < rprop) {
lean = `R +${d3.format("0.1%")(rprop - state_dem_proportion)}`;
} else {
lean = `D +${d3.format("0.1%")(dprop - state_dem_proportion)}`;
}
let content = `
<div>
<div>
<div style="display: inline-block; margin-right: 5px">
District: ${district}
</div>
<div style="display: inline-block">
Lean: ${lean}
</div>
</div>
</div>`;
tippy(d, {
content: content,
allowHTML: true,
theme: "light-border",
followCursor: true
});
});

return svg.node();
}
Insert cell
// viewof show_cities = Inputs.toggle({ label: "Show city labels:" })
// This was a toggle but it's been demoted.
show_cities = true
Insert cell
// This cell places the rect backgrounds of the city lables. Since we call the
// svg:text.getBBox() method, this needs to run *after* the labels have been
// generated; thus, it's odd placement here.
outline_cities = {
let container = d3.select(map).select("#city_background_container");
if (show_cities) {
d3.select(map)
.selectAll("text")
.each(function (o) {
let bbox = this.getBBox();
container
.append("rect")
.attr("x", bbox.x - 2)
.attr("y", bbox.y - 2)
.attr("width", bbox.width + 4)
.attr("height", bbox.height + 4)
.attr("fill", "white")
.attr("fill-opacity", 0.4)
.attr("stroke", "black")
.attr("stroke-width", 0.4)
.style("pointer-events", "none");
});
} else {
container.selectAll("rect").remove();
}
}
Insert cell
Insert cell
state_dem_proportion = {
let dem_vote_total = d3.sum(
all_boundaries[0].boundaries.features.map((o) => o.properties.dem_vote)
);
let rep_vote_total = d3.sum(
all_boundaries[0].boundaries.features.map((o) => o.properties.rep_vote)
);
return dem_vote_total / (dem_vote_total + rep_vote_total);
}
Insert cell
function partisan_lean(district) {
let d = parseFloat(district.properties.dem_vote);
let r = parseFloat(district.properties.rep_vote);
let t = r + d;
return d / t;
}
Insert cell
function efficiency_gap(districts) {
let wasted_dem_votes = d3.sum(districts.map((d) => wasted_votes(d, "dem")));
let wasted_rep_votes = d3.sum(districts.map((d) => wasted_votes(d, "rep")));
let total_votes = d3.sum(
districts.map(
(d) =>
parseFloat(d.properties.dem_vote) + parseFloat(d.properties.rep_vote)
)
);
return (wasted_dem_votes - wasted_rep_votes) / total_votes;
}
Insert cell
function wasted_votes(district, p) {
let dvote = parseFloat(district.properties.dem_vote);
let rvote = parseFloat(district.properties.rep_vote);
let tvote = rvote + dvote;

if (p == "dem") {
if (dvote < tvote / 2) {
return dvote;
} else {
return dvote - tvote / 2;
}
} else {
if (rvote < tvote / 2) {
return rvote;
} else {
return rvote - tvote / 2;
}
}
}
Insert cell
Insert cell
path = d3.geoPath().projection(projection)
Insert cell
projection = d3.geoIdentity().reflectY(true).fitSize([w, h], congress_map_data)
Insert cell
h = 0.5 * w
Insert cell
w = width < 1200 ? width : 1200
Insert cell
Insert cell
congress_map_data = topojson.feature(map_data, map_data.objects.us_congress)
Insert cell
all_boundaries = Object.keys(map_data.objects).map((s) => ({
name: s,
boundaries: topojson.feature(map_data, map_data.objects[s])
}))
Insert cell
cities = all_boundaries[3].boundaries.features.map(function (o) {
let [x, y] = projection(o.geometry.coordinates);
let city = o.properties.city;
return { x, y, city };
})
Insert cell
map_data = FileAttachment("congressional_boundaries.json").json()
Insert cell
background = FileAttachment("im0.png").image()
Insert cell
Insert cell
import { Legend } from "@d3/color-legend"
Insert cell
tippy_style = html`<link style="display: none" rel="stylesheet" href="${await require.resolve(
`tippy.js/themes/light-border.css`
)}">`
Insert cell
tippy = require("tippy.js@6")
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more