Published
Edited
Jul 12, 2020
1 fork
10 stars
Insert cell
Insert cell
dorlingCartogram = {

const svg = d3.select(DOM.svg(width, height))
.style("width", "100%")
.style("height", "auto")

const g = svg.append("g")
.attr("transform", `translate(20,20)`);
svg.append("g")
.selectAll("circle")
.data(spreadMunicipalities)
.enter().append("circle")
.attr("fill", district => partyToColor[district.properties.winner])
.attr("id", d => `path-${d.id}`)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => radiusScale(d.properties.population))
.style("stroke", "white")
.style("stroke-width", ".3px")

// Translate Canarias
svg
.selectAll(canarias)
.attr("transform", "translate(150,-300)")
return svg.node()
}
Insert cell
canarias = "#path-35001, #path-35002, #path-35003, #path-35004, #path-35005, #path-35006, #path-35007, #path-35008, #path-35009, #path-35010, #path-35011, #path-35012, #path-35013, #path-35014, #path-35015, #path-35016, #path-35017, #path-35018, #path-35019, #path-35020, #path-35021, #path-35022, #path-35023, #path-35024, #path-35025, #path-35026, #path-35027, #path-35028, #path-35029, #path-35030, #path-35031, #path-35032, #path-35033, #path-35034, #path-38001, #path-38002, #path-38003, #path-38004, #path-38005, #path-38006, #path-38007, #path-38008, #path-38009, #path-38010, #path-38011, #path-38012, #path-38013, #path-38014, #path-38015, #path-38016, #path-38017, #path-38018, #path-38019, #path-38020, #path-38021, #path-38022, #path-38023, #path-38024, #path-38025, #path-38026, #path-38027, #path-38028, #path-38029, #path-38030, #path-38031, #path-38032, #path-38033, #path-38034, #path-38035, #path-38036, #path-38037, #path-38038, #path-38039, #path-38040, #path-38041, #path-38042, #path-38043, #path-38044, #path-38045, #path-38046, #path-38047, #path-38048, #path-38049, #path-38050, #path-38051, #path-38052, #path-38053, #path-38054, #path-38901"
Insert cell
partyToColor = {
return {
PSOE:"#EE6655",
PP:"#2DAFD3",
Cs:"#FF7E36",
Vox:"#A3CE51",
UP:"#8A448C",
PNV:"#07AD5A",
EHBildu:"#dff46b",
ERC: "#F7AA2F",
JxCAT: "#62738e",
TE:"#2E8724",
PACMA:"#e8e8e8",
Otros:"#e8e8e8",
BNG: "#e8e8e8",
CC: "#e8e8e8",
NS: "#00599B",
PRC: "#C2CE0C",
CUP: "#FFED00"
}
}
Insert cell
spreadMunicipalities = applyForce(municipalities)
Insert cell
applyForce = (nodes) => {
const simulation = d3.forceSimulation(nodes)
.force("cx", d3.forceX().x(d => width / 2).strength(0.02))
.force("cy", d3.forceY().y(d => width * (5/8) / 2).strength(0.02))
.force("x", d3.forceX().x(d => d.properties.centroid ? d.properties.centroid[0] : 0).strength(1))
.force("y", d3.forceY().y(d => d.properties.centroid ? d.properties.centroid[1] : 0).strength(1))
.force("charge", d3.forceManyBody().strength(-1))
.force("collide", d3.forceCollide().radius(d => d.properties.radius + nodePadding).strength(1))
.stop()

let i = 0;
while (simulation.alpha() > 0.01 && i < 200) {
simulation.tick();
i++;
console.log(`${Math.round(100*i/200)}%`)
}


return simulation.nodes();
}
Insert cell
nodePadding = 0.1
Insert cell
municipalities = {
const projectPoint = point => d3GeoProjection.geoProject(point, projection);
return features
.map(municipality => {
const { id } = municipality;
const population = populations.get(id);
const winner = parties.get(id)
return {
...municipality,
properties: {
id,
population,
winner,
centroid: projectPoint(turf.centroid(municipality.geometry).geometry).coordinates,
radius: radiusScale(population)
}
};
}).filter(c => c.properties.centroid)
//.filter(c => c.geometry.type === "MultiPolygon")
.sort((a,b) => a.properties.centroid[0] < b.properties.centroid[0] ? -1 : 1)
.map((d, i) => {
let geometry;
if (d.geometry.type !== "MultiPolygon") {
geometry = d.geometry
} else {
geometry = {
type: d.geometry.type,
coordinates: d.geometry.coordinates.sort((a,b) =>
turf.area(turf.polygon(a)) > turf.area(turf.polygon(b)) ? -1 : 1
).slice(0, 1)
}
}
return {
...d,
rank: i,
geometry
}
});
}
Insert cell
radiusScale = {
const populationMax = d3.max([...populations.values()])
return d3.scaleSqrt()
.domain([0, populationMax])
.range([1, 15])
}
Insert cell
json = FileAttachment("es.json").json()
Insert cell
features = topojson.feature(json, json.objects.municipalities).features
Insert cell
path = d3.geoPath().projection(projection)
Insert cell
projection = d3.geoMercator()
.scale(2000)
.center([-4.8, 39.5])
.translate([width/2, (height/2)])
Insert cell
d3GeoProjection = require('d3-geo-projection@2');
Insert cell
parties = {
const data = await d3.csv(await FileAttachment("votos-11n.csv").url());

return new Map(data.map(({ id, ganador}) => [id, ganador]));
}
Insert cell
populations = {
const data = await d3.csv(await FileAttachment("votos-11n.csv").url());

return new Map(data.map(({ id, poblacion}) => [id, +poblacion]));
}

Insert cell
height = 500
Insert cell
`## Dependencies`
Insert cell
flubber = require('https://unpkg.com/flubber')
Insert cell
turf = require("@turf/turf@5")
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3@5")
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