Public
Edited
Nov 14, 2023
Insert cell
md`# Graph Layout


`
Insert cell
viewof layout = Inputs.radio(["force", "random", "radial"], {value: "force"})
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

var link = svg.selectAll(".link"),
node = svg.selectAll(".node");

// Init graph data structure
var graph = {nodes:[], links:[]};

// Generate random nodes
var nb_nodes = 100, nb_cat = 10;

graph.nodes = d3.range(nb_nodes).map(function() {
return {cat: Math.floor(nb_cat * Math.random())};
})

graph.nodes.map(function(d, i) {

graph.nodes.map(function(e, j) {
if(Math.random()>.99 && i!=j)
graph.links.push({"source": i, "target": j})

});
});

const simulation = d3.forceSimulation(graph.nodes)
.force("link", d3.forceLink(graph.links).id(d => d.index))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));

function graph_update(delay) {

link.transition().duration(delay)
.attr("x1", function(d) { return d.target.x; })
.attr("y1", function(d) { return d.target.y; })
.attr("x2", function(d) { return d.source.x; })
.attr("y2", function(d) { return d.source.y; });

node.transition().duration(delay)
.attr("transform", function(d) {
return "translate("+d.x+","+d.y+")";
});

}

function random_layout() {

simulation.stop();

graph.nodes.forEach(function(d, i) {
d.x = width/4 + 2*width*Math.random()/4;
d.y = height/4 + 2*height*Math.random()/4;
})

graph_update(0);
}
function force_layout() {

simulation.on("tick", function() {
graph_update(0)
});

}
function line_layout() {

simulation.stop();

graph.nodes.forEach(function(d, i) {
d.y = height/3 + 100;
d.x = d.x + 200
})

graph_update(500);
}

function line_cat_layout() {

simulation.stop();

graph.nodes.forEach(function(d, i) {
d.y = height/3 + d.cat * 20;
d.x = d.x + 200
})

graph_update(500);
}

function radial_layout() {

simulation.stop();

var r = height/3;

var arc = d3.arc()
.outerRadius(r);

var pie = d3.pie()
.value(function(d, i) { return 1; }); // equal share for each point

graph.nodes = pie(graph.nodes).map(function(d, i) {
d.innerRadius = 0;
d.outerRadius = r;
d.data.x = arc.centroid(d)[0]+height/3;
d.data.y = arc.centroid(d)[1]+width/3;
d.data.endAngle = d.endAngle;
d.data.startAngle = d.startAngle;
return d.data;
})

graph_update(500);
}
link = link.data(graph.links)
.enter().append("line")
.attr("class", "link")

node = node.data(graph.nodes)
.enter().append("g").attr("class", "node")
.append("circle")
.attr("r", 5)
if(layout == "random")
random_layout()
else if(layout == "line")
line_layout();
else if(layout == "line_cat")
line_cat_layout();
else if(layout == "radial")
radial_layout();
else
force_layout();
return svg.node();
}
Insert cell
html`<style>

.link {
stroke: gray;
stroke-width: 1.5px;
}

.node {
fill: #66CC66;
stroke: #000;
stroke-width: 1px;
}

.node:hover {
fill: red;
}

</style>`
Insert cell
height = 500
Insert cell
md`Treemap`
Insert cell
d3 = require("d3")
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