Public
Edited
May 2
Insert cell
Insert cell
(async () => { // Wrap the entire code in an async IIFE

// 1. Data Input
const data = await FileAttachment("keeping-track-of-the_big_picture.csv").csv({ typed: true });

// 2. Icon mapping
const iconMap = {
"Washington Post cartoonist resigns over editorial interference": "◆",
"L.A. Times Oped Interference": "◆",
"Here's the ad the Washington Post wouldn't run": "◆",
"Washington Post columnist resigns over editorial interference": "◆",
"‘60 Minutes’ Executive Producer resigns over editorial interference": "◆",
"CNN anchor Jim Acosta resigns": "◆",
"MSNBC host Joy Reid fired": "◆",
"LA Times editorial board resignations": "◆",
"Washington Post editorial constriction": "◆",
"Trump publicly attacks reporters": "●",
"Trump v. Facebook": "●",
"Trump v. ABC": "●",
"Trump v. CBS": "●",
"Trump v. Des Moines and Tribune": "●",
"Meta agrees to pay Trump $25 million to settle lawsuit over Facebook and Instagram suspensions : NPR": "◘",
"Paramount in Settlement Talks With Trump Over ‘60 Minutes’ Lawsuit - The New York Times": "◘",
"Trump inauguration: Zuckerberg, Bezos and Musk seated in front of cabinet picks": "◘",
"Media moguls praised in Oval Office": "◘",
"Trump Administration to Remove 4 Major News Outlets From Pentagon Office Space - The New York Times": "▲",
"Trump administration throws more Pentagon reporters out of workspaces": "▲",
"AP statement on Oval Office access ": "▲",
"The White House bans the AP indefinitely over the use of ‘Gulf of Mexico’ | CNN Business": "▲",
"Despite a court order, White House bars AP from Oval Office event": "▲",
"AP reporter allowed into White House event": "▲",
"White House Ends a Regular Reporting Slot for Independent Newswires - The New York Times": "▲",
"FCC investigates ABC, CBS, NBC": "■",
"F.C.C. investigates NPR PBS sponsorships": "■",
"Congress calls NPR CEO to testify": "■",
"FCC DEI investigation of Comcast, NBC ": "■",
"Trump orders key government agency to cancel all media contracts": "■",
"State department cancels media subscriptions": "■",
"White House seeks to cut funding to PBS, NPR": "■",
"Trump cuts funding for VOA RFERL": "■",
"Court halts Trump's shutdown of VOA": "■",
"Judge Orders Trump Officials to Disburse Funding for Radio Free Europe - The New York Times": "■",
"Attorney General Lifts Ban on Subpoenaing Reporters’ Notes in Leak Investigations - The New York Times": "■"
};

// 3. Chart definition
const chart = () => {
// Color mapping for the outer circles based on "Trend"
const trendColor = d3.scaleOrdinal()
.domain([
"Growing self-censorship",
"Harm to independent outlets",
"Erosion of public access",
"Media align with the President, gain benefits",
"Hostility of media environment"
])
.range([
"#730362",
"#CA2816",
"#F56200",
"#1A2E7F",
"#28124A"
]);

// Color mapping for the inner icons based on "Type"
const typeColor = (type) => {
if (type === "good") return "#1CD81F";
if (type === "bad") return "#FF312E";
return "#ccc"; // Default color if type is not "good" or "bad"
};

const root = d3.hierarchy({
name: "root",
children: Array.from(d3.group(data, d => d.Trend), ([name, children]) => ({
name: name,
children: children.map(d => ({ ...d, name: d.Items, value: 1 })), // Give each item a value of 1 for packing
}))
}).sum(d => d.value);

const packed = d3.pack()
.size([width, height])
.padding(3)(root);

const svg = d3.select(DOM.svg(width, height))
.style("width", "100%")
.style("height", "auto")
.style("font", "10px sans-serif")
.style("overflow", "visible");

const node = svg.append("g")
.selectAll("g")
.data(packed.descendants().slice(1))
.join("g")
.attr("transform", d => `translate(${d.x},${d.y})`);

node.append("circle")
.attr("r", d => d.r)
.style("fill", d => d.depth === 1 ? trendColor(d.data.name) : "#fff") // Color outer circles by trend
.style("stroke", d => d.depth === 1 ? trendColor(d.data.name) : "#ccc")
.style("stroke-width", 1.5);

const leaf = node.filter(d => d.depth === 2)
.append("a")
.attr("href", d => d.data.URL) // Use .attr("href", ...)
.attr("target", "_blank");

leaf.append("text")
.attr("dy", "0.3em")
.style("fill", d => typeColor(d.data.Type))
.style("font-size", d => d.r * 1.2) // Adjust icon size based on circle size
.style("text-anchor", "middle")
.text(d => iconMap[d.data.Items] || "?"); // Use mapped icon or a question mark if not found

leaf.append("title")
.text(d => d.data.Items);

return svg.node();
};

const width = 960;
const height = 960;

return chart(); // Call the chart function to render it

})()

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