Public
Edited
May 9, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
links
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
import {nodesFromLinks} with {nodes} from "@emfielduva/dvlib_layout"
Insert cell
nodes = nodesFromLinks(links)
Insert cell
Insert cell
Insert cell
Insert cell
network = {
// first add the locations to the nodes list, from the locations lookup table. This assumes they are not already in there.
// if the links file already has locations, this step is not needed.
nodes.forEach(d => {
d.lat = locations[d.id].lat;
d.lon = locations[d.id].lon;
});

// build the dots. Here they are mapped onto the us map using the projection.
// if this were not on a map, you would not use the projection and only tell it the x and y field from the data to use.
let nodeDots = mapViz.select("#nodes").selectAll(".node").data(nodes).join("circle")
.attr("r",5)
.attr("cx", d=>projection([d.lon,d.lat])[0])
.attr("cy", d=>projection([d.lon,d.lat])[1])
.classed("node",true);

// and now the link lines or curves between source and destination.
let linkLines = mapViz.select("#nodes").selectAll(".link").data(links).join("path")
.classed("link",true)
.attr("d", d=> {
let sourceNode = nodes.find(n => n.id == d.source);
let targetNode = nodes.find(n => n.id == d.target);
let sourceLoc = projection([sourceNode.lon,sourceNode.lat]);
let targetLoc = projection([targetNode.lon,targetNode.lat]);
let sourceXY = sourceLoc[0] + " " + sourceLoc[1];
let targetXY = targetLoc[0] + " " + targetLoc[1];

// There are two options for the links:
// straight lines between nodes.
//let pathString = "M" + sourceXY + " L" + targetXY; // Uncomment this line to use this

// or arcs between nodes
let arcMultiplier = 1.5; // this scale factor flattens or rounds the arc, and could calculate from a data value
var dx = sourceLoc[1] - sourceLoc[0],
dy = targetLoc[1] - targetLoc[0],
dr = Math.sqrt(dx * dx + dy * dy) * arcMultiplier;
let pathString = "M" + sourceLoc[0] + "," + sourceLoc[1] + "A" + dr + "," + dr + " 0 0,1 " + targetLoc[0] + "," + targetLoc[1];
return pathString;
})
.attr("marker-end", "url(#arrow)") // this arrow can be included or commented out.
.style("stroke-width", d => Math.random()*3 + "px") // instead of random in the example, this could be from a data field.
.style("stroke", "#aa5") // here one color, but can also be be based on data.
.attr("id", d=> "link_" + d.source + "-" +d.target);
let nodeText = mapViz.select("#nodes").selectAll(".nodeText").data(nodes).join("text")
.classed("nodeText",true)
.text(d => d["id"])
.attr("dx", 8)
.attr("transform", d => {
let loc = projection([d.lon,d.lat])
return "translate(" + loc[0] + "," + loc[1] +")";
})
let linkText = mapViz.select("#nodes").selectAll(".linkText").data(links).join("text")
.classed("linkText",true)
.attr("dy", -5)
.append("textPath")
.text(d => d["type"])
.attr("xlink:href", d => "#link_" + d.source + "-" +d.target) // attach it to the id of the curve
.style("text-anchor", "middle")
.attr("startOffset", "50%");
return [nodeDots,linkLines,nodeText,linkText];
}
Insert cell
Insert cell
projection = d3.geoAlbers()
Insert cell
import {getMapData, drawMapLayer} with {projection} from "@emfielduva/dvlib_maps"
Insert cell
usstates = getMapData("us_states20m")
Insert cell
mapViz = d3.select(svg)
Insert cell
mapLayers = {
let mapLayers = [];
mapLayers["usstates"] = drawMapLayer(mapViz,"usstates",usstates.features,"properties.NAME");
return mapLayers;
}
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