network = {
nodes.forEach(d => {
d.lat = locations[d.id].lat;
d.lon = locations[d.id].lon;
});
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);
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];
let arcMultiplier = 1.5;
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];
}