Line chart angles smoother using D3 - d3.js

I am trying to make the angles on this chart smoother, D3.js is being used and I already tried to apply a few ideas as solution, like adding .interpolate("basis") on the code, but for some reason the chart disappear when I do it.
Do you have any clue on what am I doing wrong? The dots are draggable and this is the intended behavior.
Here's a sample to the code: https://codepen.io/A8-XPs/pen/ePWRxZ?editors=1010
HTML:
<svg width="500" height="350"></svg>
JS:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
let points = d3.range(1, 10).map(function(i) {
return [i * width / 10, 50 + Math.random() * (height - 100)];
});
var x = d3.scaleLinear()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
let drag = d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended);
svg.append('rect')
.attr('class', 'zoom')
.attr('cursor', 'move')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.attr('width', width)
.attr('height', height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(points, function(d) { return d[0]; }));
y.domain(d3.extent(points, function(d) { return d[1]; }));
focus.append("path")
.datum(points)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
focus.selectAll('circle')
.data(points)
.enter()
.append('circle')
.attr('r', 5.0)
.attr('cx', function(d) { return x(d[0]); })
.attr('cy', function(d) { return y(d[1]); })
.style('cursor', 'pointer')
.style('fill', 'steelblue');
focus.selectAll('circle')
.call(drag);
focus.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
focus.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
function dragstarted(d) {
d3.select(this).raise().classed('active', true);
}
function dragged(d) {
d[0] = x.invert(d3.event.x);
d[1] = y.invert(d3.event.y);
d3.select(this)
.attr('cx', x(d[0]))
.attr('cy', y(d[1]))
focus.select('path').attr('d', line);
}
function dragended(d) {
d3.select(this).classed('active', false);
}
Thank you!

To get basic interpolation use
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); })
.curve(d3.curveBasis);
or
.curve(d3.curveCatmullRom.alpha(0.5))

Related

D3 v5 center path line in a bar graph

I am using d3.js v5 to plot a bar graph , Porblem is while creating a bar graph and and appending path along with bars , paths are not making to the center of a bar instead of that it is placed on the start point , is there any way to center the point, Currently the graph showing is like this
But i want the small circles and points to be in the center
sorry for bad image i am just visualizing my concept brown lines are in center and will be connected by path
var margin = {
top: 10,
right: 30,
bottom: 90,
left: 40
},
width = 900 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
this.x_axis = Array(20).fill(0).map((x, i) => (i + 1) * 5);
this.y_axis = [1.1, 2.2, 3.5, 4.9, 5.3, 6.9, 7.3, 8.1, 9.2, 8.2, 1.3, 0.1, 0.1, 0.4, 0.1, 0.4, 0.2, 0.4, 0.1, 0.2];
let data = [];
for (let i = 0; i < this.x_axis.length; i++) {
data.push({
Country: this.x_axis[i],
Value: this.y_axis[i]
})
}
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 + ")");
// Parse the Data
// X axis
var x = d3.scaleBand()
.range([0, width])
.domain(data.map(function(d) {
return d.Country;
}))
.padding(0.3);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 20])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
var x2 = d3.scaleBand()
.range([0, width]);
// Bars
svg.selectAll("mybar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.Country);
})
.attr("width", x.bandwidth())
.attr("fill", "#69b3a2")
// no bar at the beginning thus:
.attr("height", function(d) {
return height - y(0);
}) // always equal to 0
.attr("y", function(d) {
return y(0);
})
// Animation
svg.selectAll("rect")
.transition()
.duration(800)
.attr("y", function(d) {
return y(d.Value);
})
.attr("height", function(d) {
return height - y(d.Value);
})
.delay(function(d, i) {
console.log(i);
return (i * 100)
})
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("d", d3.line()
.x(function(d) {
return x(d.Country)
})
.y(function(d) {
return y(d.Value)
}))
svg.selectAll("myCircles")
.data(data)
.enter()
.append("circle")
.attr("fill", "red")
.attr("stroke", "none")
.attr("cx", function(d) {
return x(d.Country)
})
.attr("cy", function(d) {
return y(d.Value)
})
.attr("r", 3)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph"></div>
Since you're using a scaleBand, the axis automatically returns not the center, but the top left of the rectangle, by offsetting by about x.bandwidth() / 2. One workaround for this would be to use return x(d.Country) + x.bandwidth() / 2 to find the center of the bars.
var margin = {
top: 10,
right: 30,
bottom: 90,
left: 40
},
width = 900 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
this.x_axis = Array(20).fill(0).map((x, i) => (i + 1) * 5);
this.y_axis = [1.1, 2.2, 3.5, 4.9, 5.3, 6.9, 7.3, 8.1, 9.2, 8.2, 1.3, 0.1, 0.1, 0.4, 0.1, 0.4, 0.2, 0.4, 0.1, 0.2];
let data = [];
for (let i = 0; i < this.x_axis.length; i++) {
data.push({
Country: this.x_axis[i],
Value: this.y_axis[i]
})
}
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 + ")");
// Parse the Data
// X axis
var x = d3.scaleBand()
.range([0, width])
.domain(data.map(function(d) {
return d.Country;
}))
.padding(0.3);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 20])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
var x2 = d3.scaleBand()
.range([0, width]);
// Bars
svg.selectAll("mybar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.Country);
})
.attr("width", x.bandwidth())
.attr("fill", "#69b3a2")
// no bar at the beginning thus:
.attr("height", function(d) {
return height - y(0);
}) // always equal to 0
.attr("y", function(d) {
return y(0);
})
// Animation
svg.selectAll("rect")
.transition()
.duration(800)
.attr("y", function(d) {
return y(d.Value);
})
.attr("height", function(d) {
return height - y(d.Value);
})
.delay(function(d, i) {
console.log(i);
return (i * 100)
})
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("d", d3.line()
.x(function(d) {
return x(d.Country) + x.bandwidth() / 2;
})
.y(function(d) {
return y(d.Value)
}))
svg.selectAll("myCircles")
.data(data)
.enter()
.append("circle")
.attr("fill", "red")
.attr("stroke", "none")
.attr("cx", function(d) {
return x(d.Country) + x.bandwidth() / 2
})
.attr("cy", function(d) {
return y(d.Value)
})
.attr("r", 3)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="graph"></div>

how to convert scatter chart into line chart in d3 version 3

I have implemented one scatter chart using d3.js. I want to convert this chart to line chart, but i am not able to do so. I have tried to follow ( http://embed.plnkr.co/wJDcZmkEzXaLVhuLZmcQ/ ) but it didn't helped me.
This is the code for scatter chart.
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"19628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"19628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select(divId)
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
How we can add a line connecting these points ?
Please help me !!
To add a line to your existing chart, just add it using path generators.
Line generator:
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
Append the line to the svg:
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
Snippet with the above code included and a few CSS styles to make it look better:
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"10628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"17628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select('body')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX+4) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
path.domain {
fill: none;
stroke: #000;
}
.axis text {
font-size: 12px;
}
div.tooltip {
position: absolute;
background: #FFF;
padding: 5px;
border: 1px solid #DDD;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.0/d3.min.js"></script>

Update the colored area of a line chart

I create a line chart like that.
However, I added the possibility to update data. The "darker line" is updated correctly, the area no.
Here is a piece of code:
var margin = {top: 10, right: 10, bottom: 35, left: 30};
var width = 500 - margin.left - margin.right;
var height = 220 - margin.top - margin.bottom;
var x = d3.scalePoint().range([width, 0]);
var y = d3.scaleLinear().range([height, 0]);
x.domain([...new Set(dataFilter.map(function(d) {
return d.year;
}))]);
y.domain([minX, 100]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var valueline = d3.line()
.x(function(d) {
return x(d.year);
})
.y(function(d) {
return y(d.euro);
})
.defined(function(d) {
return (d.euro !== 0 && !isNaN(d.euro));
});
var area = d3.area()
.x(function(d) {
return x(d.year);
})
.y0(function(d) {
return y(d.euro);
})
.y1(height)
.defined(function(d) {
return (d.euro !== 0 && !isNaN(d.euro));
});
var svg = d3.select("#chart")
.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("path")
.attr("class", "linePath")
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1)
.attr("d", valueline(dataFilter));
svg.append('path')
.datum(dataFilter)
.attr('d', area)
.attr('fill', 'steelblue')
.attr('stroke', 'none')
.attr('opacity', '0.1')
.attr('class', 'areaLines');
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', '.15em')
.attr('transform', 'rotate(-65)');
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
function updateData() {
dataFilter = ...; // other data
// Scale the range of the data again minX is now different
x.domain([...new Set(dataFilter.map(function(d) {
return d.year;
}))]);
y.domain([minX, 100]);
var svg = d3.select("#chart").transition();
svg.select(".linePath")
.duration(1000)
.attr("d", valueline(dataFilter));
svg.select(".x.axis")
.duration(1000)
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');
// update area (doesn't work): TypeError: svg.select(...).datum is not a function
svg.select('.areaLines')
.datum(dataFilter)
.attr('d', area)
.duration(1000);
svg.select(".y.axis")
.duration(1000)
.call(yAxis);
}
When I run it I get: TypeError: svg.select(...).datum is not a function.
Why?
I found this question but I'cant able to solve the problem. Thanks a lot!!
You cannot transition the datum, it makes no sense. That's why datum() is not a transformation method.
Instead of that, transition the d attribute:
svg.select('.areaLines')
.attr('d', area(dataFilter))
.duration(1000);

How to move the mean fixed line in the scatter plot?

I have a scatter plot that don't zoom and don't move the line of mean when drag and zoom.
The mean line stay fixed.
var margin = {top: 50, right: 20, bottom: 100, left: 80};
var width = 1024 - margin.left - margin.right;
var height = 390;
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
d3.json("data.json", function(error, data) {
data.forEach(function(d)
d.date = parseDate(d.date);
d.tiempo = +d.tiempo;
});
var x = d3.time.scale().range([0, width])
.domain(d3.extent(data, function(d) { return d.date; }))
.nice();
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(data, function(d) { return d.tiempo; })])
.nice();
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 100])
.on("zoom", zoomed);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(15)
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(15)
.tickSize(-width);
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 + ")")
.call(zoom);
var Rect = svg.append("rect")
.attr("width", width)
.attr("height", height);
// Eje X
svg.append("g")
.classed("x axis", true)
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.classed("label", true)
.attr("x", width)
.attr("y", margin.bottom - 10)
.style("text-anchor", "end")
.text("Tiempo de llegada");
// Eje Y
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.classed("label", true)
.attr("transform", "rotate(-90)")
.attr("y", -margin.left)
.attr("dy", "1.91em")
.style("text-anchor", "end")
.text("Tiempos de espera")
// Objetos, puntos.
var objects = svg.append("svg")
.classed("objects", true)
.attr("width", width)
.attr("height", height);
objects.append("svg:line")
.classed("axisLine hAxisLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", width)
.attr("y2", 0)
.attr("transform", "translate(0," + height + ")");
objects.append("svg:line")
.classed("axisLine vAxisLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", height);
var puntos = objects.selectAll(".dot")
.data(data)
.enter().append("circle")
.classed("dot", true)
.attr("r", 3.5)
.attr("class", function(d) {
if(d.d7up==1) {
return "dot s7";
}else if(d.d7do==1) {
return "dot b7";
};
return "dot pu";
});
puntos.attr("transform", transform);
var meanData = [
{date: data[0].date, tiempo: 6.22},
{date: data[data.length - 1].date, tiempo: 6.22}
];
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.tiempo); });
var l = objects.selectAll(".lineamedia")
.data(meanData)
.enter().append("circle")
.classed("lineamedia", true)
.attr("r", 5)
.attr("stroke", "red")
.attr("fill", "red")
.attr("transform", transform);
var ll = objects.append("g")
.classed("grupolinea", true);
ll.append("line")
.attr("x1", x(meanData[0].date))
.attr("y1", y(meanData[0].tiempo))
.attr("x2", x(meanData[meanData.length - 1].date))
.attr("y2", y(meanData[meanData.length - 1].tiempo))
.attr("stroke", "red")
.attr("fill", "red")
.attr("stroke-width", 4)
.classed("medias", true);
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
//svg.selectAll(".dot").attr("transform", transform);
puntos.attr("transform", transform);
l.attr("transform", transform);
//svg.select(".grupolinea").attr("transform", transform);
}
function transform(d) {
return "translate(" + x(d.date) + "," + y(d.tiempo) + ")";
}
})
My data array is:
var = data[{"date":"2016-04-25 07:37:24","tiempo":29.366666666667}, {"date":"2016-04-25 08:18:36","tiempo":8.4833333333333},{"date":"2016-04-25 08:32:15","tiempo":5.25},{"date":"2016-04-25 08:40:57","tiempo":2.4166666666667},{"date":"2016-04-25 08:41:09","tiempo":5.3166666666667},{"date":"2016-04-25 08:58:10","tiempo":5.5833333333333},{"date":"2016-04-25 09:00:20","tiempo":4.2166666666667},{"date":"2016-04-25 09:00:42","tiempo":5.2666666666667}]
Is a graph de times wait where
date: of comming patient;
time: time of wait;

d3 bar chart transition from csv

I'm a d3 novice trying to create a simple, two-series bar chart that transitions when different buttons are clicked. The original chart is constructed:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
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(["#d4d4d4", "#58bd5b",]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("div.d3space").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("/assets/data/data3.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Client; }));
x1.domain(hourBuckets).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.hours, 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", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Hours");
var client = svg.selectAll(".client")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Client) + ",0)"; });
client.selectAll("rect")
.data(function(d) { return d.hours; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(hourBuckets.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; });
});
The csv being accessed is in the following format:
Client,Planned,Actual
ICC,25,50
RNR,50,47.5
MB,10,2.5
This chart renders as desired. The piece I am struggling with is getting this graph to transition to reflect different data when a link is clicked (link has id="fourweeks"). I have tried this onclick function:
window.onload = function() {
var a = document.getElementById("fourweeks");
var b = document.getElementById("eightweeks");
var c = document.getElementById("twelveweeks");
a.onclick = function() {
d3.csv("/assets/data/data1.csv", function(error, data) {
var hourBuckets = d3.keys(data[0]).filter(function(key) { return key !== "Client"; });
data.forEach(function(d) {
d.hours = hourBuckets.map(function(name) { return {name: name, value: +d[name]}; });
});
var client = svg.selectAll(".client")
client.selectAll("rect")
.data(function(d) { return d.hours; })
.transition()
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
});
}
}
...no dice. I can get this to work when creating / transitioning simple one-series bar charts that use list inputs, but not the multi-series csv ones. data2.csv is the exact same file as data1.csv, with the values adjusted slightly.
Thanks for your time reading - any advice?
First svg.selectAll(".client") returns an empty selection, because you gave these elements the class 'g' instead of 'client'.
Secondly you need to update the data of the .client-elements:
var client = svg.selectAll(".client")
.data(data);
btw. you should use selection.classed() instead of selection.attr('class')

Resources