Related
I have two questions, one about the icons and other about code duplication.
I'm using this example:
http://bl.ocks.org/rveciana/6184054/bd294b921ebf2180eccc3aca548c895367fca2d2
(Thanks Roger Veciana)
I want to add an icon to each of those cirlces but I need to put the url inside a json instead of using the script to generate those cirlces (that is inside the code). Not sure what I'm doing wrong but is not working. Any tip on how can I add the icon to each circle ?
I've also added some text, and it is working. Basically I do this:
var circlemaint = svgmaint.selectAll("circle")
.data(nodes.maint)
.enter().append("g").append("circle")
.attr("r", function(d) { return d.radius; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.style("fill", function(d) { return d.color; })
.call(d3.drag);
var txtmaint = svgmaint.selectAll("circle")
.select('text')
.data(nodes.maint)
.enter().append('text')
.text(node => node.id)
.attr('font-size', 18)
.attr('dx', -25)
.attr('dy',20)
The issue here, is that I have 9 different svg's, and I don't want to duplicate this per svg. How can avoid code duplication?
I finally call it within the tick function and I do something like this:
function tick(e) {
simulation.alpha(0.2)
circlemaint
.each(gravity(this.alpha()))
.each(collide(.5))
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
txtmaint
.each(gravity(this.alpha()))
.each(collide(.5))
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
circleXXXX
......
txtXXXXXX
......
}
And here is the code of the example:
//Based in
///http://bl.ocks.org/mbostock/1804919
var margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var rect = [50, 50, width - 50, height - 50];
var n = 20,
m = 4,
padding = 6,
maxSpeed = 3,
radius = d3.scale.sqrt().range([0, 8]),
color = d3.scale.category10().domain(d3.range(m));
var nodes = [];
for (i in d3.range(n)) {
nodes.push({
radius: radius(1 + Math.floor(Math.random() * 4)),
color: color(Math.floor(Math.random() * m)),
x: rect[0] + (Math.random() * (rect[2] - rect[0])),
y: rect[1] + (Math.random() * (rect[3] - rect[1])),
speedX: (Math.random() - 0.5) * 2 * maxSpeed,
speedY: (Math.random() - 0.5) * 2 * maxSpeed
});
}
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.gravity(0)
.charge(0)
.on("tick", tick)
.start();
var svg = d3.select("body").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 + ")");
svg.append("svg:rect")
.attr("width", rect[2] - rect[0])
.attr("height", rect[3] - rect[1])
.attr("x", rect[0])
.attr("y", rect[1])
.style("fill", "None")
.style("stroke", "#222222");
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", function(d) {
return d.radius;
})
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style("fill", function(d) {
return d.color;
})
.call(force.drag);
var flag = false;
function tick(e) {
force.alpha(0.1)
circle
.each(gravity(e.alpha))
.each(collide(.5))
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
// Move nodes toward cluster focus.
function gravity(alpha) {
return function(d) {
if ((d.x - d.radius - 2) < rect[0]) d.speedX = Math.abs(d.speedX);
if ((d.x + d.radius + 2) > rect[2]) d.speedX = -1 * Math.abs(d.speedX);
if ((d.y - d.radius - 2) < rect[1]) d.speedY = -1 * Math.abs(d.speedY);
if ((d.y + d.radius + 2) > rect[3]) d.speedY = Math.abs(d.speedY);
d.x = d.x + (d.speedX * alpha);
d.y = d.y + (-1 * d.speedY * alpha);
};
}
// Resolve collisions between nodes.
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + radius.domain()[1] + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 ||
x2 < nx1 ||
y1 > ny2 ||
y2 < ny1;
});
};
}
<script src="https://d3js.org/d3.v3.js"></script>
Thank you so much!!
This is the code of the bubble chart i created. I have used force layout to create the chart.
var margin = {
top: 10,
right: 10,
bottom: 10,
left: 10
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
d3.select('#' + divId).append('div').attr('id', 'chart').attr('class', 'chart');
var n = data.vistaJson.length;
m = 1,
padding = 5,
radius = d3.scale.sqrt().range([10, 50]),
color = d3.scale.category10().domain(d3.range(m)),
x = d3.scale.ordinal().domain(d3.range(m)).rangePoints([0, width], 1);
var xscale = d3.scale.linear()
.domain([0, 500])
.range([20, 500]);
var nodes = [];
for(var i=0; i< n; i++){
var coordinates = data.vistaJson[i].SLAB.split('_');
v = data.vistaJson[i].COUNT
nodes.push({
radius: radius(v),
color: color(i),
count: v,
cx: xscale(x(i)),
cy: xscale(height / 2),
xAxis: coordinates[0],
yAxis: coordinates[1]
});
}
var svg = d3.select("#chart").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 + ")");
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.gravity(0.5)
.charge(0.5)
.on("tick", tick)
.start();
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", function (d) {
return d.radius;
})
.style("fill", function (d) {
return d.color;
})
.call(force.drag);
var labels = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr({"x":function(d){return d.x;},
"y":function(d){return d.y;}})
.text(function(d){return d.count;})
.call(force.drag);
circle.each(gravity(.2 * e.alpha))
.each(collide(.5))
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
});
labels.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
// Move nodes toward cluster focus.
function gravity(alpha) {
return function (d) {
d.y += (d.cy - d.y) * alpha;
d.x += (d.cx - d.x) * alpha;
};
}
// Resolve collisions between nodes.
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function (d) {
var r = d.radius + radius.domain()[1] + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function (quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
I want to add title to the node which is displayed when mouse is hovered on the node .
Earlier I used pack layout and I gave title like this :
var node = vis.selectAll("g.node")
.data(bubble.nodes(classes(json), function(d) { return d.name; })
.filter(function(d) { return !d.children; }))
.enter()
.append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + xscale(d.x) + "," + xscale(d.y) + ")"; });
node.append("svg:title")
.text(function(d) { return d.xAxis + ": " + d.yAxis; });
How can we show title when using forceLayout . Please help .
I think it should look something like this in the force layout:
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", function(d) { return d.nodes ? "nonleaf" : "leaf"; })
.attr("r", 5)
.call(force.drag);
node.append("title")
.text(function(d) { return d.count; });
I hope this helps somehow. For me it works like that..
Is it possible to create a sunburst that is shaped as a semicircle? I was able to give it the shape that I want, but I have problems with the nodes. Not all the colors appear on the new shape and the text is displayed wrong.
var partition = d3.layout.partition()
.sort(null)
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(Math.PI/360, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(Math.PI, x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, d.y ? y(d.y) : d.y); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
var nodes = partition.nodes(json);
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) { return "path-" + i; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.attr("fill", function(d) { return color((d.children ? d : d.parent).name); })
.on("click", function(d,i) {
//This is a mimic of the selection in the Tree List Box - picking all the parents of the selected cell
_this.Data.SearchColumn(0, "*" + d.name + "*",false);
});
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("opacity", 1)
.style("fill","#333")
.attr("text-anchor", function(d) {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI -90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + p) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
})
.on("click", function(d,i) {
_this.Data.SearchColumn(0, "*" + d.name + "*",false);
});
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) { return d.depth ? d.name.split(" ")[0] : ""; });
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) { return d.depth ? d.name.split(" ")[1] || "" : ""; });
textEnter.append("tspan")
.text(" ");
textEnter.append("tspan")
.text(function(d) { return d.depth ? d.name.split(" ")[2] || "" : ""; } );
textEnter.append("tspan")
.text(" ");
textEnter.append("tspan")
.text(function(d) { return d.depth ? d.name.split(" ")[3] || "" : ""; } );
I've solved the problem. This is the code I used:
var w = Math.min(heightc,widthc),
h = w/2,
r = w / 2-20,
x = d3.scale.linear().range([0, 1* Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, r]),
p = 4,
color = d3.scale.category20c(),
duration = 1000;
var div = d3.select("#"+myDivId);
div.select("img").remove();
var vis = div.append("svg")
.attr("width", w + p * 2)
.attr("height", h + p * 2)
.append("g")
.attr("transform", "translate(" + (p) + "," + (r + p) + ")");
var partition = d3.layout.partition()
.sort(null)
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, d.y ? y(d.y) : d.y); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
And the rest of the code remained the same.
I make a drill down pie chart and it works well.
You can see it here.
But there is an issue:
1) click a node then it shows its children (level 1) or level 0 nodes.
2) Move mouse to another position and move back to the new node which contains its original position, the node disappears.
I think there is problem in this code (gradientPie.js)
var paths = gPie.selectAll("path").data(pieChart(currData), function(d) {return d.data.cat;});
var texts = gPie.selectAll("text").data(pieChart(currData), function(d) {return d.data.cat;});
var lines = gPie.selectAll("line").data(pieChart(currData), function(d) {return d.data.cat;});
var arcs = paths.enter().append("g").attr('class', 'slice');
arcs.append("path").attr("fill", function(d, i) { return "url(#gradient" + d.data.cat + ")"; })
.transition().duration(1000).attrTween("d", tweenIn).each("end", function(){
this._listenToEvents = true;
gradPie.transitioning = false;
})
.attr("id", function(d, i){return 'p' + i;})
.each(function(d) { this._current = d; });
arcs.append("text").attr("transform", function(d) {
var c = d3.svg.arc().outerRadius(radius * 1.4).innerRadius(radius).centroid(d);
return "translate(" + (0 + c[0]) + "," + (0 + c[1]) + ")";
})
.attr("dy", ".35em")
.attr("class", "text-main")
.style("text-anchor", "middle")
.style("fill", "#3f5763")
.style("font", "bold 14px Helvetica")
.text(function(d) {
$("#" + d.data.domID + " p").html(d.data.percent + "%");
return d.data.percent + "%";
});
arcs.append("line").attr("transform", function (d, i) {
var rAngle = ((d.startAngle + d.endAngle) / 2 - (Math.PI / 2)) * 180 / Math.PI;
return "rotate(" + rAngle + ")translate(" + radius * 1.1 + ")";
})
.attr("class", "line-ticks")
.attr('stroke-width', '1')
.attr("x2", -0.5 * radius)
.style("stroke", "#3f5763")
.style("fill", "none");
// Mouse interaction handling
paths.on("click", function(d, i){
if(this.childNodes[0]._listenToEvents && !gradPie.transitioning){
// Reset inmediatelly
d3.select(this).attr("transform", "translate(0,0)")
// Change level on click if no transition has started
paths.each(function(){
this.childNodes[0]._listenToEvents = false;
});
updateGraph(d.data.subfractions? d.data.cat : undefined);
}
})
.on("mouseover", function(d, i){
// Mouseover effect if no transition has started
if(this.childNodes[0]._listenToEvents && !gradPie.transitioning) {
// Calculate angle bisector
var ang = (d.endAngle + d.startAngle)/2;
// Transformate to SVG space
ang = (ang - (Math.PI / 2) ) * -1;
// Calculate a 10% radius displacement
var x = Math.cos(ang) * radius * 0.1;
var y = Math.sin(ang) * radius * -0.1;
d3.select(this).transition()
.duration(250).attr("transform", function() {
return "translate(" + x + ", " + y + ")";
})
}
})
.on("mouseout", function(d){
// Mouseout effect if no transition has started
if(this.childNodes[0]._listenToEvents && !gradPie.transitioning){
d3.select(this).transition()
.duration(150).attr("transform", function() {
return "translate(0,0)";
});
}
});
// Collapse sectors for the exit selection
paths.exit().transition()
.duration(1000)
.attrTween("d", tweenOut).remove();
texts.exit().transition()
.duration(100).remove();
lines.exit().transition()
.duration(100).remove();
Any help?
I have not been able to show the tooltip for this graph:
http://mbostock.github.io/d3/talk/20111018/cluster.html
I have tried the simplest way without success:
node.append("svg:title").text(function(d) { return d.key; });
And also tried this without success:
var div = d3.select("body").append("div").attr("class", "tooltip")
.style("opacity", 0);
node.append("svg:circle")
.attr("r", 3)
.on("mouseover", function (d) {
div.transition()
.duration(0)
div.style("opacity", 1)
div.html(d.key)
.style("width", d.key.length + "mm")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
div.transition()
.duration(0)
.style("opacity", 0);
});
Here is the complete code:
var w = 1280,
h = 800,
rx = w / 2,
ry = h / 2,
m0,
rotate = 0;
var cluster = d3.layout.cluster()
.size([360, ry - 120])
.sort(null);
var diagonal = d3.svg.diagonal.radial()
.projection(function (d) {
return [d.y, d.x / 180 * Math.PI];
});
var svg = d3.select("body").append("div")
.style("width", w + "px")
.style("height", w + "px");
var vis = svg.append("svg:svg")
.attr("width", w)
.attr("height", w)
.append("svg:g")
.attr("transform", "translate(" + rx + "," + ry + ")");
vis.append("svg:path")
.attr("class", "arc")
.attr("d", d3.svg.arc().innerRadius(ry - 120).outerRadius(ry).startAngle(0).endAngle(2 * Math.PI))
.on("mousedown", mousedown);
var div = d3.select("body").append("div").attr("class", "tooltip")
.style("opacity", 0);
function drawNodes(graph) {
var nodes = cluster.nodes(graph);
var link = vis.selectAll("path.link")
.data(cluster.links(nodes))
.enter().append("svg:path")
.attr("class", "link")
.attr("d", diagonal);
var node = vis.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function (d) {
return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
})
node.append("svg:circle")
.attr("r", 3)
.on("mouseover", function (d) {
div.transition()
.duration(0)
div.style("opacity", 1)
div.html(/*"info:" + "<br/>" + */d.key)
.style("width", d.key.length + "mm")
//.style("height", "10mm") ???
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
div.transition()
.duration(0)
.style("opacity", 0);
});
node.append("svg:text")
.attr("dx", function (d) {
return d.x < 180 ? 8 : -8;
})
.attr("dy", ".31em")
.attr("text-anchor", function (d) {
return d.x < 180 ? "start" : "end";
})
.attr("transform", function (d) {
return d.x < 180 ? null : "rotate(180)";
})
.text(function (d) {
return d.name;
});
// TODO: does not work...not sure why...mouseover being captured already?
node.append("svg:title")
.text(function(d) { return d.key; });
}
//});
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup);
function mouse(e) {
return [e.pageX - rx, e.pageY - ry];
}
function mousedown() {
m0 = mouse(d3.event);
d3.event.preventDefault();
}
function mousemove() {
if (m0) {
var m1 = mouse(d3.event),
dm = Math.atan2(cross(m0, m1), dot(m0, m1)) * 180 / Math.PI,
tx = "translate3d(0," + (ry - rx) + "px,0)rotate3d(0,0,0," + dm + "deg)translate3d(0," + (rx - ry) + "px,0)";
svg.style("-moz-transform", tx)
.style("-ms-transform", tx)
.style("-webkit-transform", tx);
}
}
function mouseup() {
if (m0) {
var m1 = mouse(d3.event),
dm = Math.atan2(cross(m0, m1), dot(m0, m1)) * 180 / Math.PI,
tx = "rotate3d(0,0,0,0deg)";
rotate += dm;
if (rotate > 360) rotate -= 360;
else if (rotate < 0) rotate += 360;
m0 = null;
svg.style("-moz-transform", tx)
.style("-ms-transform", tx)
.style("-webkit-transform", tx);
vis.attr("transform", "translate(" + rx + "," + ry + ")rotate(" + rotate + ")")
.selectAll("g.node text")
.attr("dx", function (d) {
return (d.x + rotate) % 360 < 180 ? 8 : -8;
})
.attr("text-anchor", function (d) {
return (d.x + rotate) % 360 < 180 ? "start" : "end";
})
.attr("transform", function (d) {
return (d.x + rotate) % 360 < 180 ? null : "rotate(180)";
});
}
}
function cross(a, b) {
return a[0] * b[1] - a[1] * b[0];
}
function dot(a, b) {
return a[0] * b[0] + a[1] * b[1];
}
The CSS of the example is preventing the nodes from capturing the pointer and thus from showing the tooltips. If you remove the line
pointer-events: none;
from the CSS for .node, it works fine.