is it possible to color groups differently - d3.js

I am planning to have different colors for each element in the .I was just exploring if this is possible.
My code:
var svg = d3.select("#graphid").append("svg")
.style("margin-left","30px")
//.style("background-color","lavender")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var focus = svg.append("g")
.attr("class", "focus")
.attr("id","focusid")
.style("background-color","#F8FCFB")
//.call(zoom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var fo= d3.select("#focusid").style("background-color","azure");
console.log(fo);
var context = svg.append("g")
.attr("id","contextid")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var contx= d3.select("#contextid").style("background-color","lavender");
............../
..............//
.............///
the groups were not getting the colors set? What am i missing here?

To solve this you need to append a 'rect' to you SVG and fill that as you can't just fill a 'g'. So create a rectangle then append the focus to that rectangle. same with context :)
//create the rectangle to fill here
var rect = svg.append('rect')
.attr('id', 'rect')
.style('fill', 'red')
.attr("height", height)
.attr("width", width);
var focus = svg.append("g")
.attr("class", "focus")
.attr("id","focusid")
//.call(zoom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Ive done it for focus, leave it up to you to do context :)
Updated fiddle : http://fiddle.jshell.net/zua7L31d/6/
Finished both them off :
Added a new rectangle for the 'context'
var contextRect = svg.append('rect')
.attr('id', 'rect2')
//.style('fill', 'red')
.attr("height", rect2Height)
.attr("width", rect1Width)
.attr('x', 0)
.attr('y', rect1Height)
;
Final fiddle : http://fiddle.jshell.net/zua7L31d/7/
Hope that helped :)

Related

D3 Scatterplot legend overlapping

I have a scatterplot that works fine, but the legend I add to it is overlapping the chart. My current approach is to make the chart DIV be 70% of the width and have the legend take up the remaining 30%. For some reason, the legend isn't showing up on the screen, even though the HTML is there.
This is the link to my initial problem: http://jsfiddle.net/chp5a09e/373/
Here is the link to what I'm currently trying: http://jsfiddle.net/chp5a09e/372/
var legend = d3.select("#legend").append("svg")
.attr("width", $("#legend").width())
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
legend.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 12)
.attr("width", 12)
.attr("height", 12)
.style("fill", function(d) {
return color(d);
})
.on("click", function(d) {
d3.selectAll(".symbol").style("opacity", 1)
if (clicked !== d) {
d3.selectAll(".symbol")
.filter(function(e) {
return e.items[columns.indexOf("Channel")] !== d;
})
.style("opacity", 0.1)
clicked = d
} else {
clicked = ""
}
});
legend.append("text")
.attr("x", width - 16)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
HTML is there
Only group (g) elements are there, and they are never visible themselves. In your original code
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
...
legend.append("rect")
the legend here is a selection of multiple g.legend elements, and thus a rect gets appended to each of them, as well as gets access to the datum bound to the parent g. However in you new code
var legend = d3.select("#legend").append("svg")
...
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
legend.selectAll(".legend")
.data(color.domain())
.enter().append("g")
...;
legend.append("rect")
The legend here refers only to the single g element that contains your whole legend. Your legend.selectAll(".legend") isn't saved into variable, so while inside the chain you set class and transform attributes properly, you don't use it to get rect appended to it -- again, legend at that point refers to the outter single g container.
Potential solution:
var legendCnt = d3.select("#legend").append("svg")
.attr("width", $("#legend").width())
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var legend = legendCnt.selectAll(".legend")
.data(color.domain())
...
You'll notice you'll need to remove or lower x attribute for text and rect, since 0 is now at the beginning of the legend instead of chart

Behavior of chaining syntax in axes generation

I'm writing d3.js (d3 v4) code to generate a simple bar chart, which I managed to do, but I am confused about the behavior of the code in generating axes.
So basically, I encapsulated the chart code into a function that contains basic accessors.
Here is some code showing the core of my chart function.
function chart(selection){
selection.each(function(data){
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.5);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var div = d3.select(this)
var svg = div.selectAll("svg")
.data([data]).enter()
.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("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("fill", "#1CE6FF")
.attr("x", function(d) { return x(d.name); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
});
So I have two questions: 1) Is it okay to use multiple code blocks invoking svg.append("g") like shown above in the code?
2) When I replace the following code :
var svg = div.selectAll("svg")
.data([data]).enter()
.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("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
With this (which is basically merging the two blocks in a single chain code chunk):
var svg = div.selectAll("svg")
.data([data]).enter()
.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 + ")").append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
...why is my y axis not showing at the right place? The <g class="y axis">...</g> tag appears nested into of a <text> tag itself nested in the x axis tag. Why the 2-block code vs single block code not behaving the same?
The structure of the code defines the structure of the resulting SVG. In the function chart() the variable svg holds the reference to the lastly appended <g> not to the previously appended <svg> itself. Note, how all statements after its first creation append to that same outer group of svg. That's one perfectly fine way of doing this.
If you concatenate the statements, however, like you did in your last snippet, the value of svg changes because the right hand side, i.e. the return value of the expression, changes. Since you last selected all text elements of the appended x-axis svg contains exactly those texts. Given that, it is pretty easy to understand that this breaks the rest of the code which will now append other contents to the text elements. Even if it was syntactically valid, which it is not, it would most likely still break the layout.
If you want to rearrange the code structure you have to make sure to have the correct references at the time they are needed.

Is there a shortcut for "create or modify" d3 axis (transition with modified data/size)?

My D3 visualizations have transitions when data changes or when resizing the window. The x and y axis do transitions as well, but I always have some code which hasn't got the typical short D3-style:
Example (the first code block is not important and is visible here only for understanding):
var x = d3.scale.linear().range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.ticks(20)
.orient('bottom');
var svg = d3.select(containerSelector).select('svg');
if (svg.empty()) {
svg = d3.select(containerSelector).append('svg');
svg.append('g');
}
var group = svg
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.select('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
Now this is the code I'd like to optimize:
var xAxisSvg = group.select('.x.axis');
if (xAxisSvg.empty()) {
group.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
} else {
xAxisSvg.transition()
.duration(duration)
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
}
Is there a similar way like enter() to create the axis-svg and apply a new logical axis? How can this repeating code be optimized?
You can make it a lot shorter by doing only the bare minimum necessary to make it run:
var xAxisSvg = group.select('.x.axis');
if (xAxisSvg.empty()) {
xAxisSvg = group.append('g')
.attr('class', 'x axis');
}
xAxisSvg.transition()
.duration(duration)
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
Alternatively, you could bind some dummy data to it and use the enter/update pattern, although I wouldn't consider that particularly good design in this case.
var xAxisSvg = group.selectAll('.x.axis').data([0]);
xAxisSvg.enter()
.append('g')
.attr('class', 'x axis');
xAxisSvg.transition()
.duration(duration)
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);

Inconsistency in append 'rect' with text

Any thoughts on why the text is missing for the first rectangle yet others are all fine?
http://jsfiddle.net/sjp700/bhwpu/
vis = d3.select("#chart")
.append('svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.style("fill", "pink")
.attr('class', 'chart');
vis.append("g") // container element
.attr("class", "x axis") // so we can style it with CSS
.attr("transform", "translate(0," + height + ")") // move into position
.call(xAxis); // add to the visualisation
//add in the y axis
vis.append("g") // container element
.attr("class", "y axis") // so we can style it with CSS
.call(yAxis); // add to the visualisation

How to fix shifted columns on histogram?

I am building a histogram with number of days as the x axis, but the columns are shifted. I want to bin the data on months, or more exactly, 30-day intervals. For some reason, when generating this graph the columns are not the right size, and the bars end up shifting a lot, as shown in the picture:
The bins are hardcoded like this:
var data = d3.layout.histogram()
.bins(20)
(values);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.y; })])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickValues([0,30,60,90,120,150,180,210,240,270,300,330,360,390,420,450,480,510,540,570,600])
.orient("bottom");
var svg = d3.select("#graph").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 bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", x(data[0].dx) - 1)
.attr("height", function(d) { return height - y(d.y); });
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", x(data[0].dx) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.y); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
What is the correct way to solve this issue?

Resources