I have been using R2D3 for drawing a force directed layout. When i run it on IE8 i see Arg: Illegal input string in Vector2D as seen below:
This happens for transform attribute i apply for the layout.
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
I doubt the translate function returns some value the browser engine is not able to read. Possible a large number. Is there any way I can position the nodes without using transform attr. Following is my code:
d3.json("egoGraph.json", function(json) {
nodeSet = json.nodes;
linkSet = json.links;
var width = 600;
var height = 500;
var centerNodeSize = 30;
var nodeSize = 10;
var rscale = d3.scale.linear().domain([0, d3.max(nodeSet, function (d) {
return d.data
})]).range([5, 20]);
var color = ['#B43104','#F5ECCE', '#F3E2A9', '#F7D358', '#FFBF00', '#FF8000'];
// Create a canvas...
var svgCanvas = d3.select('#chart').append("svg:svg").attr(
"width", width).attr("height", height).append("svg:g")
.attr("class", "focalNodeCanvas").attr("transform",
"translate(" + width / 3.333333 + "," + height / 2.352 + ")")
var node_hash = [];
//var type_hash = [];
// Create a hash that allows access to each node by its id
nodeSet.forEach(function(d, i) {
node_hash[d.journalname] = d;
//type_hash[d.type] = d.type;
});
// Append the source object node and the target object node to each link records...
linkSet.forEach(function(d, i) {
d.source = node_hash[d.sourceId];
d.target = node_hash[d.targetId];
if (d.sourceId == focalNodeID) {
d.direction = "OUT";
} else {
d.direction = "IN";
}
});
// Create a force layout and bind Nodes and Links
var force = d3.layout.force().nodes(nodeSet).links(linkSet).charge(
-1000)
//.gravity(.2)
//.linkStrength(20)
.size([ width / 8, height / 10 ]).linkDistance(function(d) {
if (width < height) {
return width * 1 / 3;
} else {
return height * 1 / 3
}
}) // Controls edge length
.on("tick", tick).start();
// Draw lines for Links between Nodes
var link = svgCanvas.selectAll(".gLink").data(force.links())
.enter().append("g").attr("class", "gLink").append("line")
.attr("class", "link").attr("stroke-width", function(d) {
return (Math.random() * (10 - 1) + 1);
}).style("stroke", "#808080").attr("x1", function(d) {
return d.source.x;
}).attr("y1", function(d) {
return d.source.y;
}).attr("x2", function(d) {
return d.target.x;
}).attr("y2", function(d) {
return d.target.y;
});
// Create Nodes
var node = svgCanvas.selectAll(".node").data(force.nodes()).enter()
.append("g").attr("class", "node").attr("fixed", function(d) {
return true
}).call(force.drag);
// Append circles to Nodes
node.append("circle").attr("x", function(d) {
return 100;
}).attr("y", function(d) {
return 30;
}).attr("r", function(d) {
if (d.journalname == focalNodeID) {
return centerNodeSize;
} else {
return rscale(d.data);
}
}) // Node radius
.style("fill", function(d) { return color[Math.floor(Math.random() * (5 - 0 + 1)) + 0]; }) // Make the nodes hollow looking
.on("mouseover", function() { d3.select(this).attr("stroke", "#808080").attr("stroke-width", 5);})
.on("mouseout", function(){ d3.select(this).attr("stroke", function(d) { return color[Math.floor(Math.random() * (5 - 0 + 1)) + 0]; }).attr("stroke-width", 0);})
.call(force.drag);
// Append text to Nodes
node.append("text").attr("x", function(d) {
if (d.journalname == focalNodeID) {
return 0;
} else {
return 20;
}
}).attr("y", function(d) {
if (d.journalname == focalNodeID) {
return 0;
} else {
return -10;
}
}).attr("text-anchor", function(d) {
return "middle";
}).attr("font-family", "Arial, Helvetica, sans-serif").style(
"font", "normal 12px Arial").text(function(d) {
return d.journalname;
});
function tick() {
link.attr("x1", function(d) {
return d.source.x;
}).attr("y1", function(d) {
return d.source.y;
}).attr("x2", function(d) {
return d.target.x;
}).attr("y2", function(d) {
return d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
});
transform doesn't work in IE8, and in IE9 you need the browser prefix ms-
So you will need to abstract your transform call, or use an expression in CSS
http://msdn.microsoft.com/en-us/library/ms532918(v=vs.85).aspx
Related
I have processed a hierarchy-based JSON file (parent-children relationship). In that I have to come across one step after another. However, in the the last level (last children), this might have 3000-5000 values. And, of course it doesn't fit within the treemap window. Therefore, I want to show it as a list view (html ul li) by scrolling through top to bottom.
Some thing like this: http://www.billdwhite.com/wordpress/wp-content/js/treemap_headers_03.html
However, it will go further until it encounter with huge amount of data (approx. 3000-5000).
Bellow is my whole code so far:
var obj = document.getElementById('chart');
var divWidth = obj.offsetWidth;
// console.log(obj.offsetWidth);
var margin = {top: 30, right: 0, bottom: 20, left: 0},
width = divWidth - 25,
height = 540 - margin.top - margin.bottom,
transitioning;
//console.log(width);
// sets x and y scale to determine size of visible boxes
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
//x(d.x + d.dx) - x(d.x)
var y = d3.scale.linear()
.domain([0, height])
.range([0, height]);
var color = d3.scale.category10();
// introduce color scale here
var treemap = d3.layout.treemap()
.children(function(d, depth) { return depth ? null : d._children; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false);
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", - margin.left + "px")
.style("margin.right", - margin.right + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// .style("shape-rendering", "crispEdges");
var grandparent = svg.append("g")
.attr("class", "grandparent");
grandparent.append("rect")
.attr("y", - margin.top)
.attr("width", width)
.attr("height", margin.top);
grandparent.append("text")
.attr("x", 6)
.attr("y", 6 - margin.top)
.attr("dy", ".75em");
// functions
function initialize(root) {
root.x = root.y = 0;
root.dx = width;
root.dy = height;
root.depth = 0;
}
function accumulate(d) {
// svg.on("click", function() {
// console.log("d._children -> " + d._children);
// console.log("d.children -> " + d.children);
// });
// console.log("d._children -> " + d._children);
// console.log("d.children -> " + d.children);
//
// if(d._children = d.children)
// {
// d.children.reduce(function(p, v) { console.log(p); return p + accumulate(v); }, 0)
//// console.log(d.children.reduce(function(p, v) { return p + accumulate(v); }, 0));
// } else {
// console.log(d.value);
// }
return (d._children = d.children)
? d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0)
: d.value;
}
function layout(d) {
if (d._children) {
// treemap nodes comes from the treemap set of functions as part of d3
// console.log(treemap.nodes({_children: d._children}));
treemap.nodes({_children: d._children});
d._children.forEach(function(c) {
// if(c._children != undefined) {
c.x = d.x + c.x * d.dx;
// console.log(c);
// console.log(d.x);
// console.log(c.x);
// console.log(d.dx);
c.y = d.y + c.y * d.dy;
c.dx *= d.dx;
c.dy *= d.dy;
c.parent = d;
// recursion
layout(c);
// } else {
// var ul = document.createElement('ul');
// var li = document.createElement('li');
//// document.getElementsByClassName('foreignobj').appendChild(ul).appendChild(li);
//
// console.log(c.name);
// li.innerHTML=li.innerHTML + c.name;
//// console.log('LIST');
// }
});
}
}
d3.json("new-data-old.json", function(root) {
//console.log(root)
initialize(root);
accumulate(root);
layout(root);
display(root);
function display(d) {
grandparent
.datum(d.parent)
.on("click", transition)
.select("text")
.text(name(d))
// color header based on grandparent's rectcolor
grandparent
.datum(d.parent)
.select("rect")
.attr("fill", function(){
// return color(d['rectcolor'])
return color(Math.random())
})
var g1 = svg.insert("g", ".grandparent")
.datum(d)
.attr("class", "depth");
var g = g1.selectAll("g")
.data(d._children)
.enter().append("g");
g.filter(function(d) { return d._children; })
.classed("children", true)
.on("click", transition);
// console.log();
// if(d._children.value == null){
//// console.log(d._children)
// return d._children || [d];
// } else {
//// console.log(d._children)
// g.selectAll(".child")
// .data(function(d) { return d._children || [d]; })
// .enter().append("div")
// .attr("class", "child")
// .html(function (d) {
// console.log(d);
// })
// ;
//// .call(rect);
// }
g.selectAll(".child")
.data(function(d) {
if(typeof d._children == "undefined") {
// console.log(d.name);
// g.remove();
// d3.select("#chart svg g")
//// .data(function(d) { return d; })
// .append("xhtml:div")
// .attr("class", "mydiv")
// .html(d.name);
return d;
} else {
// console.log('BBBBBBBB');
// d3.select("#chart")
// .append("xhtml:div")
// .attr("class", "mydiv")
// .removeAll();
return d._children || [d];
}
})
.enter().append("rect")
.attr("class", "child")
.call(rect);
g.append("rect")
.attr("class", "parent")
.call(rect)
.append("title");
/* Adding a foreign object instead of a text object, allows for text wrapping */
g.append("foreignObject")
.call(rect)
.attr("class","foreignobj")
.append("xhtml:div")
.attr("dy", ".75em")
.html(function (d) {
// console.log(d._children);
return d.name;
})
.attr("class","textdiv");
// if(d._children == undefined) {
// g.append("foreignObject")
// .call(rect)
// .attr("class","foreignobj")
// .append("xhtml:div")
// .attr("dy", ".75em")
// .html(function (d) {
//// console.log(d._children);
// return d.name;
// })
// .attr("class","textdiv");
// }
function transition(d) {
// console.log(d);
if (transitioning || !d) return;
transitioning = true;
var g2 = display(d),
t1 = g1.transition().duration(650),
t2 = g2.transition().duration(650);
console.log(g1);
console.log(g2[0].parentNode.__data__._children[0].lastchild);
// Update the domain only after entering new elements.
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
// Enable anti-aliasing during the transition.
svg.style("shape-rendering", null);
// Draw child nodes on top of parent nodes.
// svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; });
// Fade-in entering text.
g2.selectAll("text").style("fill-opacity", 0);
// g2.selectAll("foreignObject div").style("display", "none");
// Transition to the new view.
t1.selectAll("text").call(text).style("fill-opacity", 0);
t2.selectAll("text").call(text).style("fill-opacity", 1);
// console.log(t1.selectAll("rect"));
// if(t1.selectAll("rect")._children != undefined) {
// console.log('OKOKOKO');
// }
t1.selectAll("rect").call(rect);
t2.selectAll("rect").call(rect);
// t1.append("xhtml:div");
/* Foreign object */
t1.selectAll(".textdiv").style("display", "none"); /* added */
t1.selectAll(".foreignobj").call(foreign); /* added */
t2.selectAll(".textdiv").style("display", "block"); /* added */
t2.selectAll(".foreignobj").call(foreign); /* added */
// Remove the old node when the transition is finished.
t1.remove().each("end", function() {
// console.log('END');
// svg.style("shape-rendering", "crispEdges");
transitioning = false;
});
}
return g;
}
function text(text) {
text.attr("x", function(d) { return x(d.x) + 6; })
.attr("y", function(d) { return y(d.y) + 6; });
}
function rect(rect) {
rect.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
// .attr("width", function(d) { console.log(((x(d.x + d.dx) - x(d.x))/15)*d.size); return (x(d.x + d.dx) - x(d.x)); })
// .attr("width", function(d) { return x(d.x + 200); })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
// .attr("height", function(d) { return y(d.y + 150); })
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y); })
.attr("fill", function(){
// return color(d['rectcolor'])
return color(Math.random())
});
}
function divList(divList) {
divList.append("xhtml:div");
}
function foreign(foreign){ /* added */
foreign.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
// .attr("width", function(d) { return x(d.x + 200) - x(d.x); })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
// .attr("height", function(d) { return y(d.y + 150) - y(d.y); });
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y); });
}
function name(d) {
return d.parent
? name(d.parent) + " - " + d.name
: d.name;
}
function nameSave(d) {
return d.parent
? name(d.parent) + " - " + d.name
: d.name;
}
});
Any kind of help will be appreciated. Thanks in advance.
I'm using D3 and javascript to create a BiLevel Partition following this example. The labels in the left side of the diagram are upside down, and I was trying to rotate them, but I've not been successful yet.
I found numerous cases of people with the same problem, but using Sunburst. Also tried to implement those solutions, but I'm still unable to solve this problem.
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
/*var margin = {top: 350, right: 480, bottom: 350, left: 480},
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 10;*/
var width = 1200,
height = 1200,
radius = Math.min(width, height) / 2;
function filter_min_arc_size_text(d, i) {return (d.dx*d.depth*radius/3)>14};
var hue = d3.scale.category10();
var luminance = d3.scale.sqrt()
.domain([0, 1e6])
.clamp(true)
.range([90, 20]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ")");
var partition = d3.layout.partition()
.sort(function(a, b) { return d3.ascending(a.name, b.name); })
.size([2 * Math.PI, radius]);
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx - .01 / (d.depth + .5); })
.innerRadius(function(d) { return radius / 3 * d.depth; })
.outerRadius(function(d) { return radius / 3 * (d.depth + 1) - 1; });
//Tooltip description
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("opacity", 0);
function format_number(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function format_description(d) {
var description = d.description;
return /* '<b>' + d.name + '</b></br>'+*/ d.description + '<br> (' + format_number(d.value) + ')';
}
function computeTextRotation(d) {
var ang = ((d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
return (ang > 90) ? 180 + ang : ang;
}
function mouseOverArc(d) {
d3.select(this).attr("stroke","black")
tooltip.html(format_description(d));
return tooltip.transition()
.duration(50)
.style("opacity", 0.9);
}
function mouseOutArc(){
d3.select(this).attr("stroke","")
return tooltip.style("opacity", 0);
}
function mouseMoveArc (d) {
return tooltip
.style("top", (d3.event.pageY-10)+"px")
.style("left", (d3.event.pageX+10)+"px");
}
var root_ = null;
d3.json("flare.json", function(error, root) {
if (error) return console.warn(error);
// Compute the initial layout on the entire tree to sum sizes.
// Also compute the full name and fill color for each node,
// and stash the children so they can be restored as we descend.
partition
.value(function(d) { return d.size; })
.nodes(root)
.forEach(function(d) {
d._children = d.children;
d.sum = d.value;
d.key = key(d);
d.fill = fill(d);
});
// Now redefine the value function to use the previously-computed sum.
partition
.children(function(d, depth) { return depth < 2 ? d._children : null; })
.value(function(d) { return d.sum; });
var center = svg.append("circle")
.attr("r", radius / 3)
.on("click", zoomOut);
center.append("title")
.text("zoom out");
var partitioned_data=partition.nodes(root).slice(1)
var path = svg.selectAll("path")
.data(partitioned_data)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc);
var texts = svg.selectAll("text")
.data(partitioned_data)
.enter().append("text")
.filter(filter_min_arc_size_text)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d,i) {return d.name})
function zoomIn(p) {
if (p.depth > 1) p = p.parent;
if (!p.children) return;
zoom(p, p);
}
function zoomOut(p) {
if (!p.parent) return;
zoom(p.parent, p);
}
// Zoom to the specified new root.
function zoom(root, p) {
if (document.documentElement.__transition__) return;
// Rescale outside angles to match the new layout.
var enterArc,
exitArc,
outsideAngle = d3.scale.linear().domain([0, 2 * Math.PI]);
function insideArc(d) {
return p.key > d.key
? {depth: d.depth - 1, x: 0, dx: 0} : p.key < d.key
? {depth: d.depth - 1, x: 2 * Math.PI, dx: 0}
: {depth: 0, x: 0, dx: 2 * Math.PI};
}
function outsideArc(d) {
return {depth: d.depth + 1, x: outsideAngle(d.x), dx: outsideAngle(d.x + d.dx) - outsideAngle(d.x)};
}
center.datum(root);
// When zooming in, arcs enter from the outside and exit to the inside.
// Entering outside arcs start from the old layout.
if (root === p) enterArc = outsideArc, exitArc = insideArc, outsideAngle.range([p.x, p.x + p.dx]);
var new_data=partition.nodes(root).slice(1)
path = path.data(new_data, function(d) { return d.key; });
// When zooming out, arcs enter from the inside and exit to the outside.
// Exiting outside arcs transition to the new layout.
if (root !== p) enterArc = insideArc, exitArc = outsideArc, outsideAngle.range([p.x, p.x + p.dx]);
d3.transition().duration(d3.event.altKey ? 7500 : 750).each(function() {
path.exit().transition()
.style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
.attrTween("d", function(d) { return arcTween.call(this, exitArc(d)); })
.remove();
path.enter().append("path")
.style("fill-opacity", function(d) { return d.depth === 2 - (root === p) ? 1 : 0; })
.style("fill", function(d) { return d.fill; })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc)
.each(function(d) { this._current = enterArc(d); });
path.transition()
.style("fill-opacity", 1)
.attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });
});
texts = texts.data(new_data, function(d) { return d.key; })
texts.exit()
.remove()
texts.enter()
.append("text")
texts.style("opacity", 0)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.filter(filter_min_arc_size_text)
.text(function(d,i) {return d.name})
.transition().delay(750).style("opacity", 1)
}
});
function key(d) {
var k = [], p = d;
while (p.depth) k.push(p.name), p = p.parent;
return k.reverse().join(".");
}
function fill(d) {
var p = d;
while (p.depth > 1) p = p.parent;
var c = d3.lab(hue(p.name));
c.l = luminance(d.sum);
return c;
}
function arcTween(b) {
var i = d3.interpolate(this._current, b);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function updateArc(d) {
return {depth: d.depth, x: d.x, dx: d.dx};
}
d3.select(self.frameElement).style("height", margin.top + margin.bottom + "px");
</script>
The problem I have is that it is only showing the right half of the Partition.
I've been wrestling very hard with D3 to try to make a simple bubble-chart using force collide that live-updates the bubble size.
I can get the chart to show on the first data update with force collide. However subsequent data calls freeze the chart and the sizes are never updated:
https://jsfiddle.net/d2zcfjfa/1/
node = svg.selectAll('g.node')
.data(root.children)
.enter()
.append('g')
.attr('class', 'node')
.append('circle')
.attr('r', function(d) { return d.r * 1.4; })
.attr('fill', function(d) { return color(d.data.name); })
.call(d3.drag()
.on("start", dragStart)
.on("drag", dragged)
.on("end", dragEnd));
var circleUpdate = node.select('circle')
.attr('r', function(d)
{
return d.r;
});
simulation.nodes(root.children);
I can get updates to work but only without using the collide simulation as seen here:
https://jsfiddle.net/rgdox7g7/1/
node = svg.selectAll('g.node')
.data(root.children)
.enter()
.append('g')
.attr('id', function(d) { return d.id; })
.attr('class', 'node')
.attr('transform', function(d)
{
return "translate(" + d.x + "," + d.y + ")";
});
var nodeUpdate = svg.selectAll('g.node')
.transition()
.duration(2000)
.ease(d3.easeLinear);
var circleUpdate = nodeUpdate.select('circle')
.attr('r', function(d)
{
return d.r;
});
node.append("circle")
.attr("r", function(d) { return d.r; })
.style('fill', function(d) { return color(d.data.name); });
Everything I have tried to mix these two solutions together simply does not work. I have scoured the internet for other examples and nothing I can find is helping. Can someone please help me understand what to do? I never thought D3 would be such a frustration!
stackoverflow: the place where you have to answer your own questions.
here is my working solution:
https://jsfiddle.net/zc0fgh6y/
var subscription = null;
var width = 600;
var height = 300;
var maxSpeed = 1000000;
var pack = d3.pack().size([width, height]).padding(0);
var svg = d3.select('svg');
var node = svg.selectAll("g.node");
var root;
var nodes = [];
var first = true;
var scaleFactor = 1.4;
var color = d3.interpolateHcl("#0faac3", "#dd2323");
var forceCollide = d3.forceCollide()
.strength(.8)
.radius(function(d)
{
return d.r;
}).iterations(10);
var simulationStart = d3.forceSimulation()
.force("forceX", d3.forceX(width/2).strength(.04))
.force("forceY", d3.forceY(height/2).strength(.2))
.force('collide', forceCollide)
.on('tick', ticked);
var simulation = d3.forceSimulation()
.force("forceX", d3.forceX(width/2).strength(.0005))
.force("forceY", d3.forceY(height/2).strength(.0025))
.force('collide', forceCollide)
.on('tick', ticked);
function ticked()
{
if (node)
{
node.attr('transform', function(d)
{
return "translate(" + d.x + "," + d.y + ")";
}).select('circle').attr('r', function(d)
{
return d.r;
});
}
}
function rand(min, max)
{
return Math.random() * (max - min) + min;
};
setInterval(function()
{
var hosts = [];
for (var i = 0; i < 100; i++)
{
hosts.push({name: i, cpu: rand(10,100), speed: rand(0,maxSpeed)});
}
root = d3.hierarchy({children: hosts})
.sum(function(d)
{
return d.cpu ? d.cpu : 0;
});
var leaves = pack(root).leaves().map(function(item)
{
return {
id: 'node-'+item.data.name,
name: item.data.name,
r: item.r * scaleFactor,
x: width/2,
y: height/2,
cpu: item.data.cpu,
speed: item.data.speed
};
});
for (var i = 0; i < leaves.length; i++)
{
if (nodes[i] && nodes[i].id == leaves[i].id)
{
var oldR = nodes[i].newR;
nodes[i].oldR = oldR;
nodes[i].newR = leaves[i].r;
nodes[i].cpu = leaves[i].cpu;
nodes[i].speed = leaves[i].speed;
}
else
{
nodes[i] = leaves[i];
//nodes[i].r = 1;
nodes[i].oldR = 1;//nodes[i].r;
nodes[i].newR = leaves[i].r;
}
}
if (first)
{
first = false;
node = node.data(nodes, function(d) { return d.id; });
node = node.enter()
.append('g')
.attr('class', 'node');
node.append("circle")
.style("fill", 'transparent');
node.append("text")
.attr("dy", "0.3em")
.style('fill', 'transparent')
.style("text-anchor", "middle")
.text(function(d)
{
return d.name;//.substring(0, d.r / 4);
});
// transition in size
node.transition()
.ease(d3.easePolyInOut)
.duration(950)
.tween('radius', function(d)
{
var that = d3.select(this);
var i = d3.interpolate(1, d.newR);
return function(t)
{
d.r = i(t);
that.attr('r', function(d)
{
return d.r;
});
simulationStart.nodes(nodes).alpha(1);
}
});
// fade in text color
node.select('text')
.transition()
.ease(d3.easePolyInOut)
.duration(950)
.style('fill', 'white');
// fade in circle size
node.select('circle')
.transition()
.ease(d3.easePolyInOut)
.duration(950)
.style('fill', function(d)
{
return color(d.speed / maxSpeed);
});
}
else
{
// transition to new size
node.transition()
.ease(d3.easeLinear)
.duration(950)
.tween('radius', function(d)
{
var that = d3.select(this);
var i = d3.interpolate(d.oldR, d.newR);
return function(t)
{
d.r = i(t);
that.attr('r', function(d)
{
return d.r;
});
simulation.nodes(nodes).alpha(1);
}
});
// transition to new color
node.select('circle')
.transition()
.ease(d3.easeLinear)
.duration(950)
.style('fill', function(d)
{
return color(d.speed / maxSpeed);
});
}
}, 1000);
Im unable to remove old links in my force-directed visualization with d3js, when I modify the layout. That means the old link will not be deleted and instead stay in the tag. I did follow this example and did the following:
link = link.data(links, function(d) {
return d.source.id + '-' + d.target.id;
});
link.enter().append("g").attr("class", "link");
var linkline = link.append("line").attr("class", "linkline");
var linktext = link.append("text").attr("class", "linktext")
.text(function(d) {
return d[setup.label_edge];
});
link.exit().remove();
The rest is pretty much the same as in the example. However, I cant get rid of the old links, exit().remove() won't work. They will stay on the visualization.
This is what I get when I execute then code snippet from gilsha. There are still multiple entries for linkline and linktext in the <g class="link">-tag. They are not visible and appear in the upper left corner, but I still need to get rid of them. Because when I drag the graph this becomes a problem.
When writing the link.append in a new variable, it does not duplicate the lines and labels anymore, but when calling update the lines stay in the previous position and don't move anymore.
This is the tick. Do I need to link linkGroup somehow?
force.on("tick", function(e){
linkline
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
linktext
.attr("x", function(d) {
return ((d.source.x + d.target.x)/2);
})
.attr("y", function(d) {
return ((d.source.y + d.target.y)/2);
});
node.attr("transform", function(d) {
if(setup.source == 0) {
if(d.id==0){
damper = 0.1;
d.x = d.x + ($('svg').attr('width')/2 - d.x) * (damper + 0.02) * e.alpha;
d.y = d.y + ($('svg').attr('height')/2 - d.y) * (damper + 0.02) * e.alpha;
}
var x = Math.max(radius, Math.min($('svg').attr('width') - radius, d.x));
var y = Math.max(radius, Math.min($('svg').attr('height') - radius, d.y));
return "translate(" + x + "," + y + ")";
} else {
if(d.id==0){
damper = 0.1;
d.x = d.x + ($('svg').attr('width')/2 - d.x) * (damper + 0.02) * e.alpha;
d.y = d.y + ($('svg').attr('height')/2 - d.y) * (damper + 0.02) * e.alpha;
}
var x = Math.max(radius, Math.min($('svg').attr('width') - imagesize2(d.metadata[setup.external[1]])[0], d.x));
var y = Math.max(radius, Math.min($('svg').attr('height') - imagesize2(d.metadata[setup.external[1]])[1], d.y));
return "translate(" + x + "," + y + ")";
}
});
There is no error in the part of code you added in the question. The problem will be due to some other part of the code. Verify the dataset is as expected before and after update.
EDIT: To resolve the duplication of links and texts, append links and link labels to linkGroup rather than link. Since you are using group elements for links, dynamic update of links may cause layering issues of links(Links may lay over nodes). You can resolve that issue by using insert function as shown in code and snippet.
link = link.data(links, function(d) {
return d.source.id + '-' + d.target.id;
});
/* var linkGroup = link.enter().insert("g",".node").attr("class", "link");//Use insert to resolve dom element layering issue. */
var linkGroup = link.enter().append("g").attr("class", "link");
var linkline = linkGroup.append("line")
.attr("class", "linkline");
var linktext = linkGroup.append("text")
.attr("class", "linktext")
.text(function(d, i) {
return i;
});
link.exit().remove();
var width = 960,
height = 500;
var color = d3.scale.category10();
var nodes = [],
links = [];
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.charge(-400)
.linkDistance(120)
.size([width, height])
.on("tick", tick);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var node = svg.selectAll(".node"),
link = svg.selectAll(".link");
// 1. Add three nodes and three links.
setTimeout(function() {
var a = {
id: "a"
},
b = {
id: "b"
},
c = {
id: "c"
};
nodes.push(a, b, c);
links.push({
source: a,
target: b
}, {
source: a,
target: c
}, {
source: b,
target: c
});
start();
}, 0);
// 2. Remove node B and associated links.
setTimeout(function() {
nodes.splice(1, 1); // remove b
links.shift(); // remove a-b
links.pop(); // remove b-c
start();
}, 3000);
// Add node B back.
setTimeout(function() {
var a = nodes[0],
b = {
id: "b"
},
c = nodes[1];
nodes.push(b);
links.push({
source: a,
target: b
}, {
source: b,
target: c
});
start();
}, 6000);
function start() {
link = link.data(links, function(d) {
return d.source.id + '-' + d.target.id;
});
var linkGroup = link.enter().insert("g",".node").attr("class", "link"); //Use insert to resolve dom element layering issue.
//var linkGroup = link.enter().append("g").attr("class", "link");
var linkline = linkGroup.append("line")
.attr("class", "linkline");
var linktext = linkGroup.append("text")
.attr("class", "linktext")
.text(function(d, i) {
return i;
});
link.exit().remove();
node = node.data(force.nodes(), function(d) {
return d.id;
});
node.enter().append("circle").attr("class", function(d) {
return "node " + d.id;
}).attr("r", 8);
node.exit().remove();
force.start();
}
function tick() {
node.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
link.selectAll("line").attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
}
.link {
stroke: #000;
stroke-width: 1.5px;
}
.node {
fill: #000;
stroke: #fff;
stroke-width: 1.5px;
}
.node.a {
fill: #1f77b4;
}
.node.b {
fill: #ff7f0e;
}
.node.c {
fill: #2ca02c;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have some problems using d3.js visualization network library when I want to show more than one network visualization graph.
Since I draw a new graph only the last one has its force layout working and I can't find out where things are going wrong.
Here is the jsfiddle :
https://jsfiddle.net/7mn0qy5b/2/
Here is my source :
HTML
<div id="graph1"></div>
<div id="graph2"></div>
CSS
#graph1, #graph2 {
width: 250px;
height: 250px;
border: 1px solid #000;
}
.link {
stroke: #626262;
strokeWidth: 2px;
}
JS
var graph = {};
function myGraph(el) {
this.link = {};
this.node = {};
this.container = el;
// Add and remove elements on the graph object
this.addNode = function (id) {
nodes.push({"id":id});
update();
};
this.removeNode = function (id) {
var i = 0;
var n = findNode(id);
while (i < links.length) {
if ((links[i]['source'] == n)||(links[i]['target'] == n))
{
links.splice(i,1);
}
else i++;
}
nodes.splice(findNodeIndex(id),1);
update();
};
this.removeLink = function (source,target){
for(var i=0;i<links.length;i++)
{
if(links[i].source.id == source && links[i].target.id == target)
{
links.splice(i,1);
break;
}
}
update();
};
this.removeallLinks = function(){
links.splice(0,links.length);
update();
};
this.removeAllNodes = function(){
nodes.splice(0,links.length);
update();
};
this.addLink = function (source, target, value) {
links.push({"source":findNode(source),"target":findNode(target),"value":value});
update();
};
var findNode = function(id) {
for (var i in nodes) {
if (nodes[i]["id"] === id) return nodes[i];
};
return null;
};
var findNodeIndex = function(id) {
for (var i=0;i<nodes.length;i++) {
if (nodes[i].id==id){
return i;
}
};
return null;
};
// set up the D3 visualisation in the specified element
var w = 250,
h = 250;
this.vis = d3.select(el)
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("id","svg")
.attr("pointer-events", "all")
.attr("viewBox","0 0 "+w+" "+h)
.attr("perserveAspectRatio","xMinYMid")
.append('svg:g');
this.force = d3.layout.force();
var nodes = this.force.nodes(),
links = this.force.links();
self = this;
var update = function () {
self.link = self.vis.selectAll("line")
.data(links, function(d) {
return d.source.id + "-" + d.target.id;
});
self.link.enter().append("line")
.attr("id",function(d){return d.source.id + "-" + d.target.id;})
.attr("class","link")
.append("title")
.text(function(d){
return d.value;
});
self.link.exit().remove();
self.node = self.vis.selectAll("g.node")
.data(nodes, function(d) {
return d.id;
});
var nodeEnter = self.node.enter().append("g")
.attr("class", "node")
.call(self.force.drag);
nodeEnter.append("svg:circle")
.attr("r", 16)
.attr("id",function(d) { return "svgNode_"+self.container+"_"+d.id;})
.attr("class","nodeStrokeClass");
nodeEnter.append("svg:text")
.attr("class","textClass")
.text( function(d){return d.id;}) ;
self.node.exit().remove();
self.force.on("tick", function() {
//console.log(self.container);
/*self.node.attr("cx", function(d) { return d.x = Math.max(d.radius, Math.min(svg[spaceId].attr('width') - d.radius, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(d.radius, Math.min(svg[spaceId].attr('height') - d.radius, d.y)); })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
*/self.node.attr("transform", function(d) { return "translate("+d.x+","+d.y+")"; });
self.link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
});
// Restart the force layout.
self.force
.gravity(.06)
.distance(100)
.charge(-300)
.size([w, h])
.start();
};
// Make it all go
update();
}
graph["#graph1"] = new myGraph("#graph1");
graph["#graph1"].addNode('A');
graph["#graph1"].addNode('B');
graph["#graph1"].addNode('C');
graph["#graph1"].addLink('A','B','10');
graph["#graph1"].addLink('A','C','8');
graph["#graph1"].addLink('B','C','15');
setTimeout(function() {
graph["#graph2"] = new myGraph("#graph2");
graph["#graph2"].addNode('D');
graph["#graph2"].addNode('E');
graph["#graph2"].addNode('F');
graph["#graph2"].addLink('D','E','10');
graph["#graph2"].addLink('D','F','8');
graph["#graph2"].addLink('E','F','15');
}, 2000);
Thank you for your help, I'm getting mad...
This line
self = this;
is missing a var keyword. Without it, self is assigned to global window scope instead the local myGraph scope. On the second run of myGraph constructor, first myGraph's window.self is overwritten with the new value. Therefore events in both myGraph objects reference the second self, which causes the breakage.
You might want to enable strict mode, so that the compiler will throw a warning on such a badly traceable error.