Hello I'm new to d3 and I couldn't update the data in my bar chart.
I'm creating the bars using following code.
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter) + 1; })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth() - 1)
.attr("height", function(d) { return height - y(d.frequency); })
I was able to modify the data using hard coded values:
var bar = svg.selectAll(".bar");
bar.transition().duration(750).data(data).enter()
.attr("y", function(d) { return 0; })
.attr("height", function(d) { return Math.random()*100; });
How can i properly bind the new data?
See the snippet below for an example of updating the bar chart with new data programmatically and showing a transition animation in the process.
var data = [{x:10, y:10, w:50, h:25},
{x:10, y:40, w:150, h:25},
{x:10, y:70, w:70, h:25}];
var g = d3.select("g");
// Bind initial data to empty selection, then use enter()
// to access the virtual selection from the data-join
// and subsequently append a rect for each virtual selection
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) {return d.w; })
.attr("height", function(d) { return d.h; });
var new_data = [{x:10, y:10, w:150, h:25},
{x:10, y:40, w:50, h:25},
{x:10, y:70, w:100, h:25}];
// Bind the new data to the rect objects. Since new_data
// is of the same size as number of rects, enter() and exit()
// selections from data-join will be empty and the rects
// with updated bound data are now available in the default
// update selection.
g.selectAll(".bar")
.data(new_data) // grab the update selection by default
.transition().duration(3000)
.attr("width", function(d) { return d.w; }); // Update the attribute
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="400" height="400">
<g>
</g>
</svg>
Related
I am trying to set up a d3 force visualization with nodes and links. I have my nodes displaying properly but am having some trouble with links. Could someone take a look at my json file and then my code and guide me through the process of getting the links to display?
Here's the json data (sources and targets for links are at the bottom):
https://api.myjson.com/bins/4t8na
And here's the code for the visualization:
<script type= "text/javascript">
var w = 1000,
h = 650;
var svg = d3.select("body").append("svg")
.attr("height", 0)
.attr("width", 0)
.style("border", "1px solid black");
var data; // a global
var force = d3.layout.force()
.size([w, h])
.linkDistance([150])
.charge([-1050])
.gravity(0.5)
.on("tick", tick);
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var circles = svg.selectAll(".node");
d3.json("https://api.myjson.com/bins/1rnhq", function(error, json) {
if (error) return console.warn(error);
data = json;
var nodes = data;
console.log(data);
force.nodes(data)//.links()
.start();
// Update nodes.
circles = circles.data(data);
circles.exit().remove();
var nodeEnter = circles.enter().append("g")
.attr("class", "node")
.style("fill", "#000")
.style("opacity", 0.75)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", click)
.call(force.drag);
nodeEnter.append("circle")
.attr("r", function(d) { return d.sector == "Academia" ? 1:5 });
nodeEnter.attr("cursor", "pointer");
//Update links
var links = svg.selectAll(".link")
.data(data.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", "1px");
links.exit().remove();
function mouseover() {
d3.select(this).select("circle").transition()
.duration(250)
.attr('r', 10);
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(250)
.attr('r', 5);
}
nodeEnter.append("text")
.attr("text-anchor", "middle")
.style("font-size", ".75em")
.attr("dy", "-0.85em").text(function (d) { return d.name });
var tooltip = svg.append("rect")
.attr("x", 1000)
.attr("y", 0)
.attr("width", 900)
.attr("height", 700)
.attr("opacity", 0.85);
function click() {
d3.select(tooltip).transition()
.duration(450)
.attr("x", 650)
};
});
function tick() {
links.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; });
circles.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
};
// create svg nodes for each json object: "sector"
// create svg nodes for each json object: "name"
// load links.json
// create svg links from links json file
// style links
// sort json objects by projects
// get google map: coastal virginia
// sort json objects: "name" by geography
// get googe map U.S.
</script>
The main problem is inside your JSON links
You have values like this:
{"source":52,"target":28},{"source":52,"target":29},{"source":52,"target":30},{"source":52,"target":31}
But there is no Node with index 52 thus everything was breaking on load.
However you code has lot of other errors like
circles.exit().remove();//this is incorrect coz circles in your case is not a selection
And many more :)
Working code here
Hope this helps!
I believe the problem is that data.links does not exist, what you have is data[#].links. So when you do .data(data.links) on your var links = ..., you are passing an undefined attribute there.
Try this:
var links = svg.selectAll(".link")
.data(data)
// ...
When I add or remove a data element, the axes update correctly. It also looks like the "bars" object is updated with the correct the "x" and "height" values, but changes don't show up in the DOM.
Here's the update function:
updateChart: function() {
var chart = this,
xScale = this.scales.x,
yScale = this.scales.y,
data = this.collection.toJSON()
padding = this.config.barPadding;
xScale.domain(chart.collection.pluck(chart.config.xAttr));
yScale.domain([0, d3.max(chart.collection.pluck(chart.config.yAttr))]).nice();
var bars = this.svg.select(".bar").data(data);
bars.exit().remove();
bars.enter().append("rect")
.attr("class", "bar")
.attr("id", function(d) { return "bar-" + d[chart.config.xAttr]; })
.attr("data-x", function(d) { return d[chart.config.xAttr]; })
.attr("data-y", function(d) { return d[chart.config.yAttr]; });
chart.svg.select(".x.axis").transition().duration(1000).call(chart.xAxis);
chart.svg.select(".y.axis").transition().duration(1000).call(chart.yAxis);
bars.transition().duration(1000)
.attr("x", function(d) { return xScale(d[chart.config.xAttr]); })
.attr("y", function(d) { return yScale(d[chart.config.yAttr]); })
.attr("width", xScale.rangeBand())
.attr("height", function(d) { return chart.height - yScale(d[chart.config.yAttr]); });
console.log(bars);
}
I'm developing a stacked chart application.
http://jsfiddle.net/NYEaX/174/
I've placed it inside a jquery plugin to create multiple instances etc... different properties and eventually different data sources.
For now I am having problems animating the chart bars and the axis.
Animate bar code
animateBars: function(selector, data){
var w = $(selector).data("width");
var h = $(selector).data("height");
var margin = methods.getMargin(h);
methods.setDimensions(w, h, margin);
//methods.setX();
//methods.setY();
//methods.setDomain(data);
var initialHeight = 0;
//var svg = d3.select(selector + " .stackedchart");
var barholder = d3.select(selector + " .barholder");
var state = barholder.selectAll(".state")
.data(data)
.enter()
.append("g")
.attr("class", "g")
.attr("x", function(d) {
return methods.x(d.Label);
})
.attr("transform", function(d) {
return "translate(" + methods.x(d.Label) + ",0)";
});
var bar = state.selectAll("rect")
.data(function(d) {
return d.blocks;
});
// Enter
bar.enter()
.append("rect")
.attr("width", methods.x.rangeBand())
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
})
.style("fill", function(d) {
return methods.color(d.name);
});
// Update
bar
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
})
.transition()
.duration(500)
.attr("x", function(d) {
return methods.x(d.Label);
})
.attr("width", methods.x.rangeBand())
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
});
// Exit
bar.exit()
.transition()
.duration(250)
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
})
.remove();
}
One problem is that "state" is generated from the "enter()" method, so all your "bar" calls are only being executed when your "g.class" is being generated, not on update. Change this:
var state = barholder.selectAll(".state")
.data(data)
.enter()
.append("g")...
to this:
var state = barholder.selectAll(".state")
.data(data);
state.enter().append("g")...
See if that helps a bit. It doesn't seem to affect your fiddle, but you might be having problems other than d3. Try simplifying your fiddle and get the d3 stuff working by itself first.
I'm working on this bar chart application.
http://jsfiddle.net/NYEaX/166/
How do I
a) animate the bars so they grow from the bottom
b) morph the axis accordingly to the new data set
animateBars: function(data){
var svg = d3.select(methods.el["selector"] + " .barchart");
var barrects = d3.select(methods.el["selector"] + " .barrects");
var initialHeight = 0;
var bar = barrects.selectAll("rect")
.data(data);
// Enter
bar.enter()
.append("rect")
.attr("class", "bar")
.attr("y", initialHeight);
// Update
bar
.attr("height", initialHeight)
.transition()
.duration(500)
.attr("x", function(d) { return methods.x(d.letter); })
.attr("width", methods.x.rangeBand())
.attr("y", function(d) { return methods.y(d.frequency); })
.attr("height", function(d) { return methods.height - methods.y(d.frequency); })
// Exit
bar.exit()
.transition()
.duration(250)
.attr("y", initialHeight)
.attr("height", initialHeight)
.remove();
},
For the former, set the y attribute to be the max height instead of 0:
.attr("y", methods.height)
For the latter, recompute the x domain and call the axis component again:
methods.x.domain(data.map(function(d) { return d.letter; }));
svg.select("g.x").call(methods.xAxis);
Complete example here.
I am having issues trying to build this jquery based d3.js barchart plugin.
the bars are displaced to the left, not sure why
the bars are not updating to new data.
I've tried to get the bars to animate - but not had any success.
http://jsfiddle.net/NYEaX/161/
Here is the animate bars function
animateBars: function(data){
var svg = d3.select(methods.el["selector"] + " .barchart");
var bars = svg.selectAll(".bar")
.data(data);
bars
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return methods.x(d.letter); })
.attr("width", methods.x.rangeBand())
.attr("y", function(d) { return methods.y(d.frequency); })
.attr("height", function(d) { return methods.height - methods.y(d.frequency); })
.transition()
.duration(300)
bars
.transition()
.duration(300)
bars.exit()
.transition()
.duration(300)
}
I fixed the transition for new bars in your jsfiddle. I hope it reveals the functionality of transitions:
http://jsfiddle.net/NYEaX/893/
How it works: After it sets the initial height and y value, it adds a transition to the end height and y value.
bars
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return methods.x(d.letter); })
.attr("width", methods.x.rangeBand())
.attr("y", function(d) { return methods.y(0); })
.attr("height", function(d) { return methods.height - methods.y(0); })
.transition().duration(1000)
.attr("y", function(d) { return methods.y(d.frequency); })
.attr("height", function(d) { return methods.height - methods.y(d.frequency); });