Published
Edited
Dec 27, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
numDataPoints = {
let numDataPoints = 0;

//Dynamic, random dataset
let dataset = []; //Initialize empty array
let inputdata = null;

if (datasetname == "STATEN_ISLAND_Num_Traffic_Accidents2017.csv") {
inputdata = d3.csvParse(
await FileAttachment(
"STATEN_ISLAND_Num_Traffic_Accidents2017.csv"
).text(),
(d, _, columns) => {
return d;
}
);

numDataPoints = inputdata.length;
} else if (
datasetname == "STATEN_ISLAND_Num_Traffic_Weekly_Accidents2017.csv"
) {
inputdata = d3.csvParse(
await FileAttachment(
"STATEN_ISLAND_Num_Traffic_Weekly_Accidents2017.csv"
).text(),
(d, _, columns) => {
return d;
}
);

numDataPoints = inputdata.length;
} else if (datasetname == "averagerainfall.csv") {
inputdata = d3.csvParse(
await FileAttachment("averagerainfall.csv").text(),
(d, _, columns) => {
return d;
}
);

numDataPoints = inputdata.length;
} else if (datasetname == "randomdailypatterns") {
numDataPoints = numSampleDataPoints;
}
return numDataPoints;
}
Insert cell
inputdata = d3.csvParse(
await FileAttachment("averagerainfall.csv").text(),
(d, _, columns) => {
let total = 0;
for (let i = 1; i < columns.length; ++i)
total += d[columns[i]] = +d[columns[i]];
d.total = total;
return d;
}
)
Insert cell
dataset = {
//Dynamic, random dataset
let dataset = []; //Initialize empty array
let inputdata = null;

if (datasetname == "STATEN_ISLAND_Num_Traffic_Accidents2017.csv") {
inputdata = d3.csvParse(
await FileAttachment(
"STATEN_ISLAND_Num_Traffic_Accidents2017.csv"
).text(),
(d, _, columns) => {
let total = 0;
for (let i = 1; i < columns.length; ++i)
total += d[columns[i]] = +d[columns[i]];
d.total = total;
return d;
}
);
inputdata.forEach((obj, i) => {
dataset.push([i + 1, parseInt(obj.frequency), obj.hour]); //Add new number to array
});
} else if (
datasetname == "STATEN_ISLAND_Num_Traffic_Weekly_Accidents2017.csv"
) {
inputdata = d3.csvParse(
await FileAttachment(
"STATEN_ISLAND_Num_Traffic_Weekly_Accidents2017.csv"
).text(),
(d, _, columns) => {
let total = 0;
for (let i = 1; i < columns.length; ++i)
total += d[columns[i]] = +d[columns[i]];
d.total = total;
return d;
}
);
inputdata.forEach((obj, i) => {
let str = "";
switch (parseInt(obj.week)) {
case 0:
str = "Sun";
break;
case 1:
str = "Mon";
break;
case 2:
str = "Tue";
break;
case 3:
str = "Wed";
break;
case 4:
str = "Thu";
break;
case 5:
str = "Fri";
break;
case 6:
str = "Sat";
break;
}
dataset.push([i + 1, parseInt(obj.frequency), str]); //Add new number to array
});
} else if (datasetname == "averagerainfall.csv") {
inputdata = d3.csvParse(
await FileAttachment("averagerainfall.csv").text(),
(d, _, columns) => {
let total = 0;
for (let i = 1; i < columns.length; ++i)
total += d[columns[i]] = +d[columns[i]];
d.total = total;
return d;
}
);
inputdata.forEach((obj, i) => {
dataset.push([i + 1, parseInt(obj.Rainfall), obj.State]); //Add new number to array
});
} else if (datasetname == "randomdailypatterns") {
// var xRange = getNext() * 50; //Max range of new x values
// console.log("getNext(): "+getNext());
pseudorandomNumberGenerator();
let yRange = pseudorandomNumberGenerator() * 1000; //Max range of new y values

for (var i = 0; i < numDataPoints; i++) {
//Loop numDataPoints times
// var newNumber1 = Math.floor(getNext() * xRange); //New random integer
let randomNumber = pseudorandomNumberGenerator();
let newNumber2 = Math.floor(randomNumber * yRange); //New random integer
// console.log("yRange: "+yRange+", newNumber2: "+newNumber2+", randomNumber: "+randomNumber);

dataset.push([i + 1, newNumber2, i + 1]); //Add new number to array, d[4] used for tickformat, e.g., string
}
}

let startAngle = 0;
let endAngle = 2 * Math.PI;
let itemAngle = (endAngle - startAngle) / numDataPoints;

return dataset;
}
Insert cell
angleBetweenPoints = (p1, p2) => {
if (p1[0] == p2[0] && p1[1] == p2[1]) return Math.PI / 2;
else return Math.atan2(p2[1] - p1[1], p2[0] - p1[0]);
}
Insert cell
toDegrees = rad => {
return rad * (180 / Math.PI);
}
Insert cell
arc = d3
.arc()
.innerRadius(d => rScale(0))
.outerRadius(d => rScale(d[1]))
.startAngle(d => {
// console.log("xScaleRadial("+d[0]+")="+xScaleRadial(d[0]));
return xScaleRadial(d[0]);
})
.endAngle(d => xScaleRadial(d[0]) + xScaleRadial.bandwidth())
.padAngle(0.01)
.padRadius(innerRadius)
Insert cell
svgGroupRadial = {
let svgGroup = svgsRadial
.append('g')
.attr('transform', 'translate(' + [w / 2, h / 2] + ')');

svgGroup
.append("def")
.append("path")
.attr("id", "label-path")
.attr(
"d",
"m0 " +
-labelRadius +
" a" +
labelRadius +
" " +
labelRadius +
" 0 1,1 -0.01 0"
);
return svgGroup;
}
Insert cell
renderRadial = {
svgsRadial.selectAll(".cleanOnInit").remove();
//to override previous transform rotation
svgGroupRadial.attr('transform', 'translate(' + [w / 2, h / 2] + ')');
configuration.angle = 0;

svgGroupRadial
.selectAll(".radialline")
.data(d3.range(numDataPoints))
.enter()
.append("line")
.attr("y2", -barHeight - 20)
.attr("class", "radialline" + " cleanOnInit")
.style("stroke", "black")
.style("stroke-width", ".5px")
.attr("transform", function(d, i) {
return "rotate(" + (i * 360) / numDataPoints + ")";
});

svgGroupRadial
.selectAll('.radialarc')
.data(dataset)
.enter()
.append('path')
.attr("class", "radialarc" + " cleanOnInit")
.attr('d', function(d) {
return arc(d);
})
.attr('fill', 'lightsteelblue') //lightsteelblue
.attr('stroke', 'white')
.attr('stroke-width', 1);

let circles = svgGroupRadial
.selectAll(".radialcircle")
.data(yScaleRadial.ticks(5))
.enter()
.append("circle")
.attr("class", "radialcircle" + " cleanOnInit")
.attr("r", function(d) {
return rScale(d);
})
.style("fill", "none")
.style("stroke", "black")
.style("visibility", bEnableDashedLine ? "visible" : "hidden")
.style("stroke-dasharray", "2,2")
.style("stroke-width", ".5px");

svgGroupRadial
.selectAll(".radiallabel")
.data(Array.from(dataset, d => d[2]))
.enter()
.append("text")
.attr("class", "radiallabel" + " cleanOnInit")
.style("text-anchor", "middle")
.style("font-size", "14px")
.style("font-weight", "bold")
.style("fill", function(d, i) {
return "#3e3e3e";
})
.append("textPath")
.attr("xlink:href", "#label-path")
.attr("startOffset", function(d, i) {
return (i * 100) / numDataPoints + 50 / numDataPoints + '%';
})
.text(function(d) {
return d;
});

let angle = 0;
let dragstartangle = 0;
svgGroupRadial
.append("rect") //Make a new rectangle
.attr("x", -w / 4) //Set rect's position and size…
.attr("y", -h / 2)
.style("stroke-width", "2")
.style("storke", "rgb(0,0,0)")
// .style("fill", "none")
.style("fill", "rgb(255,255,255)")
.style("fill-opacity", 0)
.attr("class", " cleanOnInit")
.attr("width", h)
.attr("height", h)
.attr("draggable", true)
.call(
d3
.drag()
.on('start', () => {
let exy = [d3.event.x, d3.event.y],
dxy = [0, 0];
dragstartangle = angleBetweenPoints(dxy, exy);
})
.on("drag", () => {
let exy = [d3.event.x, d3.event.y],
dxy = [0, 0];
angle = configuration.angle + angleBetweenPoints(dxy, exy);
angle -= dragstartangle;
configuration.angle = angle;
svgGroupRadial.attr(
"transform",
'translate(' +
[w / 2, h / 2] +
')' +
"rotate(" +
toDegrees(configuration.angle) +
",0, 0) "
);
})
.on("end", () => {})
);

svgsRadial
.append("g")
.attr("class", "yAxis" + " cleanOnInit")
.attr('transform', 'translate(' + [w / 2, 0] + ')')
.style("visibility", bEnableTickMarks ? "visible" : "hidden")
.call(yAxisRadial);

svgsRadial
.selectAll(".outercircle")
.data([{ x: 0, y: 0 }])
.enter()
.append("circle")
.attr("cx", w / 2)
.attr("cy", h / 2)
.attr("r", h / 2 - 2)
.attr("class", "outercircle")
.style("fill", "none")
.style("stroke", "black")
.style("stroke-width", "1.5px");

svgsRadial
.selectAll(".innercircle")
.data([{ x: 0, y: 0 }])
.enter()
.append("circle")
.attr("cx", w / 2)
.attr("cy", h / 2)
.attr("r", innerRadius)
.attr("class", "innercircle")
.style("fill", "#E0E2DB")
.style("stroke", "black")
.style("stroke-width", "1.5px");
}
Insert cell
xAxisRadial = d3
.axisLeft()
.scale(xScaleRadial)
.tickFormat((d, i) => {
return d[4];
})
// .ticks(5)
Insert cell
yAxisRadial = d3
.axisLeft()
.scale(yScaleRadial)
.ticks(5)
Insert cell
xScaleRadial = d3
.scaleBand()
.domain(Array.from(dataset, d => d[0]))
.range([0, 2 * Math.PI])
.align(0)
Insert cell
yScaleRadial = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([outerRadius, padding]);
Insert cell
rScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([innerRadius, outerRadius-padding+innerRadius]);
Insert cell
xAxisLinear = d3
.axisBottom()
.scale(xScaleLinear)
// .ticks(5);
.tickFormat((d, i) => {
return dataset[i][2];
})
.tickValues(Array.from(dataset, d => d[0]))
Insert cell
yAxisLinear = d3
.axisLeft()
.scale(yScaleLinear)
.ticks(5)
Insert cell
xScaleLinear = d3
.scaleBand()
.domain(Array.from(dataset, x => x[0]))
.range([padding, w - padding * 2])
.padding(0.1)
Insert cell
yScaleLinear = d3
.scaleLinear()
.domain([
0,
d3.max(dataset, function(d) {
return d[1];
})
])
.range([h - padding, padding])
Insert cell
w = 800;
Insert cell
h = 400;
Insert cell
width = w - padding * 3 - paddingBtnBars
Insert cell
paddingBtnBars = (w - padding * 3 - numDataPoints * xScaleLinear.bandwidth()) /
(numDataPoints + 1)
Insert cell
height = h - padding
Insert cell
shiftDistance = {
let shiftDistance = [
{ x: -width, y: -height },
{ x: 0, y: -height },
{ x: width, y: -height },
{ x: -width, y: 0 }, //left
{ x: 0, y: 0 }, //centre
{ x: width, y: 0 }, //right
{ x: -width, y: height },
{ x: 0, y: height },
{ x: width, y: height }
];
return shiftDistance;
}
Insert cell
svgGroupLinear = svgsLinear
.selectAll(".clipPathGroup")
.data([{ x: 0, y: 0 }])
.enter()
.append("g")
Insert cell
renderLinear = {
svgsLinear.selectAll(".cleanLinearOnInit").remove();
//override previous axis transform translation
configuration.xAxisX = 0;

//Create Y axis
svgsLinear
.append("g")
.attr("class", "yAxisLinear" + " cleanLinearOnInit")
.style("visibility", bEnableTickMarks ? "visible" : "hidden")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxisLinear);

d3.select("g.yAxisLinear")
.select("path")
.remove();

//Create horizontal lines
let horizontallines = svgsLinear
.selectAll(".horizontalline")
.data(yScaleLinear.ticks(5))
.enter()
.append("line")
.attr("class", "horizontalline" + " cleanLinearOnInit")
.attr("x1", function(d) {
return padding;
})
.attr("y1", function(d) {
return yScaleLinear(d);
})
.attr("x2", function(d) {
return w - padding * 2;
})
.attr("y2", function(d) {
return yScaleLinear(d);
})
.style("fill", "none")
.style("stroke", "black")
.style("visibility", bEnableDashedLine ? "visible" : "hidden")
.style("stroke-dasharray", "2,2")
.style("stroke-width", ".5px");

//Create a clipPath
svgsLinear
.append("clipPath") //Make a new clipPath
.attr("id", "chart-area") //Assign an ID
.attr("class", ".clipPathRect" + " cleanLinearOnInit")
.append("rect")
.attr("x", padding) //Set rect's position and size…
.attr("y", padding)
.attr("width", w - padding * 3)
.attr("height", h);

svgsLinear
.append("rect") //Make a new rectangle
.attr("x", padding) //Set rect's position and size…
.attr("y", padding)
.style("stroke-width", "2")
.style("storke", "rgb(0,0,0)")
.style("fill", "rgb(255,255,255)")
.style("fill-opacity", 0)
.attr("width", w - padding * 3)
.attr("height", h)
.attr("class", "linearrect" + " cleanLinearOnInit")
.attr("draggable", true)
.call(
d3
.drag()
.on('start', () => {
configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;
})
.on("drag", () => {
transformGroup(
bHorizontalWrappingConfig,
bVerticalWrappingConfig,
bDiagonalWrappingConfig,
graphics,
d3.event.x - configuration.dragStartX,
d3.event.y - configuration.dragStartY,
configuration.internalDataset
);

configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;
})
.on("end", () => {
transformGroup(
bHorizontalWrappingConfig,
bVerticalWrappingConfig,
bDiagonalWrappingConfig,
graphics,
d3.event.x - configuration.dragStartX,
d3.event.y - configuration.dragStartY,
configuration.internalDataset
);

configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;
})
);

svgGroupLinear
.attr("fill", "lightsteelblue")
.style("mix-blend-mode", "multiply")
.attr("clip-path", "url(#chart-area)") //Add reference to clipPath
.call(
d3
.drag()
.on('start', () => {
configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;
})
.on("drag", () => {
transformGroup(
bHorizontalWrappingConfig,
bVerticalWrappingConfig,
bDiagonalWrappingConfig,
graphics,
d3.event.x - configuration.dragStartX,
d3.event.y - configuration.dragStartY,
configuration.internalDataset
);

configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;
})
.on("end", () => {
transformGroup(
bHorizontalWrappingConfig,
bVerticalWrappingConfig,
bDiagonalWrappingConfig,
graphics,
d3.event.x - configuration.dragStartX,
d3.event.y - configuration.dragStartY,
configuration.internalDataset
);

configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;
})
);

//Create X axis
for (let k = 0; k < 3; k++) {
svgGroupLinear
.append("g")
.attr("class", "xAxis" + k + " cleanLinearOnInit")
.attr(
"transform",
"translate(" + shiftDistance[3 + k].x + "," + (h - padding) + ")"
)
.call(xAxisLinear);

svgGroupLinear
.selectAll("g.xAxis" + k)
.select("path")
.remove();
}

//Create bars
for (let k = 0; k < 3; k++) {
svgGroupLinear
.selectAll(".bar" + k)
.data(configuration.internalDataset)
.enter()
.append("rect")
.attr("class", "bar" + k + " cleanLinearOnInit")
.attr("x", function(d) {
return d[2] + shiftDistance[3 + k].x;
})
.attr("y", function(d) {
return yScaleLinear(d[1]);
})
.attr("height", d => yScaleLinear(0) - yScaleLinear(d[1]))
.attr("width", xScaleLinear.bandwidth());
}
return svgGroupLinear;
}
Insert cell
updateView = (offsetX, offsetY) => {
for (let k = 0; k < 3; k++) {
let bars = svgGroupLinear
.selectAll(".bar" + k)
.data(configuration.internalDataset);
//update
bars
.attr("x", function(d) {
return d[2] + shiftDistance[3 + k].x;
})
.attr("y", function(d) {
return yScaleLinear(d[1]);
});

//enter
bars
.enter()
.append("rect")
.attr("class", "bar" + k)
.attr("x", function(d) {
return d[2] + shiftDistance[3 + k].x;
})
.attr("y", function(d) {
return yScaleLinear(d[1]);
})
.attr("height", d => yScaleLinear(0) - yScaleLinear(d[1]))
.attr("width", xScaleLinear.bandwidth());

//exit
bars.exit().remove();

//update
let xAxisView = svgsLinear.selectAll(".xAxis" + k);
xAxisView.attr(
"transform",
"translate(" +
(shiftDistance[3 + k].x + configuration.xAxisX) +
"," +
(h - padding) +
")"
);
}
}
Insert cell
transformGroup = (
bHorizontalWrappingConfig,
bVerticalWrappingConfig,
bDiagonalWrappingConfig,
graphics,
offsetX,
offsetY,
dataset
) => {
if (
bDiagonalWrappingConfig != null &&
bDiagonalWrappingConfig &&
graphics.indexOf("matrix") >= 0
) {
} else if (
bDiagonalWrappingConfig != null &&
bDiagonalWrappingConfig &&
graphics.indexOf("rockscissorpaper") >= 0
) {
} else {
if (bHorizontalWrappingConfig != null && bHorizontalWrappingConfig) {
for (let datapoint of dataset) {
datapoint[2] += offsetX;
}
configuration.xAxisX += offsetX;
}
if (bVerticalWrappingConfig != null && bVerticalWrappingConfig);
}

updateView(offsetX, offsetY);

if (configuration.xAxisX > width) {
for (let datapoint of dataset) {
datapoint[2] -= width;
}
configuration.xAxisX -= width;
} else if (configuration.xAxisX < 0) {
for (let datapoint of dataset) {
datapoint[2] += width;
}
configuration.xAxisX += width;
}
}
Insert cell
padding = 30
Insert cell
innerRadius = 30;
Insert cell
outerRadius = h / 2 - innerRadius;
Insert cell
barHeight = outerRadius - padding + innerRadius
Insert cell
labelRadius = barHeight * 1.025 + 5
Insert cell
configuration = {
bEnableDashedLine;
bEnableTickMarks;
let configuration = new Object({
dragStartX: 0,
dragStartY: 0,
xAxisX: 0,
xAxisY: 0,
angle: 0,
internalDataset: []
});
if (dataset != null && dataset.length > 0) {
//fill in the pixel position of data point
dataset.forEach(datapoint => {
configuration.internalDataset.push([
datapoint[0],
datapoint[1],
xScaleLinear(datapoint[0]),
0
]);
});
}
return configuration;
}
Insert cell
svgsRadial = d3.select(visualRadial)
Insert cell
svgsLinear = d3.select(visualLinear)
Insert cell
bHorizontalWrappingConfig = true
Insert cell
bVerticalWrappingConfig = false
Insert cell
bDiagonalWrappingConfig = false
Insert cell
graphics = "barchart"
Insert cell
Math.random = pseudorandomNumberGenerator;
Insert cell
{
//test pseudorandom number generator
numDataPoints;
// Create xmur3 state:
let seedfunct = xmur3("" + prngSeed);
// Output four 32-bit hashes to provide the seed for sfc32.
let rand = sfc32(seedfunct(), seedfunct(), seedfunct(), seedfunct());

// Output one 32-bit hash to provide the seed for mulberry32.
rand = mulberry32(seedfunct());
mutable pseudorandomNumberGenerator = rand;
}
Insert cell
mutable pseudorandomNumberGenerator = null;
Insert cell
xmur3 = (str) => {
for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++)
h = Math.imul(h ^ str.charCodeAt(i), 3432918353),
h = h << 13 | h >>> 19;
return function() {
h = Math.imul(h ^ h >>> 16, 2246822507);
h = Math.imul(h ^ h >>> 13, 3266489909);
return (h ^= h >>> 16) >>> 0;
}
}
Insert cell
sfc32 = (a, b, c, d) => {
return function() {
a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0;
var t = (a + b) | 0;
a = b ^ b >>> 9;
b = c + (c << 3) | 0;
c = (c << 21 | c >>> 11);
d = d + 1 | 0;
t = t + d | 0;
c = c + t | 0;
return (t >>> 0) / 4294967296;
}
}
Insert cell
mulberry32 = (a) => {
return function() {
var t = a += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}
Insert cell
d3 = require("d3@5")
Insert cell
import {radio} from "@jashkenas/inputs"
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