I'm Trying to implement the simple chart similar to the example as in below link from d3js
http://bl.ocks.org/NPashaP/96447623ef4d342ee09b
Goal is to implement this example from .csv file as for my requirement data changes dynamically.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body{
width:1060px;
margin:50px auto;
}
path { stroke: #fff; }
path:hover { opacity:0.9; }
rect:hover { fill:blue; }
.axis { font: 10px sans-serif; }
.legend tr{ border-bottom:1px solid grey; }
.legend tr:first-child{ border-top:1px solid grey; }
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path { display: none; }
.legend{
margin-bottom:76px;
display:inline-block;
border-collapse: collapse;
border-spacing: 0px;
}
.legend td{
padding:4px 5px;
vertical-align:bottom;
}
.legendFreq, .legendPerc{
align:right;
width:50px;
}
</style>
<body>
<div id='dashboard'>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
function dashboard(id, fData){
var barColor = 'steelblue';
function segColor(c){ return {low:"#807dba", mid:"#e08214",high:"#41ab5d"}[c]; }
// compute total for each state.
fData.forEach(function(d){d.total=d.freq.low+d.freq.mid+d.freq.high;});
// function to handle histogram.
function histoGram(fD){
var hG={}, hGDim = {t: 60, r: 0, b: 30, l: 0};
hGDim.w = 500 - hGDim.l - hGDim.r,
hGDim.h = 300 - hGDim.t - hGDim.b;
//create svg for histogram.
var hGsvg = d3.select(id).append("svg")
.attr("width", hGDim.w + hGDim.l + hGDim.r)
.attr("height", hGDim.h + hGDim.t + hGDim.b).append("g")
.attr("transform", "translate(" + hGDim.l + "," + hGDim.t + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, hGDim.w], 0.1)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
hGsvg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + hGDim.h + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
// Create function for y-axis map.
var y = d3.scale.linear().range([hGDim.h, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
// Create bars for histogram to contain rectangles and freq labels.
var bars = hGsvg.selectAll(".bar").data(fD).enter()
.append("g").attr("class", "bar");
//create the rectangles.
bars.append("rect")
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr('fill',barColor)
.on("mouseover",mouseover)// mouseover is defined below.
.on("mouseout",mouseout);// mouseout is defined below.
//Create the frequency labels above the rectangles.
bars.append("text").text(function(d){ return d3.format(",")(d[1])})
.attr("x", function(d) { return x(d[0])+x.rangeBand()/2; })
.attr("y", function(d) { return y(d[1])-5; })
.attr("text-anchor", "middle");
function mouseover(d){ // utility function to be called on mouseover.
// filter for selected state.
var st = fData.filter(function(s){ return s.State == d[0];})[0],
nD = d3.keys(st.freq).map(function(s){ return {type:s, freq:st.freq[s]};});
// call update functions of pie-chart and legend.
pC.update(nD);
leg.update(nD);
}
function mouseout(d){ // utility function to be called on mouseout.
// reset the pie-chart and legend.
pC.update(tF);
leg.update(tF);
}
// create function to update the bars. This will be used by pie-chart.
hG.update = function(nD, color){
// update the domain of the y-axis map to reflect change in frequencies.
y.domain([0, d3.max(nD, function(d) { return d[1]; })]);
// Attach the new data to the bars.
var bars = hGsvg.selectAll(".bar").data(nD);
// transition the height and color of rectangles.
bars.select("rect").transition().duration(500)
.attr("y", function(d) {return y(d[1]); })
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr("fill", color);
// transition the frequency labels location and change value.
bars.select("text").transition().duration(500)
.text(function(d){ return d3.format(",")(d[1])})
.attr("y", function(d) {return y(d[1])-5; });
}
return hG;
}
// function to handle pieChart.
function pieChart(pD){
var pC ={}, pieDim ={w:250, h: 250};
pieDim.r = Math.min(pieDim.w, pieDim.h) / 2;
// create svg for pie chart.
var piesvg = d3.select(id).append("svg")
.attr("width", pieDim.w).attr("height", pieDim.h).append("g")
.attr("transform", "translate("+pieDim.w/2+","+pieDim.h/2+")");
// create function to draw the arcs of the pie slices.
var arc = d3.svg.arc().outerRadius(pieDim.r - 10).innerRadius(0);
// create a function to compute the pie slice angles.
var pie = d3.layout.pie().sort(null).value(function(d) { return d.freq; });
// Draw the pie slices.
piesvg.selectAll("path").data(pie(pD)).enter().append("path").attr("d", arc)
.each(function(d) { this._current = d; })
.style("fill", function(d) { return segColor(d.data.type); })
.on("mouseover",mouseover).on("mouseout",mouseout);
// create function to update pie-chart. This will be used by histogram.
pC.update = function(nD){
piesvg.selectAll("path").data(pie(nD)).transition().duration(500)
.attrTween("d", arcTween);
}
// Utility function to be called on mouseover a pie slice.
function mouseover(d){
// call the update function of histogram with new data.
hG.update(fData.map(function(v){
return [v.State,v.freq[d.data.type]];}),segColor(d.data.type));
}
//Utility function to be called on mouseout a pie slice.
function mouseout(d){
// call the update function of histogram with all data.
hG.update(fData.map(function(v){
return [v.State,v.total];}), barColor);
}
// Animating the pie-slice requiring a custom function which specifies
// how the intermediate paths should be drawn.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) { return arc(i(t)); };
}
return pC;
}
// function to handle legend.
function legend(lD){
var leg = {};
// create table for legend.
var legend = d3.select(id).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(lD).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d){ return segColor(d.type); });
// create the second column for each segment.
tr.append("td").text(function(d){ return d.type;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq')
.text(function(d){ return d3.format(",")(d.freq);});
// create the fourth column for each segment.
tr.append("td").attr("class",'legendPerc')
.text(function(d){ return getLegend(d,lD);});
// Utility function to be used to update the legend.
leg.update = function(nD){
// update the data attached to the row elements.
var l = legend.select("tbody").selectAll("tr").data(nD);
// update the frequencies.
l.select(".legendFreq").text(function(d){ return d3.format(",")(d.freq);});
// update the percentage column.
l.select(".legendPerc").text(function(d){ return getLegend(d,nD);});
}
function getLegend(d,aD){ // Utility function to compute percentage.
return d3.format("%")(d.freq/d3.sum(aD.map(function(v){ return v.freq; })));
}
return leg;
}
// calculate total frequency by segment for all state.
var tF = ['low','mid','high'].map(function(d){
return {type:d, freq: d3.sum(fData.map(function(t){ return t.freq[d];}))};
});
// calculate total frequency by state for all segment.
var sF = fData.map(function(d){return [d.State,d.total];});
var hG = histoGram(sF), // create the histogram.
pC = pieChart(tF), // create the pie-chart.
leg= legend(tF); // create the legend.
}
</script>
<script>
var freqData=[
{State:'AL',freq:{low:4786, mid:1319, high:249}}
,{State:'AZ',freq:{low:1101, mid:412, high:674}}
,{State:'CT',freq:{low:932, mid:2149, high:418}}
,{State:'DE',freq:{low:832, mid:1152, high:1862}}
,{State:'FL',freq:{low:4481, mid:3304, high:948}}
,{State:'GA',freq:{low:1619, mid:167, high:1063}}
,{State:'IA',freq:{low:1819, mid:247, high:1203}}
,{State:'IL',freq:{low:4498, mid:3852, high:942}}
,{State:'IN',freq:{low:797, mid:1849, high:1534}}
,{State:'KS',freq:{low:162, mid:379, high:471}}
];
dashboard('#dashboard',freqData);
</script>
http://plnkr.co/edit/URzbAymLSJs3t1zoZLdI?p=preview is plnkr created for the same.
I've tried as in this Accessing values from [Object][Object] data in D3.js but its not working for me.
Can anyone suggest me with the code .Thanks!
Related
I have a data table with the following format:
Month,KPI,Type,Unit,S40401,S40402,S40403
JAN,A,Units FTP,PC,2000,4000,6000
JAN,B,Invoice Sales FTP,EUR,2000,4000,6000
JAN,C,Gross Sales Actual FTP,EUR,2000,4000,6000
JAN,D,Net Sales FTP,EUR,2000,4000,6000
JAN,E,CMC FTP,EUR,2000,4000,6000
FEB,A,Units FTP,PC,2000,4000,6000
FEB,B,Invoice Sales FTP,EUR,2000,4000,6000
FEB,C,Gross Sales Actual FTP,EUR,2000,4000,6000
FEB,D,Net Sales FTP,EUR,2000,4000,6000
FEB,E,CMC FTP,EUR,2000,4000,6000
...
...
...
If that data contains only one variable/KPI (e.g. one out of A-E), there´s no problem at all to parse the data into a D3-Chart. But since I want to select specific rows, it gets complicated. I can´t adapt the CSV file, the data has to be in this structure. So my only solution is filtering by rows.
How can I filter the data using .filter() to parse specific rows?
For example: Filter the data in a way that only the "A-rows" or "B-rows" from
column "KPI" are selected?
I suppose this part of the code where the CSV is imported needs the filter function. I already tried but no success:
<script>
var freqData;
d3.csv("export.csv", function(data) {
data = csv.filter(function(row) {
return row['KPI'] == 'C';
freqData = data.map(function(d) { return {
Month: d.Month,
freq: {
S40401: +d.S40401,
S40402: +d.S40402,
S40403: +d.S40403
}}
});
dashboard('#dashboard',freqData);
});
</script>
Below the whole code:
<script>
function dashboard(id, fData){
var barColor = 'steelblue';
function segColor(c){ return {S40401:"#04B404",S40402:"#045FB4",S40403:"#B40404"}[c]; }
// compute total for each state.
fData.forEach(function(d){d.total=d.freq.S40401+d.freq.S40402+d.freq.S40403;});
// function to handle histogram.
function histoGram(fD){
var hG={}, hGDim = {t: 60, r: 0, b: 30, l: 0};
hGDim.w = 500 - hGDim.l - hGDim.r,
hGDim.h = 300 - hGDim.t - hGDim.b;
//create svg for histogram.
var hGsvg = d3.select(id).append("svg")
.attr("width", hGDim.w + hGDim.l + hGDim.r)
.attr("height", hGDim.h + hGDim.t + hGDim.b).append("g")
.attr("transform", "translate(" + hGDim.l + "," + hGDim.t + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, hGDim.w], 0.1)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
hGsvg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + hGDim.h + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
// Create function for y-axis map.
var y = d3.scale.linear().range([hGDim.h, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
// Create bars for histogram to contain rectangles and freq labels.
var bars = hGsvg.selectAll(".bar").data(fD).enter()
.append("g").attr("class", "bar");
//create the rectangles.
bars.append("rect")
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr('fill',barColor)
.on("mouseover",mouseover)// mouseover is defined below.
.on("mouseout",mouseout);// mouseout is defined below.
//Create the frequency labels above the rectangles.
bars.append("text").text(function(d){ return d3.format(".3f")(d[1])})
.attr("x", function(d) { return x(d[0])+x.rangeBand()/2; })
.attr("y", function(d) { return y(d[1])-5; })
.attr("text-anchor", "middle");
function mouseover(d){ // utility function to be called on mouseover.
// filter for selected state.
var st = fData.filter(function(s){ return s.Month == d[0];})[0],
nD = d3.keys(st.freq).map(function(s){ return {type:s, freq:st.freq[s]};});
// call update functions of pie-chart and legend.
pC.update(nD);
leg.update(nD);
}
function mouseout(d){ // utility function to be called on mouseout.
// reset the pie-chart and legend.
pC.update(tF);
leg.update(tF);
}
// create function to update the bars. This will be used by pie-chart.
hG.update = function(nD, color){
// update the domain of the y-axis map to reflect change in frequencies.
y.domain([0, d3.max(nD, function(d) { return d[1]; })]);
// Attach the new data to the bars.
var bars = hGsvg.selectAll(".bar").data(nD);
// transition the height and color of rectangles.
bars.select("rect").transition().duration(500)
.attr("y", function(d) {return y(d[1]); })
.attr("height", function(d) { return hGDim.h - y(d[1]); })
.attr("fill", color);
// transition the frequency labels location and change value.
bars.select("text").transition().duration(500)
.text(function(d){ return d3.format(".3f")(d[1])})
.attr("y", function(d) {return y(d[1])-5; });
}
return hG;
}
// function to handle pieChart.
function pieChart(pD){
var pC ={}, pieDim ={w:250, h: 250};
pieDim.r = Math.min(pieDim.w, pieDim.h) / 2;
// create svg for pie chart.
var piesvg = d3.select(id).append("svg")
.attr("width", pieDim.w).attr("height", pieDim.h).append("g")
.attr("transform", "translate("+pieDim.w/2+","+pieDim.h/2+")");
// create function to draw the arcs of the pie slices.
var arc = d3.svg.arc().outerRadius(pieDim.r - 10).innerRadius(0);
// create a function to compute the pie slice angles.
var pie = d3.layout.pie().sort(null).value(function(d) { return d.freq; });
// Draw the pie slices.
piesvg.selectAll("path").data(pie(pD)).enter().append("path").attr("d", arc)
.each(function(d) { this._current = d; })
.style("fill", function(d) { return segColor(d.data.type); })
.on("mouseover",mouseover).on("mouseout",mouseout);
// create function to update pie-chart. This will be used by histogram.
pC.update = function(nD){
piesvg.selectAll("path").data(pie(nD)).transition().duration(500)
.attrTween("d", arcTween);
}
// Utility function to be called on mouseover a pie slice.
function mouseover(d){
// call the update function of histogram with new data.
hG.update(fData.map(function(v){
return [v.Month,v.freq[d.data.type]];}),segColor(d.data.type));
}
//Utility function to be called on mouseout a pie slice.
function mouseout(d){
// call the update function of histogram with all data.
hG.update(fData.map(function(v){
return [v.Month,v.total];}), barColor);
}
// Animating the pie-slice requiring a custom function which specifies
// how the intermediate paths should be drawn.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) { return arc(i(t)); };
}
return pC;
}
// function to handle legend.
function legend(lD){
var leg = {};
// create table for legend.
var legend = d3.select(id).append("table").attr('class','legend');
// create one row per segment.
var tr = legend.append("tbody").selectAll("tr").data(lD).enter().append("tr");
// create the first column for each segment.
tr.append("td").append("svg").attr("width", '16').attr("height", '16').append("rect")
.attr("width", '16').attr("height", '16')
.attr("fill",function(d){ return segColor(d.type); });
// create the second column for each segment.
tr.append("td").text(function(d){ return d.type;});
// create the third column for each segment.
tr.append("td").attr("class",'legendFreq')
.text(function(d){ return d3.format(".3f")(d.freq);});
// create the fourth column for each segment.
tr.append("td").attr("class",'legendPerc')
.text(function(d){ return getLegend(d,lD);});
// Utility function to be used to update the legend.
leg.update = function(nD){
// update the data attached to the row elements.
var l = legend.select("tbody").selectAll("tr").data(nD);
// update the frequencies.
l.select(".legendFreq").text(function(d){ return d3.format(".3f")(d.freq);});
// update the percentage column.
l.select(".legendPerc").text(function(d){ return getLegend(d,nD);});
}
function getLegend(d,aD){ // Utility function to compute percentage.
return d3.format("%")(d.freq/d3.sum(aD.map(function(v){ return v.freq; })));
}
return leg;
}
// calculate total frequency by segment for all state.
var tF = ['S40401','S40402','S40403'].map(function(d){
return {type:d, freq: d3.sum(fData.map(function(t){ return t.freq[d];}))};
});
// calculate total frequency by state for all segment.
var sF = fData.map(function(d){return [d.Month,d.total];});
var hG = histoGram(sF), // create the histogram.
pC = pieChart(tF), // create the pie-chart.
leg= legend(tF); // create the legend.
}
</script>
<script>
var freqData;
d3.csv("dataset_sales.csv", function(data) {
freqData = data.map(function(d) { return {
Month: d.Month,
freq: {
S40401: +d.S40401,
S40402: +d.S40402,
S40403: +d.S40403
}}
});
dashboard('#dashboard',freqData);
});
</script>
You werent closing your functions and you werent filtering on your data.
You loaded your data as data and then ran your filter function on csv.
The below should do what you are looking to do
var freqData;
d3.csv("test.csv", function(data) {
filteredData = data.filter(function(row) {
return row['KPI'] == 'C';
});
freqData = filteredData.map(function(d) {
return {
Month: d.Month,
freq: {
S40401: +d.S40401,
S40402: +d.S40402,
S40403: +d.S40403
}
}
});
console.log("freqData", freqData);
});
Output:
[{
"Month": "JAN",
"freq": {
"S40401": 2000,
"S40402": 4000,
"S40403": 6000
}, {
"Month": "FEB",
"freq": {
"S40401": 2000,
"S40402": 4000,
"S40403": 6000
}
}]
I have created this curve line chart and I filled by purple color only. Then I decided to add color range and I used color scaling method is Category10 but it is not working. I am new to D3.js and it has been my headache for the past week.
In order to make it look more presentable, I need to add colors and I did tried to add this way by using .style attribute but
function buildLine(data){
var xScale=d3.scale.linear()
.domain([0,d3.max(data,function(d){return d.spend;})])
.range([0, w]);
var yScale=d3.scale.linear()
.domain([0,d3.max(data,function(d){return d.alpha;})])
.range([h/2, 0]);
var yAxisGen=d3.svg.axis().scale(yScale).orient("left");
var xAxisGen=d3.svg.axis().scale(xScale).orient("bottom");
var svg=d3.select("body").append("svg").attr({width:w,height:h});
var yAxis= svg.append("g")
.call(yAxisGen)
.attr("class","axis")
.attr("transform", "translate(50, " +10 +")");
var xAxisTranslate = h/2 + 10;
var xAxis= svg.append("g")
.call(xAxisGen)
.attr("class","axis")
.attr("transform", "translate(50, " + xAxisTranslate +")");
Adding color function here
var color=d3.scale.category10
var lineFun = d3.svg.line()
.x(function (d) { return xScale(d.x); })
.y(function (d) { return yScale(d.y); })
.interpolate("basis");
Here I tried to add it dynamically. '.style does not work. WHy?'
var viz = svg.selectAll("path")
.data(data)
.enter()
.append("path")
.attr({
d: function(d) {
return lineFun(d.arr)
},
"stroke": "purple",
"stroke-width": 2,
"fill": "none",
"class":"line"
})
.style("stroke",function(){
return d.color=color(d.arr);
})
.attr("transform", "translate(48, " + 10 +")")
;
}
d3.json("sample-data.json",function(error,data){
//check the file loaded properly
if (error) { //is there an error?
console.log(error); //if so, log it to the console
} else { //If not we're golden!
//Now show me the money!
ds=data; //put the data in the global var
}
data.forEach(function(jsonData){
var lineData = d3.range(0, jsonData.spend, 100)
.map(x => [x, (jsonData.alpha * (1 - Math.pow(2.71828, (-jsonData.beta * x))))] );
/* console.log("this is the data:",lineData);*/
//i think line date returns an array with each item in it also an array of 2 items
var arr = [];
for (var i = 0; i < lineData.length; i++) {
arr.push({
x: lineData[i][0],
y: lineData[i][1]
});
}
jsonData.arr = arr;
console.log(jsonData);
});
buildLine(data);
});
These are the problems:
You have to call the scale function:
var color=d3.scale.category10()
//parentheses here ----------^
You cannot use the argument d if you don't set the anonymous function parameter:
.style("stroke",function(d){
//parameter here-----^
return d.color=color(d.arr);
})
scale.category10() works on a "first come, first served" basis. You just need the index:
.style("stroke",function(d,i){
return d.color=color(i);
});
Here is a demo showing how to use that scale:
var color = d3.scale.category10();
var divs = d3.select("body").selectAll(null)
.data(d3.range(10))
.enter()
.append("div")
.style("background-color", function(d, i) {
return color(i)
})
div {
width: 20px;
height: 20px;
display: inline-block;
margin: 2px;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
I want to develop "drill down" "pie chart" using "D3.JS". I found the below sample, and perfect to me to use.
D3 Js sample drill down pie chart
The above sample is perfect fit for me.
Additionally from the sample drill down pie chart, I want to place TEXT LABEL in each section of pie chart divided.
I followed many samples,
Sample 1 - pie chart with section text label
Sample 2 - JSFiddle - pie chart with section text label
Based on the above samples for placing text label in pie chart sections, I followed the below code, I tried adding sample "var dataSet", "var arcs = svg.selectAll("g.slice")" code.
But, when I execute the program, it doesn't display any text label in centre of each section in pie chart.
Could someone guide me to fix this please?
<!doctype html>
<html>
<head>
<head>
<meta charset="utf-8">
<title>Drill down pie chart test</title>
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js?2.9.6"></script>
<style type="text/css">
body {
text-align: center;
padding: 50px;
font-family: "Helvetica Neue",Arial,Sans-serif;
font-weight: 200;
color: #333;
}
.header {
font-size: 20px;
}
.sector {
cursor: pointer;
}
.slice text {
font-size: 16pt;
font-family: Arial;
}
</style>
</head>
<body>
<script type="text/javascript">
// Globals
var width = 500,
height = 400,
margin = 50,
radius = Math.min(width - margin, height - margin) / 2,
// Pie layout will use the "val" property of each data object entry
pieChart = d3.layout.pie().sort(null).value(function(d){return d.val;}),
arc = d3.svg.arc().outerRadius(radius),
MAX_SECTORS = 15, // Less than 20 please
colors = d3.scale.category20();
var dataSet = [
{"legendLabel":"One", "magnitude":20},
{"legendLabel":"Two", "magnitude":40},
{"legendLabel":"Three", "magnitude":50},
{"legendLabel":"Four", "magnitude":16},
{"legendLabel":"Five", "magnitude":50},
{"legendLabel":"Six", "magnitude":8},
{"legendLabel":"Seven", "magnitude":30}];
//mydata = {"Medical", "Agriculture", "Security"};
var st = {};
st.data = [{"label":"less than a week","value":169,"pos":0},{"label":"1 week - 30 days","value":1,"pos":1},{"label":"30 - 90 days","value":22,"pos":2},{"label":"90 - 180 days","value":35,"pos":3},{"label":"180 days - 1 year","value":47,"pos":4},{"label":"more than 1 year","value":783,"pos":5}] ;
// Synthetic data generation ------------------------------------------------
var data = [];
var numSectors = 8; //Math.ceil(Math.random()*MAX_SECTORS);
for(i = -1; i++ < numSectors; ) {
var children = [];
var numChildSectors = Math.ceil(Math.random()*MAX_SECTORS);
var color = colors(i);
for( j=-1; j++ < numChildSectors; ){
// Add children categories with shades of the parent color
children.push(
{ cat: "cat"+((i+1)*100+j),
val: Math.random(),
color: d3.rgb(color).darker(1/(j+1))
});
}
data.push({
cat: "cat"+i,
val: Math.random(),
color: color,
children: children});
}
// --------------------------------------------------------------------------
// SVG elements init
var svg = d3.select("body").append("svg").data([dataSet]).attr("width", width).attr("height", height),
defs = svg.append("svg:defs"),
// .data(pieChart)
// Declare a main gradient with the dimensions for all gradient entries to refer
mainGrad = defs.append("svg:radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", 0).attr("cy", 0).attr("r", radius).attr("fx", 0).attr("fy", 0)
.attr("id", "master"),
// The pie sectors container
arcGroup = svg.append("svg:g")
.attr("class", "arcGroup")
.attr("filter", "url(#shadow)")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"),
// Header text
header = svg.append("text").text("Biotechnology")
.attr("transform", "translate(10, 20)").attr("class", "header");
//svg.append("text").attr("text-anchor", "middle").text("$" + "sample"),
//svg.append("text").text("sample").attr("text-anchor", "middle")
/*svg.append("text")
.attr("transform", "translate(" + arcGroup.centroid(d) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text("sample");
*/
// Declare shadow filter
var shadow = defs.append("filter").attr("id", "shadow")
.attr("filterUnits", "userSpaceOnUse")
.attr("x", -1*(width / 2)).attr("y", -1*(height / 2))
.attr("width", width).attr("height", height);
shadow.append("feGaussianBlur")
.attr("in", "SourceAlpha")
.attr("stdDeviation", "4")
.attr("result", "blur");
shadow.append("feOffset")
.attr("in", "blur")
.attr("dx", "4").attr("dy", "4")
.attr("result", "offsetBlur");
shadow.append("feBlend")
.attr("in", "SourceGraphic")
.attr("in2", "offsetBlur")
.attr("mode", "normal");
/* var arcs = svg.selectAll("g.slice")
arcs.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.value; });
*/
// Redraw the graph given a certain level of data
function updateGraph(cat){
var currData = data;
// Simple header text
if(cat != undefined){
currData = findChildenByCat(cat);
d3.select(".header").text("Biotechnology → "+cat);
} else {
d3.select(".header").text("Biotechnology");
}
// Create a gradient for each entry (each entry identified by its unique category)
var gradients = defs.selectAll(".gradient").data(currData, function(d){return d.cat;});
gradients.enter().append("svg:radialGradient")
.attr("id", function(d, i) { return "gradient" + d.cat; })
.attr("class", "gradient")
.attr("xlink:href", "#master");
gradients.append("svg:stop").attr("offset", "0%").attr("stop-color", getColor );
gradients.append("svg:stop").attr("offset", "90%").attr("stop-color", getColor );
gradients.append("svg:stop").attr("offset", "100%").attr("stop-color", getDarkerColor );
/*var arcs = defs.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class","slice");
arcs.append("svg:text").attr("transform", function(d){
d.innerRadius = 0;
d.outerRadius = r;
return "translate(" + arc.centroid(d) + ")";}).attr("text-anchor", "middle").text( function(d, i) {
return (data[i].value / tot ) * 100 > 10 ? ((data[i].value / tot ) * 100).toFixed(1) + "%" : "";
}
).attr("fill","#fff")
.classed("slice-label",true);
*/
// Create a sector for each entry in the enter selection
var paths = arcGroup.selectAll("path")
.data(pieChart(currData), function(d) {return d.data.cat;} );
paths.enter().append("svg:path").attr("class", "sector");
// Each sector will refer to its gradient fill
paths.attr("fill", function(d, i) { return "url(#gradient"+d.data.cat+")"; })
.transition().duration(1000).attrTween("d", tweenIn).each("end", function(){
this._listenToEvents = true;
});
// Mouse interaction handling
paths.on("click", function(d){
if(this._listenToEvents){
// Reset inmediatelly
d3.select(this).attr("transform", "translate(0,0)")
// Change level on click if no transition has started
paths.each(function(){
this._listenToEvents = false;
});
updateGraph(d.data.children? d.data.cat : undefined);
}
})
.on("mouseover", function(d){
// Mouseover effect if no transition has started
if(this._listenToEvents){
// Calculate angle bisector
var ang = d.startAngle + (d.endAngle - d.startAngle)/2;
// Transformate to SVG space
ang = (ang - (Math.PI / 2) ) * -1;
// Calculate a 10% radius displacement
var x = Math.cos(ang) * radius * 0.1;
var y = Math.sin(ang) * radius * -0.1;
d3.select(this).transition()
.duration(250).attr("transform", "translate("+x+","+y+")");
}
})
.on("mouseout", function(d){
// Mouseout effect if no transition has started
if(this._listenToEvents){
d3.select(this).transition()
.duration(150).attr("transform", "translate(0,0)");
}
});
// Collapse sectors for the exit selection
paths.exit().transition()
.duration(1000)
.attrTween("d", tweenOut).remove();
// NEWLY ADDED START
// Select all <g> elements with class slice (there aren't any yet)
var arcs = svg.selectAll("g.slice")
// Associate the generated pie data (an array of arcs, each having startAngle,
// endAngle and value properties)
.data(pie)
// This will create <g> elements for every "extra" data element that should be associated
// with a selection. The result is creating a <g> for every object in the data array
.enter()
// Create a group to hold each slice (we will have a <path> and a <text>
// element associated with each slice)
.append("svg:g")
.attr("class", "slice"); //allow us to style things in the slices (like text)
arcs.append("svg:path")
//set the color for each slice to be chosen from the color function defined above
.attr("fill", function(d, i) { return color(i); } )
//this creates the actual SVG path using the associated data (pie) with the arc drawing function
.attr("d", arc);
// Add a legendLabel to each arc slice...
arcs.append("svg:text")
.attr("transform", function(d) { //set the label's origin to the center of the arc
//we have to make sure to set these before calling arc.centroid
d.outerRadius = outerRadius + 50; // Set Outer Coordinate
d.innerRadius = outerRadius + 45; // Set Inner Coordinate
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
.style("fill", "Purple")
.style("font", "bold 12px Arial")
<!-- .text(function(d, i) { return dataSet[i].legendLabel; }); //get the label from our original dat -->
.text(function(d, i) { return "Test"; }); //get the label from our original dat
// Add a magnitude value to the larger arcs, translated to the arc centroid and rotated.
arcs.filter(function(d) { return d.endAngle - d.startAngle > .2; }).append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
//.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")rotate(" + angle(d) + ")"; })
.attr("transform", function(d) { //set the label's origin to the center of the arc
//we have to make sure to set these before calling arc.centroid
d.outerRadius = outerRadius; // Set Outer Coordinate
d.innerRadius = outerRadius/2; // Set Inner Coordinate
return "translate(" + arc.centroid(d) + ")rotate(" + angle(d) + ")";
})
.style("fill", "White")
.style("font", "bold 12px Arial")
.text(function(d) { return d.data.magnitude; });
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
// END
}
// "Fold" pie sectors by tweening its current start/end angles
// into 2*PI
function tweenOut(data) {
data.startAngle = data.endAngle = (2 * Math.PI);
var interpolation = d3.interpolate(this._current, data);
this._current = interpolation(0);
return function(t) {
return arc(interpolation(t));
};
}
// "Unfold" pie sectors by tweening its start/end angles
// from 0 into their final calculated values
function tweenIn(data) {
var interpolation = d3.interpolate({startAngle: 0, endAngle: 0}, data);
this._current = interpolation(0);
return function(t) {
return arc(interpolation(t));
};
}
// Helper function to extract color from data object
function getColor(data, index){
return data.color;
}
// Helper function to extract a darker version of the color
function getDarkerColor(data, index){
return d3.rgb(getColor(data, index)).darker();
}
function findChildenByCat(cat){
for(i=-1; i++ < data.length - 1; ){
if(data[i].cat == cat){
return data[i].children;
}
}
return data;
}
//.text(function(d, i) { return categorydata[i].label; });
// Start by updating graph at root level
updateGraph();
</script>
<!-- <p>Drill down pie chart test by Marc Baiges Camprubí (marcbc#gmail.com) in D3.js -->
</body>
</html>
Instead of doing this:
var arcs = svg.selectAll("g.slice")
do this
var arcs = arcGroup.selectAll("g.slice")
reason so that the text label and the path of the pie all are in same group.
Give proper inner and outer radius for placing the labels in the center (so that the centroid is calculated on basis of the new inner outer radius of the arc)
arcs.append("svg:text")
.attr("transform", function(d) { //set the label's origin to the center of the arc
//we have to make sure to set these before calling arc.centroid
d.outerRadius = radius - 20; // Set Outer Coordinate
d.innerRadius = radius - 100; // Set Inner Coordinate
return "translate(" + arc.centroid(d) + ")";
})
Next give proper data in the text:
.text(function(d, i) { return "Test"; }); //get the label from our original data
do this
.text(function(d, i) { return d.data.cat; }); //get the label from our original data
working code here
How would I apply an animated transition when sorting a pie chart? I don't mean updating values, I mean sorting each arc of the pie to move into place the way bars do here.
There are plenty of transition examples for updating using new values of an existing data set (or switching data sets). I can't find anything on how to re-sort (using the same values, same data set).
I'm using this for now which simply redraws the arc by applying the same tween used when initializing the rendering, but it starts each arc from zero.
.attr('d', arc)
.transition()
.duration(1000)
.attrTween("d", tweenPie);
function tweenPie(b) {
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arc(i(t)); };
};
Would I need to store the existing start and end angles somehow and run the tween from those?
I see something kind of like that here, though this example is updating values, not sorting.
Thanks.
Building on the Bostock example here.
setInterval(change, 2000);
var sort = false;
function change() {
sort = !sort;
if (sort){
pie = d3.layout.pie() //<-- pie with default sort
.value(function(d) {
return d.value;
});
} else {
pie = d3.layout.pie() //<-- pie with no sort
.value(function(d) {
return d.value;
})
.sort(null);
}
path = path.data(pie); // compute the new angles
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
Full code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 400px;
}
text {
font: 10px sans-serif;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<body>
<script>
var width = 400,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.value(function(d) {
return d.value;
})
.sort(null);
var defaultSort = pie.sort;
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = [{
value: 1
}, {
value: 5
}, {
value: 2
}, {
value: 6
}
];
var path = svg.datum(data).selectAll("path")
.data(pie)
.enter().append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc)
.each(function(d) {
this._current = d;
}); // store the initial angles
setInterval(change, 2000);
var sort = false;
function change() {
sort = !sort;
if (sort){
pie = d3.layout.pie()
.value(function(d) {
return d.value;
});
} else {
pie = d3.layout.pie()
.value(function(d) {
return d.value;
})
.sort(null);
}
path = path.data(pie); // compute the new angles
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
</script>
</body>
Learning D3 I created a chart based off this example. The chart is implemented as a JS closure, with Mike Bostock’s convention for creating reusable components in D3. (or as close as I can get)
When zooming and panning, the line path is not redrawn correctly.
In my chart I have a scatter plot and a line path joining the dots. The dots work but not the line. It's (maybe) something to do with rebinding the xScale during the onzoom behavior.... I've tried exposing the line function / object and a bunch of trial and error stuff but am at my wits end. Any help much appreciated.
Please see this codepen, or run the embedded code snippet.
http://codepen.io/Kickaha/pen/epzNyw
var MyNS = MyNS || {};
MyNS.EvalChartSeries = function () {
var xScale = d3.time.scale(),
yScale = d3.scale.linear();
//I tried exposing the line function / object to be able to call it in the on zoom ... no dice.
//var line = d3.svg.line();
var EvalChartSeries = function (selection) {
selection.each(function (dataIn) {
//select and bind data for scatter dots
spots = d3.select(this).selectAll("circle")
.data(dataIn);
//enter and create a circle for any unassigned datum
spots.enter().append("circle");
//update the bound items using the x-y scale function to recalculate
spots
.attr("r", 8)
.attr("cx", function (d) { return xScale(d.dt); })
.attr("cy", function (d) { return yScale(d.spot); })
.style("fill", function (d) {
switch (d.eva) {
case 1: return "green"; break;
case 2: return "red"; break;
case 3: return "blue"; break;
case 4: return "yellow"; break;}
});
//exit to remove any unused data, most likely not needed in this case as the data set is static
spots.exit().remove();
//here the line function/object is assigned it's scale and bound to data
var line = d3.svg.line().x(function (d) { return xScale(d.dt); })
.y(function (d) { return yScale(d.spot); }).interpolate("linear");
//and here is where the line is drawn by appending a set of svg path points
//, it does not use the select, enter, update, exit logic because a line while being a set of points is one thing (http://stackoverflow.com/questions/22508133/d3-line-chart-with-enter-update-exit-logic)
lines = d3.select(this)
.append("path");
lines
.attr('class', 'line')
.attr("d", line(dataIn))
.attr("stroke", "steelblue").attr("stroke-width", 1);
});
};
//The scales are exposed as properties, and they return the object to support chaining
EvalChartSeries.xScale = function (value) {
if (!arguments.length) {
return xScale;
}
xScale = value;
return EvalChartSeries;
};
EvalChartSeries.yScale = function (value) {
if (!arguments.length) {
return yScale;
}
yScale = value;
return EvalChartSeries;
};
/*
Here I tried to expose the line function/object as a property to rebind it to the xAxis when redrawing ... didnt work
EvalChartSeries.line = function (value) {
if (!arguments.length) {
return line;
}
line = value;
//linePath.x = function (d) { return xScale(d.dt); };
return EvalChartSeries;
};*/
//the function returns itself to suppport method chaining
return EvalChartSeries;
};
//The chart is defined here as a closure to enable Object Orientated reuse (encapsualtion / data hiding etc.. )
MyNS.DotsChart = (function () {
data = [{"dt":1280780384000,"spot":1.3173999786376953,"eva":4},
{"dt":1280782184000,"spot":1.3166999816894531,"eva":4},
{"dt":1280783084000,"spot":1.3164000511169434,"eva":4},
{"dt":1280781284000,"spot":1.3167999982833862,"eva":4},
{"dt":1280784884000,"spot":1.3162000179290771,"eva":4},
{"dt":1280783984000,"spot":1.3163000345230103,"eva":4},
{"dt":1280785784000,"spot":1.315999984741211,"eva":4},
{"dt":1280786684000,"spot":1.3163000345230103,"eva":4},
{"dt":1280787584000,"spot":1.316100001335144,"eva":4},
{"dt":1280788484000,"spot":1.3162000179290771,"eva":4},
{"dt":1280789384000,"spot":1.3164000511169434,"eva":4},
{"dt":1280790284000,"spot":1.3164000511169434,"eva":4},
{"dt":1280791184000,"spot":1.3166999816894531,"eva":4},
{"dt":1280792084000,"spot":1.3169000148773193,"eva":4},
{"dt":1280792984000,"spot":1.3170000314712524,"eva":4},
{"dt":1280793884000,"spot":1.3174999952316284,"eva":4},
{"dt":1280794784000,"spot":1.3171000480651855,"eva":4},
{"dt":1280795684000,"spot":1.3163000345230103,"eva":2},
{"dt":1280796584000,"spot":1.315600037574768,"eva":2},
{"dt":1280797484000,"spot":1.3154000043869019,"eva":2},
{"dt":1280798384000,"spot":1.3147000074386597,"eva":2},
{"dt":1280799284000,"spot":1.3164000511169434,"eva":2},
{"dt":1280800184000,"spot":1.3178000450134277,"eva":4},
{"dt":1280801084000,"spot":1.3176000118255615,"eva":4},
{"dt":1280801984000,"spot":1.3174999952316284,"eva":4},
{"dt":1280802884000,"spot":1.3193000555038452,"eva":3},
{"dt":1280803784000,"spot":1.32260000705719,"eva":4},
{"dt":1280804684000,"spot":1.3216999769210815,"eva":4},
{"dt":1280805584000,"spot":1.3233000040054321,"eva":4},
{"dt":1280806484000,"spot":1.3229000568389893,"eva":4},
{"dt":1280807384000,"spot":1.3229999542236328,"eva":2},
{"dt":1280808284000,"spot":1.3220000267028809,"eva":2},
{"dt":1280809184000,"spot":1.3224999904632568,"eva":2},
{"dt":1280810084000,"spot":1.3233000040054321,"eva":2},
{"dt":1280810984000,"spot":1.3240000009536743,"eva":2},
{"dt":1280811884000,"spot":1.3250000476837158,"eva":4},
{"dt":1280812784000,"spot":1.3253999948501587,"eva":4},
{"dt":1280813684000,"spot":1.3248000144958496,"eva":4},
{"dt":1280814584000,"spot":1.3250000476837158,"eva":4},
{"dt":1280815484000,"spot":1.3249000310897827,"eva":4},
{"dt":1280816384000,"spot":1.3238999843597412,"eva":2},
{"dt":1280817284000,"spot":1.3238999843597412,"eva":2},
{"dt":1280818184000,"spot":1.322700023651123,"eva":2},
{"dt":1280819084000,"spot":1.32260000705719,"eva":2},
{"dt":1280819984000,"spot":1.3219000101089478,"eva":2},
{"dt":1280820884000,"spot":1.323199987411499,"eva":4},
{"dt":1280821784000,"spot":1.3236000537872314,"eva":4},
{"dt":1280822684000,"spot":1.3228000402450562,"eva":4},
{"dt":1280823584000,"spot":1.3213000297546387,"eva":2},
{"dt":1280824484000,"spot":1.3214999437332153,"eva":2},
{"dt":1280825384000,"spot":1.3215999603271484,"eva":2},
{"dt":1280826284000,"spot":1.320199966430664,"eva":2},
{"dt":1280827184000,"spot":1.3187999725341797,"eva":2},
{"dt":1280828084000,"spot":1.3200000524520874,"eva":2},
{"dt":1280828984000,"spot":1.3207000494003296,"eva":1}
];
var minDate = d3.min(data, function (d) { return d.dt; }),
maxDate = d3.max(data, function (d) { return d.dt; });
var yMin = d3.min(data, function (d) { return d.spot; }),
yMax = d3.max(data, function (d) { return d.spot; });
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Set up the drawing area
var margin = {top: 20, right: 20, bottom: 30, left: 35},
width = 1600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
//select the single element chart in the html body (this is expected to exist) and append a svg element
var plotChart =d3.select('#chart')
.append("svg:svg")
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('svg:g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var plotArea = plotChart.append('g')
.attr('clip-path', 'url(#plotAreaClip)');//http://stackoverflow.com/questions/940451/using-relative-url-in-css-file-what-location-is-it-relative-to
plotArea.append('clipPath')
.attr('id', 'plotAreaClip')
.append('rect')
.attr({ width: width, height: height });
// Scales
var xScale = d3.time.scale(),
yScale = d3.scale.linear();
// Set scale domains
xScale.domain([minDate, maxDate]);
yScale.domain([yMin, yMax]).nice();
// Set scale ranges
xScale.range([0, width]);
yScale.range([height, 0]);
// Axes
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.ticks(5);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left');
/* var line = d3.svg.line()
.x(function (d) { return xScale(d.dt); })
.y(function (d) { return yScale(d.spot); }).interpolate("linear");
*/
plotChart.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
plotChart.append('g')
.attr('class', 'y axis')
.call(yAxis);
// Data series
var series = MyNS.EvalChartSeries()
.xScale(xScale)
.yScale(yScale);
// .line(line); exposing this property did nothing
//appending a group 'g' tag binding the data and calling on our d3 line+dots chart object to process it
var dataSeries = plotArea.append('g')
.attr('class', 'series')
.datum(data)
.call(series);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Zooming and panning
//on zoom check extents , then most importantny redraw the chart
var zoom = d3.behavior.zoom()
.x(xScale)
.on('zoom', function() {
if (xScale.domain()[0] < minDate) {
zoom.translate([zoom.translate()[0] - xScale(minDate) + xScale.range()[0], 0]);
} else if (xScale.domain()[1] > maxDate) {
zoom.translate([zoom.translate()[0] - xScale(maxDate) + xScale.range()[1], 0]);
}
//most important to redraw "on zoom"
redrawChart();
});
//an overlay area to catch mouse events from the full area of the chart (not just the rendered dots and line)
var overlay = d3.svg.area()
.x(function (d) { return xScale(d.dt); })
.y0(0)
.y1(height);
//an area is a path object, not to be confused with our line path
plotArea.append('path')
.attr('class', 'overlay')
.attr('d', overlay(data))
.call(zoom);
redrawChart();
updateZoomFromChart();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper methods
function redrawChart() {
//redraws the scatter data series
dataSeries.call(series);
//redraws the xaxis to show the current zoom pan area
plotChart.select('.x.axis').call(xAxis);
// plotChart.select(".line")
// .attr("class", "line");
// .attr("d", line);
//filters the data set to what is visible given teh current zoom pan state
var yExtent = d3.extent(data.filter(function (d) {
var dt = xScale(d.dt);
return dt > 0 && dt < width;
}), function (d) { return d.spot; });
yScale.domain(yExtent).nice();
//this scales the y axis to maximum visibility as the line is zoomed and panned
plotChart.select(".y.axis").call(yAxis);
}
//takes care of zooming and panning past the ends of the data.
function updateZoomFromChart() {
var fullXDomain = maxDate - minDate,
currentXDomain = xScale.domain()[1] - xScale.domain()[0];
var minXScale = currentXDomain / fullXDomain,
maxXScale = minXScale * 20;
zoom.x(xScale)
.scaleExtent([minXScale, maxXScale]);
}})()
#chart {
margin-top: 20px;
margin-bottom: 20px;
width: 660px;
}.chart .overlay {
stroke-width: 0px;
fill-opacity: 0;
}
.overlay {
stroke-width: 0px;
fill-opacity: 0;
}
body {
padding: 10px 20px;
background: #ffeeee;
font-family: sans-serif;
text-align: center;
color: #7f7;
}.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
How do I get the line to redraw correctly?
Thank you for a very well documented question.
What you are doing, is that on zoom you re-draw the line, without removing the one already existing in your SVG element. May I suggest the following:
Change your zoom method to:
var zoom = d3.behavior.zoom()
.x(xScale)
.on('zoom', function() {
if (xScale.domain()[0] < minDate) {
zoom.translate([zoom.translate()[0] - xScale(minDate) + xScale.range()[0], 0]);
} else if (xScale.domain()[1] > maxDate) {
zoom.translate([zoom.translate()[0] - xScale(maxDate) + xScale.range()[1], 0]);
}
// add the following line, to remove the lines already present
d3.selectAll('.line').remove()
//most important to redraw "on zoom"
redrawChart();
});
I am sure there are better ways of doing it, but I think this will get you started.
Hope it helps.