I'm following the box plot on this page Box Plot Link. In this example it reads in the box plot values from a data file. I'm trying to add a button that will read in new data from another data file and update the box plot.
Here's the code they use to first create the box plot.
var svg = d3.select("#viz").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("class", "box")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart.duration(1000));
and this to update the box plots every second
setInterval(function() {
svg.datum(randomize).call(chart.duration(1000));
}, 2000);
When I click on the update button it calls my snippet here:
var svg = d3.select("#viz").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("class", "box")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.call(chart.duration(1000));
However this won't update my boxplot. If I want the box plot to change, I have to first remove the box plot SVG first
d3.selectAll('svg').remove();
But I'd like to have an transition rather than new box plots. Why do I have to remove the previous SVG first? Any help?
So I made the following changes to the update of the data
var svg = d3.select("#viz").selectAll("svg").data(data)
svg.call(chart.duration(duration));
svg.enter().append("svg")
.attr("class", "box")
.attr("id", function (d, i) {
return "box_" + i;
})
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.append("g")
.attr("id", function (d, i) {
return "g_" + i;
})
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart.duration(duration))
svg.exit().remove();
and there is actually some bugs with that box.js code that needs to be fixed which I found about here:
D3 Datum Update Boxplot
Related
I am creating a visualisation using d3.js and svg images in PowerBi (this uses version 3 of d3).
I have got my visual working, however my legend is not rendering. I tested this in a browser, and the legend items appear in the elements of the page, but just aren't showing up.
My code for the legend items are
var pbi = {
width:1108,
height:636,
colors:[
"#A70240",
"#4A2366",
"#009A44",
"#A0D081",
"#01B5BB",
"#137B88",
"#5D6771",
"#CDC8C1"
]
var margin = {top: 20, right: 30, bottom: 30, left: 140},
width = pbi.width - margin.left - margin.right,
height = pbi.height - margin.top - margin.bottom,
legendleft = pbi.width - margin.right;
var ly = d3.scale.ordinal() // For legend
.rangeRoundBands([0, height], barPad, barOuterPad);
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
----
ly.domain(rData.map(function(d) { return d.milestone; })); // Legend
var milestoneMap = {}; // Maps years to colours
var legendArray = []; // For legend
rData.forEach(function (d) {
var entry = d.year;
var rowEntry = {
entry: entry, // Axis label
milestone: d.milestone, // For colour lookup
date: d.date, // For X position of points
y: y(entry)
}
if (!(d.milestone in milestoneMap)) {
// First occurrence of each year saved to legend
legendArray.push({milestone: d.milestone});
rowArray.push(rowEntry);
var legend = svg.append("g").attr("id", "legend")
.attr("transform", "translate(" + legendleft + "," + margin.top + ")").selectAll(null)
.data(legendArray)
.enter();
// Legend agency labels
legend.append("text")
.attr("class", "milestoneLabel")
.attr("x", 25)
.attr("y", function(d) { return ly(d.milestone)+5; })
.style("stroke", "black")
.style("stroke-width", .3)
.text(function(d) { return d.milestone; });
legend.append("circle")
.attr("class", "legend")
.attr("cx", 12)
.attr("cy", function(d) { return ly(d.milestone); })
.attr("r", 8)
.style("stroke", "black")
.style("stroke-width", 1)
.style("fill", function(d, i) { return milestoneMap[d.milestone]; });
This is omitting code that calculates all other elements.
Why is it that the legend circle and label is appearing as an element on the page but isn't rendering anything?
Thanks
Turns out, I just needed to add my legend left to my svg.
From
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
to
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right + legendleft)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
In this datavisualization, I have a curious problem : my svg is not correctly aligned with the whole container.
I'm using a viewbox:
var svg = d3.select("#viz")
.attr("viewBox", [
margin.left,
margin.top,
(width+margin.left),
(height+margin.bottom)
].join(" "))
.append("svg")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Does anyone have any idea why?
I'm attempting to load a d3.js bullet chart into an existing svg object:
<span id='element'><svg class="elm_12"></svg></span>
and in the js:
var svg = d3.select(elementid)
.data(data)
.enter().append("svg")
.attr("class", "bullet")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
I assume I can't add an svg element to an existing svg element. Any thoughts on the best way to accomplish this?
I was going to place this as a comment but there are simply too many code statements. What about this:
<span id='element'></span>
...
var svg = d3.select("#element")
.append("svg")
.attr("class", "bullet")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg
.data(data)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
Actually, this was enough for me to figure it out - THANK YOU.
var svg = d3.select(elementid)
.data(data)
.enter()
.append("svg")
.attr("class", "bullet")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
I create a collabsible tree like in this example (http://bl.ocks.org/mbostock/4339083). I tried to change the background color of the SVG. Therefor I use a "rect" element before inserting the "g" element:
svg = d3.select("#"+targetDIVName).append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "svg_graph")
.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "green") //for example
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
With this method the graph is hidden. Is there any solution to set the "rect" element as background and get the graph visible?
Thank You
SVG elements are drawn in the order in which they are added. So if you want the rect to be in the background, add it as the first element after creating the SVG.
To get a solid background colour, you could alternatively use the viewport-fill attribute, which is not supported by all browsers though.
Still learning d3.js.
I would like to ignore the selection of an SVG panel when using .selectAll("svg").
I am building a visualization comprising four SVG panels. The top SVG panel is used to display header/title information for the visualization.
var svgHeader = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
The next two SVG panels are dynamically created using a range of two numbers representing two years.
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2012, 2013))
.enter().append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 200)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
The final SVG contains detail information as the user interacts with the visualization.
Problem: I want to exclude the first SVG panel from the .selectAll("svg") which is used to create the two middle panels. I would like to dynamically build SVG panels and have them locate underneath the previously created header SVG.
Is there any way to exclude the header SVG when dynamically creating the middle panels?
I think the best way you should be going about this is taking advantage of classes and adding an appropriate class to the different svgs and then selecting based on the class rather than the svg. This way you know what each of the svgs represent and you can easily reference them.'
var svgHeader = d3.select("body")
.append("svg")
.attr("class", "svgHeader")
.attr("width", width + margin.left + margin.right)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
And then the other two you add a different class name
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2012, 2013))
.enter().append("svg")
.attr("class", "data")
.attr("width", width + margin.left + margin.right)
.attr("height", 200)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.append("g");
Now you can do d3.selectAll("svg.data") and select only svg elements with the class data
Alternatively, you can embed your svg elements in different divs. Assuming you have a div whose id is 'center-div' the following snippet returns you only the svgs contained in it.
d3.selectAll("#center-div svg")
Please also consider that you can append whatever DOM element via d3, so divs can be dinamically generated.