Public
Edited
Mar 6, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
subheader = svg`
<svg class="subheader">
<style>
<!-- Make sure to add a namespace class or the style will bleed to other elements -->

.subheader, .subheader tspan {
font-family: Helvetica;
font-size: 18px;
}

.left {
fill: #c01717;
font-weight: bold;
}

.right {
fill: #2b4b8e;
font-weight: bold;
}

</style>

<text class="subheader" x="0" y="0" dy="1.1em" fill="#535353">
Relative strength in the EU2019 election as well as the tilt compared to EU2014.
<tspan x="0" dy="1.5em">
<tspan class="left">↖</tspan> tilting left &#160; <tspan class="right">↗</tspan> tilting right
</tspan>
</text>
</svg>
`
Insert cell
Insert cell
chartElem = Plot.plot({
// size and margins
height: 540,
margin: 0,

projection: {
type: "azimuthal-equal-area",
domain: europeGeo,
inset: 16
},

length: { domain: [0, 1], range: [5, 200] },

marks: [
() =>
svg`
<defs>
<filter id="outline">
<feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="0.5"></feMorphology>
<feFlood flood-color="#fff" flood-opacity="1" result="FLOOD"></feFlood>
<feComposite in="FLOOD" in2="DILATED" operator="in" result="OUTLINE"></feComposite>
<feMerge>
<feMergeNode in="OUTLINE" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
`,

// background (oceans and lakes)
Plot.frame({ fill: "#f7fbfd" }),

// base map

Plot.geo(worldGeo, {
fill: "#fff",
strokeWidth: 0.5,
stroke: "#cdcdcd"
}),

// lean map
Plot.geo(europeGeo, {
fill: (d) => {
if (election2019Lean[d.id] < 0) {
return "#c01717";
} else {
return "#2b4b8e";
}
},
fillOpacity: (d) => {
return Math.abs(election2019Lean[d.id]) / maxSeatsCountry.get(d.id);
},
//fill: "#fff",
stroke: "#535353",
strokeWidth: 0.5
}),

// data map
/*
Plot.geo(europeGeo, {
fill: (d) => {
const red = electionDiffGroup[0];
const blue = electionDiffGroup[1];
const green = electionDiffGroup[2];

if (red[d.id] > blue[d.id]) {
return "#c01717";
} else {
return "#2b4b8e";
}
},
fillOpacity: (d) => {
const red = election2019Grouped[0];
const blue = election2019Grouped[1];
const green = election2019Grouped[2];
if (red[d.id] > blue[d.id]) {
return Math.abs(red[d.id]) / maxSeatsCountry.get(d.id);
} else {
return Math.abs(blue[d.id]) / maxSeatsCountry.get(d.id);
}
},
//fill: "#fff",
stroke: "#535353",
strokeWidth: 0.5
}),*/

Plot.vector(
europeGeo.features,
Plot.centroid({
anchor: "start",
length: (d) => {
const red = electionDiffGroup[0];
const blue = electionDiffGroup[1];
const green = electionDiffGroup[2];
if (red[d.id] > blue[d.id]) {
return Math.abs(red[d.id]) / maxSeatsCountry.get(d.id);
} else {
return Math.abs(blue[d.id]) / maxSeatsCountry.get(d.id);
}
},
stroke: (d) => {
const red = electionDiffGroup[0];
const blue = electionDiffGroup[1];
const green = electionDiffGroup[2];

if (red[d.id] > blue[d.id]) {
return "#c01717";
} else {
return "#2b4b8e";
}
},
rotate: (d) => {
const red = electionDiffGroup[0];
const blue = electionDiffGroup[1];
const green = electionDiffGroup[2];

if (red[d.id] > blue[d.id]) {
return -60;
} else {
return 60;
}
},
strokeWidth: 4,
imageFilter: "url(#outline)"
})
)
]
})
Insert cell
Insert cell
Insert cell
Insert cell
{
// make sure to add all the chart elements you want in the order you want them
const elements = [
//{ element: header, offsest: 100, transform: (el) => textwrap(el, 100) }, // not working properly
{ element: header, offsest: 100 },
{ element: subheader, offset: 16 },
{ element: chartElem, offset: 16 },
{ element: footer, offset: 0 }
];

// create new svg container
const chart = d3.create("svg");

// wrap all elements in groups for cleaner rendering and add the offset to the datum of the group
elements.forEach((el, i) => {
chart
.append("g")
.datum({ offset: el.offset, transform: el.transform })
.append(() => el.element);
});

chart.call(joinElement);

return chart.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
redGroup = ["S&D", "The Left"]
Insert cell
greenGroup = ["Greens/EFA"]
Insert cell
Insert cell
worldGeo = {
const geo = await FileAttachment("ne_10m_admin_0_countries.json").json();
return topojson.feature(geo, geo.objects.ne_10m_admin_0_countries);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
electionDiffGroup = createGroupMap(electionDiff)
Insert cell
Insert cell
election2019Lean = {
const leanObj = {};

Object.entries(election2019Grouped[0]).forEach((d, i) => {
if (d[0] != "Group") {
leanObj[d[0]] = election2019Grouped[1][d[0]] - d[1];
}
});

return leanObj;
}
Insert cell
election2019LeanArray = Object.entries(election2019Lean).map((d) => {
const o = { country: d[0], value: d[1] };
return o;
})
Insert cell
election2019LeanArray
select sum("value") from election2019LeanArray
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