I'm trying to put two (or even more -> that depends on the users settings) d3 Wordclouds to one page. However I'll get the following result:
It seems, that the word list won't be parsed correct.
My code looks like this:
(The php $Block variable specifies the position, where the recent wordcloud should be shown.)
var container = "svg_<?php echo $Block;?>";
var w = $('#word_cloud_<?php echo $Block;?>').width();
var h = w*0.75;
if($( window ).width()<400){
var maxRange=20;
}else if($( window ).width()<800){
var maxRange=40;
}else if($( window ).width()<1200){
var maxRange=60;
}else if($( window ).width()>=1200){
var maxRange=95;
}
var list_<?php echo $Block;?>=<?php echo $jsonWort; ?>;
var wordSize=12;
var layout;
generate_<?php echo $Block;?>(list_<?php echo $Block;?>);
function generate_<?php echo $Block;?>(list) {
//Blacklist wird gefiltert!
var blacklist=["ein","sind"];
list=list.filter(function(x) { return blacklist.indexOf(x) < 0 });
// Liste wird verarbeitet
result = { };
for(i = 0; i < list.length; ++i) {
if(!result[list[i]])
result[list[i]] = 0;
++result[list[i]];
}
var newList = _.uniq(list);
var frequency_list = [];
var len = newList.length;
for (var i = 0; i < len; i++) {
var temp = newList[i];
frequency_list.push({
text : temp,
freq : result[newList[i]],
time : 0
});
}
frequency_list.sort(function(a,b) { return parseFloat(b.freq) - parseFloat(a.freq) } );
for(var t = 0 ; t < len ; t++)
{
var addTime = (100 * t) +500;
frequency_list[t].time=addTime;
}
for(i in frequency_list){
if(frequency_list[i].freq*wordSize > 160)
wordSize = 3;
}
var sizeScale = d3.scale.linear().domain([0, d3.max(frequency_list, function(d) { return d.freq} )]).range([5, maxRange]);
layout= d3.layout.cloud().size([w, h])
.words(frequency_list)
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return sizeScale(d.freq); })
.on("end",draw)
.start();
}
function draw(words) {
var fill = d3.scale.category20();
d3.select(container).remove();
d3.select("#word_cloud_<?php echo $Block;?>").append(container)
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + [w/2, h/2] + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.transition()
.duration(function(d) { return d.time} )
.attr('opacity', 1)
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "rotate(" + d.rotate + ")";
})
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; });
}
I believe that the mistake has to be somewhere in
var sizeScale = d3.scale.linear().domain([0, d3.max(frequency_list, function(d) { return d.freq} )]).range([5, maxRange]);
layout= d3.layout.cloud().size([w, h])
.words(frequency_list)
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font("Impact")
.fontSize(function(d) { return sizeScale(d.freq); })
.on("end",draw)
.start();
But I'm not able to find it.
You overwrite it because of the line
d3.select(container).remove();
With d3 you are basically creating complex SVGs on the fly. You can have as many elements as you want. As long as you do not remove them, they'll remain there. But you need to also make sure that they do not overlap. See here and (very cool!) here
Related
I have a issue with d3.js i am trying to make a grid where every line have "two state" (two color) as you can see. I generate the elements and it works great (maybe i could do it easier). My problem is that i need a "transformation". Which would be that if you click to a rectangle every sides of the rectangle change color.
My question specifically, is how can i make this function?
Thank you in advance.
Here is where im stuck:
// grid basic variables
var dimension = 10,
width = 50,
height = 50;
function gridData() {
var data = new Array();
// rectangle variables
var rectXpos = 0,
rectYpos = 0,
rectWidth = width,
rectHeight = height;
click = 0;
// iterate for rows
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension; column++) {
// rectClass = "rect" + rectXpos.toString() + rectYpos.toString();
data.push({
x: rectXpos,
y: rectYpos,
width: rectWidth,
height: rectHeight,
// class: rectClass,
click: click
});
// increment the x position. I.e. move it over by 50 (width variable)
rectXpos += rectWidth;
}
// reset the x position after a row is complete
rectXpos = 0;
// increment the y position for the next row. Move it down 50 (height variable)
rectYpos += rectHeight;
}
return data;
}
var gridData = gridData();
// I like to log the data to the console for quick debugging
console.log(gridData);
var grid = d3.select("#grid")
.append("svg")
.attr("width", width*dimension)
.attr("height",height*dimension);
var rect = grid.selectAll(".square")
.data(gridData)
.enter().append("rect")
.attr("class","rect")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.width; })
.attr("height", function(d) { return d.height; })
.style("fill", "#f2f2f2")
.style("stroke", "#fff")
.on('click', function(d) {
d.click ++;
d3.select(".vline" + d.x.toString() + d.y.toString() + (d.x + 50).toString() + d.y.toString()).style("stroke","#f4363f");
// d3.select(".vline" + d.x.toString() + (d.y + 50).toString() + (d.x + 50).toString() + (d.y + 50).toString()).style("stroke","#f4363f");
// d3.select(".hline" + d.x.toString() + d.y.toString() + d.x.toString() + (d.y + 50).toString()).style("stroke","#f4363f");
// d3.select(".hline" + (d.x + 50).toString() + d.y.toString() + (d.x + 50).toString() + (d.y + 50).toString()).style("stroke","#f4363f");
});
function hlinegriddata() {
var data = new Array();
// line variables
var hlineX1 = 0,
hlineY1 = 0,
hlineX2 = 0,
hlineY2 = 50,
click = 0;
var lineLength = width;
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension + 1; column++) {
hlineClass = "hline" + hlineX1.toString() + hlineY1.toString() + hlineX2.toString() + hlineY2.toString();
data.push({
x1: hlineX1,
y1: hlineY1,
x2: hlineX2,
y2: hlineY2,
class: hlineClass,
click: click
});
// increment the x position for the next line
hlineX1 += lineLength;
hlineX2 += lineLength;
}
// reset the x position after a row is complete
hlineX1 = 0;
hlineX2 = 0;
// increment the y position for the next row. Move it down 50 (height variable)
hlineY1 += lineLength;
hlineY2 += lineLength;
}
return data;
}
var hlinegriddata = hlinegriddata();
// I like to log the data to the console for quick debugging
console.log(hlinegriddata);
var hline = grid.selectAll(".hline")
.data(hlinegriddata)
.enter().append("line")
.attr("class", function(d) { return d.class; })
.attr("x1", function(d) { return d.x1; })
.attr("y1", function(d) { return d.y1; })
.attr("x2", function(d) { return d.x2; })
.attr("y2", function(d) { return d.y2; })
.style("stroke", "#fff")
.style("stroke-width", "4")
.style("cursor", "pointer")
.on('click', function(d) {
d.click ++;
if ((d.click)%2 == 0 ) { d3.select(this).style("stroke","#fff"); }
if ((d.click)%2 == 1 ) { d3.select(this).style("stroke","#f4363f"); }
});
function vlinegriddata() {
var data = new Array();
// line variables
var vlineX1 = 0,
vlineY1 = 0,
vlineX2 = 50,
vlineY2 = 0,
click = 0;
var lineLength = width;
// iterate for rows
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension + 1; column++) {
vlineClass = "vline" + vlineX1.toString() + vlineY1.toString() + vlineX2.toString() + vlineY2.toString();
data.push({
x1: vlineX1,
y1: vlineY1,
x2: vlineX2,
y2: vlineY2,
class: vlineClass,
click: click
});
// increment the x position for the next line
vlineY1 += lineLength;
vlineY2 += lineLength;
}
// reset the x position after a row is complete
vlineY1 = 0;
vlineY2 = 0;
// increment the y position for the next row. Move it down 50 (height variable)
vlineX1 += lineLength;
vlineX2 += lineLength;
}
return data;
}
var vlinegriddata = vlinegriddata();
// I like to log the data to the console for quick debugging
console.log(vlinegriddata);
var vline = grid.selectAll(".vline")
.data(vlinegriddata)
.enter().append("line")
.attr("class", function(d) { return d.class; })
.attr("x1", function(d) { return d.x1; })
.attr("y1", function(d) { return d.y1; })
.attr("x2", function(d) { return d.x2; })
.attr("y2", function(d) { return d.y2; })
.style("stroke", "white")
.style("stroke-width", "4")
.style("cursor", "pointer")
// .on("click", function(){var nextColor = this.style.stroke == "white" ? "magenta" : "white";
// d3.select(this).style("stroke", nextColor);});
.on('click', function(d) {
d.click ++;
if ((d.click)%2 == 0 ) { d3.select(this).style("stroke","#fff"); }
if ((d.click)%2 == 1 ) { d3.select(this).style("stroke","#f4363f"); }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="grid"></div>
Not sure I understand the purpose of all those lines. You could just stroke the rect:
.on('click', function(d) {
s.click ++;
d3.select(this)
.style("stroke", "#f4363f")
.style("stroke-width", "1px");
});
Running code:
// grid basic variables
var dimension = 10,
width = 50,
height = 50;
function gridData() {
var data = new Array();
// rectangle variables
var rectXpos = 0,
rectYpos = 0,
rectWidth = width,
rectHeight = height;
click = 0;
// iterate for rows
for (var row = 0; row < dimension; row++) {
// iterate for cells/columns inside rows
for (var column = 0; column < dimension; column++) {
// rectClass = "rect" + rectXpos.toString() + rectYpos.toString();
data.push({
x: rectXpos,
y: rectYpos,
width: rectWidth,
height: rectHeight,
// class: rectClass,
click: click
});
// increment the x position. I.e. move it over by 50 (width variable)
rectXpos += rectWidth + 1;
}
// reset the x position after a row is complete
rectXpos = 0;
// increment the y position for the next row. Move it down 50 (height variable)
rectYpos += rectHeight + 1;
}
return data;
}
var gridData = gridData();
// I like to log the data to the console for quick debugging
console.log(gridData);
var grid = d3.select("#grid")
.append("svg")
.attr("width", width*dimension)
.attr("height",height*dimension);
var rect = grid.selectAll(".square")
.data(gridData)
.enter().append("rect")
.attr("class","rect")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.width; })
.attr("height", function(d) { return d.height; })
.style("fill", "#f2f2f2")
.style("stroke", "#fff")
.on('click', function(d) {
d.click ++;
d3.select(this)
.style("stroke", "#f4363f")
.style("stroke-width", "1px");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="grid"></div>
I have a visual of a two ringed pie chart.
What I'm building is the following:
- if a sector's radians are too small for the specified text then the text is hidden. This is done and seems to work ok
- if text is hidden then a label should appear outside the pie. This is also done when the visual is initially rendered.
When the radio button is pressed the labels outside the pie should transition accordingly. I've attempted the transition and slowed it to 3500 and these labels are just slowly transitioning off the screen.
How do I fix this transition?
The snippet I've added to try to create the transition starts at line 241:
var arcs2 = svg.data([json]).selectAll(".arcG");
arcs2.data(partition.nodes)
.transition()
.duration(3500)
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("text-anchor", "middle");
svg.selectAll(".theTxtsOuter")
.text(function(d, i) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return d.name;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return d.name;
} else {
return;
}
});
This is a plunk of the working(!) visual:
https://plnkr.co/edit/jYVPCL?p=preview
Here is the complete javascript used by the pie:
function pieChart(dataFile) {
var plot;
var vis;
var width = 400,
height = 400,
radius = Math.min(width, height) / 2.1,
color = d3.scale.ordinal()
.range(["#338ABA", "#016da9", "#4c96d5"])
.domain([0, 2]);
var labelr = radius + 5 // radius for label anchor
var div = d3.select("body")
.append("div")
.attr("class", "toolTip");
var arc = d3.svg.arc()
.startAngle(function(d) {
return d.x;
})
.endAngle(function(d) {
return d.x + d.dx;
})
.outerRadius(function(d) {
return (d.y + d.dy) / (radius);
})
.innerRadius(function(d) {
return d.y / (radius);
});
//check if the svg already exists
plot = d3.select("#svgPIEChart");
if (plot.empty()) {
vis = d3.select("#pieChart")
.append("svg")
.attr({
id: "svgPIEChart"
});
} else {
vis = d3.select("#svgPIEChart");
vis.selectAll("*").remove();
}
//group of the svg element
var svg = vis
.append("g")
.attr({
'transform': "translate(" + width / 2 + "," + height * .52 + ")"
});
//svg element
vis.attr({
//set the width and height of our visualization (these will be attributes of the <svg> tag
width: width,
height: height
});
d3.text(dataFile, function(text) {
var csv = d3.csv.parseRows(text);
var json = buildHierarchy(csv);
// it seems d3.layout.partition() can be either squares or arcs
var partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(function(d) {
return d.SalesRev;
});
var path = svg.data([json]).selectAll(".theArc")
.data(partition.nodes)
.enter()
.append("path")
.attr("class", "theArc")
.attr("id", function(d, i) {
return "theArc_" + i;
}) //Give each slice a unique ID
.attr("display", function(d) {
return d.depth ? null : "none";
})
.attr("d", arc)
.style("stroke", "#fff")
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.attr("fill-rule", "evenodd")
.style("opacity", 0.01)
.style("stroke-opacity", 0.01)
.each(stash);
path.transition()
.duration(PIEOBJ.transTime)
.style("opacity", 1)
.style("stroke-opacity", 1)
path
.on("mouseout", mouseout)
.on("mousemove", function(d) {
div.style("left", d3.event.pageX + 10 + "px");
div.style("top", d3.event.pageY - 25 + "px");
div.style("display", "inline-block");
div.html(d.name + "<br>" + PIEOBJ.formatShrtInt(d.SalesRev));
})
var txts = svg.data([json]).selectAll(".theTxts")
.data(partition.nodes)
.enter()
.append("text");
txts
.attr("class", "theTxts")
.attr("dx", 10) //Move the text from the start angle of the arc
.attr("dy", 15) //Move the text down
.style("opacity", 0)
txts
.transition()
.duration(PIEOBJ.transTime)
.style("opacity", 1);
var txtPths = txts.append("textPath")
// .attr("xlink:href", function(d, i) {
.attr("href", function(d, i) {
return "#theArc_" + i;
})
.text(function(d) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return;
} else {
return d.name;
}
});
/* ------- TEXT LABELS OUTSIDE THE PIE-------*/
//var arcs = svg.selectAll(".theArc");
var arcs = svg.data([json]).selectAll(".arcG")
.data(partition.nodes)
.enter()
.append("g")
.attr("class", "arcG");
arcs.append("text")
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
console.log(c, h);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("text-anchor", "middle")
.text(function(d, i) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return d.name;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return d.name;
} else {
return;
}
})
.attr("class", "theTxtsOuter");
/* ----------------------------------------*/
d3.selectAll("input").on("change", function change() {
function createValueFunc(val) {
// currentMeasure = val;
return function(d) {
return d[val];
};
}
value = createValueFunc(this.value);
PIEOBJ.currentMeasure = this.value;
var path2 = svg.data([json]).selectAll(".theArc");
path2
.data(partition.value(value).nodes)
.transition()
.duration(1500)
.attrTween("d", arcTween)
.each("start", function() {
d3.select(this)
.on("mouseout", null) //CLEARING the listeners
.on("mousemove", null);
})
.each("end", function() {
d3.select(this)
.on("mouseout", mouseout) //attaching the listeners
.on("mousemove", function(d) {
div.style("left", d3.event.pageX + 10 + "px");
div.style("top", d3.event.pageY - 25 + "px");
div.style("display", "inline-block");
div.html(d.name + "<br>" + PIEOBJ.formatShrtInt(value(d)));
});
});
svg.selectAll("textPath")
.text(function(d) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return;
} else {
return d.name;
}
});
var arcs2 = svg.data([json]).selectAll(".arcG");
arcs2.data(partition.nodes)
.transition()
.duration(3500)
.attr("transform", function(d) {
var c = arc.centroid(d),
x = c[0],
y = c[1],
// pythagorean theorem for hypotenuse
h = Math.sqrt(x * x + y * y);
return "translate(" + (x / h * labelr) + ',' +
(y / h * labelr) + ")";
})
.attr("text-anchor", "middle");
svg.selectAll(".theTxtsOuter")
.text(function(d, i) {
if (d.name === 'root') {
return;
} else if ((d.depth === 1) && (d.dx < (d.name.length * 0.15))) {
return d.name;
} else if ((d.depth === 2) && (d.dx < (d.name.length * 0.1))) {
return d.name;
} else {
return;
}
});
// the following deletes what was originally created and then recreates the text
// svg.selectAll("#titleX").remove();
});
function mouseout() {
div.style("display", "none"); //<< gets rid of the tooltip <<
}
// Stash the old values for transition.
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
}
// Interpolate the arcs in data space.
function arcTween(a) {
var i = d3.interpolate({
x: a.x0,
dx: a.dx0
}, a);
return function(t) {
var b = i(t);
a.x0 = b.x;
a.dx0 = b.dx;
return arc(b);
};
}
});
}
// // Take a 2-column CSV and transform it into a hierarchical structure suitable
// // for a partition layout.
function buildHierarchy(csv) {
var root = {
"name": "root",
"children": []
};
for (var i = 0; i < csv.length; i++) {
var sequence = csv[i][0];
// var APD = +csv[i][1];
var SalesRev = +csv[i][1];
var Amount = +csv[i][2];
if (isNaN(SalesRev)) { // e.g. if this is a header row
continue;
}
var parts = sequence.split("-");
var currentNode = root;
for (var j = 0; j < parts.length; j++) {
var children = currentNode.children;
var nodeName = parts[j];
var childNode;
if (j + 1 < parts.length) {
// Not yet at the end of the sequence; move down the tree.
var foundChild = false;
for (var k = 0; k < children.length; k++) {
if (children[k].name == nodeName) {
childNode = children[k];
foundChild = true;
break;
}
}
// If we don't already have a child node for this branch, create it.
if (!foundChild) {
childNode = {
"name": nodeName,
"children": []
};
children.push(childNode);
}
currentNode = childNode;
} else {
// Reached the end of the sequence; create a leaf node.
childNode = {
"name": nodeName,
// "APD": APD,
"SalesRev": SalesRev,
"Amount": Amount
};
children.push(childNode);
}
}
}
root.children.forEach(function(v) {
v.SalesRev = 0;
v.Amount = 0;
v.children.forEach(function(a) {
v.SalesRev += a.SalesRev;
v.Amount += a.Amount;
});
});
return root;
}
When you initially position them you are transforming the text elements. When you transition them you are positioning the outer g elements. These causes conflicting transforms. Use:
arcs2.data(partition.nodes)
.select('text') //<-- apply on child text
.transition()
.duration(3500)
.attr("transform", function(d) {
...
});
Updated plunker.
I am trying to add panning only (don't want zoom for now), to my D3 force-directed graph visualization. No matter what I try, I keep getting an error - "Cannot read property 'translate' of null". I feel as though I am setting things up like in all the examples I find, but something isn't quite right.
Here's my latest attempt. As far as I know, the relevant code should be at the top where I define svg, but I included the rest in case something is affecting it of which I'm not aware.
var isiOS = false;
var agent = navigator.userAgent.toLowerCase();
if(agent.indexOf('iphone') >= 0 || agent.indexOf('ipad') >= 0){
isiOS = true;
};
var h = 1000;
var w = 1000;
var width = 1500,
var height = 1500;
var default_node_color = '#FF9900';
var shining_node_color = '#fc5f05';
var text_color = 'white';
var link_color = '#666699';
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", function() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}))
.append('g');
var g = svg.append('g');
var force = d3.layout.force()
.gravity(0.05)
.distance(100)
.charge(-100)
.size([w, h]);
d3.xhr("{{ url_for('visualization_data', interactions_filter = interactions_filter)}}")
.header("Content-Type", "application/json")
.post(
JSON.stringify({
"fileindicator": "{{fileindicator}}"
}), function(error, rawData) {
if (error) throw error;
var json = JSON.parse(rawData.response);
force
.nodes(json.nodes)
.links(json.links)
.start();
var link_scale = d3.scale.linear()
.domain([0, d3.max(json.links, function(d) {
return (d.value);
})])
.range([1, 13]);
var link = g.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return link_scale(d.value);
})
.style('stroke', link_color)
.attr("fill-opacity", .75);
var node = g.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag)
.style("fill", default_node_color);
var radius_scale = d3.scale.sqrt()
.domain([0, d3.max(json.nodes, function(d) {
return (d.n_interactions);
})])
.range([2, 17]);
if (isiOS) {
// single click centers node
var centerEvent = "click";
} else {
// double click centers node
var centerEvent = "dblclick";
}
var all_links = d3.selectAll('.link');
var all_nodes = d3.selectAll('.node');
//populate select box
var select_box = $('.chzn-select');
select_box.empty();
select_box.trigger('chosen:updated');
var character_to_node_id = []
for (var i = 0; i < all_nodes[0].length; i++) {
var option = '<option value="' + all_nodes[0][i].__data__.index + '">' + all_nodes[0][i].__data__.name + '</option>';
character_to_node_id.push(option);
select_box.append("<option value='' selected='selected'></option>");
select_box.append(character_to_node_id);
var onClick = window.onClick = function(d, i) {
// console.log(this)
// console.log('d, i, this:', d, i, this);
var index_of_clicked_node = d.index;
// Moves clicked node to center
var dcx = (w / 2 - d.x * zoom.scale());
var dcy = (h / 2 - d.y * zoom.scale());
zoom.translate([dcx, dcy]);
g.transition()
.duration(2000)
.attr("transform", "translate(" + dcx + "," + dcy + ")scale(" + zoom.scale() + ")");
//select all links NOT related to the clicked node and fade
var fade_links = d3.selectAll('.link').filter(function(d) {
return index_of_clicked_node !== d.source.index && index_of_clicked_node !== d.target.index;
});
// console.log(fade_links);
fade_links.style('opacity', .2);
var active_links = d3.selectAll('.link').filter(function(d) {
return index_of_clicked_node === d.source.index || index_of_clicked_node === d.target.index;
});
// active_links.style('stroke', 'red')
active_links.style('opacity', .85);
var active_link_id_list = [];
active_links[0].forEach(function(link) {
active_link_id_list.push(link.__data__.target.index)
active_link_id_list.push(link.__data__.source.index)
});
console.log('number of active links: ', active_link_id_list.length);
//select all nodes not connected to the clicked node and fade
var fade_nodes = d3.selectAll('.node').filter(function(d) {
return !active_link_id_list.includes(d.index);
});
fade_nodes.style('opacity', .2);
var active_nodes = d3.selectAll('.node').filter(function(d) {
return active_link_id_list.includes(d.index);
});
console.log('number of active nodes: ', active_nodes);
d3.selectAll('circle').filter(function(d, i) {
if (d.name === name) {
// call click event on the circle
window.onClick(d, i);
}
});
active_nodes.style('opacity', .85)
.transition()
.duration(1000)
.style('fill', shining_node_color)
.transition()
.duration(1000)
.style('fill', default_node_color);
};
node.append('circle')
.attr("class", "circle")
.attr("r", function(d, i) {
return radius_scale(d.n_interactions);
})
.style('opacity', .85)
.style('stroke', 'black')
.style('stroke-width', 10)
.style('stroke-opacity', 0.0)
.call(force.drag)
.on(centerEvent, onClick);
var text_scale = d3.scale.linear()
.domain([0, d3.max(json.nodes, function(d) {
return (d.n_interactions);
})])
.range([6, 16]);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.attr('fill', 'black')
.text(function(d) {
return d.name
})
.style("font-size", function(d) {
return text_scale(d.n_interactions) + "px";
})
.style('font-family', "futura-pt")
.style('fill', text_color);
force.on("tick", function() {
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
$('#loading-text').hide();
I have been trying to merge a set of paths in d3, So that one color blends into the other so it appears as though forms a gradient i tried to use to create a gradient but its always in a single direction which does not work out.
Fiddle here https://jsfiddle.net/roug3/jnpe5v3p/
var mapGroup = d3.select("svg");
function renderARC() {
var txData = {x: 200 , y : 200 , angle : 30};
var etxD = {etxSN : "TX500"};
if(d3.select(".arc"+etxD.etxSN).node()){
return;
}
var arcLevel = 5;
var arcSpan = 20;
var arcAngle = 2.0944;
var txAngle = txData.angle + 0;
var startAngle = txAngle - (arcAngle / 2);
var endAngle = txAngle + (arcAngle / 2);
var x = txData.x;
var y = txData.y;
var cwidth = 20;
var dataset = {};
for(var i = 1;i<= arcLevel;i++){
dataset[i] = [i];
}
var color = ["#ee4035","#f37736","#fdf498","#7bc043","#0392cf"]
// var color = ["#009933" , "#33cc33" ,"#ff3300" , "#ffcc66" ,"#ff6699" ,"#4dffff"];
var pie = d3.layout.pie()
.sort(null)
.startAngle(startAngle)
.endAngle(endAngle);
var arc = d3.svg.arc();
var gs = mapGroup.append("g").classed("arc"+etxD.etxSN , true).classed("arcSegment" , true);
console.log(gs);
var ggs = gs.selectAll("g").data(d3.values(dataset)).enter().append("g");
var arcP = ggs.selectAll("path").data(function (d) {
return pie(d);
})
.enter();
arcP.append("path").
attr("class" , function (d, i) {
return "arcID"+etxD.etxSN+i;
})
.attr("fill", function (d, i, j) {
// var cspan = Math.floor(Math.random() * arcLevel);
return color[ j ];
})
.attr("d", function (d, i, j) {
return arc.innerRadius(cwidth * j + arcSpan).outerRadius(cwidth * (j + 1) + arcSpan)(d);
}).
attr("transform" , "translate("+x+","+y+")");
}
renderARC();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width=500 height=500></svg>
Any Suggestions
Thanks
This is as close as I could get it : https://jsfiddle.net/thatoneguy/jnpe5v3p/2/
With the help of these :
http://jsfiddle.net/Qh9X5/1110/
http://www.w3schools.com/svg/svg_grad_radial.asp
Basically you have to create a radial blur using the dataset :
var grads = mapGroup.append("defs").selectAll("radialGradient").data(pie(d3.values(dataset)))
.enter().append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", function(d, i) {
return cwidth * (i + 1) + arcSpan
})
.attr("id", function(d, i) {
return "grad" + i;
}).attr("transform", "translate(" + x + "," + y + ")");;
grads.append("stop")
.attr("offset", "80%")
.style("stop-color", function(d, i) {return color[i];});
grads.append("stop")
.attr("offset", "100%")
.style("stop-color", function(d, i) {
if (color[i + 1]) {
console.log(color[i + 1])
return color[i + 1];
} else {
return color[i];
}
})
Then select this to fill your paths :
arcP.append("path").
attr("class", function(d, i) {
return "arcID" + etxD.etxSN + i;
})
.attr("fill", function(d, i) {
console.log(count)
count++;
return "url(#grad" + count + ")";
})
.attr("d", function(d, i, j) {
return arc.innerRadius(cwidth * j + arcSpan).outerRadius(cwidth * (j + 1) + arcSpan)(d);
}).
attr("transform", "translate(" + x + "," + y + ")");
var mapGroup = d3.select("svg");
function renderARC() {
var txData = {
x: 200,
y: 200,
angle: 30
};
var etxD = {
etxSN: "TX500"
};
if (d3.select(".arc" + etxD.etxSN).node()) {
return;
}
var arcLevel = 5;
var arcSpan = 20;
var arcAngle = 2.0944;
var txAngle = txData.angle + 0;
var startAngle = txAngle - (arcAngle / 2);
var endAngle = txAngle + (arcAngle / 2);
var x = txData.x;
var y = txData.y;
var cwidth = 20;
var dataset = {};
for (var i = 1; i <= arcLevel; i++) {
dataset[i] = [i];
}
var color = ["#ee4035", "#f37736", "#fdf498", "#7bc043", "#0392cf"]
// var color = ["#009933" , "#33cc33" ,"#ff3300" , "#ffcc66" ,"#ff6699" ,"#4dffff"];
var pie = d3.layout.pie()
.sort(null)
.startAngle(startAngle)
.endAngle(endAngle);
var arc = d3.svg.arc();
var gs = mapGroup.append("g").classed("arc" + etxD.etxSN, true).classed("arcSegment", true);
console.log(gs);
var ggs = gs.selectAll("g").data(d3.values(dataset)).enter().append("g");
var arcP = ggs.selectAll("path").data(function(d) {
return pie(d);
})
.enter();
var grads = mapGroup.append("defs").selectAll("radialGradient").data(pie(d3.values(dataset)))
.enter().append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", function(d, i) {
return cwidth * (i + 1) + arcSpan
})
.attr("id", function(d, i) {
return "grad" + i;
}).attr("transform", "translate(" + x + "," + y + ")");;
grads.append("stop")
.attr("offset", "80%")
.style("stop-color", function(d, i) {return color[i];});
grads.append("stop")
.attr("offset", "100%")
.style("stop-color", function(d, i) {
if (color[i + 1]) {
console.log(color[i + 1])
return color[i + 1];
} else {
return color[i];
}
})
var count = -1;
arcP.append("path").
attr("class", function(d, i) {
return "arcID" + etxD.etxSN + i;
})
.attr("fill", function(d, i) {
console.log(count)
count++;
return "url(#grad" + count + ")";
})
.attr("d", function(d, i, j) {
return arc.innerRadius(cwidth * j + arcSpan).outerRadius(cwidth * (j + 1) + arcSpan)(d);
}).
attr("transform", "translate(" + x + "," + y + ")");
}
renderARC();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width=500 height=500></svg>
It isn't perfect but will put you on the right track :) Hope this helps
So, I've been updating a functioning but not elegant D3 chart using https://github.com/NickQiZhu/d3-cookbook/blob/master/src/chapter9/pie-chart.html
My class looks like this:
function doughnutChart(selector_div) {
"use strict";
var _chart = {};
var _width = 200, _height = 200,
_data = [],
_svg, _bodyG, _pieG,
_radius = 100,
_inner_radius = 50;
_chart.render = function() {
if (!_svg) {
_svg = d3.select(selector_div).append("svg")
.attr("height", _height)
.attr("width", _width);
}
renderBody(_svg);
};
function renderBody(svg) {
if (!_bodyG) {
_bodyG = svg.append("g")
.attr("class", "body");
}
renderDoughnut();
}
function renderDoughnut() {
var pie = d3.layout.pie()
.sort(function (d) {
return d.id;
})
.value(function (d) {
return d.count + d.abnormal;
});
var arc = d3.svg.arc()
.outerRadius(_radius)
.innerRadius(_inner_radius);
if (!_pieG) {
_pieG = _bodyG.append("g")
.attr("class", "pie")
.attr("transform", "translate("
+ _radius
+ ","
+ _radius + ")");
}
renderSlices(pie, arc);
renderLabels(pie, arc);
}
}
function renderSlices(pie, arc) {
var slices = _pieG.selectAll("path.arc")
.data(pie(_data));
slices.enter()
.append("path")
.attr("class", "arc")
.attr("fill", function(d) {
return d.data.visualisation_colour;
});
slices.transition()
.attrTween("d", function(d) {
var currentArc = this.__current__;
if (!currentArc) {
currentArc = {startAngle: 0, endAngle: 0};
}
var interpolate = d3.interpolate(currentArc, d);
this.__current__ = interpolate(1);
return function(t) {
return arc(interpolate(t));
};
});
}
function renderLabels() {
_pieG.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "inside")
.text(function(d) {
var total = 0;
for (var j = 0; j < _data.length; j++) {
total = total + _data[j].count + _data[j].abnormal;
}
return total;
});
}
_chart.data = function(d) {
if (!arguments.length) {
return _data;
}
_data = d;
return _chart;
};
return _chart;
}
When I use:
var chart = doughnutChart("#chart").data(data);
chart.render()
I get a nice chart rendered. But the update doesn't work:
data = $.map(cell_types, function(key, value) {
return key;
});
chart.render();
The main issue is:
How do I update this chart? I'm not sure how to get updated data into the chart. Calling render() again does not update the data despite the data variable being updated, and I can't seem to pass new data in. The book's example doesn't appear to have this issue, as testing that works without issue.
There is actually a simple error in syntax here:
if (!_pieG) {
_pieG = _bodyG.append("g")
.attr("class", "pie")
.attr("transform", "translate("
+ _radius
+ ","
+ _radius + ")");
renderSlices(pie, arc);
renderLabels(pie, arc);
}
Should actually be:
if (!_pieG) {
_pieG = _bodyG.append("g")
.attr("class", "pie")
.attr("transform", "translate("
+ _radius
+ ","
+ _radius + ")");
}
renderSlices(pie, arc);
renderLabels(pie, arc);
And then it updates correctly.