Published
Edited
Apr 24, 2021
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@^5.8")
Insert cell
Insert cell
graphData = FileAttachment("loveactually@2.json").json()
Insert cell
//use map() function to extract the names in the json file.
allNodesNames = graphData.nodes.map(function(d){return d.name});
Insert cell
graphData["nodes"]
Insert cell
graphData["links"]
Insert cell
allNodesText = graphData.nodes.map(function(d){return d.actor});
Insert cell
// use the idToNode function to map node ID with a pointer

idToNode =
{
let dict = {};
graphData.nodes.forEach(function(n) {
dict[n.id] = n;
});
return dict;
}
Insert cell
// cuse the idToTargetNodes to create a dictionary with node ID and link data

idToTargetNodes =
{
let dict = {};
graphData.nodes.forEach(function (n) {
dict[n.id] = [];
graphData.links.forEach(function (l) {
if (l.source === n.id) {
dict[n.id].push(l.target);
}
});
});
return dict;
}
Insert cell
Insert cell
margin = ({top: 20, bottom: 20, left: 30, right: 30});
Insert cell
height = 1000 - margin.top - margin.bottom;
Insert cell
width = 600
Insert cell
Insert cell
// define yScale
yScale = d3.scalePoint()
.domain(allNodesNames)
.range([0,height])
Insert cell
relationship = d3.set(graphData["links"], d => d.relationship);
Insert cell
// use the d3 color scheme "dark2" to color code different types of relationships
color = {return d3.scaleOrdinal(d3.schemeDark2).domain(relationship.values());
}
Insert cell
gender = d3.set(graphData["nodes"], d=> d.gender);
Insert cell
// I also wanted to show the gender of the character, so I'm setting two color scheme to show the difference.
gender_color = {return d3.scaleOrdinal(d3.schemePastel1).domain(gender.values());
}
Insert cell
gender_color2 = {return d3.scaleOrdinal(d3.schemeSet1).domain(gender.values());
}
Insert cell
Insert cell
Insert cell
// adapted from https://www.d3-graph-gallery.com/graph/arc_highlight.html

loveActuallyArcs2 = {
const radius = 8
const container = d3.select(DOM.svg(width+margin.left+margin.right,
height+margin.top+margin.bottom))
const arcGroup = container
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// create the nodes
const nodes = arcGroup.selectAll("nodes")
.data(graphData.nodes)
.enter().append("circle")
.attr("cx", margin.left+300)
.attr("cy", d => yScale(d.name))
.attr("r", radius)
.attr("fill", d=> gender_color(d.gender))
.attr("id", d => d.id)
// create the node labels
const nodeLabels = arcGroup.selectAll("nodeLabels")
.data(graphData.nodes)
.enter().append("text")
.attr("x", margin.left+280)
.attr("y", d => yScale(d.name)+5)
.attr("fill", "darkgrey")
.style("text-anchor", "end")
.text(d => d.name)
// adding the actors' name
const actor = arcGroup.selectAll("actor")
.data(graphData.nodes)
.enter().append("text")
.attr("x", margin.left +20)
.attr("y", d => yScale(d.name)+5)
.attr("fill", "white")
.style("text-anchor", "start")
.text(d=> d.actor)
// This code builds up the SVG path element; see nodesAndArcs for details
function buildArc(d) {
let start = yScale(idToNode[d.source].name);
let end = yScale(idToNode[d.target].name);
const arcPath = ['M', margin.left+300, start, 'A', Math.abs(start - end)/2, ',', Math.abs(start-end)/2, 0,0,",",
start < end ? 1: 0, margin.left+300, end].join(' ');
return arcPath;
}
// create the arcs
const arcs = arcGroup.selectAll("arcs")
.data(graphData.links)
.enter().append("path")
.attr("d", d => buildArc(d))
.style("fill", "none")
.attr("stroke", d => color(d.relationship));
// mouseover animations
nodes.on('mouseover', function(d) {
// Highlight selected node
d3.select(this).style("fill", d=>gender_color2(d.gender));
// Highlight arcs
arcs
.style('stroke', function (arcd) {
return arcd.source === d.id || arcd.target === d.id ? d=> color(d.relationship): d=> color(d.relationship);})
.style('stroke-width', function (arcd) {
return arcd.source === d.id || arcd.target === d.id ? 4 : 1;})
// Show actor names
actor
.style('fill', function (actord) {
return actord.name === d.name ? 'steelblue' : 'white' ;});
// Highlight node labels
nodeLabels
.style("fill", function (nodeLabeld){
return nodeLabeld.id == d.id ? 'black' : 'lightgrey';})
.style('font-weight', function (nodeLabeld) {
return nodeLabeld.id == d.id ? 'bold' : 'normal';})
});
// remove highlighting when user mouse moves out of node by restoring default colors and thickness
nodes.on('mouseout', function (d) {
nodes.style("fill", d=> gender_color(d.gender));
arcs.style('stroke', d=>color(d.relationship));
arcs.style('stroke-width', 1);
actor.style('fill','white');
});
return container.node();
}
Insert cell
import {swatches} from "@d3/color-legend"
Insert cell
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