Public
Edited
Dec 15, 2023
Insert cell
Insert cell
viewof anglecdu = Inputs.range([0, 360], {value: 45, step: 1, label: "Angle CDU"})
Insert cell
viewof anglespd = Inputs.range([0, 360], {value: 35, step: 1, label: "Angle SPD"})
Insert cell
viewof anglefdp = Inputs.range([0, 360], {value: 165, step: 1, label: "Angle FDP"})
Insert cell
viewof angleg = Inputs.range([0, 360], {value: 20, step: 1, label: "Angle Greens"})
Insert cell
viewof angleafd = Inputs.range([0, 360], {value: 25, step: 1, label: "Angle AFD"})
Insert cell
{
// Set the size of the SVG canvas
var width = 800;
var height = 400;
var centerX = width/2;
var centerY = height/2;
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px dotted #000");
const g = svg
.append("g")
.attr("transform",
"translate(" + centerX + "," + centerY + ")");

var numPoints = height/9;
var spacing = width*2 / numPoints;
var startX = -centerX*2; // Start the grid at the top left desipte transformation
var startY = - centerY*2;

const lineData = d3.range(numPoints).flatMap(i => {
return d3.range(numPoints).map(j => {
return { x: startX + i * spacing, y: startY + j * spacing};
});
});

// Create a scale for the radius
const radiusScale = d3.scaleSqrt()
.domain([d3.min(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}}),
d3.max(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}})]) // Range of Results
.range([5, 10]); // Adjust the range according to your desired maximum radius

function generatepattern(party) {

let angle = []
if (party.party === "CDU/CSU") {
angle = anglecdu
} else if (party.party === "SPD") {
angle = anglespd
} else if (party.party === "FDP") {
angle = anglefdp
} else if (party.party === "AFD") {
angle = angleafd
} else {
angle = angleg
}

var offsetX = Math.random() * 5; // Slight offset for x
var offsetY = Math.random() * 5; // Slight offset for y

//var angle = party.angle;
var color = party.color;
var alpha = party.alpha;
var radius = party.result;
g.append("g")
.selectAll("circle")
.data(lineData)
.join("circle")
.attr('cx', d => d.x + offsetX)
.attr('cy', d => d.y + offsetY)
.attr('r', radiusScale(radius))
.attr("opacity", 1)
.attr("fill", color)
.attr("transform", "rotate(" + angle + ")");
}

selectedElection.forEach(function(selectedElection) {
generatepattern(selectedElection);
});
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
viewof Wahl = Inputs.select(ojsdata.map(d => d.Wahltag), {label: "Wahl", unique: true})
Insert cell
// Current BEST PRACTICE
{
// Set the size of the SVG canvas
var width = 800;
var height = 400;
var centerX = width/2;
var centerY = height/2;
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px dotted #000");
const g = svg
.append("g")
.attr("transform",
"translate(" + centerX + "," + centerY + ")");

// Create a scale for the radius
const radiusScale = d3.scaleSqrt()
.domain([d3.min(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}}),
d3.max(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}})]) // Range of Results
.range([2.5, 5]); // Adjust the range according to your desired maximum radius

var numPoints = height/3.5;
var spacing = width*2 / numPoints;
var startX = -centerX*2; // Start the grid at the top left desipte transformation
var startY = - centerY*2;

function generatepattern(party) {

var offsetX = Math.random() * 1.5; // Slight offset for x
var offsetY = Math.random() * 1.5; // Slight offset for y

const lineData = d3.range(numPoints).flatMap(i => {
return d3.range(numPoints).map(j => {
return { x: startX + i * spacing + offsetX, y: startY + j * spacing + offsetY};
});
});

var angle = party.angle;
var color = party.color;
var alpha = party.alpha;
var radius = party.result;
g.append("g")
.selectAll("circle")
.data(lineData)
.join("circle")
.attr('cx', d => d.x + offsetX)
.attr('cy', d => d.y + offsetY)
.attr('r', radiusScale(radius))
.attr("fill", color)
.attr("transform", "rotate(" + angle + ")");
}

selectedElection.forEach(function(selectedElection) {
generatepattern(selectedElection);
});
return svg.node();
}
Insert cell
rotatedData = {
// Assuming SelectedElection is an array of objects with properties: party, result, color
const numPoints = 10; // You can adjust this based on your needs

const rotatedGrids = selectedElection.map(function (election) {
// Create the grid for each party
const lineData = d3.range(numPoints).flatMap(function (i) {
return d3.range(numPoints).map(function (j) {
return { x: i * 50, y: j * 50 };
});
});

// Rotate the grid for each party
const rotatedData = lineData.map(function (d) {
const yValues = lineData.map(data => data.y);
const medianY = yValues[Math.floor(yValues.length / 2)];
const xValues = lineData.map(data => data.x);
const medianX = xValues[Math.floor(xValues.length / 2)];
const angle = election.angle * (Math.PI / 180);

const rotatedx = d.x + Math.cos(angle) * (medianX - d.x) - Math.sin(angle) * (medianY - d.y);
const rotatedy = d.y + Math.sin(angle) * (medianX - d.x) + Math.cos(angle) * (medianY - d.y);
return { x: rotatedx, y: rotatedy, party: election.party, color: election.color, result: election.result };
});

return rotatedData;
});

// Flattening the array of rotated grids if needed
const flattenedRotatedGrids = rotatedGrids.flat();

return flattenedRotatedGrids;
}
Insert cell
chartrepeat = {
const width = 600;
const height = 400;
const xTiles = 20;
const yTiles = 30;
const margin = { top: 20, bottom: 20, left: 50, right: 50 };

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("border", "1px dotted #000");

const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);

// Create a scale for the radius
const radiusScale = d3.scaleSqrt()
.domain([d3.min(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}}),
d3.max(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}})]) // Range of Results
.range([2, 5]); // Adjust the range according to your desired maximum radius

const tileWidth = (width - margin.left - margin.right) / xTiles;
const tileHeight = (height - margin.top - margin.bottom) / yTiles;

for (let i = 0; i < xTiles; ++i) {
for (let j = 0; j < yTiles; ++j) {

selectedElection.forEach(d => {
if (d.party === "CDU/CSU") {
if (j % 2 != 0) {
d.x_adapt = tileWidth/2;
d.y_adapt = 0;
} else {
d.x_adapt = 0;
d.y_adapt = 0
}
} else {
d.x_adapt = (Math.random() - 0.5)*tileWidth;
d.y_adapt = (Math.random() - 0.5)*tileHeight;
}
});

g.append("g")
.selectAll("circle")
.data(selectedElection)
.join("circle")
.attr('cx', d => i*tileWidth + d.x_adapt) // X position (random here)
.attr('cy', d => j*tileHeight + d.y_adapt) // Y position (random here)
.attr('r', d => radiusScale(d.result))
.attr("opacity", 1)
.attr("fill", d => d.color)
}
}
return svg.node();
}
Insert cell
chartyear = {
const width = 600;
const height = 400;
const margin = { top: 20, bottom: 20, left: 50, right: 50 };

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("border", "1px dotted #000");

const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);

// Create a scale for the radius
const radiusScale = d3.scaleSqrt()
.domain([d3.min(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}}),
d3.max(selectedElection,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}})]) // Range of Results
.range([3, 50]); // Adjust the range according to your desired maximum radius

g.append("g")
.selectAll("circle")
.data(selectedElection)
.join("circle")
.attr("r", 3)
.attr('cx', d => Math.random() * width) // X position (random here)
.attr('cy', d => Math.random() * height) // Y position (random here)
.attr('r', d => radiusScale(d.result))
.attr("opacity", 0.7)
.attr("fill", d => d.color);
return svg.node();
}
Insert cell
chart = {
const width = 600;
const height = 400;
const margin = { top: 20, bottom: 20, left: 50, right: 50 };

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("border", "1px dotted #000");

const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);

// Create a scale for the radius
const radiusScale = d3.scaleSqrt()
.domain([d3.min(ojsdata,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}}),
d3.max(ojsdata,
d => {if (d.result === "NA" || isNaN(parseFloat(d.result))) {return undefined;}
else {return +d.result;}})]) // Range of Results
.range([3, 50]); // Adjust the range according to your desired maximum radius


g.append("g")
.selectAll("circle")
.data(ojsdata)
.join("circle")
.attr("r", 3)
.attr('cx', d => Math.random() * width) // X position (random here)
.attr('cy', d => Math.random() * height) // Y position (random here)
.attr('r', d => radiusScale(d.result))
.attr("opacity", d => d.alpha)
.attr("fill", d => d.color);
return svg.node();
}
Insert cell
ojsdata = d3.csv("https://raw.githubusercontent.com/jasminskoenig/kpbrehmer/main/data/bundestag_clean.csv?token=GHSAT0AAAAAACJN63TOGYYIWRY37DB323DCZL4KSTA")
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