// setup global canvas var cv = d3wb.config() .attr("margin", "60 50 50 60") .data([ "Bundestagswahl392-Wahlkreis.csv", "Bundestagswahl393-Gemeinde.csv", "Bundestagswahl395-Kommunalwahlbezirk.csv", "Bundestagswahl396-Wahlbezirk.csv", "Bundestagswahl398-Stadtbezirk.csv" ]) .toCanvas() d3wb.util.changeCSVSeparator(";") // read all the data and create chart d3.queue() .defer(d3.csv, cv.data[0]) .defer(d3.csv, cv.data[1]) .defer(d3.csv, cv.data[2]) .defer(d3.csv, cv.data[3]) .defer(d3.csv, cv.data[4]) .await(function(error, dataWk, dataGe, dataKom, dataBz, dataSt) { createChart(dataGe, dataKom) }) var createChart = function(dataGe, dataKom) { // chart groups var totalGroup = cv.append("g") // average for each party in bonn var districtGroup = cv.append("g") // result per district // setup labels and colors var mainParties = ["CDU", "SPD", "GRÜNE", "DIE LINKE", "FDP", "AfD"] var partyColors = ["#292929", "#DA002F", "#21CD31", "#FF00FA", "#FDEF4D", "#6A92D5", "#AAA"] var remaParties = "SONSTIGE" var globalBarColor = "#e7e3e3" var voices = ["Erststimmen", "Zweitstimmen"] // calculate basic aggregated data var districts = getDistrictNames(dataKom) var cityTotal = consolidateDataPerParty(dataGe, 0) var cityTotalCsv = d3wb.util.jsonAttributeMapToCSV(cityTotal) cityTotalCsv.sort(sortByParty) // -------------------------------------------------------------------- var mainBarChart = wbBarChart() .xSelector("key") .width(cv.wid) .height(cv.hei) .yExtent([0.0, 40.0]) .widthFactor(0.7) .fill(partyColors) .valuesShow(true) .valuesFill(d3wb.color.foreground) .valueFormat(function(v) { return d3.format(",.2f")(v) + " %" }) var cityBarChart = wbBarChart() .xSelector("key") .width(cv.wid) .height(cv.hei) .fill(globalBarColor) .yExtent([0.0, 40.0]) // Draw chart for first element in data set var overviewCsv = updateData(0, 0) districtGroup.datum(overviewCsv).call(mainBarChart) totalGroup.datum(cityTotalCsv).call(cityBarChart) var title = d3wb.add.title( voices[0] + " – " + districts[0]) .color(d3wb.color.foreground) cv.call(title) title.update() // Draw axis components cv.call( d3wb.add.yAxisLabel("Stimmanteile in %") .color(d3wb.color.foreground) ) cv.call( d3wb.add.xAxisBottom() .scale(mainBarChart.scaleX()) .y(cv.hei) .fontSize("150%") .color(d3wb.color.foreground) ) cv.call( d3wb.add.yAxis() .scale(mainBarChart.scaleY()) .fontSize("150%") .color(d3wb.color.foreground) ) cv.call( d3wb.add.infoBox("Die hellgrau hinterlegte Balken stellen\ndas Bonner Gesamtergebnis der jeweiligen Partei dar.") .fill(d3wb.color.background) .color(d3wb.color.foreground) ) createDropdowns(cv, districts, voices) // -------------------------------------------------------------------- function updateData(index, voice) { mainBarChart.ySelector(voice == 0 ? "pct1" : "pct2") cityBarChart.ySelector(voice == 0 ? "pct1" : "pct2") var overview = consolidateDataPerParty(dataKom, index) var overviewCsv = d3wb.util.jsonAttributeMapToCSV(overview) overviewCsv.sort(function(a, b) { var idxA = a.key == remaParties ? 99 : mainParties.indexOf(a.key); var idxB = b.key == remaParties ? 99 : mainParties.indexOf(b.key); return idxA - idxB }) return overviewCsv } function updateVisualization(index, voice) { var overviewCsv = updateData(index, voice) title.text(voices[voice] + " – " + districts[index]) title.update() mainBarChart.update(overviewCsv) cityBarChart.update(cityTotalCsv) } function dropdownOnChange() { district = d3.select("#select-district").property("value") voice = d3.select("#select-voice").property("value") updateVisualization(districts.indexOf(district), voices.indexOf(voice)) } function createDropdowns() { var selectDistrict = d3.select(cv.div) .append("select") .attr("id", "select-district") .on("change", dropdownOnChange) selectDistrict .selectAll("option") .data(districts).enter() .append("option") .text(function(d) { return d; }); var selectVoice = d3.select(cv.div) .append("select") .attr("id", "select-voice") .on("change", dropdownOnChange) selectVoice .selectAll("option") .data(voices).enter() .append("option") .text(function(d) { return d; }); d3wb.util.injectCSS(` #select-district { position: absolute; top: ` + (cv.mar.top) + `px; right: 5px; } #select-voice { position: absolute; top: ` + (cv.mar.top + 25) + `px; right: 5px; } `) } function sortByParty(a, b) { var idxA = a.key == remaParties ? 99 : mainParties.indexOf(a.key); var idxB = b.key == remaParties ? 99 : mainParties.indexOf(b.key); return idxA - idxB } function getDistrictNames(data) { var districts = [] data.forEach(function(d) { districts.push(d["Name"]) }) return districts } function consolidateDataPerParty(data, index) { index = index || 0 // setup data structures var partyPct = {} partyPct[remaParties] = { "pct1": 0.0, "tot1": 0.0, "pct2": 0.0, "tot2": 0.0 } // find all partys for which we have percentual results var parties = data.columns.filter(function(d) { return d.match("Z_.*_Proz") }) parties.forEach(function(p) { // read name and percent var party = p.replace(/^Z_/, "").replace(/_Proz$/, "") var pct1 = +data[index][party + "_Proz"].replace(",", ".") var tot1 = +data[index][party].replace(",", ".") var pct2 = +data[index]["Z_" + party + "_Proz"].replace(",", ".") var tot2 = +data[index]["Z_" + party].replace(",", ".") // either store separately or sum up amongst others if (mainParties.includes(party)) { partyPct[party] = { "pct1": pct1, "tot1": tot1, "pct2": pct2, "tot2": tot2 }; } else { partyPct["SONSTIGE"]["pct1"] += pct1; partyPct["SONSTIGE"]["tot1"] += tot1; partyPct["SONSTIGE"]["pct2"] += pct2; partyPct["SONSTIGE"]["tot2"] += tot2; } }) return partyPct; } }