Public
Edited
Feb 8, 2023
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
function generateData() {
// This function is used to iterate over all the rows of the raw data and we make an array of objects to count the number of bird strikes across each flight phase for every state.
let obj = {};
birds.forEach((b) => {
if (
b.flight_phase !== "" &&
b.origin !== "N/A" &&
state_map.has(b.origin)
) {
let origin = state_map.get(b.origin);
obj = {
...obj,
[origin]: {
...obj[origin],
[b.flight_phase]: {
value:
obj[origin] !== undefined &&
obj[origin][b.flight_phase] !== undefined &&
obj[origin][b.flight_phase].value !== undefined
? obj[origin][b.flight_phase].value + 1
: 1
}
}
};
}
});

return obj;
}
Insert cell
Insert cell
function stateAbbr() {
// To map the US state name with their abbreviation and to filter out canadian states from the data.
let map = new Map();
let states = [
["Arizona", "AZ"],
["Alabama", "AL"],
["Alaska", "AK"],
["Arkansas", "AR"],
["California", "CA"],
["Colorado", "CO"],
["Connecticut", "CT"],
["Delaware", "DE"],
["Florida", "FL"],
["Georgia", "GA"],
["Hawaii", "HI"],
["Idaho", "ID"],
["Illinois", "IL"],
["Indiana", "IN"],
["Iowa", "IA"],
["Kansas", "KS"],
["Kentucky", "KY"],
["Louisiana", "LA"],
["Maine", "ME"],
["Maryland", "MD"],
["Massachusetts", "MA"],
["Michigan", "MI"],
["Minnesota", "MN"],
["Mississippi", "MS"],
["Missouri", "MO"],
["Montana", "MT"],
["Nebraska", "NE"],
["Nevada", "NV"],
["New Hampshire", "NH"],
["New Jersey", "NJ"],
["New Mexico", "NM"],
["New York", "NY"],
["North Carolina", "NC"],
["North Dakota", "ND"],
["Ohio", "OH"],
["Oklahoma", "OK"],
["Oregon", "OR"],
["Pennsylvania", "PA"],
["Rhode Island", "RI"],
["South Carolina", "SC"],
["South Dakota", "SD"],
["Tennessee", "TN"],
["Texas", "TX"],
["Utah", "UT"],
["Vermont", "VT"],
["Virginia", "VA"],
["Washington", "WA"],
["West Virginia", "WV"],
["Wisconsin", "WI"],
["Wyoming", "WY"]
];

for (let i = 0; i < states.length; i++) {
map.set(states[i][0], states[i][1]);
}

return map;
}
Insert cell
state_map = stateAbbr()
Insert cell
Insert cell
function finalData() {
// To generate the data in the format required by the viz.
let entries = Object.entries(generateData());

let data = entries.map(([key, val]) => {
let phases = Object.entries(val);
let obj = {};
// let total = 0;

obj = {
State: key
};

for (const [k, v] of Object.entries(val)) {
// total += v.value;
obj = {
...obj,
[k]: v.value
};
}

// return {
// ...obj,
// total
// };
return obj;
});

return data;
}
Insert cell
old_data = finalData()
Insert cell
Insert cell
total_data = {
return old_data.map((d) => {
console.log(d);
let total = 0;
for (let i = 0; i < columns.length; i++) {
if (d[columns[i]]) {
total += d[columns[i]];
} else {
d[columns[i]] = 0;
}
}
d.total = total;
return d;
});
}
Insert cell
Insert cell
data = total_data.sort((a, b) => {
let x = a.total;
let y = b.total;

if (x > y) return -1;
if (x < y) return 1;
})
Insert cell
arc = d3.arc()
.innerRadius(d => y(d[0]))
.outerRadius(d => y(d[1]))
.startAngle(d => x(d.data.State))
.endAngle(d => x(d.data.State) + x.bandwidth())
.padAngle(0.01)
.padRadius(innerRadius)
Insert cell
x = d3.scaleBand()
.domain(data.map(d => d.State))
.range([0, 2 * Math.PI])
.align(0)
Insert cell
// This scale maintains area proportionality of radial bars
y = d3.scaleRadial()
.domain([0, d3.max(data, d => d.total)])
.range([innerRadius, outerRadius])
Insert cell
z = d3
.scaleOrdinal()
.domain(columns.slice(1))
.range([
"#98abc5",
"#8a89a6",
"#7b6888",
"#6b486b",
"#a05d56",
"#d0743c",
"#ff8c00"
])
Insert cell
xAxis = (g) =>
g.attr("text-anchor", "middle").call((g) =>
g
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr(
"transform",
(d) => `
rotate(${((x(d.State) + x.bandwidth() / 2) * 180) / Math.PI - 90})
translate(${innerRadius},0)
`
)
.call((g) => g.append("line").attr("x2", -5).attr("stroke", "#000"))
.call((g) =>
g
.append("text")
.attr("transform", (d) =>
(x(d.State) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) <
Math.PI
? "rotate(90) translate(0,16)"
: "rotate(-90) translate(0,-9)"
)
.text((d) => d.State)
)
)
Insert cell
yAxis = (g) =>
g
.attr("text-anchor", "end")
.call((g) =>
g
.append("text")
.attr("x", -6)
.attr("y", (d) => -y(y.ticks(10).pop()))
.attr("dy", "-1em")
.text("Number of Bird Strikes Incident reported")
)
.call((g) =>
g
.selectAll("g")
.data(y.ticks(10).slice(1))
.join("g")
.attr("fill", "none")
.call((g) =>
g
.append("circle")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.5)
.attr("r", y)
)
.call((g) =>
g
.append("text")
.attr("x", -6)
.attr("y", (d) => -y(d))
.attr("dy", "0.35em")
.attr("stroke", "#fff")
.attr("stroke-width", 5)
.text(y.tickFormat(10, "s"))
.clone(true)
.attr("fill", "#000")
.attr("stroke", "none")
)
)
Insert cell
legend = (g) =>
g
.append("g")
.selectAll("g")
.data(columns.reverse())
.join("g")
.attr(
"transform",
(d, i) => `translate(-40,${(i - (columns.length - 1) / 2) * 20})`
)
.call((g) =>
g.append("rect").attr("width", 18).attr("height", 18).attr("fill", z)
)
.call((g) =>
g
.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", "0.35em")
.text((d) => d)
)
Insert cell
width = 975
Insert cell
height = width
Insert cell
innerRadius = 180
Insert cell
outerRadius = Math.min(width, height) * 0.67
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