https://hansardbrowser.s3.amazonaws.com/Cooccurmatrix/index%20%2812.12.2015%207.43.36%20PM%29.html
I am currently trying to recreate the co-occurrence matrix from the D3 Les Miserables demo.
I am able to add more variables to the "order" drop down list, and I am currently trying to add the code to change the color as well.
The current setting defaults to "party" as the color option, but how can I insert the new coloroption value into the .style code and refresh?
Original grouping change code:
d3.select("#order").on("change", function() {
clearTimeout(timeout);
order(this.value);
});
function order(value) {
x.domain(orders[value]);
var t = svg.transition().duration(2500);
t.selectAll(".row")
.delay(function(d, i) { return x(i) * 4; })
.attr("transform", function(d, i) { return "translate(0," + x(i) + ")"; })
.selectAll(".cell")
.delay(function(d) { return x(d.x) * 4; })
.attr("x", function(d) { return x(d.x); });
t.selectAll(".column")
.delay(function(d, i) { return x(i) * 4; })
.attr("transform", function(d, i) { return "translate(" + x(i) + ")rotate(-90)"; });
}
My attempt to add the color change function in (sorry i am extremely new to D3 and javascript)
d3.select("#coloroption").on("change", function() {
coloroption(this.value);
});
function coloroption(value) {
var cell = d3.select(this).selectAll(".cell")
.data(row.filter(function(d) { return d.z; }))
.enter().append("rect")
.attr("class", "cell")
.attr("x", function(d) { return x(d.x); })
.attr("width", x.rangeBand())
.attr("height", x.rangeBand())
.style("fill-opacity", function(d) { return z(d.z); })
.style("fill", function(d) { return nodes[d.x].(coloroption[value]) == nodes[d.y].(coloroption[value]) ? c(nodes[d.x].(coloroption[value]) ) : null; })
//.on("mouseover", mouseover)
// .on("mouseout", mouseout);
}
Not sure if this is the right way I should handle this
This .(coloroption[value]) is not valid javascript; you need to access the properties using bracket notation. The d3 portion of this is then a simple .selectAll with a style change:
d3.select("#coloroption").on("change", function() {
// get selected value from dropdown
var opt = this.options[this.selectedIndex].value;
svg.selectAll(".cell")
.style("fill", function(d){
// get it by bracket notation
return nodes[d.x][opt] == nodes[d.y][opt] ? c(nodes[d.x][opt]) : null;
})
});
Example here.
Related
I'm trying to put labels on my d3js grouped bar chart, which is supposed to be easy, but I haven't been able to do it correctly. I know it must be similar to the way I'm adding the rects but no.. I tried to follow this example but it didn't work.
This is how I add my rectangles:
var rectG = pp.selectAll('rect')
.data(dataFilter);
rectG.exit().remove();
var rectGEnter= rectG.enter().append("g")
.append("g")
.attr("transform", function(d) {return "translate(" + x(d.group) + ",0)"; })
.selectAll("rect")
.data(function(d) { return subgroups.map(function(key) {return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return xSubgroup(d.key); })
.attr("y", function(d) { return y(d.value? d.value : 0); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function(d) { return height - y(d.value? d.value : 0); })
.attr("fill", function(d) { return getColor(d.key); })
so I tried to do the same with the lables but didn't work, except for this:
rectGEnter= rectG.enter().append("g")
.append("g")
.attr("transform", function(d) {return "translate(" + x(d.group) + ",0)"; })
.selectAll("text")
.data(function(d) { return subgroups.map(function(key) {return {key: key, value: d[key]}; }); })
.enter().append("text")
.attr("x", function(d) { return xSubgroup(d.key)+1; })
.attr("y", function(d) { return y(d.value? d.value : 0); })
.attr("text-anchor", "start")
.style("alignment-baseline", "middle")
.text(function(d) { return (d.value? d.value : 0); });
}
and if ok for the first time, but if I update my data the labels don't update well, I haven an example here, any help will be apreciate
If I understand right you only want to remove the old labels. I tried it in you plunker. The following line of code does the trick.
function updates(selectedGroup) {
pp.selectAll("rect").remove();
pp.selectAll("text").remove(); // <-- remove the old text elements
...
EDIT:
To prevent the removal of the labels we can simply add a class to specify which text we want to remove.
rectGEnter= rectG.enter().append("g")
.append("g")
.attr("transform", function(d) {return "translate(" + x(d.group) + ",0)"; })
.selectAll("text")
.data(function(d) { return subgroups.map(function(key) {return {key: key, value: d[key]}; }); })
.enter().append("text")
.attr('class', 'valueLabels') // <-- Here we add a class
.attr("x", function(d) { return xSubgroup(d.key)+1; })
.attr("y", function(d) { return y(d.value? d.value : 0); })
.attr("text-anchor", "start")
.style("alignment-baseline", "middle")
.text(function(d) { return (d.value? d.value : 0); });
Now we can specify the class when removing the elements.
function updates(selectedGroup) {
pp.selectAll("rect").remove();
pp.selectAll('g').selectAll('g').selectAll("text.valueLabels").remove(); // <-- remove the old text elements
...
I'm pretty new to coding in D3. I'm working on a near real-time circle pack chart that gets its underlying data from an ajax call and resizes the nodes based on changes in data values. The challenge I'm facing is likely to be dreadfully simple, but I've not yet found a similar-enough example online to leverage as a solution.
When I run this code, I know that the text values are actually being passed properly as the data changes. However, what's happening is that the code keeps appending text tags to the svg "g" nodes (with the updated values) rather than changing the existing element to reflect the updated value. The result is a layered text mess in the middle of an otherwise attractive bubble.
I have tried using d3.exit().remove() to no avail - it's possible that I misused it and that it's actually the appropriate technique to apply.
Would someone be willing to provide some guidance on how I should accomplish 2 specific things:
1) I'd like to re-use existing "text" elements rather than remove + append unless it's not practical.
2) I'd like to update the values of an existing "text" element with new data without refreshing the page.
The full code for the .js file is here below. I'm aware that I can use "svg" instead of "svg:svg", etc. but I haven't gotten to the tidying-up stage on this file yet.
var Devices = {
setup_devices : function() {
var r = 500,
format = d3.format(",d"),
fill = d3.scale.category10();
var bubble = d3.layout.pack()
.sort(null)
.size([r, r])
.padding(1.5);
var chart = d3.select("#device_info").append("svg:svg")
.attr("width", r)
.attr("height", r)
.attr("class", "bubble")
.append("svg:g")
.attr("transform", "translate(2, 2)");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Device:</strong> <span style='color:red'>" + d.name + "</span>";
});
chart.call(tip);
setInterval(function() {
console.log("Devices Refreshing");
$.ajax({
type: "GET",
url: "/devices",
dataType: "json",
beforeSend: function() {
},
error: function( jqXHR, textStatus, thrownError ) {
return true;
},
success: function(data) {
update(data);
return true;
}
});
d3.timer.flush();
}, 2000);
function update(data) {
var updated = chart.data([data]).selectAll("g.node")
.data(bubble.nodes);
updated.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.attr("data-name", function(d) {
return d.name;
})
.attr("data-device", function(d) {
return d.device_id;
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.append("svg:circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return fill(d.name); })
.attr("text-anchor", "middle")
.attr("dy", ".3em")
.text(function(d) { return d.value + "%" });
updated.append("svg:text")
.attr("text-anchor", "middle")
.attr("dy", ".3em")
.text(function(d) { return d.value + "%" });
updated.transition()
.duration(1000)
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
updated.select("circle").transition()
.duration(1000)
.attr("r", function(d) { return d.r; })
.text(function(d) { return d.value + "%" });
}
}
}
You just need to handle the enter and update selections separately -- to the enter selection you append, for the update selection you reuse existing elements.
var enterGs = updated.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.attr("data-name", function(d) {
return d.name;
})
.attr("data-device", function(d) {
return d.device_id;
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
enterGs.append("circle");
enterGs.append("text")
.attr("text-anchor", "middle")
.attr("dy", ".3em");
updated.select("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return fill(d.name); });
updated.select("text")
.text(function(d) { return d.value + "%" });
I'm trying to add a coloured rectangle on the left of each checkbox of a list, which are created automatically by d3.js with this code:
d3.selectAll("#checkbox")
.selectAll("label")
.data(dataset, function (d) { return d.Grazie; })
.enter()
.append("div")
.attr("id", function (d) { return d.Grazie; })
.append("label")
.attr("class", "label")
.attr("id", function (d) { return d.Grazie; })
.text(function (d) { return d.Grazie; })
.append("input")
.attr({
type: "checkbox",
class: "Grazie",
name: "mode",
value: function (d, i) { return d.Grazie; }
})
.attr("id", function (d, i) { return d.Grazie; })
.attr("x", margin)
.attr("y", margin)
.attr("selected", null);
I tried appending an svg and then a rectangle after this process, but what I get is that the rectangle is placed after the text and the checkbox, with this code:
d3.select("#checkbox")
.select("#Sans")
.append("svg")
.attr("width", 8 + "px")
.attr("height", 8 + "px")
.append("rect")
.attr("fill", function (d) {
if (d.Grazie == "Sans") { return "#386cb0"; }
else { return "#f0027f"}})
.attr("x", 0)
.attr("y", 0)
.attr("width", 8 + "px")
.attr("height", 8 + "px");
The order I'd like to have is:
Rectangle - text - checkbox
I guess that the code for the rectangle could be integrated while creating the checkbox, but wasn't able to make it work. Any help is really appreciated, thanks!
You should break things up like this:
var entries = d3.select("#checkbox") // select is more appropriate than selectAll
.selectAll("div.list-entry")
.data(dataset, function (d) { return d.Grazie; })
var newEntries = entries.enter() // newEntries holds the result of .enter()
.append("div")
.attr("id", function (d) { return d.Grazie; })
.attr("class", "list-entry")
newEntries // now you can append various stuff into each div in newEntries
.append("label")
.attr("class", "label")
.attr("id", function (d) { return d.Grazie; })
.text(function (d) { return d.Grazie; })
newEntries
.append("input")
.attr({
type: "checkbox",
class: "Grazie",
name: "mode",
value: function (d, i) { return d.Grazie; }
})
.attr("id", function (d, i) { return d.Grazie; })
.attr("x", margin)
.attr("y", margin)
.attr("selected", null);
Then you can insert the rectangle creation code right before the label creation code (following the same pattern used for label and input creation).
But... is there a reason why you're using and <svg> and a <rect> to make a simple rectangle? That's complicated. Why not just use a plain old <div> with css width, height, background-color and display:inline-block or float:left?
I have circles arranged with the pack layout, from a dataset which periodically updates the radii.
The code I started out with is this standard example for a bubble chart: http://bl.ocks.org/mbostock/4063269
Whenever the circle sizes change, they transition. Often when circles grow, they move to overlap other circles. I don't want them to overlap each other.
I'm still pretty new to d3, have moved the code around a lot and tried everything I can think of, but no luck.
The function makeBubbles is passed raw Json (see below).
function makeBubbles(root){
var diameter = $(window).width(),
diameterh = $(window).height(),
format = d3.format(",d"),
color = d3.scale.category20();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameterh])
.value(function(d){return d.value; })
.padding(1.5);
var svg = d3.select("svg")
.attr("width", diameter)
.attr("height", diameterh)
.attr("class", "bubble");
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root)).filter(function(d) { return !d.children; }), function(d){ console.log(d); return d.className; });
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.style("fill", function(d) { return color(d.packageName); })
.on("click", function(d) { window.location = d.url; })
.attr("r", 0)
.transition()
.duration(1000)
.attr("r", function(d) { return d.r; });
node.transition().duration(1000).attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.exit().transition().duration(200).attr("transform", "scale(0.001)").remove();
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 6); })
.attr("opacity",0)
.transition().duration(1000)
.attr("opacity",1);
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: name, className: node.name, value: node.size, url: node.url});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameterh + "px");
}
Data passed looks something like this (varying as the dataset is updated):
{"name":"bubbles","children":[{"name":"tourism","children":[{"name":"tourism","children":[{"name":"practical","children":[{"name":"ACCOMM","size":13,"url":"#"},{"name":"HIRE","size":2,"url":"#"}]},{"name":"activity","children":[{"name":"EVENT","size":6,"url":"#"},{"name":"TOUR","size":3,"url":"#"}]},{"name":"leisure","children":[{"name":"RESTAURANT","size":168,"url":"#"},{"name":"ATTRACTION","size":8,"url":"#"}]}]}]}]}
I had a similar problem.
I slightly modified (mostly simplified) your code, and here you can find working example.
My approach is not to use transformations. Without them, the code looks more readable and maintainable. So, I propose a simple solution, I hope you can use it in your case.
Label. transition is maybe not the best, but you can change it.
On jsfiddle, its impossible to integrate json files, so the data is inside javascript. In your code, you would need to handle loading json, but the core idea from my example can be applied without change.
The key function is:
function updateVis() {
if (dataSource == 0)
pack.value(function(d) { return d.size; });
if (dataSource == 1)
pack.value(function(d) { return 100; });
if (dataSource == 2)
pack.value(function(d) { return 1 +
Math.floor(Math.random()*501); });
var data1 = pack.nodes(data);
titles.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.text(function(d) {
return (d.children ? "" : d.name + ": " + format(d.value));
});
circles.transition()
.duration(5000)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r; });
labels.transition()
.duration(5000)
.attr("opacity", 0)
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.each("end", function(d){
d3.select(this).text(function(d) {
return d.children ? "" : d.name.substring(0, d.r / 4);
});
d3.select(this).transition()
.duration(1000)
.attr("opacity", 1);
});
};
I am creating an interactive bubble chart of fortune 500 data. I am trying to reduce a selection but can't figure out why it's not working as I expect.
var bubble = d3.layout.pack()
...
d3.json("js/data/fortune2009.json", function(json) {
var node = svg.data([json]).selectAll("g.node")
.data(bubble.nodes); // filter here?
node.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("circle")
.attr("r", function(d) { return d.r; })
.attr("class", function(d) { return "q" + color(d.Profit) + "-9"; });
node.filter(function(d) { return !d.children; })
.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.Company.substring(0, d.r / 3); });
});
The JSON data looks like this:
{
"children":[
{
"Year" : "2009",
"Rank" : "1",
"Revenue" : "442",
"Profit" : "851.00",
"Company" : "Exxon Mobil"
},
....
}
I want to remove the parent node. I am trying to use a filter function like described here.
.filter(function(d) { return !d.children; })
But if i add my filter function where I initialize the node selection, either nothing happens or I get an error.
(The filter works when I apply it before appending text)
How do I solve this?
UPDATE: Here is a JSFIDDLE http://jsfiddle.net/B9w4N/8/ showing my problem.
You had the right idea. You just needed to apply the filter where the circles are created (in addition to where the text is created):
node
.filter(function(d) { return !d.children; })
.append("circle")
.attr("r", function(d) { return d.r; });
Forked version on JSFiddle
You can filter the array that the layout outputs:
var node = svg.data([fortune2009]).selectAll("g.node")
.data(function(d) {
return bubble.nodes(d).filter(function(v) {
return v.depth > 0;
});
});
See updated fiddle: http://jsfiddle.net/B9w4N/10/