myChart = {
const div = html`<div style='max-width: 2000px; overflow-x: auto; padding: 0px; margin: 0px;'></div>`;
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})`);
const subgraphWidth = width * 2 / 8;
const subgraphHeight = height * 1 / 5;
const subgraph = svg.append("g")
.attr("id", "subgraph")
.attr("transform", `translate(${width - subgraphWidth - 20}, 0)`);
subgraph.append("text")
.style("font-size", "16px")
svg.append('defs').append('marker')
.attr("id", 'arrowhead')
.attr('viewBox', '-0 -5 10 10')
.attr('refX', 24)
.attr('refY', 0)
.attr('orient', 'auto')
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('xoverflow', 'visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke', 'none');
svg.append("text")
.text("Stakeholders")
.attr("text-anchor", "middle")
.attr("x", width / 2)
.style("font-size", "20px")
//create some data
const dataset = createData(stakeholders_links)
console.log("dataset is ...", dataset);
// Initialize the links
const link = svg.selectAll(".links")
.data(dataset.links)
.enter()
.append("line")
.attr("class", "nodeLinks")
.attr("stroke", "#999")
// .attr("stroke-width", "2px")
.attr("stroke-width", d => d.weight)
.style("opacity", 0.8)
.attr("id", d => "line" + d.source + d.target)
//.attr('marker-end','url(#arrowhead)') //The marker-end attribute defines the arrowhead or polymarker that will be drawn at the final vertex of the given shape.
//The <title> element provides an accessible, short-text description of any SVG container element or graphics element.
//Text in a <title> element is not rendered as part of the graphic, but browsers usually display it as a tooltip.
link.append("title")
.text("type");
const edgepaths = svg.selectAll(".edgepath") //make path go along with the link provide position for link labels
.data(dataset.links)
.enter()
.append('path')
.attr('class', 'edgepath')
.attr('fill-opacity', 0)
.attr('stroke-opacity', 0)
.attr('id', function (d, i) { return 'edgepath' + i })
.style("pointer-events", "none");
const edgelabels = svg.selectAll(".edgelabel")
.data(dataset.links)
.enter()
.append('text')
.style("pointer-events", "none")
.attr('class', 'edgelabel')
.attr('id', function (d, i) { return 'edgelabel' + i })
.attr('font-size', 10)
.attr('fill', '#aaa');
// edgelabels.append('textPath') //To render text along the shape of a <path>, enclose the text in a <textPath> element that has an href attribute with a reference to the <path> element.
// .attr('xlink:href', function (d, i) {return '#edgepath' + i})
// .style("text-anchor", "middle")
// .style("pointer-events", "none")
// .attr("startOffset", "50%")
// .text(d => d.type);
// Initialize the nodes
const node = svg.selectAll(".nodes")
.data(dataset.nodes)
.enter()
.append("g")
.attr("class", "nodes")
node.call(d3.drag() //sets the event listener for the specified typenames and returns the drag behavior.
.on("start", dragstarted) //start - after a new pointer becomes active (on mousedown or touchstart).
.on("drag", dragged) //drag - after an active pointer moves (on mousemove or touchmove).
);
node.append("circle")
.attr("r", d => 25)//+ d.runtime/20 )
.attr("id", d => "circle" + d.id)
.classed("nodeCircle", true)
.style("stroke", "grey")
.style("stroke-opacity", 0.3)
.style("stroke-width", 5)
.style("fill", d => colorScale(d.group))
node.append("title")
.text(d => d.id + ": " + d.name + " - " + d.group);
let func = function (e) {
node.append("text")
.classed("nodeText", true)
.attr("id", d => "id" + d.id)
.attr("dy", 0)//4
.attr("dx", 0)//-15
.attr('text-anchor', 'middle')
.text(d => d.name)
.call(wrap, 55);
}
var timer = d3.timeout(func, 10);
// node.append("text")
// .attr("dy",12)
// .attr("dx", -8)
// .text(d=> d.runtime);
//set up dictionary of neighbors
var neighborTarget = {};
for(var i = 0; i<dataset.nodes.length; i++ ) {
var id = dataset.nodes[i].id;
neighborTarget[id] = dataset.links.filter(function (d) {
// console.log(d.source.id)
return d.source.id == id;
}).map(function (d) {
return d.target.id;
})
}
var neighborSource = {};
for (var i = 0; i < dataset.nodes.length; i++) {
var id = dataset.nodes[i].id;
neighborSource[id] = dataset.links.filter(function (d) {
return d.target.id == id;
}).map(function (d) {
return d.source.id;
})
}
console.log("neighborSource is ", neighborSource);
console.log("neighborTarget is ", neighborTarget);
///hover
var nodeSelected = null;
// d3.select("body").on("click", function(d){
// nodeSelected=null;
// d3.selectAll(".active").classed("active", false);
// d3.selectAll(".nodeCircle").transition().duration(300).style("opacity", 1);
// d3.selectAll(".nodeText").transition().duration(300).style("opacity", 1);
// d3.selectAll(".nodeLinks").transition().duration(300).style("opacity", 1);
// })
node.selectAll(".nodeCircle")
.on("mouseover", function (d) {
if (nodeSelected == null) {
var thisNode = d.id
var connected = dataset.links.filter(function (e) {
return e.source.id === thisNode || e.target.id === thisNode
});
d3.selectAll(".nodeLinks")
.transition().duration(300)
.style("opacity", function (d) {
// return (connected.map(d => d.source.id).indexOf(d.id) > -1 || connected.map(d => d.target.id).indexOf(d.id) > -1) ? 0.7 : 0.1
return (d.source.id == thisNode || d.target.id == thisNode) ? 1 : 0.05
});
d3.selectAll(".nodeCircle")
.transition().duration(300)
.style("opacity", function (d) {
return (connected.map(d => d.source.id).indexOf(d.id) > -1 || connected.map(d => d.target.id).indexOf(d.id) > -1) ? 0.7 : 0.1
});
d3.selectAll(".nodeText")
.transition().duration(300)
.style("opacity", function (d) {
return (connected.map(d => d.source.id).indexOf(d.id) > -1 || connected.map(d => d.target.id).indexOf(d.id) > -1) ? 0.7 : 0.1
});
d3.select(this).transition().duration(300).style('opacity', 1);
d3.selectAll("#id" + d.id + ".nodeText").transition().duration(300).style("opacity", 1);
// d3.select(this).attr('opacity', 1);
}
})
.on("mouseout", function (d) {
if (nodeSelected == null) {
var thisNode = d.id
d3.selectAll(".nodeCircle").transition().duration(300).style("opacity", 1);
d3.selectAll(".nodeText").transition().duration(300).style("opacity", 1);
d3.selectAll(".nodeLinks").transition().duration(300).style("opacity", 1);
}
})
.on("click", function (d) {
var selectedNode = d3.select(this)
if (selectedNode.attr("class") != "nodeCircle active") {
if (d3.selectAll(".active")._groups[0].length != 0) {
d3.selectAll(".active")
.classed("active", false);
}
selectedNode.classed("active", true);
nodeSelected = selectedNode
var thisNode = d.id
var connected = dataset.links.filter(function (e) {
return e.source.id === thisNode || e.target.id === thisNode
});
d3.selectAll(".nodeLinks").style("opacity", function (d) {
// return (connected.map(d => d.source.id).indexOf(d.id) > -1 || connected.map(d => d.target.id).indexOf(d.id) > -1) ? 0.7 : 0.1
return (d.source.id == thisNode || d.target.id == thisNode) ? 1 : 0.05
});
d3.selectAll(".nodeCircle").style("opacity", function (d) {
return (connected.map(d => d.source.id).indexOf(d.id) > -1 || connected.map(d => d.target.id).indexOf(d.id) > -1) ? 0.7 : 0.1
});
d3.selectAll(".nodeText").style("opacity", function (d) {
return (connected.map(d => d.source.id).indexOf(d.id) > -1 || connected.map(d => d.target.id).indexOf(d.id) > -1) ? 0.7 : 0.1
});
d3.select(this).style('opacity', 1);
d3.selectAll("#id" + d.id + ".nodeText").style("opacity", 1);
} else {
nodeSelected = null;
selectedNode.classed("active", false);
}
if (d3.selectAll(".active")._groups[0].length == 0) {
subgraph.selectAll("text")
.transition().duration(300)
.text("")
.attr("dy", 14)
.attr("dx", 10)
.style("font-size", "14px")
.attr('text-anchor', 'start')
.call(wrap, 200);
} else {
subgraph.selectAll("text")
.transition().duration(300)
.text("Selected: " + d.name)
.attr("dy", 14)
.attr("dx", 10)
.style("font-size", "14px")
.attr('text-anchor', 'start')
.call(wrap, 200);
}
})
//Listen for tick events to render the nodes as they update in your Canvas or SVG.
simulation
.nodes(dataset.nodes)
.on("tick", ticked);
simulation.force("link")
.links(dataset.links);
if (confirm) {
var existSource = false
//add node
if (!existSource) {
console.log("insert new node");
stakeholders_nodes.push({
id: st_data.nodes.length,
name: newNode,
group: group,
index: st_data.nodes.length,
// weight: 0
});
}
//add link
stakeholders_links.push({
"source": newNode,
"target": existingNode,
"weight": 1,
"distance": weight,
index: st_data.links.length,
});
simulation
.nodes(dataset.nodes)
.on("tick", ticked);
simulation.force("link")
.links(dataset.links);
// simulation.restart()
// return "do this" + confirm
} else {
// return "do nothing"
}
// 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("transform", d => `translate(${d.x},${d.y})`);
edgepaths.attr('d', d => 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y);
}
//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;
}
//drawing the legend
const legend_g = svg.selectAll(".legend")
.data(colorScale.domain())
.enter().append("g")
.attr("transform", (d, i) => `translate(${width - 50},${i * 20})`);
legend_g.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 5)
.attr("fill", colorScale);
legend_g.append("text")
.attr("x", 10)
.attr("y", 5)
.text(d => d);
return div
}