Placement of .transition for lines in d3? - d3.js

I'm looking to get a transition animation into my line graph, but I'm not really sure where the .transition() would go. I tried to put it in where I thought it should go, but that just froze the graph. Here's my code:
var vis = d3.select('#visual'),
WIDTH = 1000,
HEIGHT = 500,
MARGINS = {
top: 20,
right: 20,
bottom: 20,
left: 50
},
xRange = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([d3.min(lineData, function(d) {
return d.x;
}), d3.max(lineData, function(d) {
return d.x;
})]),
yRange = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([d3.min(lineData, function(d) {
return d.y - 1;
}), d3.max(lineData, function(d) {
return d.y;
})]),
//setup x
xAxis = d3.svg.axis()
.scale(xRange)
.tickSize(5)
.tickSubdivide(true),
//setup y
yAxis = d3.svg.axis()
.scale(yRange)
.tickSize(5)
.orient("left")
.tickSubdivide(true);
vis.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis);
vis.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
var lineFunc = d3.svg.line()
.x(function(d) {
return xRange(d.x);
})
.y(function(d) {
return yRange(d.y);
});
vis.append("svg:path")
.attr("class", "myLine")
.attr("d", lineFunc(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
// lineFunc.transition(); <--Where I thought the tag should be
// lineFunc.exit().transition()
// .remove();
And here's the whole thing if necessary: https://jsfiddle.net/hppkqfy7/

Transitions in D3 usually apply to attribute(s) and .transition() should be called before setting the new value of the attribute(s). This will tell D3 to interpolate between old and new.
In your case, you'd call .transition() just before setting the updated d attribute:
d3.select(".myLine")
.transition()
.attr("d", lineFunc(lineData));
Complete demo here.

Related

How can I highlight a part of a grouped chart when filter d3

I start with d3 using version 3.
I created a grouped bar chart that looks like this:
I added a drop-down list that contains all the genders (Woman (femme), Couple...).
I'm blocked because I can't highlight the bars that correspond to a certain gender when I choose from the drop-down list.
I found on the internet that I could use this code:
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
the "Section" part contains my selected gender
I would like that when I select, for example torque, that the bars corresponding to the torque remain in color and the others are in gray.
This is the all code :
source : https://bl.ocks.org/hydrosquall/7966e9c8e8414ffcd8b5
Highlight : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600,
height = 250 ;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#54E868", "#54CCE8", "#6395FF", "#50FFC5"]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
var svg = d3.select("body").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 + ")");
d3.csv("../CSV/GenreOrigine.csv", function(error, data) {
if (error) throw error;
//Couples, Famille...
var genreNames = d3.keys(data[0]).filter(function(key) { return key !== "Origine"; });
data.forEach(function(d) {
//Valeur pour chacune des origines par rapport au genre
d.genres = genreNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Origine; }));
x1.domain(genreNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.genres, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0.3)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Nombre de personnes");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var Origine = svg.selectAll(".Origine")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Origine) + ",0)"; });
Origine.selectAll("rect")
.data(function(d) { return d.genres; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.style("fill", function(d) { return color(d.name) })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.name)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.name));
});
Origine.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
var legend = svg.selectAll(".legend")
.data(genreNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
d3.select('#inds')
.on("change", function () {
var sect = document.getElementById("inds");
var section = sect.options[sect.selectedIndex].value;
//some code here
});
});
You can select all the rect in the SVG and filter them in the function, so the code would look like:
var sect = document.getElementById('inds');
var section = sect.options[sect.selectedIndex].value;
if(section !== '') {
d3.selectAll("rect")
.attr('opacity', function(d) {
if(d.name !== section) {
return 0.2;
} else {
return 1;
}
})
} else {
d3.selectAll('rect')
.attr('opacity', 1)
}
In the example above, I changed the opacity attribute, but you can change fill with the same approach.

Change chart colors

I have a bar chart/histogram, all working fine.
I need to change the text and lines on the chart is on a black background.I
Also, the bar colors need to be orange.
I have had a look around the web, and seen some references to .attr("style":...); and have tried this without success.
Any pointers gratefully received.
<script>
// set the dimensions of the canvas
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 1890 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// set the ranges
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
// define the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
// add the SVG element
var svg = d3.select("body").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 + ")");
// load the data
d3.json("/assets/js/risk_hist_values.json", function(error, data) {
// the number of columns in this chart
var numCols = data.length;
data.forEach(function(d) {
d.Letter = d.bin_no;
d.Freq = +d.count;
});
// scale the range of the data
x.domain(data.map(function(d) { return d.Letter; }));
y.domain([0, d3.max(data, function(d) { return d.Freq; })]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Count:</strong> <span style='color:red'>" + d.count + "</span>";
})
// call the tips
svg.call(tip);
// add axis
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("Count");
// Add bar chart
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.Letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.Freq); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.on('click', drill)
.attr("id", function(d, i){ return 'b_'+i+''; })
.attr("height", function(d) { return height - y(d.Freq); });
});
function drill(){
alert( 'drilling' );
}
</script>

Data lines disappearing when using rangePoints on x-axis ordinal scale in line chart with D3

I've adapted this code from the multi-line line chart example here. The biggest issue I'm now having after researching what changes I needed to make is that the data lines disappear when I use .rangePoints on the x-axis ordinal scale. With just .range, the x-axis displays nothing and the data lines are bunched up along the left side of the y-axis. This has something to do with the fact I altered the original code from a time scale to ordinal, but I'm stumped as to what further changes I need to make.
Code below:
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 500 - margin.left - margin.right,
height = 280 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.episodes); })
.y(function(d) { return y(d.season); });
var svg = d3.select("body").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 + ")");
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "episodes"; }));
var seasons = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.episodes, season: +d[name]};
})
};
});
x.domain(data.map(function(d) { return d.episodes; }));
y.domain([
d3.min(seasons, function(c) { return d3.min(c.values, function(v) { return v.season; }); }),
d3.max(seasons, function(c) { return d3.max(c.values, function(v) { return v.season; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Viewership (in mlns)");
var s = svg.selectAll(".city")
.data(seasons)
.enter().append("g")
.attr("class", "city");
s.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
s.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.season) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});

D3.js, Sortable Barchart not displaying

I`m using d3.js, Sortable Barchart with the following code:
function countrydownloads(input) {
d3.select(".barchartCountryDownloads").select("svg").remove();
var data = [{"country":"Austria","downloads":"10000"},{"country":"Germany","downloads":"20000"},{"country":"Spain","downloads":"30000"}];
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, 1);
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")
.tickFormat(formatPercent);
var svg = d3.select("body").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 + ")");
data.forEach(function(d) {
d.downloads = +d.downloads;
});
x.domain(data.map(function(d) { return d.country; }));
y.domain([0, d3.max(data, function(d) { return d.downloads; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.country); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.downloads); })
.attr("height", function(d) { return height - y(d.downloads); });
d3.select("input").on("change", change);
var sortTimeout = setTimeout(function() {
d3.select("input").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(sortTimeout);
// Copy-on-write since tweens are evaluated after a delay.
var x0 = x.domain(data.sort(this.checked
? function(a, b) { return b.downloads - a.downloads; }
: function(a, b) { return d3.ascending(a.country, b.country); })
.map(function(d) { return d.country; }))
.copy();
var transition = svg.transition().duration(750),
delay = function(d, i) { return i * 50; };
transition.selectAll(".bar")
.delay(delay)
.attr("x", function(d) { return x0(d.country); });
transition.select(".x.axis")
.call(xAxis)
.selectAll("g")
.delay(delay);
};
}
I don`t get anything displayed. I tried Barchart with the same kind of data, there it worked. Where is the mistake with Sortable Barchart?
.append("g:")
should be
.append("g")
If you haven't used them before, you might want to check out the chrome dev tools. Small typos like this are a lot easier to debugger when you can step through your code to see exactly what line is is causing the error to occur.

How to add space between axis and line in D3 line chart?

Given this simple chart I created:
var data = [["2013-01-24 06:38:02.235191", 52], ["2013-01-23 06:38:02.235310", 54], ["2013-01-22 06:38:02.235330", 45], ["2013-01-21 06:38:02.235346", 53]],
maxValue = d3.max(data, function (d) { return d[1]; }),
margin = {top: 20, right: 20, bottom: 50, left: 50},
width = 500 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
svg, x, y, xAxis, yAxis, line;
$.each(data, function(index, val) {
val[0] = new Date(val[0]);
});
x = d3.time.scale()
.range([0, width])
y = d3.scale.linear()
.domain([0, maxValue])
.range([height, 0]);
xAxis = d3.svg.axis()
.scale(x)
.tickSize(4, 2, 0)
.ticks(d3.time.days, 1)
.tickFormat(d3.time.format("%m/%d"))
.orient("bottom");
yAxis = d3.svg.axis()
.scale(y)
// .ticks(5)
// .tickValues([0, maxValue * 0.25, maxValue * 0.5, maxValue * 0.75, maxValue])
.tickSize(4, 2, 0)
.orient("left");
line = d3.svg.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
svg = d3.select("#chart-holder").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 + ")");
x.domain(d3.extent(data, function(d) { return d[0]; }));
y.domain(d3.extent(data, function(d) { return d[1]; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("transform", function(d) {
return "rotate(-60)translate(" + -this.getBBox().height * 1.7 + "," +
-this.getBBox().width/4 + ")";
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
svg
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("fill", "#0b8da0")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d[0]); })
.attr("cy", function(d) { return y(d[1]); });
How can I add a space between axis and line, so it won't touch the axis.
Also is there a way to force yAxis ticks to always start from 0, no matter what is the smallest value in the data set?
Working example can be found here: http://jsfiddle.net/n7Vmr/
You can just change the domain of the scale used to draw the y-axis, look for the line
y.domain(d3.extent(data, function(d) { return d[1]; }));
needs to be changed, if you want it one less than the smallest value in your dataset use
y.domain([d3.min(data, function (d) { return d[1]; })-1, maxValue]);
or if you want it to start from 0, regardless of the data
y.domain([0, maxValue]);

Resources