Public
Edited
May 6, 2023
3 forks
1 star
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.

return [nodeDots,linkLines];
}
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

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