chart = {
button;
const saveHTML = html`<button class="button_click" id="add">Add a Bar</button>`;
const svgHTML = html`<svg width="${width}" height="${height}"></svg>`;
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("width", "100%")
.style("height", "680")
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.attr("align", "center")
.attr("justify-content", "center")
.attr("id", "chart")
.attr("text-anchor", "middle")
svg.append("style").text(`
current_link_style {
stroke: lightgrey;
stroke-opacity: 1;
}
`);
var background = svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "#044B9466")
.attr("fill-opacity", "0.0")
.on("dblclick", remove_textblock)
const g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") ;
// define data
const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
// disjoint graph settings
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(90))
// .force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-400))
.force("x", d3.forceX())
.force("y", d3.forceY());
// zoom options - https://stackoverflow.com/questions/64193193/translate-zoom-after-domain-change
svg.call(d3.zoom()
.extent([[margin.right,margin.top], [width+margin.right, height+margin.top]])
.on("zoom", zoomed)
)
.on("dblclick.zoom", null);
//pressed markers
svg.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", 0)
.attr("markerWidth", 4.5)
.attr("markerHeight", 3)
.attr("fill", "lightgrey")
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
svg.append("svg:defs").selectAll("marker")
.data(["end2"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 17)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 4)
.attr("fill", "lightgrey")
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
//regular markers
svg.append("svg:defs").selectAll("marker")
// .data(["end3"])
// .enter().append("svg:marker")
.data(types)
.join("marker")
.attr("id", d => `arrow-${d}`)
// .attr("id", "marker_regular")
.attr("viewBox", "0 -5 10 10")
.attr("refX", 17)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 4)
.attr("orient", "auto")
.append("svg:path")
// .attr("fill", "red")
.attr("fill", color3)
.attr("d", 'M0,-5L10,0L0,5');
// create text from d.sentence
let current_link = undefined;
let format = d3.format(",d")
let text_blocks_number = 0
let y_indent = 80
// let textblock = svg.selectAll("#details-popup");
let width_default = width/2.5
let link_info_text = '';
function selectOccupation(i,d){
// prevent double selection
if (d3.select(this).style('stroke-width') == "5"){
return;
// current_link = d3.select(this).attr("fill", "none")
// .style("stroke-width", 3)
// .style("stroke", d => color3(d.predicate))
// .attr("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`);
} else{
// function for the first entry
if(current_link == undefined){
let textblock = svg.selectAll("#details-popup")
.data([d])
.enter()
.append("g")
.attr("id", "details-popup")
.attr("font-size", 14)
.attr("font-family", "sans-serif")
.attr("text-anchor", "start")
.attr("transform", d => `translate(10, 20)`);
text_blocks_number += 1 ;
// link_info_text = link_info_text + d.sentence
link_info_text = function(d) { return "Item count: " + d.sentence }
// add rectangle around the text
textblock.append("rect")
.attr("x", 0)
.attr("width", wrapping)
.attr("height", height/6+29.5)
.attr("fill", "grey")
.attr("occupacy", "grey")
.attr("fill-opacity", "0.2")
.attr("y", y_indent-70)
textblock.append("text")
.attr("y", y_indent)
.attr("x", 0)
.text(d => text_blocks_number+ ": " + d.sentence)
.call(wrap2, wrapping, 0.35, 1.3, 1, true, true);
current_link = d3.select(this)
.style("stroke", function (d, i) {
return "#d1d1d1";
})
//
current_link.style("stroke-width", "5");
current_link.attr("marker-end", "url(#end)")
.attr("id", "link_all")
current_link.classed("current_link_style", true);
// .attr("class", "current_link_style")
;
// current_link.attr("marker-end", d => `url(${new URL(`#arrow-${d.type}`, location)})`);
// function for the second and subsequent entries
} else{
link_info_text = link_info_text + d.sentence;
//define new selection
let textblock = svg.selectAll("#details-popup");
//define new selection
textblock.data([d])
.enter()
.append("g")
.attr("id", "details-popup")
.attr("font-size", 14)
.attr("font-family", "sans-serif")
.attr("text-anchor", "start")
.attr("transform", d => `translate(10, 20)`);
text_blocks_number += 1 ;
// if (y_indent+ 113*(text_blocks_number-1) -41 - 112.5 >= height-180){
if (text_blocks_number >5){
textblock.append("text")
.attr("y", y_indent-55)
.attr("x", 0 )
.attr("font-size", 14)
.attr("fill", "red")
.text("Please clear links info by double clicking on the background")
}else{
current_link = d3.select(this).style("stroke", "#d1d1d1")
.attr("id", "link_all")
current_link.classed("current_link_style", true)
// .attr("class", "current_link_style");
current_link.style("stroke-width", "5");
// current_link.attr("marker-end", "url(#end)");
textblock.append("rect")
.attr("x", 0)
.attr("width", wrapping)
.attr("height", height/6)
.attr("fill", "grey")
.attr("occupacy", "grey")
.attr("fill-opacity", "0.2")
.attr("y", y_indent+ 113*(text_blocks_number) -41 - 112.5)
textblock.append("text")
.attr("y", y_indent+ 113*(text_blocks_number) -1 - 112.5)
.attr("x", 0)
.text(d => text_blocks_number+ ": " + d.sentence)
// .text("test" + text_blocks_number)
.call(wrap2, wrapping, 0.35, 1.3, 1, true, true);
} }
}
}
links.sort(function(a,b) {
if (a.source > b.source) {return 1;}
else if (a.source < b.source) {return -1;}
else {
if (a.target > b.target) {return 1;}
if (a.target < b.target) {return -1;}
else {return 0;}
}
});
// any links with duplicate source and target get an incremented 'linknum'
for (var i=0; i<links.length; i++) {
if (i != 0 &&
links[i].source == links[i-1].source &&
links[i].target == links[i-1].target) {
links[i].linknum = links[i-1].linknum + 1;
}
else {links[i].linknum = 1;};
};
let link = g.append("g")
.attr("fill", "none")
.attr("stroke-width", 3)
.selectAll("path")
.data(links)
.join("path")
.attr("stroke", d => color3(d.predicate))
//.attr("stroke","lightgrey")
.attr("stroke-opacity","0.8")
.attr("id", "link_all")
// .attr("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`)
// .attr("id", "link_all");
// .attr("marker-end", d => "url(#end3)")
// link.on("click", selectOccupation);
link
.on('mouseover', function(d) {
d3.select(this)
.attr("stroke-width", 3)
.style("stroke-opacity", 0.5)
})
.on('mouseout', function(d) {
d3.select(this).style("stroke-opacity", "1")
.attr("stroke-width", 3)
});
// link.append("title")
// .text(d => d.predicate +": " + d.sentence);
// .text(d => d.linknum);
// define node options
const node = g.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(nodes)
.enter().append("g")
.classed("node", true)
.classed("fixed", d => d.fx !== undefined);
node.on("mouseover", node_click)
function node_click (evt, d) {
// d3.selectAll('.current_link_style').style("stroke", "blue")
d3.selectAll("#link_all")
// .style("marker-end", d => "url(#end2)")
.transition().duration(300)
.style("stroke", "lightgrey")
.filter(l => l.source.id === d.id || l.target.id === d.id)
// .style("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`)
.style("stroke", "#d5f2bd")
// .style("stroke", l => color3(l.predicate))
d3.selectAll("#current_link")
.style("stroke-width", 3)
.style("stroke","#d62728")
// .attr("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`);
}
node.on("mouseout", dblclick)
node.on("dblclick", dblclick)
function dblclick (evt, d) {
// link
// .attr("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`)
// .attr("stroke", l => color3(l.predicate))
// ;
d3.selectAll("#link_all")
.transition().duration(300)
// .style("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`)
// .style("stroke", l => color3(l.predicate))
.style("stroke", "#d5f2bd")
.filter(".current_link_style")
.style("stroke", "lightgrey")
// .style("stroke", function (l) {
// if (d3.select(this).attr('stroke-width') !== 5) {
// return color3(l.predicate)
// }else {
// return "lightgrey"
// }})
// return color3(d.predicate)
// }else {
// return "lightgrey"}
// })
};
function remove_textblock(){
d3.select("#details-popup").remove()
d3.selectAll("#current_link")
.attr("id","#link_all2")
.style("stroke-width", 3)
.style("stroke", d => color3(d.predicate))
// .attr("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`)
//
current_link = undefined;
text_blocks_number = 0
y_indent = 80
d3.selectAll("#link_all").classed("current_link_style", false)
d3.selectAll("#link_all")
.style("stroke-width", 3)
// .attr("stroke", "green")
.style("stroke", d => color3(d.predicate))
// .attr("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`);
link
// .attr("marker-end", d => `url(${new URL(`#arrow-${d.predicate}`, location)})`)
.attr("stroke", l => color3(l.predicate))
;
}
// define circles options
const circles = node.append("circle")
// .attr("stroke", "#fff")
// .style("stroke","#d62728")
.style("stroke","darkslategray")
.style("stroke-opacity",0.85)
.attr("stroke-width", d => stroke_width_key(d.key_node))
// .attr("stroke-width", 2)
// .attr("border-width", 1)
// .attr("border-color", "grey")
.attr("r", d => radius_width_group(d.group))
.attr("fill", d =>color(d.group))
.attr("fill-opacity", "0.6")
// yield svg.node();
const drag = d3
.drag()
.on("start", dragstart)
.on("drag", dragged);
node.call(drag)
function click(event, d) {
delete d.fx;
delete d.fy;
d3.select(this).classed("fixed", false);
simulation.alpha(1).restart();
}
function dragstart() {
d3.select(this).classed("fixed", true);
}
function dragged(event, d) {
d.fx = clamp(event.x, -width, width);
d.fy = clamp(event.y, -height, height);
simulation.alpha(1).restart();
}
node.append("text")
.text(function(d) {
return d.label;
})
// .attr('font-size', 14)
.style("font-weight", function (font_weigth_width_group2) {
return font_weigth_width_group2.key_node ==1 || font_weigth_width_group2.group == "go_molecular_function"|| font_weigth_width_group2.group =="go_biological_process"|| font_weigth_width_group2.group == "go_cellular_component" ? "700" : "400";})
.style("font-size", function (font_size_width_group2) {
return font_size_width_group2.key_node ==1 || font_size_width_group2.group == "go_molecular_function"|| font_size_width_group2.group =="go_biological_process"|| font_size_width_group2.group == "go_cellular_component" ? 15.5 : 13;})
// .attr("font-weight", d => font_weigth_width_group(d.group))
.attr('x', 13.5)
// .attr('y', -10.5)
.attr("y", function (upper_text) {
return upper_text.group == "go_molecular_function"|| upper_text.group =="go_biological_process"|| upper_text.group == "go_cellular_component" ? -18.7 : -10.5;})
node.append("title")
.text(d => d.id);
simulation.on("tick", () => {
link.attr("d", linkArc);
node.attr("transform", d => `translate(${d.x},${d.y})`); });
function linkArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
x1 = d.source.x,
y1 = d.source.y,
x2 = d.target.x,
y2 = d.target.y,
dr = (d.linknum !=1)?Math.sqrt(dx * dx + dy * dy):0,
arc = dr*d.linknum/2;
let drx = arc
let dry = arc
let xRotation = 0, // degrees
largeArc = 0, // 1 or 0
sweep = 1; // 1 or 0
// // Self edge.
if ( x1 === x2 && y1 === y2 ) {
// // Fiddle with this angle to get loop oriented.
// xRotation = -45;
// // Needs to be 1.
largeArc = 1;
// // Change sweep to change orientation of loop.
// sweep = 0;
// // Make drx and dry different to get an ellipse
// // instead of a circle.
drx = 33;
dry = 20;
// // For whatever reason the arc collapses to a point if the beginning
// // and ending points of the arc are the same, so kludge it.
x2 = x2 + 1;
y2 = y2 + 1;
}
return "M" + x1 + "," + y1 + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + x2 + "," + y2;
// return "M" + x1 + "," + y1 +
// "A" + drx + "," + dry + " 0 0,1 " + x2 + "," + y2;
};
// function linkArc(d) {
// var dx = d.target.x - d.source.x,
// dy = d.target.y - d.source.y,
// x1 = d.source.x,
// y1 = d.source.y,
// x2 = d.target.x,
// y2 = d.target.y,
// // dr = (d.linknum !=1)?Math.sqrt(dx * dx + dy * dy +d.linknum*10):0;
// dr = (d.linknum !=1)?Math.sqrt(dx * dx + dy * dy):0,
// arc = dr*d.linknum/2;
// return "M" + d.source.x + "," + d.source.y +
// "A" + arc + "," + arc + " 0 0,1 " + d.target.x + "," + d.target.y;
// };
invalidation.then(() => simulation.stop());
//Zoom functions
function zoomed({transform}) {
g.attr("transform", transform)
}
return svg.node();
}