D3.js How to hide/show line when click select options? - d3.js

I tried to use D3.js to draw lines when you click on different checkbox. It will get data of that option. Here is an example I used D3 multi-series line chart with tooltips and legend. I have got the specific data object that is corresponding the checkbox I choose. Howevery, I don't know how to draw it on svg. Please help me and I will appreciate you so much.
I also find a website "www.cotrino.com/starpaths/" that shows the final effect I want to implement.
My D3 effect
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis--x path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<script src="http://d3js.org/d3.v4.js"></script>
<body>
<svg width="1000" height="500"></svg>
<div id="disease_list"></div>
</body>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//make a clip path for the graph
var clip = svg.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var parseTime = d3.timeParse("%Y-%m");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]);
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { console.log(d.date); return x(d.date); })
.y(function(d) { console.log(d.date); return y(d.count); });
var color = d3.scaleOrdinal(d3.schemeCategory20);
d3.csv("./top10highestNormalize.csv", type, function(error, data) {
if (error) throw error;
var diseases = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {date: d.date, count: d[id]};
})
};
});
console.log(diseases);
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(diseases, function(c) { return d3.min(c.values, function(d) { return d.count; }); }),
d3.max(diseases, function(c) { return d3.max(c.values, function(d) { return d.count; }); })
]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Count");
/*
var disease = g.selectAll(".disease")
.data(diseases)
.enter().append("g")
.attr("class", "disease");
*/
// Create the shape selectors
var selector = d3.select("#disease_list").append("select");
labels = selector.selectAll("option")
.data(diseases)
.enter()
.append("option")
.attr("value",function(d,i) {return i;})
.text(function(d) {return d.id;});
var menu = d3.select("#disease_list select")
.on("change", redraw);
// var series = menu.property("value");
//console.log(series);
// all the meat goes in the redraw function
function redraw() {
console.log("redraw start");
// get value from menu selection
// the option values are set in HTML and correspond
//to the [type] value we used to nest the data
var series = menu.property("value");
console.log(series);
// only retrieve data from the selected series, using the nest we just created
var adata = diseases[series];
console.log(adata);
}
});
function type(d, _, columns) {
d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
}
</script>
top10highestNormalize.csv
date,disseminated sclerosis,sclerosis,gestural tics,venereal disease,bite,cot death,venereal disease,cardiovascular disease,diseases vascular,pruritis,pus,cystic fibrosis,fibroses
2010-04,0,0,0,0,0,0,0,0,0,0,0,0,0
2010-05,0,0,0.06898023,0.068783069,0.085790885,0.065761258,0.068783069,0,0,0.001204094,0.023051592,0,0
2010-06,0.076923077,0.076923077,0.190584554,0.199972867,0.201072386,0.171789373,0.199972867,0.071428571,0.071428571,0.004816376,0.031284303,0.2,0.2
2010-07,0.230769231,0.230769231,0.221590101,0.224664225,0.225201072,0.235167977,0.224664225,0.214285714,0.285714286,0.00602047,0.038419319,0,0
2010-08,0.538461538,0.538461538,0.174797326,0.182471849,0.174262735,0.192041935,0.182471849,0.071428571,0.071428571,0.003612282,0.023051592,0,0
2010-09,0.230769231,0.230769231,0.287725786,0.277845611,0.252010724,0.259471051,0.277845611,0,0,0.004214329,0.046652031,0,0
2010-10,0.076923077,0.076923077,0.295406059,0.299416633,0.285969616,0.265665952,0.299416633,0,0.071428571,0.007224564,0.03402854,0.066666667,0.066666667
2010-11,0.153846154,0.153846154,0.284027877,0.279337946,0.261840929,0.276149631,0.279337946,0,0,0.006622517,0.050493963,0,0
2010-12,0.153846154,0.153846154,0.271511876,0.237552571,0.213583557,0.237312366,0.237552571,0.142857143,0.142857143,0.004214329,0.035126235,0,0
2011-01,0.076923077,0.076923077,0.306642014,0.312440646,0.28150134,0.305694544,0.312440646,0.142857143,0.142857143,0.006622517,0.046103183,0,0.066666667
2011-02,0.076923077,0.076923077,0.288721377,0.262243929,0.219839142,0.25899452,0.262243929,0.142857143,0.142857143,0.007224564,0.038968167,0,0.066666667
2011-03,0.076923077,0.076923077,0.271654103,0.255324922,0.253798034,0.266857279,0.255324922,0.071428571,0.071428571,0.007224564,0.051591658,0,0
2011-04,0.461538462,0.461538462,0.291423695,0.252068919,0.235031278,0.284250655,0.252068919,0,0,0.009030704,0.045005488,0,0
2011-05,0.153846154,0.153846154,0.448158157,0.380681047,0.351206434,0.439123183,0.380681047,0,0,0.011438892,0.079582876,0.333333333,0.4
2011-06,0.153846154,0.153846154,0.498079932,0.437661104,0.391420912,0.424827258,0.437661104,0.142857143,0.142857143,0.009632751,0.063117453,0,0.066666667
2011-07,0,0,0.410467928,0.424094424,0.419124218,0.379080295,0.424094424,0,0.071428571,0.009030704,0.061470911,1,1
2011-08,0.076923077,0.076923077,0.268382876,0.262922263,0.238605898,0.267810341,0.262922263,0.214285714,0.214285714,0.002408188,0.038968167,0,0
2011-09,0.230769231,0.230769231,0.510027023,0.469949803,0.470956211,0.444841553,0.469949803,0,0,0.014449127,0.075740944,0.133333333,0.2
2011-10,0.076923077,0.076923077,0.462380885,0.434540768,0.431635389,0.417679295,0.434540768,0.142857143,0.142857143,0.006622517,0.073545554,0,0.066666667
2011-11,0.153846154,0.153846154,0.519698478,0.457061457,0.415549598,0.443888492,0.457061457,0.142857143,0.142857143,0.01384708,0.06805708,0.2,0.2
2011-12,1,1,0.382449154,0.35002035,0.319928508,0.315701692,0.35002035,0,0,0.002408188,0.060373216,0,0
2012-01,0.461538462,0.461538462,0.492390841,0.45312712,0.409294013,0.45389564,0.45312712,0.571428571,0.571428571,0.007224564,0.060373216,0,0
2012-02,0.076923077,0.076923077,0.382875836,0.375932709,0.350312779,0.369073147,0.375932709,0.071428571,0.071428571,0.003612282,0.049945115,0.066666667,0.066666667
2012-03,0.923076923,1,1,0.922127255,1,0.871098404,0.922127255,0.5,0.5,0.01384708,0.171789243,0,0.066666667
2012-04,0.230769231,0.307692308,0.699331532,0.676977344,0.63360143,0.645699309,0.676977344,0.142857143,0.142857143,0.012040939,0.092206367,0.133333333,0.133333333
2012-05,0.846153846,0.846153846,0.801735173,0.752408086,0.776586238,0.7436264,0.752408086,0.785714286,0.785714286,0.016857315,0.131723381,0.466666667,0.466666667
2012-06,0.384615385,0.461538462,0.730479306,0.732193732,0.625558534,0.657850846,0.732193732,0,0,0.011438892,0.118002195,0.6,0.666666667
2012-07,0.384615385,0.384615385,0.751386716,0.738434405,0.71849866,0.714081487,0.738434405,0.285714286,0.285714286,0.009030704,0.126783754,0.2,0.2
2012-08,0.384615385,0.461538462,0.700327123,0.643467643,0.619302949,0.646890636,0.643467643,0.285714286,0.285714286,0.012642986,0.150933041,0.2,0.266666667
2012-09,0.076923077,0.230769231,0.72137676,0.701804368,0.63538874,0.70455087,0.701804368,0.214285714,0.214285714,0.011438892,0.130076839,0.066666667,0.066666667
2012-10,0.230769231,0.230769231,0.846252311,0.863112196,0.796246649,0.825827972,0.863112196,0.071428571,0.071428571,0.036724865,0.127881449,0.333333333,0.333333333
2012-11,0.692307692,0.692307692,0.895605177,1,0.798927614,0.909935668,1,0.214285714,0.357142857,0.012642986,0.143798024,0,0.133333333
2012-12,0.923076923,1,0.795903854,0.803283137,0.683646113,0.827257565,0.803283137,0.142857143,0.142857143,0.008428657,0.104829857,0.6,0.6
2013-01,0.230769231,0.384615385,0.92106386,0.964862298,0.848078642,0.944007624,0.964862298,0.285714286,0.357142857,0.015653221,0.146542261,0.533333333,0.733333333
2013-02,0.153846154,0.307692308,0.830322856,0.872880206,0.798927614,0.755777937,0.872880206,0.142857143,0.142857143,0.010234798,0.110318332,0,0.066666667
2013-03,0.230769231,0.230769231,0.927037406,0.944105277,0.885612154,0.953061711,0.944105277,0.142857143,0.142857143,0.009632751,0.131174533,0,0.133333333
2013-04,0.384615385,0.384615385,0.796046082,0.775471442,0.671134942,0.715749345,0.775471442,0,0,0.012040939,0.12349067,0.133333333,0.133333333
2013-05,0.923076923,1,0.824633765,0.844254511,0.742627346,0.843697879,0.844254511,0.142857143,0.142857143,0.015653221,0.149286498,0,0
2013-06,0.307692308,0.307692308,0.884369222,0.949667616,0.865951743,1,0.949667616,0.071428571,0.071428571,0.020469597,0.135016465,0.466666667,0.466666667
2013-07,0.461538462,0.461538462,0.864172948,0.935829602,0.843610366,0.939480581,0.935829602,0.071428571,0.071428571,0.015051174,0.128979144,0.066666667,0.2
2013-08,0.153846154,0.153846154,0.670886076,0.738163071,0.753351206,0.821300929,0.738163071,0.071428571,0.214285714,0.012642986,0.098243688,0,0
2013-09,0.230769231,0.230769231,0.876262267,0.861484195,0.744414656,0.996426019,0.861484195,0,0,0.024081878,0.144895719,0.066666667,0.066666667
2013-10,0.615384615,0.615384615,0.917508178,0.885361552,0.806970509,0.841315225,0.885361552,0.642857143,0.642857143,0.030704395,0.115806806,0.2,0.4
2013-11,0,0.076923077,0.857061584,0.903540904,0.791778374,0.845127472,0.903540904,0.5,0.5,0.012642986,0.093852909,0,0
2013-12,0.230769231,0.230769231,0.704878396,0.719169719,0.584450402,0.81915654,0.719169719,0.285714286,0.5,0.015653221,0.108122942,0,0
2014-01,0.461538462,0.461538462,0.900014223,0.856328856,0.717605004,0.98903979,0.856328856,0.357142857,0.5,0.030102348,0.137211855,0,0.066666667
2014-02,0,0,0.707865169,0.703296703,0.63717605,0.796997856,0.703296703,1,1,0.012642986,0.097145993,0,0
2014-03,0.230769231,0.230769231,0.815531219,0.800434134,0.7256479,0.786275911,0.800434134,0.714285714,0.714285714,0.009632751,0.099341383,0.533333333,0.6
2014-04,0.153846154,0.153846154,0.756506898,0.790259124,0.615728329,0.778174887,0.790259124,0,0,0.011438892,0.12349067,0,0
2014-05,0.461538462,0.461538462,0.85990613,0.767331434,0.705987489,0.78008101,0.767331434,0.142857143,0.285714286,0.014449127,0.13611416,0.066666667,0.133333333
2014-06,0.076923077,0.153846154,0.670886076,0.713064713,0.615728329,0.735763641,0.713064713,0.285714286,0.285714286,0.010836845,0.102634468,0,0
2014-07,0.076923077,0.076923077,0.672592803,0.801655135,0.621090259,0.680009531,0.801655135,0.071428571,0.071428571,0.007224564,0.103183315,0,0
2014-08,0.384615385,0.461538462,0.487270659,0.58377425,0.486148347,0.575887539,0.58377425,0.071428571,0.071428571,0.005418423,0.079582876,0,0.133333333
2014-09,0,0.076923077,0.715545442,0.678062678,0.669347632,0.705980462,0.678062678,0,0,0.01384708,0.103183315,0,0.066666667
2014-10,0.230769231,0.307692308,0.742995306,0.723511057,0.630920465,0.679294734,0.723511057,0,0,0.016857315,0.1064764,0,0
2014-11,0,0,0.672735031,0.623388957,0.583556747,0.64927329,0.623388957,0,0,0.004816376,0.115806806,0.066666667,0.066666667
2014-12,0.307692308,0.384615385,0.591096572,0.55704789,0.478999106,0.491303312,0.55704789,0.285714286,0.428571429,0.003010235,0.074643249,0,0
2015-01,0.076923077,0.153846154,0.659223439,0.561117894,0.531724754,0.605432452,0.561117894,0.071428571,0.071428571,0.007224564,0.094401756,0.133333333,0.133333333
2015-02,0.230769231,0.307692308,0.61840421,0.564780898,0.512064343,0.585656421,0.564780898,0.071428571,0.071428571,0.007224564,0.096597146,0,0
2015-03,0,0,0.770302944,0.677927011,0.599642538,0.675482487,0.677927011,0.071428571,0.071428571,0.009632751,0.111964874,0.066666667,0.2
2015-04,0.076923077,0.076923077,0.706016214,0.61687695,0.731903485,0.563497736,0.61687695,0.071428571,0.071428571,0.008428657,0.097145993,0,0
2015-05,0,0.076923077,0.655383303,0.614027947,0.55406613,0.6154396,0.614027947,0.071428571,0.071428571,0.012642986,0.099341383,0,0
2015-06,0,0.076923077,0.564357844,0.540632207,0.527256479,0.598284489,0.540632207,0.142857143,0.142857143,0.00602047,0.091657519,0,0
2015-07,0.076923077,0.076923077,0.486417295,0.525301859,0.511170688,0.566356922,0.525301859,0,0,0.015653221,0.08726674,0.066666667,0.066666667
2015-08,0.230769231,0.230769231,0.408476746,0.386379053,0.320822163,0.465094115,0.386379053,0,0,0.003010235,0.056531284,0,0
2015-09,0.538461538,0.538461538,0.870999858,0.792701126,0.747095621,0.883964737,0.792701126,0,0,0.013245033,0.156421515,0,0
2015-10,0.153846154,0.153846154,0.469492249,0.435490435,0.320822163,0.51227067,0.435490435,0,0,0.174593618,0.221734358,0,0
2015-11,0.153846154,0.153846154,0.322998151,0.309455976,0.273458445,0.346676197,0.309455976,0,0,0.462974112,0.481888035,0.133333333,0.133333333
2015-12,0.076923077,0.076923077,0.342767743,0.309320309,0.27971403,0.384798666,0.309320309,0,0,0.464780253,0.482436883,0.066666667,0.066666667
2016-01,0.307692308,0.384615385,0.415872564,0.349477683,0.358355675,0.442458899,0.349477683,0,0,0.559903672,0.581229418,0.066666667,0.066666667
2016-02,0,0,0.445455838,0.403744404,0.316353887,0.457469621,0.403744404,0,0,0.54846478,0.568605928,0.066666667,0.066666667
2016-03,0,0,0.471198976,0.400352734,0.317247542,0.508220157,0.400352734,0.142857143,0.142857143,0.604455148,0.628430296,0,0
2016-04,0,0,0.582989617,0.570343237,0.575513852,0.603764594,0.570343237,0.214285714,0.214285714,1,1,0,0

You need to create your line variable:
var myLine = svg.append("path");
And then, inside redraw(), changing it according to the option selected:
myLine.datum(adata.values)
.attr("d", line);
Here is a demo plunker: https://plnkr.co/edit/YjGO9TLDBXj13JQuO5bm?p=preview
PS: I changed your x-scale range:
var x = d3.scaleTime().range([margin.left, width]);
And also added a call to redraw() when the code runs for the first time.

Related

D3JS makes date duplicates

I have this d3js code:
var tooltip = tooltipd3();
var svg = d3.select("svg#svg-day"),
margin = {
top: 20,
right: 30,
bottom: 30,
left: 25,
padding: 15
},
width = 700 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// parse the periodo / time
var parseTime = d3.timeParse("%Y-%m-%d");
// set the ranges
var x = d3.scaleTime().range([0, width - margin.padding]);
var y = d3.scaleLinear().range([height, 0]);
// define the area
var area = d3.area()
.x(function(d) {
return x(d.periodo) + (margin.left + margin.padding);
})
.y0(height)
.y1(function(d) {
return y(d.guadagno);
});
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.periodo) + (margin.left + margin.padding);
})
.y(function(d) {
return y(d.guadagno);
});
var div = d3.select("svg#svg-day")
.append("div") // declare the tooltip div
.attr("class", "tooltip") // apply the 'tooltip' class
.style("opacity", 0);
// get the data
d3.csv(base_url() + 'graph/getStatementsDaily/', function(error, data) {
if (error) throw error;
$('.graph-loading').hide();
// format the data
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
d.guadagno = +d.guadagno;
});
// scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.periodo;
}));
y.domain([0, d3.max(data, function(d) {
return d.guadagno + ((d.guadagno / 100) * 10); // 10% in più sulla scala numerica
})]);
// add the area
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area);
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.periodo) + (margin.left + margin.padding);
})
.attr("cy", function(d) {
return y(d.guadagno);
})
.on('mouseover', function(d) {
var html = '<h5>' + d.guadagno + ' €</h5>';
tooltip.mouseover(html); // pass html content
})
.on('mousemove', tooltip.mousemove)
.on('mouseout', tooltip.mouseout);
// add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + (margin.left + margin.padding) + "," + (height) + ")")
//HERE IS THE DATES CODE
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m")))
// add the Y Axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate (" + (margin.left + margin.padding) + " 0)")
.call(d3.axisLeft(y));
});
The dates care coming from a CSV file that has this format:
periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0
The result is fine with lots of dates, but with 7 dates I get duplicates as you can see here:
Why is this?? And how do I fix it?
This is something that bothers a lot of people new to D3: the ticks in the axis, specially when using a time scale, are automatically generated. In your case, given the date interval in your domain, it coincidentally ended up creating two ticks for each day. But pay attention to this: those ticks represent different times (hours) in the same day (you can see that if you remove the tickFormat in the axis generator).
Let's see your code generating the x axis:
var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
.range([20, 480])
.domain(d3.extent(data, function(d) {
return d.periodo;
}));
var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m"))(svg.append("g").attr("transform", "translate(0,50)"));
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0</pre>
As you can see, there are two ticks for each day (remember, for different hours).
Let's show that this is a coincidence: This is the same code, but changing the last date for 2017-05-20:
var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
.range([20, 480])
.domain(d3.extent(data, function(d) {
return d.periodo;
}));
var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m"))(svg.append("g").attr("transform", "translate(0,50)"));
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-20,0.0</pre>
Back to your code.
The solution is quite simple: using intervals. Let's set the interval for each tick:
d3.axisBottom(x).ticks(d3.timeDay)
Here is the same code with that change only:
var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
.range([20, 480])
.domain(d3.extent(data, function(d) {
return d.periodo;
}));
var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m")).ticks(d3.timeDay)(svg.append("g").attr("transform", "translate(0,50)"));
pre {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0</pre>

d3 line graph using csv

I have a data in csv like this:
date,partner,units
2012-05-01,team1,34.12
2012-04-30,team1,45.56
2012-04-27,team2,67.89
2012-04-26,team1,78.54
2012-04-25,team2,89.23
2012-04-24,team2,99.23
2012-04-23,team2,101.34
I want to plot two lines (one for team1, one for team2 using this data), but I am just getting a scatterplot using the following complete d3 code, is my filtering wrong?
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
#line1 {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
#line2 {
fill: none;
stroke: red;
stroke-width: 2px;
}
</style>
<input type="button" onclick="hideLine()">
<input type="button" onclick="showLine()">
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%Y-%m-%d");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the 1st line
var valueline = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.units); });
// define the 2nd line
var valueline2 = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.units); });
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 + ")");
// Get the data
d3.csv("data1.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.units = +d.units;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
m = d3.max(data, function(d) {
var m = d.units;
return m;
});
console.log("Max:", m);
y.domain([0,m]);
// Add the valueline path.
svg.append("path")
.data([data])
.filter(function(d) { return d.partner == 'team1'; })
.attr("id", "line1")
.attr("d", valueline);
console.log("DATA", data)
svg.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("cx", function (d, i) {
return x(d.date);
})
.attr("cy", function(d) {
return y(d.units);
})
.attr("r", 4)
.on("mouseover", function(d) { console.log(d.units) });
// Add the valueline2 path.
svg.append("path")
.data([data])
.filter(function(d) { return d.partner == 'team2'; })
.attr("id", "line2")
.style("stroke", "red")
.attr("d", valueline2)
.on("mouseover", function(d) {console.log(d)});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
});
function hideLine() {
console.log("Hideline");
d3.select("#line2").attr("style", "opacity:0");
}
function showLine() {
console.log("ShowLine");
d3.select("#line2").attr("style", "opacity:1");
}
</script>
</body>
Yes, your filter is wrong. You are wrapping your data in another [] which means the filter is only operating on the outer array.
Do this instead:
svg.append("path")
.datum(data.filter(function(d) { return d.partner == 'team1'; }))
.attr("id", "line1")
.attr("d", valueline);
Working example here.

Making a grouped bar chart when my groups are varied sizes?

I am trying to make a bar chart out of some grouped data. This is dummy data, but the structure is basically the same. The data: election results includes a bunch of candidates, organized into the districts they were running in, and the total vote count:
district,candidate,votes
Dist 1,Leticia Putte,3580
Dist 2,David Barron,1620
Dist 2,John Higginson,339
Dist 2,Walter Bannister,2866
[...]
I'd like to create a bar or column chart (either, honestly, though my end goal is horizontal) that groups the candidates by district.
Mike Bostock has an excellent demo but I'm having trouble translating it intelligently for my purposes. I started to tease it out at https://jsfiddle.net/97ur6cwt/6/ but my data is organized somewhat differently -- instead of rows, by group, I have a column that sets the category. And there might be just one candidate or there might be a few candidates.
Can I group items if the groups aren't the same size?
My answer is similar to #GerardoFurtado but instead I use a d3.nest to build a domain per district. This removes the need for hardcoding values and cleans it up a bit:
y0.domain(data.map(function(d) { return d.district; }));
var districtD = d3.nest()
.key(function(d) { return d.district; })
.rollup(function(d){
return d3.scale.ordinal()
.domain(d.map(function(c){return c.candidate}))
.rangeRoundBands([0, y0.rangeBand()], pad);
}).map(data);
districtD becomes a map of domains for your y-axis which you use when placing the rects:
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", function(d,i) {
return color(d.district);
})
.attr("x", 0)
.attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate); })
.attr("height", function(d){
return districtD[d.district].rangeBand();
})
.attr("width", function(d) {
return x(d.votes);
});
I'm off to a meeting but the next step is to clean up the axis and get the candidate names on there.
Full running code:
var url = "https://gist.githubusercontent.com/amandabee/edf73bc0bbe131435c952f5ed47524a6/raw/99febb9971f76e36af06f1b99913fcaa645ecb3e/election.csv"
var m = {top: 10, right: 10, bottom: 50, left: 110},
w = 800 - m.left - m.right,
h = 500 - m.top - m.bottom,
pad = .1;
var x = d3.scale.linear().range([0, w]);
y0 = d3.scale.ordinal().rangeRoundBands([0, h], pad);
var color = d3.scale.category20c();
var yAxis = d3.svg.axis()
.scale(y0)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
.tickFormat(d3.format("$,.0f"));
var svg = d3.select("#chart").append("svg")
.attr("width", w + m.right + m.left + 100)
.attr("height", h + m.top + m.bottom)
.append("g")
.attr("transform",
"translate(" + m.left + "," + m.top + ")");
// This moves the SVG over by m.left(110)
// and down by m.top (10)
d3.csv(url, function(error, data) {
data.forEach(function(d) {
d.votes = +d.votes;
});
y0.domain(data.map(function(d) { return d.district; }));
districtD = d3.nest()
.key(function(d) { return d.district; })
.rollup(function(d){
console.log(d);
return d3.scale.ordinal()
.domain(d.map(function(c){return c.candidate}))
.rangeRoundBands([0, y0.rangeBand()], pad);
})
.map(data);
x.domain([0, d3.max(data, function(d) {
return d.votes;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "middle");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", function(d,i) {
return color(d.district);
})
.attr("x", 0)
.attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate); })
.attr("height", function(d){
return districtD[d.district].rangeBand();
})
.attr("width", function(d) {
return x(d.votes);
});
svg.selectAll(".label")
.data(data)
.enter().append("text")
.text(function(d) {
return (d.votes);
})
.attr("text-anchor", "start")
.attr("x", function(d) { return x(d.votes)})
.attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand()/2;})
.attr("class", "axis");
});
.axis {
font: 10px sans-serif;
}
.axis path, .axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
An alternate version which sizes the bars the same and scales the outer domain appropriately:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<style>
.label {
font: 10px sans-serif;
}
.axis {
font: 11px sans-serif;
font-weight: bold;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
var url = "https://gist.githubusercontent.com/amandabee/edf73bc0bbe131435c952f5ed47524a6/raw/99febb9971f76e36af06f1b99913fcaa645ecb3e/election.csv"
var m = {
top: 10,
right: 10,
bottom: 50,
left: 110
},
w = 800 - m.left - m.right,
h = 500 - m.top - m.bottom,
pad = .1, padPixel = 5;
var x = d3.scale.linear().range([0, w]);
var y0 = d3.scale.ordinal();
var color = d3.scale.category20c();
var yAxis = d3.svg.axis()
.scale(y0)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
.tickFormat(d3.format("$,.0f"));
var svg = d3.select("#chart").append("svg")
.attr("width", w + m.right + m.left + 100)
.attr("height", h + m.top + m.bottom)
.append("g")
.attr("transform",
"translate(" + m.left + "," + m.top + ")");
// This moves the SVG over by m.left(110)
// and down by m.top (10)
d3.csv(url, function(error, data) {
data.forEach(function(d) {
d.votes = +d.votes;
});
var barHeight = h / data.length;
y0.domain(data.map(function(d) {
return d.district;
}));
var y0Range = [0];
districtD = d3.nest()
.key(function(d) {
return d.district;
})
.rollup(function(d) {
var barSpace = (barHeight * d.length);
y0Range.push(y0Range[y0Range.length - 1] + barSpace);
return d3.scale.ordinal()
.domain(d.map(function(c) {
return c.candidate
}))
.rangeRoundBands([0, barSpace], pad);
})
.map(data);
y0.range(y0Range);
x.domain([0, d3.max(data, function(d) {
return d.votes;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "middle");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", function(d, i) {
return color(d.district);
})
.attr("x", 0)
.attr("y", function(d) {
return y0(d.district) + districtD[d.district](d.candidate);
})
.attr("height", function(d) {
return districtD[d.district].rangeBand();
})
.attr("width", function(d) {
return x(d.votes);
});
var ls = svg.selectAll(".labels")
.data(data)
.enter().append("g");
ls.append("text")
.text(function(d) {
return (d.votes);
})
.attr("text-anchor", "start")
.attr("x", function(d) {
return x(d.votes)
})
.attr("y", function(d) {
return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand() / 2;
})
.attr("class", "label");
ls.append("text")
.text(function(d) {
return (d.candidate);
})
.attr("text-anchor", "end")
.attr("x", -2)
.attr("y", function(d) {
return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand() / 2;
})
.style("alignment-baseline", "middle")
.attr("class", "label");
});
</script>
</body>
</html>
This is a partial solution: https://jsfiddle.net/hb13oe4v/
The main problem here is creating a scale for each group with a variable domain. Unlike Bostock's example, you don't have the same amount of bars(candidates) for each group(districts).
So, I had to do a workaround. First, I nested the data in the most trivial way:
var nested = d3.nest()
.key(function(d) { return d.district; })
.entries(data);
And then created the groups accordingly:
var district = svg.selectAll(".district")
.data(nested)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(0," + y(d.key) + ")"; });
As I couldn't create an y1 (x1 in Bostock's example) scale, I had to hardcode the height of the bars (which is inherently bad). Also, for centring the bars in each group, I created this crazy math, that puts one bar in the center, the next under, the next above, the next under and so on:
.attr("y", function(d, i) {
if( i % 2 == 0){ return (y.rangeBand()/2 - 10) + (i/2 + 0.5) * 10}
else { return (y.rangeBand()/2 - 10) - (i/2) * 10}
})
Of course, all this can be avoided and coded way more elegantly if we could set a variable scale for each group.

d3 show labels only for ticks with data in a bar chart

First, I'm new with D3. :)
I have a stacked bar chart. My y axis is a ordinal scale (state).
In my data, I could have total=0 for some ticks. So, I just want to see the labels when the total > 0 but maintain all the ticks of the data.
var data = [{ "state":"A", "total":"10"},
{ "state":"B", "total":"0"},
{ "state":"C", "total":"0"},
{ "state":"D", "total":"20"},
{ "state":"E", "total":"0"},
{ "state":"F", "total":"50"}]
I've tried this code, but this remove all the ticks with total = 0. I just want to remove the label of that tick.
yAxis.tickValues(dataSet.map( function(d,i)
{
if (d.total > 0)
return d.state;
else
if(i % 4 === 0 ) return d.state;
})
.filter(function (d)
{ return !!d; } ));
Thanks,
Filipe
UPDATE
Here is the code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" class="ocks-org do-not-copy">
<head>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div id="timeLine"></div>
<!--style>
svg {
font: 10px sans-serif;
}
path {
fill: steelblue;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style-->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var timeline;
var labels;
var margin = {
top : 20,
right : 10,
bottom : 60,
left : 80
},
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.rangeRound([0, width]);
var y = d3.scale.ordinal()
.rangeBands([height, 0], 0.1);
var color = d3.scale.ordinal()
.range(["#1f77b4", "#2ca02c", "#E53524"]);
//var color = d3.scale.category10()
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.format(".2s"))
.ticks(10);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
timeline = d3.select("#timeLine").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 dataSet;
var all_nodes_t;
var all_updates_t;
var test;
var entity = "ALL";
dataSet = [
{ "Date":"2014-01", "insert":"27", "remove":"17","updates":"427"},
{ "Date":"2014-02", "insert":"27", "remove":"17","updates":"427"},
{ "Date":"2014-03", "insert":"27", "remove":"17","updates":"427"},
{ "Date":"2014-04", "insert":"0", "remove":"0","updates":"0"},
{ "Date":"2014-05", "insert":"27", "remove":"17","updates":"427"},
];
color.domain(d3.keys(dataSet[0]).filter(function (key) {
return key !== "Date";
}));
dataSet.forEach(function (d) {
var x0 = 0;
d.ages = color.domain().map(function (name) {
return {
name : name,
x0 : x0,
x1 : x0 += +d[name]
};
});
d.total = d.ages[d.ages.length - 1].x1;
});
//HERE
yAxis.tickFormat(dataSet.map(function(d) {
d.total == 0 ? "" : d.Date;
}));
y.domain(dataSet.map(function (d) {
return d.Date;
}));
x.domain([0, d3.max(dataSet, function (d) {
return (d.total + 5);
})]);
timeline.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 1)
.attr("dx", "42em")
.attr("dy", "3em")
.style("text-anchor", "end")
.text("Operations");
timeline.append("g")
.attr("class", "y axis")
.call(yAxis);
var layer = timeline.selectAll(".state")
.data(dataSet)
.enter().append("g")
.attr("class", "rect")
.attr("transform", function (d) {
return "translate(0," + y(d.Date) + ")";
});
var rect = layer.selectAll("rect")
.data(function (d) {
return d.ages;
})
.enter().append("rect")
.attr("class", "rect")
.attr("width", 0)
.attr("x", width)
.attr('y', function (d, i) {
return y(d.Date);
})
.attr("height", y.rangeBand())
.style("fill", function (d) {
return color(d.name);
});
rect.transition()
.duration(600)
.delay(function (d, i) {
return i * 300;
})
.attr("width", function (d) {
return x(d.x1) - x(d.x0);
})
.attr("x", function (d) {
return x(d.x0);
});
</script>
</body>
</html>
You can use .tickFormat() to suppress the labels for those particular values. As the thing you want to check isn't part of the data that's available to the scale, you'll need to find it in your entire data:
yAxis.tickFormat(function(d) {
var val = 0;
dataSet.forEach(function(item) {
if(item.Date == d) val = item.total;
});
return val == 0 ? "" : d;
});
This will suppress the label (return "") if the total is 0 or the value can't be found in the data set. Complete demo here.

Adding traces to d3.js animated bubble chart

I'm trying to build an animated time series chart which shows a 'trace' or snail trail following the moving dot. I have been trying to integrate KoGor's http://bl.ocks.org/KoGor/8163022 but haven't had luck- I think the problem lies in tweenDash() - The original function was designed for a single trace- this one has one per company.
Attached below is a working example- the time series scrubbing and movable data labels work, just not the trace aspect.
Thanks,
RL
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<body bgcolor="#000000">
<title>BPS</title>
<style>
#import url(style.css);
#chart {
margin-left: -40px;
height: 506px;
display:inline;
}
#buffer {
width: 100px;
height:506px;
float:left;
}
text {
font: 10px sans-serif;
color: #ffffff;
}
.dot {
stroke: #000;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.label {
fill: #777;
}
.year.label {
font: 900 125px "Helvetica Neue";
fill: #ddd;
}
.year.label.active {
fill: #aaa;
}
.overlay {
fill: none;
pointer-events: all;
cursor: ew-resize;
}
</style>
<div>
<div id="buffer"></div><div id="chart"></div>
</div>
<script src="d3.v3.min.js"></script>
<script>
var source = '[{"name":"ABCD","AUM":[[2010,1000.6],[2011,1200.6],[2012,1300.1],[2013,1400.5],[2014,1600.0]],"AUA":[[2010,3000.6],[2011,3300.2],[2012,4000.0],[2013,4500.8],[2014,6000.3]],"marketPercentage":[[2010,40.4],[2011,39.7],[2012,38.5],[2013,37.1],[2014,36.5]],"fill":[[2010,0],[2011,-1],[2012,-1],[2013,-1],[2014,-1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-30],[2011,-20],[2012,-20],[2013,-20],[2014,-10]]},{"name":"EFGH","AUM":[[2010,32.8],[2011,43.2],[2012,58.3],[2013,78.8],[2014,92]],"AUA":[[2010,327.3],[2011,439.3],[2012,547.0],[2013,710.0],[2014,824.0]],"marketPercentage":[[2010,1.0],[2011,1.2],[2012,1.5],[2013,1.8],[2014,1.9]],"fill":[[2010,0],[2011,1],[2012,1],[2013,1],[2014,1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-10],[2011,-10],[2012,-10],[2013,-10],[2014,-10]]},{"name":"HIJK","AUM":[[2010,0.1],[2011,0.5],[2012,1.2],[2013,2.4],[2014,2.6]],"AUA":[[2010,159.6],[2011,176.7],[2012,199.9],[2013,235.1],[2014,269.0]],"marketPercentage":[[2010,0.1],[2011,0.1],[2012,0.1],[2013,0.1],[2014,0.1]],"fill":[[2010,0],[2011,0],[2012,0],[2013,1],[2014,1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-10],[2011,-10],[2012,-10],[2013,-10],[2014,-10]]}]';
// Various accessors that specify the four dimensions of data to visualize.
function x(d) { return d.AUM; }
function y(d) { return d.AUA; }
function xo(d) {return d.xOffset; }
function yo(d) {return d.yOffset; }
function radius(d) { return d.marketPercentage; }
function key(d) { return d.name; }
// Chart dimensions.
var margin = {top: 19.5, right: 19.5, bottom: 19.5, left: 39.5},
width = 960 - margin.right,
height = 500 - margin.top - margin.bottom;
// Various scales. These domains make assumptions of data, naturally.
var xScale = d3.scale.linear().domain([0, 2000]).range([0, width]),
yScale = d3.scale.linear().domain([0, 5000]).range([height, 0]),
radiusScale = d3.scale.sqrt().domain([0, 500]).range([0, 40]),
colorScale = d3.scale.category10();
// The x & y axes.
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(12, d3.format(",d")),
yAxis = d3.svg.axis().scale(yScale).orient("left");
// Create the SVG container and set the origin.
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 + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.style("fill", "#FFFFFF")
.call(xAxis);
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.style("fill", "#FFFFFF")
.call(yAxis);
// Add an x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.style("fill", "#FFFFFF")
.attr("x", width)
.attr("y", height - 6);
//.text("income per capita, inflation-adjusted (dollars)");
// Add a y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 6)
.attr("dy", ".75em")
.style("fill", "#FFFFFF")
.attr("transform", "rotate(-90)")
// .text("life expectancy (years)")
;
// Add the year label; the value is set on transition.
var label = svg.append("text")
.attr("class", "year label")
.attr("text-anchor", "end")
.attr("y", height - 24)
.attr("x", width)
.text(2010);
//d3.json("investments_v04ANON.json", function(companies) {
companies = JSON.parse(source)
// A bisector since many company's data is sparsely-defined.
var bisect = d3.bisector(function(d) { return d[0]; });
// Add a dot per company. Initialize the data at 2010, and set the colors.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2010))
.enter().append("circle")
.attr("class", "dot")
// .style("fill", function(d) { return colorScale(color(d)); })
.style("fill", function(d) {return colorScale(interpolateData(2010)) })
.call(position)
.sort(order);
var lineTraces = svg.append("path")
.attr("class", "lineTrace")
.selectAll(".traces")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.data(interpolateData(2010));
//yields a mouseover label - "title" precludes need for separate mouseover event.
// dot.append("title")
// .text(function(d) { return d.name; });
//.text(function(d) {return d.AUM});
var theLabel = svg.append("g")
.attr("class", "texts")
.selectAll(".theLabel")
.data(interpolateData(2010))
.enter().append("text")
.attr("class", "text")
.text("hey")
.call(position2);
// Add an overlay for the year label.
var box = label.node().getBBox();
var overlay = svg.append("rect")
.attr("class", "overlay")
.attr("x", box.x)
.attr("y", box.y)
.attr("width", box.width)
.attr("height", box.height)
.on("mouseover", enableInteraction);
// Start a transition that interpolates the data based on year.
svg.transition()
.duration(30000)
.ease("linear")
.tween("year", tweenYear)
.attrTween("stroke-dasharray", tweenDash)
.each("end", enableInteraction);
// Positions the dots based on data.
function position(dot) {
dot .attr("cx", function(d) { return xScale(x(d)); })
.attr("cy", function(d) { return yScale(y(d)); })
.attr("r", function(d) { return radiusScale(radius(d)); })
.style("fill", function(d) {return d.fill>0 ? "green" : "red"} );//{return d.fill});
}
//function from: http://bl.ocks.org/KoGor/8163022
function tweenDash() {
var i = d3.interpolateString("0," + 5, 5 + "," + 5); // interpolation of stroke-dasharray style attr
// var l = path.node().getTotalLength();
// var i = d3.interpolateString("0," + l, l + "," + l); // interpolation of stroke-dasharray style attr
return function(t) {
var marker = d3.select(".dots");
// var p = path.node().getPointAtLength(t * l);
var p = lineTraces.node().getPointAtLength(t * 5);
marker.attr("transform", "translate(" + p.x + "," + p.y + ")");//move marker
return i(t);
}
}
function position2(theLabel) {
theLabel.attr("x", function(d) { return xScale(x(d)) + xo(d); })
.attr("y", function(d) { return yScale(y(d)) + yo(d); })
.attr("text-anchor", "end")
.style("fill", "#FFFFFF")
.text(function(d) { return d.name + ": AUM:" + Math.round(d.AUM) + ", AUA: " + Math.round(d.AUA) });//{return d.fill});
}
// Defines a sort order so that the smallest dots are drawn on top.
function order(a, b) {
return radius(b) - radius(a);
}
// After the transition finishes, you can mouseover to change the year.
function enableInteraction() {
var yearScale = d3.scale.linear()
.domain([2010, 2014])
.range([box.x + 10, box.x + box.width - 10])
.clamp(true);
// Cancel the current transition, if any.
svg.transition().duration(0);
overlay
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove)
.on("touchmove", mousemove);
function mouseover() {
label.classed("active", true);
}
function mouseout() {
label.classed("active", true);
label.classed("active", false);
}
function mousemove() {
displayYear(yearScale.invert(d3.mouse(this)[0]));
}
}
// Tweens the entire chart by first tweening the year, and then the data.
// For the interpolated data, the dots and label are redrawn.
function tweenYear() {
var year = d3.interpolateNumber(2010, 2014);
return function(t) { displayYear(year(t)); };
}
// Updates the display to show the specified year.
function displayYear(year) {
dot.data(interpolateData(year), key).call(position).sort(order);
theLabel.data(interpolateData(year), key).call(position2).sort(order);
label.text(Math.round(year));
}
// Interpolates the dataset for the given (fractional) year.
function interpolateData(year) {
return companies.map(function(d) {
return {
// name: d.name + ": AUM:" + interpolateValues(d.AUM, year) + ", AUA: " + interpolateValues(d.AUA, year),
// name: d.name + ": AUM:" + d.AUM + ", AUA: " + d.AUA,
// name: interpolateValues(d.AUM, year),
name: d.name,
AUM: interpolateValues(d.AUM, year),
marketPercentage: interpolateValues(d.marketPercentage, year),
AUA: interpolateValues(d.AUA, year),
fill: interpolateValues(d.fill, year),
xOffset: interpolateValues(d.xOffset, year),
yOffset: interpolateValues(d.yOffset, year)
};
});
}
// Finds (and possibly interpolates) the value for the specified year.
function interpolateValues(values, year) {
var i = bisect.left(values, year, 0, values.length - 1),
a = values[i];
if (i > 0) {
var b = values[i - 1],
t = (year - a[0]) / (b[0] - a[0]);
return a[1] * (1 - t) + b[1] * t;
}
return a[1];
};
//});
</script>
Mark- the second version you built works very well. I'm now trying to address the individual line segments. I've added an attribute 'toggleSwitch' but the below code runs 1x and captures only the initial state of the object.
var lineTraces = svg.append("g")
.selectAll(".traces")
.data([0,1,2,4,5,6,7,8,9,10,11,12])
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.attr("d", line)
.each(function(d,i){
d3.select(this)
.datum([someData[i]])
.attr("nothing", function(i) {console.log(i[0])})
.attr("d", line)
.style("stroke-dasharray", function(i) {return (i[0]["toggleSwitch"]<0 ? "0,0": "3,3")})
});
console log, one per object:
Object { name: "TheName", Impact: 120, bubbleSize: 30.4, YoY: 11, toggleSwitch: 0, xOffset: 5, yOffset: -30 }
The example you linked to had a pre-established path and then attrTweened the "stroke-dasharray" on it. Your first problem is that you need to establish that path for each company. Then you can tween it.
// set up a line to create the path
var line = d3.svg.line()
.x(function(d) { return xScale(x(d)); })
.y(function(d) { return yScale(y(d)); })
.interpolate("basis");
// for each company add the path
var lineTraces = svg.append("g")
.selectAll(".traces")
.attr("fill","red")
.data([0,1,2]) // 3 companies
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.each(function(d,i){
// get the line data and add path
var lineData = [interpolateData(2010)[i],interpolateData(2011)[i],
interpolateData(2012)[i],interpolateData(2013)[i],interpolateData(2014)[i]];
d3.select(this)
.datum(lineData)
.attr("d", line);
});
Now set up the transitions on each path:
lineTraces.each(function(){
var path = d3.select(this);
path.transition()
.duration(30000)
.ease("linear")
.attrTween("stroke-dasharray", tweenDash)
});
Where tweenDash is:
function tweenDash() {
var l = lineTraces.node().getTotalLength();
var i = d3.interpolateString("0," + l, l + "," + l); // interpolation of stroke-dasharray style attr
return function(t) {
var p = lineTraces.node().getPointAtLength(t);
return i(t);
}
}
Here's an example.
You'll see it's not perfect, the timings are off. If I get a bit more time, I'll try and come back and straighten it out.
EDITS
Gave this some thought last night and it dawned on me that there's an easier, more succinct way to add the trace. Instead of pre-defining the path and then attrTweening the "stroke-dasharray", just build the path as you go:
var someData = interpolateData(2010);
// add the paths like before
var lineTraces = svg.append("g")
.selectAll(".traces")
.data([0,1,2])
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.attr("d", line)
.each(function(d,i){
d3.select(this)
.datum([someData[i]])
.attr("d", line);
});
// Tweens the entire chart by first tweening the year, and then the data.
// For the interpolated data, the dots and label are redrawn.
function tweenYear() {
var year = d3.interpolateNumber(2010, 2014);
// added "addTrace" function
return function(t) { addTrace(year(t)); displayYear(year(t)); };
}
// append the data and draw the path
function addTrace(year){
var thisData = interpolateData(year);
lineTraces.each(function(d,i){
var trace = d3.select(this);
trace.datum().push(thisData[i]);
trace.attr("d", line);
});
}
This produces much better results.

Resources