Public
Edited
Nov 21, 2023
1 star
Insert cell
Insert cell
Insert cell
Insert cell
nodeGraph={
const div = html`<div style='max-width: 100%; overflow-x: auto; padding: 0px; margin: 0px;'></div>`;
const margin = ({top: 100, right: 100, bottom: 100, left: 0});
const width = 1200
// - margin.left - margin.right
const height = 1200// - margin.top - margin.bottom

//create dummy data
const dataset = graphData
// Initialize connections property.
dataset.nodes.forEach(node => {
node.connections = 0;
});

// console.log({nodes: dataset.nodes})

// Adding counting of connections
dataset.links.forEach(link => {
const sourceNode = dataset.nodes.find(node => node.id === link.source.id)
if (sourceNode) {
sourceNode.connections += 1
}
const targetNode = dataset.nodes.find(node => {

return node.id === link.target.id
})
console.log(targetNode)
if (targetNode) {
targetNode.connections += 1
}
})

let scaleNodeSize = d3.scaleSqrt()
.domain([1, d3.max(dataset.nodes, d => d.connections)])
.range([30, 60]);

//create a simulation for an array of nodes, and compose the desired forces.
const simulation = d3.forceSimulation()
// .force("link", d3.forceLink().id(d => d.id).distance(100).strength(0)) // Defina a força de link como zero
// .force("charge", d3.forceManyBody().strength(0)) // Defina a força de carga como zero
// .force("center", d3.forceCenter(width / 2, height / 2))
.force("link", d3.forceLink() // This force provides links between nodes
.id(d => d.id)
.distance(100)
// .strength(1)
)
.force("charge", d3.forceManyBody().strength(-50)) // This adds repulsion (if it's negative) between nodes.
.force("center", d3.forceCenter(width / 2, height / 2)) // This force attracts nodes to the center of the svg area
.force("collision", d3.forceCollide().radius(80)) // Defina o raio de colisão adequado
// .force("collision", d3.forceCollide().radius(d => scaleNodeSize(d.connections) + 20)) // Use a escala do tamanho do nó


// simulation.force("link").strength(10); // Ajuste o valor de linkStrength conforme necessário

const svg = d3.select(div)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);



// Initialize the links
const link = svg.append("g")
.attr("class", "links")
.attr("d", function(d) {
console.log({d})
if(!d?.target) {
return;
}
const dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return `M${d.source.x},${d.source.y}A${dr},${dr} 0 0,1 ${d.target.x},${d.target.y}`;
})
.selectAll("line")
.data(dataset.links)
.enter()
.append("line");


// Initialize the nodes
const node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(dataset.nodes)
.enter()
.append("circle")
.attr("r", d => {
const connections = d.connections
const size = scaleNodeSize(connections)
return size
})
.attr("class", (d) => d.type)
.call(d3.drag() //sets the event listener for the specified typenames and returns the drag behavior.
.on("start", (event, d) => {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
})
// .on("drag", (event, d) => {
// d.fx = event.x;
// d.fy = event.y;
// })
.on("end", (event, d) => {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
})
// .on("start", dragstarted) //after a new pointer becomes active (on mousedown or touchstart).
.on("drag", dragged) //after an active pointer moves (on mousemove or touchmove).
// .on("end", dragended) //after an active pointer becomes inactive (on mouseup, touchend or touchcancel).
);

// const drag = d3.drag()
// .on("start", (event, d) => {
// if (!d3.event.active) simulation.alphaTarget(0.3).restart(); // Reinicie a simulação
// d.fx = d.x; // Fixe a posição do nó na posição atual
// d.fy = d.y;
// })
// .on("drag", (event, d) => {
// d.fx = event.x; // Atualize a posição fixa do nó para seguir o cursor do mouse
// d.fy = event.y;
// })
// .on("end", (event, d) => {
// if (!d3.event.active) simulation.alphaTarget(0); // Pare a simulação
// d.fx = null; // Libere a posição fixa do nó
// d.fy = null;
// });

// // Aplique a função de arrastar aos nós
// node.call(drag);
// Variável de controle para ativar ou desativar as forças
// let forcesActive = false;

// Adicione os nós e arestas ao gráfico de grafo

// // Adicione um event listener para alternar entre ativar e desativar as forças
// d3.select(div).on("keydown", () => {
// if (d3.event.key === "a") {
// // Ative as forças
// simulation.force("link").strength(0.1); // Ajuste o valor da força de link conforme necessário
// simulation.force("charge").strength(-50); // Ajuste o valor da força de carga conforme necessário
// forcesActive = true;
// } else if (d3.event.key === "d") {
// // Desative as forças
// simulation.force("link").strength(0); // Desligue a força de link
// simulation.force("charge").strength(0); // Desligue a força de carga
// forcesActive = false;
// }
// });

// Desabilite as forças de simulação quando um nó é clicado
// node.on("start", (event, d) => {
// simulation.alphaTarget(0.1).restart(); // Diminua a taxa de aquecimento da simulação
// d.fx = d.x; // Fixe a posição do nó na posição atual
// d.fy = d.y;
// });
// // Atualize a posição do nó quando o mouse é movido
// node.on("drag", (event, d) => {
// d.fx = devent.x; // Atualize a posição fixa do nó para seguir o cursor do mouse
// d.fy = event.y;
// });
// // Reabilite as forças de simulação quando o mouse é solto
// node.on("end", (event, d) => {
// simulation.alphaTarget(0); // Restaure a taxa de aquecimento da simulação
// d.fx = null; // Libere a posição fixa do nó
// d.fy = null;
// });

// Text to nodes
const text = svg.append("g")
.attr("class", "nodes-text")
.attr("paint-order", "stroke")
.selectAll("text")
.data(dataset.nodes)
.enter()
.append("text")
.text(d => {
const name = d.id.match(/^(.*)\@/)
const text = name ? name[1] : d.id
const cased = text.charAt(0).toUpperCase() + text.slice(1)
return cased//.substring(0, 20)
})
.attr("text-anchor", "middle")

//Listen for tick events to render the nodes as they update in your Canvas or SVG.
simulation.nodes(dataset.nodes)//sets the simulation’s nodes to the specified array of objects, initializing their positions and velocities, and then re-initializes any bound forces;
.on("tick", ticked);//use simulation.on to listen for tick events as the simulation runs.
// After this, Each node must be an object. The following properties are assigned by the simulation:
// index - the node’s zero-based index into nodes
// x - the node’s current x-position
// y - the node’s current y-position
// vx - the node’s current x-velocity
// vy - the node’s current y-velocity

simulation.force("link").links(dataset.links);//sets the array of links associated with this force, recomputes the distance and strength parameters for each link, and returns this force.
// After this, Each link is an object with the following properties:
// source - the link’s source node;
// target - the link’s target node;
// index - the zero-based index into links, assigned by this method
// simulation.alpha(0);


// This function is run at each iteration of the force algorithm, updating the nodes position
// (the nodes data array is directly manipulated).
function ticked() {
link.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

node.attr("cx", d => d.x)
.attr("cy", d => d.y);

text.attr("x", d => d.x) //position of the lower left point of the text
.attr("y", d => d.y); //position of the lower left point of the text
}

//When the drag gesture starts, the targeted node is fixed to the pointer
//The simulation is temporarily “heated” during interaction by setting the target alpha to a non-zero value.
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();//sets the current target alpha to the specified number in the range [0,1].
d.fy = d.y; //fx - the node’s fixed x-position. Original is null.
d.fx = d.x; //fy - the node’s fixed y-position. Original is null.
}

//When the drag gesture starts, the targeted node is fixed to the pointer
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}

//the targeted node is released when the gesture ends
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return div
}
Insert cell
graphData = csvFile.text().then(text => {
const parser = d3.dsvFormat(";")
// parser.parse(text).map(row => {
// console.log(row)
// })

const rows = parser.parse(text)
.map(row => ({
type: row["Type of CBP"].trim(),
micro: row["Microorganisms"].trim(),
biomass: row["Biomass"].trim(),
product: row["Primary native products"].trim(),
group: "Grupo " + row["Grupos co-culture"].trim()
}))
.filter(row => row.type === 'Co-culture')
// .filter(row => row.type === 'Single')

const nodes = rows
.reduce((prev, row) => {
// const nodesFromRow = new Set(prev)
// nodesFromRow.add(row.biomass)
// nodesFromRow.add(row.micro)
// nodesFromRow.add(row.product)
// nodesFromRow.add(row.type)

const nodesToAdd = [
{id: row.biomass, type: 'biomass', size: 30},
{id: row.micro, type: 'micro', size: 35},
{id: row.product, type: 'product', size: 40},
{id: row.type, type: 'type', size: 50},
{id: row.group, type: 'group', size: 25}
]

return prev.concat(nodesToAdd)
}, [])
.reduce((prev, node) => {
if (prev.find(n => n.id === node.id)) {
return prev
}
return prev.concat(node)
}, [])
// .concat({id: 'CBP', size: 60})
// .map(node => ({id: node, size: 40}))

// console.log(nodes)

const links = rows
.reduce((prev, row) => {

if (row.type === 'Co-culture') {
const linkGroup = {source: row.type, target: row.group}
const linkA = {source: row.group, target: row.micro}
const linkB = {source: row.micro, target: row.biomass}
const linkC = {source: row.biomass, target: row.product}
return prev.concat([linkGroup, linkA, linkB, linkC])
}
const linkA = {source: row.type, target: row.micro}
const linkB = {source: row.micro, target: row.biomass}
const linkC = {source: row.biomass, target: row.product}

// const linkBase = {source: 'CBP', target: row.type}
// const linkA = {source: row.product, target: row.biomass}
// const linkB = {source: row.biomass, target: row.micro}
// const linkC = {source: row.micro, target: row.type}

return prev.concat([linkA, linkB, linkC])
}, [])
.reverse()
.concat([
// {source: "Single", target: "CBP"},
// {source: "Co-culture", target: "CBP"},
])

// console.log({nodes, links})

return {nodes, links}
})
Insert cell
Insert cell
d3 = require('d3@5')
Insert cell
import {ForceGraph} from "@d3/force-directed-graph"
Insert cell
import {fileInput} from "@mbostock/file-input-with-initial-value"
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