Related
I am trying to create two charts, a stacked bar and a bar, each displaying a different dataset. But I would like to transition from the stacked bar chart to the bar chart when I click on a button and vice versa. The code I put together works only the first time, then after that when I want to transition back to the stacked bar chart from the bar chart all the bars stack on top of each other in the form of a single bar. Can someone point me in the right direction of how to transition back from bar to stacked bar? Any help would be appreciated. (I haven't really messed with the axes yet so it is normal that they are not changing).
Here is a link to how it currently looks:https://jhjanicki.github.io/stackbartobar/
Below is my code:
var value = 'stack';
var data = [{
name: "Shihuahuaco",
value: 1067,
china: 772
}, {
name: "Cachimbo",
value: 283,
china: 1
}, {
name: "Estoraque",
value: 204,
china: 150
}, {
name: "Cumala",
value: 154,
china: 0
}, {
name: "Ishpingo",
value: 108,
china: 3
}, {
name: "Huayruro",
value: 108,
china: 1
}, {
name: "Tornillo",
value: 61,
china: 4
}, {
name: "Congona",
value: 54,
china: 0
}, {
name: "Capirona",
value: 37,
china: 5
}, {
name: "Tahuari",
value: 33,
china: 14
}, {
name: "Marupa",
value: 33,
china: 1
}, {
name: "Quinilla",
value: 28,
china: 4
}, {
name: "Azucar huayo",
value: 22,
china: 15
}, {
name: "Protium sp.",
value: 19,
china: 0
}, {
name: "Nogal",
value: 15,
china: 6
}, {
name: "Ana Caspi",
value: 14,
china: 2
}, {
name: "Cedro",
value: 14,
china: 0
}, {
name: "Carapa guianensis",
value: 12,
china: 0
}, {
name: "Leche caspi",
value: 12,
china: 0
}, {
name: "Andiroba",
value: 11,
china: 0
}, {
name: "Copaiba",
value: 7,
china: 4
}, {
name: "Palo baston",
value: 6,
china: 0
}, {
name: "Moena",
value: 5,
china: 0
}, {
name: "Almendro",
value: 5,
china: 0
}, {
name: "Chancaquero",
value: 4,
china: 0
}, {
name: "Caimitillo",
value: 3,
china: 1
}, {
name: "Nogal amarillo",
value: 3,
china: 0
}, {
name: "Couma macrocarpa",
value: 3,
china: 0
}, {
name: "Tulpay",
value: 3,
china: 0
}, {
name: "Carapa",
value: 3,
china: 0
}, {
name: "Dacryodes olivifera",
value: 2,
china: 0
}, {
name: "Capinuri",
value: 2,
china: 2
}, {
name: "Brosimum alicastrum",
value: 2,
china: 0
}, {
name: "Paramachaerium ormosioide",
value: 2,
china: 0
}, {
name: "Brosimum sp.",
value: 2,
china: 0
}, {
name: "Manchinga",
value: 2,
china: 0
}];
// data for stacked bar
var points = [{
'lon': 105.3,
'lat': 33.5,
'name': 'China',
'GTF': 1024,
"ID": "CHN"
},
{
'lon': -70.9,
'lat': 18.8,
'name': 'Dominican Republic',
'GTF': 470,
"ID": "DOM"
},
{
'lon': -101,
'lat': 38,
'name': 'USA',
'GTF': 248,
"ID": "USA"
},
{
'lon': -102.5,
'lat': 22.7,
'name': 'Mexico',
'GTF': 220,
"ID": "MEX"
},
{
'lon': 2.98,
'lat': 46,
'name': 'France',
'GTF': 85,
"ID": "FRA"
}
];
//data for bar
var margin = {
top: 20,
right: 30,
bottom: 150,
left: 60
},
widthB = 700 - margin.left - margin.right,
heightB = 500 - margin.top - margin.bottom;
var dataIntermediate = ['value', 'china'].map(function(key, i) {
return data.map(function(d, j) {
return {
x: d['name'],
y: d[key]
};
})
})
var dataStackLayout = d3.layout.stack()(dataIntermediate);
var svg = d3.select("#chart").append("svg")
.attr("width", widthB + margin.left + margin.right)
.attr("height", heightB + margin.top + margin.bottom)
var gBar = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr('class', 'gBar');
var x = d3.scale.ordinal()
.rangeRoundBands([0, widthB], .2);
var y = d3.scale.linear()
.range([heightB, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(8)
.tickFormat(function(d) {
return y.tickFormat(4, d3.format(",d"))(d)
});
data.forEach(function(d) {
d.value = +d.value; // coerce to number
d.china = +d.china;
});
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
function(d) {
return d.y0 + d.y;
})]).nice();
var layer;
var bars;
//axes
gBar.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (heightB + 10) + ")")
.call(xAxis)
.selectAll("text")
.style('font-size', '14px')
.style('font-family', 'Alegreya')
.style("text-anchor", "end")
.attr("dx", "-0.40em")
.attr("dy", ".10em")
.attr("transform", function(d) {
return "rotate(-65)"
});
gBar.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style('font-size', '16px')
.style('font-family', 'Alegreya');
function draw() {
if (value == 'stack') {
layer = gBar.selectAll(".stack")
.data(dataStackLayout);
layer.exit()
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(1500)
.style("fill", "none")
.remove();
layer.enter().append("g")
.attr("class", "stack")
.style("fill", function(d, i) {
return i == 0 ? '#b4d5c3' : '#ecaeb3';
});
bars = layer.selectAll("rect")
.data(function(d) {
return d;
});
// the "EXIT" set:
bars.exit()
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(1500)
.attr("y", y(0))
.attr("height", heightB - y(0))
.style('fill-opacity', 1e-6)
.remove();
// the "ENTER" set:
bars.enter().append("rect")
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(3000)
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand());
// the "UPDATE" set:
bars.transition().delay(function(d, i) {
return 30 * i;
}).duration(1500).attr("x", function(d) {
return x(d.x);
})
.attr("width", x.rangeBand()) // constant, so no callback function(d) here
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
});
} else { // draw bar
x.domain(points.map(function(d) {
return d.name;
}));
y.domain([0, 1024]).nice();
bars = layer.selectAll("rect")
.data(points);
// the "EXIT" set:
bars.exit()
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(1500)
.attr("y", y(0))
.attr("height", heightB - y(0))
.style('fill-opacity', 1e-6)
.remove();
// the "ENTER" set:
bars.enter().append("rect")
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(3000)
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.GTF);
})
.attr("height", function(d) {
return heightB - y(d.GTF);;
})
.attr("width", x.rangeBand());
// the "UPDATE" set:
bars.transition().delay(function(d, i) {
return 30 * i;
}).duration(1500).attr("x", function(d) {
return x(d.name);
})
.attr("width", x.rangeBand()) // constant, so no callback function(d) here
.attr("y", function(d) {
return y(d.GTF);
})
.attr("height", function(d) {
return heightB - y(d.GTF);
});
}
}
window.onload = draw();
$("#click").on('click', function() {
if (value == 'stack') {
value = 'bar';
} else {
value = 'stack';
}
draw();
});
body {
font-family: 'Alegreya', serif;
}
.axis text {
font: 10px sans-serif;
}
.axis path {
fill: none;
stroke: #000;
stroke-width: 0px;
shape-rendering: crispEdges;
}
.axis line {
fill: none;
stroke: #000;
stroke-width: 0.5px;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.js"></script>
<div id="chart"></div>
<p id="click"> click here to change </p>
The problem in your code is that you're changing the scales' domain for the bar chart, but you're not changing them back for the stacked bar chart.
Therefore, you should put this in the draw() section (conditional statement) for the stacked bar:
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
function(d) {
return d.y0 + d.y;
})]).nice();
Here is your code with that change (I also put a call for the x axis):
var value = 'stack';
var data = [{
name: "Shihuahuaco",
value: 1067,
china: 772
}, {
name: "Cachimbo",
value: 283,
china: 1
}, {
name: "Estoraque",
value: 204,
china: 150
}, {
name: "Cumala",
value: 154,
china: 0
}, {
name: "Ishpingo",
value: 108,
china: 3
}, {
name: "Huayruro",
value: 108,
china: 1
}, {
name: "Tornillo",
value: 61,
china: 4
}, {
name: "Congona",
value: 54,
china: 0
}, {
name: "Capirona",
value: 37,
china: 5
}, {
name: "Tahuari",
value: 33,
china: 14
}, {
name: "Marupa",
value: 33,
china: 1
}, {
name: "Quinilla",
value: 28,
china: 4
}, {
name: "Azucar huayo",
value: 22,
china: 15
}, {
name: "Protium sp.",
value: 19,
china: 0
}, {
name: "Nogal",
value: 15,
china: 6
}, {
name: "Ana Caspi",
value: 14,
china: 2
}, {
name: "Cedro",
value: 14,
china: 0
}, {
name: "Carapa guianensis",
value: 12,
china: 0
}, {
name: "Leche caspi",
value: 12,
china: 0
}, {
name: "Andiroba",
value: 11,
china: 0
}, {
name: "Copaiba",
value: 7,
china: 4
}, {
name: "Palo baston",
value: 6,
china: 0
}, {
name: "Moena",
value: 5,
china: 0
}, {
name: "Almendro",
value: 5,
china: 0
}, {
name: "Chancaquero",
value: 4,
china: 0
}, {
name: "Caimitillo",
value: 3,
china: 1
}, {
name: "Nogal amarillo",
value: 3,
china: 0
}, {
name: "Couma macrocarpa",
value: 3,
china: 0
}, {
name: "Tulpay",
value: 3,
china: 0
}, {
name: "Carapa",
value: 3,
china: 0
}, {
name: "Dacryodes olivifera",
value: 2,
china: 0
}, {
name: "Capinuri",
value: 2,
china: 2
}, {
name: "Brosimum alicastrum",
value: 2,
china: 0
}, {
name: "Paramachaerium ormosioide",
value: 2,
china: 0
}, {
name: "Brosimum sp.",
value: 2,
china: 0
}, {
name: "Manchinga",
value: 2,
china: 0
}];
var points = [{
'lon': 105.3,
'lat': 33.5,
'name': 'China',
'GTF': 1024,
"ID": "CHN"
}, {
'lon': -70.9,
'lat': 18.8,
'name': 'Dominican Republic',
'GTF': 470,
"ID": "DOM"
}, {
'lon': -101,
'lat': 38,
'name': 'USA',
'GTF': 248,
"ID": "USA"
}, {
'lon': -102.5,
'lat': 22.7,
'name': 'Mexico',
'GTF': 220,
"ID": "MEX"
}, {
'lon': 2.98,
'lat': 46,
'name': 'France',
'GTF': 85,
"ID": "FRA"
}];
var margin = {
top: 20,
right: 30,
bottom: 150,
left: 60
},
widthB = 700 - margin.left - margin.right,
heightB = 500 - margin.top - margin.bottom;
var dataIntermediate = ['value', 'china'].map(function(key, i) {
return data.map(function(d, j) {
return {
x: d['name'],
y: d[key]
};
})
})
var dataStackLayout = d3.layout.stack()(dataIntermediate);
var svgBar = d3.select("#chart").append("svg")
.attr("width", widthB + margin.left + margin.right)
.attr("height", heightB + margin.top + margin.bottom)
var gBar = svgBar.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr('class', 'gBar');
var x = d3.scale.ordinal()
.rangeRoundBands([0, widthB], .2);
var y = d3.scale.linear()
.range([heightB, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(8)
.tickFormat(function(d) {
return y.tickFormat(4, d3.format(",d"))(d)
});
data.forEach(function(d) {
d.value = +d.value; // coerce to number
d.china = +d.china;
});
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
function(d) {
return d.y0 + d.y;
})]).nice();
var layer;
// this part
var bars;
var gX = gBar.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (heightB + 10) + ")");
gBar.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style('font-size', '16px')
.style('font-family', 'Alegreya');
function draw() {
if (value == 'stack') {
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
y.domain([0, d3.max(dataStackLayout[dataStackLayout.length - 1],
function(d) {
return d.y0 + d.y;
})]).nice();
layer = gBar.selectAll(".stack")
.data(dataStackLayout);
layer.exit()
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(1500)
.style("fill", "none")
.remove();
layer.enter().append("g")
.attr("class", "stack")
.style("fill", function(d, i) {
return i == 0 ? '#b4d5c3' : '#ecaeb3';
});
bars = layer.selectAll("rect")
.data(function(d) {
return d;
});
bars.exit()
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(1500)
.attr("y", y(0))
.attr("height", heightB - y(0))
.style('fill-opacity', 1e-6)
.remove();
bars.enter().append("rect")
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(3000)
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand());
// the "UPDATE" set:
bars.transition().delay(function(d, i) {
return 30 * i;
}).duration(1500).attr("x", function(d) {
return x(d.x);
}) // (d) is one item from the data array, x is the scale object from above
.attr("width", x.rangeBand()) // constant, so no callback function(d) here
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.style("fill-opacity", 1);
gX.call(xAxis)
.selectAll("text")
.style('font-size', '14px')
.style('font-family', 'Alegreya')
.style("text-anchor", "end")
.attr("dx", "-0.40em")
.attr("dy", ".10em")
.attr("transform", function(d) {
return "rotate(-65)"
});
} else {
x.domain(points.map(function(d) {
return d.name;
}));
y.domain([0, 1024]).nice();
// this part
bars = layer.selectAll("rect")
.data(points);
bars.exit()
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(1500)
.attr("y", y(0))
.attr("height", heightB - y(0))
.style('fill-opacity', 1e-6)
.remove();
bars.enter().append("rect")
.transition()
.delay(function(d, i) {
return 30 * i;
})
.duration(3000)
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.GTF);
})
.attr("height", function(d) {
return heightB - y(d.GTF);;
})
.attr("width", x.rangeBand());
// the "UPDATE" set:
bars.transition().delay(function(d, i) {
return 30 * i;
}).duration(1500).attr("x", function(d) {
return x(d.name);
}) // (d) is one item from the data array, x is the scale object from above
.attr("width", x.rangeBand()) // constant, so no callback function(d) here
.attr("y", function(d) {
return y(d.GTF);
})
.attr("height", function(d) {
return heightB - y(d.GTF);
});
gX.call(xAxis);
}
}
window.onload = draw();
$("#click").on('click', function() {
if (value == 'stack') {
value = 'bar';
} else {
value = 'stack';
}
draw();
});
body {
font-family: 'Alegreya', serif;
}
.axis text {
font: 10px sans-serif;
}
.axis path {
fill: none;
stroke: #000;
stroke-width: 0px;
shape-rendering: crispEdges;
}
.axis line {
fill: none;
stroke: #000;
stroke-width: 0.5px;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.js"></script>
<div id="chart"></div>
<button id="click"> click here to change </button>
PS: Besides that, there is a lot of other minor changes you should do in your code, both for performance and design. As this is (now) a running code, I suggest you post further questions about how to improve it on Code Review, using the d3.js tag.
I drew a multi series line using d3 and added a vertical line when mouse hover over points. And I want to hide the vertical line and the text in 2018 point which has no data. I don't know how to select the line at the specific point which is translate(415,0), so I am not able to change the style to display:none;
Here is the code:
var data =[
{
'timescale': '2015',
'Not': 31,
'Nearly': 59,
'Standard': 81,
'Exceed':100
},
{
'timescale': '2016',
'Not': 28,
'Nearly': 55,
'Standard': 78,
'Exceed':100
},
{
'timescale': '2017',
'Not': 25,
'Nearly': 51,
'Standard': 75,
'Exceed':100
},
{
'timescale': '2018',
'Not': "null",
'Nearly': "null",
'Standard': "null",
'Exceed':"null"
},
{
'timescale': '2019',
'Not': 41,
'Nearly': 67,
'Standard': 90,
'Exceed':100
},
{
'timescale': '2020',
'Not': 36,
'Nearly': 61,
'Standard': 86,
'Exceed':100
},
{
'timescale': '2021',
'Not': 31,
'Nearly': 55,
'Standard': 82,
'Exceed':100
}
];
//d3.csv("test.csv", function(error,data){
console.log(data);
// set the dimensions and margins of the graph
var margin = { top: 20, right: 80, bottom: 30, left: 50 },
svg = d3.select('svg'),
width = +svg.attr('width') - margin.left - margin.right,
height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// set the ranges
var x = d3.scaleBand().rangeRound([0, width]).padding(1),
y = d3.scaleLinear().rangeRound([height, 0]),
z = d3.scaleOrdinal(["#BBB84B","#789952","#50DB51","#2D602A"]);
// define the line
var line = d3.line()
.defined(function (d) {
return !isNaN(d.total);
})
.x(function(d) { return x(d.timescale); })
.y(function(d) { return y(d.total); });
// scale the range of the data
z.domain(d3.keys(data[0]).filter(function(key) {
return key !== "timescale";
}));
var trends = z.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
timescale: d.timescale,
total: +d[name]
};
})
};
});
x.domain(data.map(function(d) { return d.timescale; }));
y.domain([0, d3.max(trends, function(c) {
return d3.max(c.values, function(v) {
return v.total;
});
})]);
// Draw the line
var trend = g.selectAll(".trend")
.data(trends)
.enter()
.append("g")
.attr("class", "trend");
trend.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.name); });
// Draw the empty value for every point
var points = g.selectAll('.points')
.data(trends)
.enter()
.append('g')
.attr('class', 'points')
.append('text');
// Draw the circle
trend
.style("fill", "#FFF")
.style("stroke", function(d) { return z(d.name); })
.selectAll("circle.line")
.data(function(d){return d.values} )
.enter()
.append("circle")
.filter(function(d) { return d.timescale !== "2018" })
.attr("r", 5)
.style("stroke-width", 3)
.attr("cx", function(d) { return x(d.timescale); })
.attr("cy", function(d) { return y(d.total); });
// Draw the axis
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).ticks(6));
var focus = g.append('g')
.attr('class','focus')
.style('display', 'none');
focus.append('line')
.attr('class', 'x-hover-line hover-line')
.attr('y1' , 0)
.attr('y2', height)
.style('stroke',"black");
svg.append('rect')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove);
// var timeScales = data.map(function(name) { return x(name.timescale); });
// console.log(timeScales);
var timeScales = [106,209,312,415,518,621,724];
// d3.select('.focus')
// .attr("x1",415)
// .attr("y1",0)
// .attr("x2",415)
// .attr("y2",height)
// .style("display","none");
function mouseover() {
focus.style("display", null);
d3.selectAll('.points text').style("display", null);
}
function mouseout() {
focus.style("display", "none");
d3.selectAll('.points text').style("display", "none");
}
function mousemove() {
var i = d3.bisect(timeScales, d3.mouse(this)[0], 1);
var di = data[i-1];
focus.attr("transform", "translate(" + x(di.timescale) + ",0)");
d3.selectAll('.points text')
.attr('x', function(d) { return x(di.timescale) + 5; })
.attr('y', function(d) { return y(d.values[i-1].total)-5; })
.text(function(d) { return d.values[i-1].total; })
.style('fill', function(d) { return z(d.name); });
}
body {
font-family: 'Proxima Nova', Georgia, sans-serif;
}
.line {
fill: none;
stroke-width: 3px;
}
.overlay {
fill: none;
pointer-events: all;
}
.hover-line {
stroke-width: 2px;
stroke-dasharray: 3,3;
}
<svg width="960" height="500"></svg>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js'></script>
In the mousemove function only perform the transformation if timescale is not 2018:
function mousemove() {
var i = d3.bisect(timeScales, d3.mouse(this)[0], 1);
var di = data[i-1];
if (di.timescale !== '2018') {
focus.attr("transform", "translate(" + x(di.timescale) + ",0)");
d3.selectAll('.points text')
.attr('x', function(d) { return x(di.timescale) + 5; })
.attr('y', function(d) { return y(d.values[i-1].total)-5; })
.text(function(d) { return d.values[i-1].total; })
.style('fill', function(d) { return z(d.name); });
}
}
you have using bisection as a tip
so it will draw only when it selected, lets change that so it will not draw the 2018 when mouseover it
var data =[
{
'timescale': '2015',
'Not': 31,
'Nearly': 59,
'Standard': 81,
'Exceed':100
},
{
'timescale': '2016',
'Not': 28,
'Nearly': 55,
'Standard': 78,
'Exceed':100
},
{
'timescale': '2017',
'Not': 25,
'Nearly': 51,
'Standard': 75,
'Exceed':100
},
{
'timescale': '2018',
'Not': "null",
'Nearly': "null",
'Standard': "null",
'Exceed':"null"
},
{
'timescale': '2019',
'Not': 41,
'Nearly': 67,
'Standard': 90,
'Exceed':100
},
{
'timescale': '2020',
'Not': 36,
'Nearly': 61,
'Standard': 86,
'Exceed':100
},
{
'timescale': '2021',
'Not': 31,
'Nearly': 55,
'Standard': 82,
'Exceed':100
}
];
//d3.csv("test.csv", function(error,data){
// console.log(data);
// set the dimensions and margins of the graph
var margin = { top: 20, right: 80, bottom: 30, left: 50 },
svg = d3.select('svg'),
width = +svg.attr('width') - margin.left - margin.right,
height = +svg.attr('height') - margin.top - margin.bottom;
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// set the ranges
var x = d3.scaleBand().rangeRound([0, width]).padding(1),
y = d3.scaleLinear().rangeRound([height, 0]),
z = d3.scaleOrdinal(["#BBB84B","#789952","#50DB51","#2D602A"]);
// define the line
var line = d3.line()
.defined(function (d) {
return !isNaN(d.total);
})
.x(function(d) { return x(d.timescale); })
.y(function(d) { return y(d.total); });
// scale the range of the data
z.domain(d3.keys(data[0]).filter(function(key) {
return key !== "timescale";
}));
var trends = z.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
timescale: d.timescale,
total: +d[name]
};
})
};
});
x.domain(data.map(function(d) { return d.timescale; }));
y.domain([0, d3.max(trends, function(c) {
return d3.max(c.values, function(v) {
return v.total;
});
})]);
// Draw the line
var trend = g.selectAll(".trend")
.data(trends)
.enter()
.append("g")
.attr("class", "trend");
trend.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.name); });
// Draw the empty value for every point
var points = g.selectAll('.points')
.data(trends)
.enter()
.append('g')
.attr('class', 'points')
.append('text');
// Draw the circle
trend
.style("fill", "#FFF")
.style("stroke", function(d) { return z(d.name); })
.selectAll("circle.line")
.data(function(d){return d.values} )
.enter()
.append("circle")
.filter(function(d) { return d.timescale !== "2018" })
.attr("r", 5)
.style("stroke-width", 3)
.attr("cx", function(d) { return x(d.timescale); })
.attr("cy", function(d) { return y(d.total); });
// Draw the axis
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).ticks(6));
var focus = g.append('g')
.attr('class','focus')
.style('display', 'none');
focus.append('line')
.attr('class', 'x-hover-line hover-line')
.attr('y1' , 0)
.attr('y2', height)
.style('stroke',"black");
svg.append('rect')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove);
// var timeScales = data.map(function(name) { return x(name.timescale); });
// console.log(timeScales);
var timeScales = [106,209,312,415,518,621,724];
// d3.select('.focus')
// .attr("x1",415)
// .attr("y1",0)
// .attr("x2",415)
// .attr("y2",height)
// .style("display","none");
function mouseover() {
focus.style("display", null);
d3.selectAll('.points text').style("display", null);
}
function mouseout() {
focus.style("display", "none");
d3.selectAll('.points text').style("display", "none");
}
function mousemove() {
var i = d3.bisect(timeScales, d3.mouse(this)[0], 1);
var di = data[i-1];
console.log(di)
if (di.timescale =="2018"){
focus.style('display','none')
d3.selectAll('.points text').style("display", "none");
}else{
focus.style('display','block')
d3.selectAll('.points text').style("display", "block");
focus.attr("transform", "translate(" + x(di.timescale) + ",0)");}
d3.selectAll('.points text')
.attr('x', function(d) { return x(di.timescale) + 5; })
.attr('y', function(d) { return y(d.values[i-1].total)-5; })
.text(function(d) { return d.values[i-1].total; })
.style('fill', function(d) { return z(d.name); });
}
body {
font-family: 'Proxima Nova', Georgia, sans-serif;
}
.line {
fill: none;
stroke-width: 3px;
}
.overlay {
fill: none;
pointer-events: all;
}
.hover-line {
stroke-width: 2px;
stroke-dasharray: 3,3;
}
<svg width="960" height="500"></svg>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js'></script>
I'm having issues with my update() function. Here, within svg.append('rect') I have .on('click') where I simply change the data, then run update().
Why doesn't this work? How do I make it work?
var width = 640,
height = 480;
var graphNodes = [
{ id: 0, x: 39, y: 343, r: 15 },
{ id: 1, x: 425, y: 38, r: 15 },
{ id: 2, x: 183, y: 417, r: 15 },
{ id: 3, x: 564, y: 31, r: 15 },
{ id: 4, x: 553, y: 351, r: 15 },
{ id: 5, x: 454, y: 298, r: 15 },
{ id: 6, x: 493, y: 123, r: 15 },
{ id: 7, x: 471, y: 427, r: 15 },
{ id: 8, x: 142, y: 154, r: 15 }
];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
svg.append('rect')
.attr('class', 'graph')
.attr('width', width)
.attr('height', height)
.attr('fill', 'lightblue')
.attr('opacity', 0.3)
.on('click', function(){
graphNodes[8].id = 'hey there'; // <----- Why doesn't this happen?
update();
});
var nodeGroup = svg.selectAll('.nodes')
.data(graphNodes, function(d){ return d.id; })
.enter().append('g')
.attr('class', 'node');
nodeGroup.append('circle')
.attr('cx', function(d) { return d.x })
.attr('cy', function(d) { return d.y })
.attr("r", function(d){ return d.r; })
.attr("fill", "gray");
nodeGroup.append('text')
.attr("dx", function(d){ return d.x + 20; })
.attr("dy", function(d){ return d.y + 5; })
.text(function(d) { return d.id });
function update() {
if(nodeGroup){
// Update nodes
var node = nodeGroup.data(graphNodes, function(d){ return d.id; }),
nodeEnter = node.enter().append('g')
.attr('class', 'node');
nodeEnter.append('circle')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.attr('r', function(d){ return d.r; })
.attr('fill', 'gray');
nodeEnter.append('text')
.attr("dx", function(d){ return d.x + 20; })
.attr("dy", function(d){ return d.y + 5; })
.text(function(d) { return d.id });
nodeGroup = nodeEnter.merge(node);
node.exit().remove();
}
}
Here's a fiddle
Typed this as a comment in the message you left me, but here it is as an answer.
You need to separate out the things you do on enter, on update and on exit. On enter you want to just append and set any attributes that never change. On update you want to add/change the text and add/change the radius. On exit you remove. Here I've properly handled the enter, update, exit paradigm:
// bind the data
var node = nodeGroup.data(graphNodes, function(d){ return d.id; }),
// this is the enter selection
nodeEnter = node.enter().append('g')
.attr('class', 'node');
// append to enter selection
// append and set color, we never change color
nodeEnter.append('circle')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.attr('fill', 'gray');
// again entering, append text and set position
nodeEnter.append('text')
.attr("dx", function(d){ return d.x + 20; })
.attr("dy", function(d){ return d.y + 5; });
// nodeGroup is the enter + update selection
nodeGroup = nodeEnter.merge(node);
// change the things we want to change on every update
nodeGroup.select("text")
.text(function(d) { return d.text ? d.text : d.id });
nodeGroup.select("circle")
.attr('r', function(d){ return d.r; })
// exit, just remove
node.exit().remove();
Running code:
var width = 640,
height = 480;
var graphNodes = [
{ id: 0, x: 39, y: 343, r: 15 },
{ id: 1, x: 425, y: 38, r: 15 },
{ id: 2, x: 183, y: 417, r: 15 },
{ id: 3, x: 564, y: 31, r: 15 },
{ id: 4, x: 553, y: 351, r: 15 },
{ id: 5, x: 454, y: 298, r: 15 },
{ id: 6, x: 493, y: 123, r: 15 },
{ id: 7, x: 471, y: 427, r: 15 },
{ id: 8, x: 142, y: 154, r: 15 }
];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
svg.append('rect')
.attr('class', 'graph')
.attr('width', width)
.attr('height', height)
.attr('fill', 'lightblue')
.attr('opacity', 0.3)
.on('click', function(){
/*graphNodes.push({
x: d3.mouse(this)[0],
y: d3.mouse(this)[1],
id: graphNodes.length,
r: 15
});*/
graphNodes.splice(2, 1);
graphNodes[Math.floor(Math.random() * graphNodes.length)].text = "Tomato!";
graphNodes[Math.floor(Math.random() * graphNodes.length)].r = Math.random() * 30;
update();
});
var nodeGroup = svg.selectAll('.nodes')
.data(graphNodes, function(d){ return d.id; })
.enter().append('g')
.attr('class', 'node');
nodeGroup.append('circle')
.attr('cx', function(d) { return d.x })
.attr('cy', function(d) { return d.y })
.attr("r", function(d){ return d.r; })
.attr("fill", "gray");
nodeGroup.append('text')
.attr("dx", function(d){ return d.x + 20; })
.attr("dy", function(d){ return d.y + 5; })
.text(function(d) { return d.id });
function update() {
if(nodeGroup){
// Update nodes
var node = nodeGroup.data(graphNodes, function(d){ return d.id; }),
nodeEnter = node.enter().append('g')
.attr('class', 'node');
// this is the enter selection
// append and set color, we never change color
nodeEnter.append('circle')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.attr('fill', 'gray');
nodeEnter.append('text')
.attr("dx", function(d){ return d.x + 20; })
.attr("dy", function(d){ return d.y + 5; });
// nodeGroup is the enter + update selection
nodeGroup = nodeEnter.merge(node);
// change the things we want to change on every update
nodeGroup.select("text")
.text(function(d) { return d.text ? d.text : d.id });
nodeGroup.select("circle")
.attr('r', function(d){ return d.r; })
// exit, just remove
node.exit().remove();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
I am facing a difficult task in combining the Bivariate Area Graph with Line Graph in a single graph d3.js. If I am successful with the bivariate area graph i am not able to draw a line chart in the same graph and vice versa. Either one is working for me. I have provided data for bivariate area which is at x and y axis. whether it is possible to draw the line chart with same data? I have gone through lot of graphs available online and i couldn't find one. Help needed in drawing the line chart with bivariate area chart.
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.area {
fill: steelblue;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
// data
var data = [{
"month": "January",
"high": "59.5",
"low" : "57.0"
}, {
"month": "February",
"high": "59.5",
"low" : "53.4"
}, {
"month": "March",
"high": "59.0",
"low" : "53.4"
}, {
"month": "April",
"high": "62.4",
"low" : "54.7"
}, {
"month": "May",
"high": "58.3",
"low" : "52.7"
}, {
"month": "June",
"high": "62.1",
"low" : "54.5"
}, {
"month": "July",
"high": "60.8",
"low" : "53.4"
}, {
"month": "August",
"high": "61.0",
"low" : "52.5"
}, {
"month": "September",
"high": "62.4",
"low" : "52.9"
}, {
"month": "October",
"high": "65.3",
"low" : "54.0"
}, {
"month": "November",
"high": "70.3",
"low" : "55.0"
}, {
"month": "December",
"high": "82.2",
"low" : "58.6"
}];
// margins
var margin = {top: 20, right: 30, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parsing data
var parseDate = d3.time.format("%B").parse;
// x-axis encoding
var x = d3.time.scale()
.range([0, width]);
// y-axis encoding
var y = d3.scale.linear()
.range([height, 0]);
// x-axis scaling
var xAxis = d3.svg.axis()
.scale(x)
.tickFormat(d3.time.format("%b"))
.orient("bottom");
// y-axis scaling
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// area draw
var area = d3.svg.area()
.x(function(d) { return x(d.month); })
.y0(function(d) { return y(d.low); })
.y1(function(d) { return y(d.high); });
// adding the svg element
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//function
function chart(error, data) {
data.forEach(function(d) {
d.month = parseDate(d.month);
d.low = +d.low;
d.high = +d.high;
});
// setting scales
x.domain(d3.extent(data, function(d) { return d.month;})).range([0, 600]);
y.domain([d3.min(data, function(d) { return d.low; }),
d3.max(data, function(d) { return d.high; })]);
// area logic
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
// appending x axis data
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// text label for the x axis
svg.append("text")
.attr("x", 0 )
.attr("y", 480 )
.style("text-anchor", "middle")
.text("2015");
// appending y axis data
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (ºF)");
}
chart([],data);
/*);*/
</script>
Do it this way.
Working code http://jsfiddle.net/cyril123/7a4pp6gx/2/
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.area {
fill: steelblue;
}
.line {
fill: transparent;
stroke:red;
stroke-width:3px;
}
.line1 {
fill: transparent;
stroke:green;
stroke-width:3px;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
// data
var data = [{
"month": "January",
"high": "59.5",
"low" : "57.0"
}, {
"month": "February",
"high": "59.5",
"low" : "53.4"
}, {
"month": "March",
"high": "59.0",
"low" : "53.4"
}, {
"month": "April",
"high": "62.4",
"low" : "54.7"
}, {
"month": "May",
"high": "58.3",
"low" : "52.7"
}, {
"month": "June",
"high": "62.1",
"low" : "54.5"
}, {
"month": "July",
"high": "60.8",
"low" : "53.4"
}, {
"month": "August",
"high": "61.0",
"low" : "52.5"
}, {
"month": "September",
"high": "62.4",
"low" : "52.9"
}, {
"month": "October",
"high": "65.3",
"low" : "54.0"
}, {
"month": "November",
"high": "70.3",
"low" : "55.0"
}, {
"month": "December",
"high": "82.2",
"low" : "58.6"
}];
// margins
var margin = {top: 20, right: 30, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parsing data
var parseDate = d3.time.format("%B").parse;
// x-axis encoding
var x = d3.time.scale()
.range([0, width]);
// y-axis encoding
var y = d3.scale.linear()
.range([height, 0]);
// x-axis scaling
var xAxis = d3.svg.axis()
.scale(x)
.tickFormat(d3.time.format("%b"))
.orient("bottom");
// y-axis scaling
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// area draw
var area = d3.svg.area()
.x(function(d) { return x(d.month); })
.y0(function(d) { return y(d.low); })
.y1(function(d) { return y(d.high); });
var line = d3.svg.line()
.x(function(d) { return x(d.month); })
.y(function(d) { return y(d.high); });
var line1 = d3.svg.line()
.x(function(d) { return x(d.month); })
.y(function(d) { return y(d.low); });
// adding the svg element
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//function
function chart(error, data) {
data.forEach(function(d) {
d.month = parseDate(d.month);
d.low = +d.low;
d.high = +d.high;
});
// setting scales
x.domain(d3.extent(data, function(d) { return d.month;})).range([0, 600]);
y.domain([d3.min(data, function(d) { return d.low; }),
d3.max(data, function(d) { return d.high; })]);
// area logic
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
svg.append("path")
.datum(data)
.attr("class", "line1")
.attr("d", line1);
// appending x axis data
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// text label for the x axis
svg.append("text")
.attr("x", 0 )
.attr("y", 480 )
.style("text-anchor", "middle")
.text("2015");
// appending y axis data
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (ºF)");
}
chart([],data);
/*);*/
</script>
I have been at it for a couple of weeks and cant seem to figure out how to draw the below chart with multiple paths.
Focus+Context via Brushing chart
I have tried to create a jsfiddle but was not able to replicate the screen that i do get. At this point what i have is similar to the original chart just with one path instead of area and the brushing works. Basically trying to combine the Focus chart and Multi-Series Line Chart Multiseries chart .
However when I try to add another path nothing works. Please suggest any ideas or changes that I need to make for it to work. Also are there any other similar charts (or chart exaples) that I can look at. The data could be rearranged in any way or form for this to work.
Jsfiddle
<div id='dashboardChart'>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
id="svg">
</div>
path {
fill:none;
stroke:white;
stroke-width:2px;
}
.axis path, .axis line {
fill: none;
stroke: #CCC;
shape-rendering: crispEdges;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.path_green {
stroke:green;
}
.path_red {
stroke:red;
}
.path_yellow {
stroke:yellow;
}
function drawChart() {
var margin = {
top: 5,
right: 10,
bottom: 100,
left: 50
},
margin2 = {
top: 200,
right: 10,
bottom: 20,
left: 50
},
width = 1075 - margin.left - margin.right,
height = 280 - margin.top - margin.bottom,
height2 = 280 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brush);
var area = d3.svg.area()
.interpolate("monotone")
.x(function (d) {
return x(d.date);
})
.y0(height)
.y1(function (d) {
return y(d.red);
});
var area2 = d3.svg.area()
.interpolate("monotone")
.x(function (d) {
return x2(d.date);
})
.y0(height2)
.y1(function (d) {
return y2(d.red);
});
var svg = d3.select("#dashboardChart #svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var data = [{
"date": "2013-02-08T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 1
}, {
"date": "2013-02-07T05:00:00.000Z",
"data": null,
"red": 485,
"yellow": 0,
"green": 491
}, {
"date": "2013-02-06T05:00:00.000Z",
"data": null,
"red": 2884,
"yellow": 0,
"green": 2881
}, {
"date": "2013-02-05T05:00:00.000Z",
"data": null,
"red": 3191,
"yellow": 0,
"green": 3188
}, {
"date": "2013-02-04T05:00:00.000Z",
"data": null,
"red": 180,
"yellow": 0,
"green": 184
}, {
"date": "2013-02-03T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-02-02T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-02-01T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-31T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-30T05:00:00.000Z",
"data": null,
"red": 1,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-29T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 2
}, {
"date": "2013-01-28T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-27T05:00:00.000Z",
"data": null,
"red": 1,
"yellow": 1,
"green": 1
}, {
"date": "2013-01-26T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 1
}, {
"date": "2013-01-25T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-24T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-23T05:00:00.000Z",
"data": null,
"red": 49,
"yellow": 0,
"green": 45
}, {
"date": "2013-01-22T05:00:00.000Z",
"data": null,
"red": 59,
"yellow": 0,
"green": 64
}, {
"date": "2013-01-21T05:00:00.000Z",
"data": null,
"red": 119,
"yellow": 1,
"green": 125
}, {
"date": "2013-01-20T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 1,
"green": 0
}, {
"date": "2013-01-19T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-18T05:00:00.000Z",
"data": null,
"red": 84,
"yellow": 0,
"green": 81
}, {
"date": "2013-01-17T05:00:00.000Z",
"data": null,
"red": 76,
"yellow": 1,
"green": 77
}, {
"date": "2013-01-16T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 1,
"green": 0
}, {
"date": "2013-01-15T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-14T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-13T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-12T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-11T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}, {
"date": "2013-01-10T05:00:00.000Z",
"data": null,
"red": 0,
"yellow": 0,
"green": 0
}];
x.domain(d3.extent(data.map(function (d) {
return d.date;
})));
y.domain([0, d3.max(data.map(function (d) {
return d.red;
}))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("clip-path", "url(#clip)")
.attr("d", area)
.attr("class", "path_red");
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("d", area2)
.attr("class", "path_red");
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
function brush() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.select("path").attr("d", area);
focus.select(".x.axis").call(xAxis);
}
}
drawChart();
As per your comment, you were able to plot the three areas but had difficulty in brushing them. I have an working example here: http://jsfiddle.net/BVzyq/1/ wherein, I have added three <path> elements corresponding to the three colors in the data: ['red', 'yellow', 'green'].
I abstracted out the functions which could take in a color and return the appropriate d value:
var area = function (color) {
return d3.svg.area()
.interpolate("monotone")
.x(function (d) {
return x(d.date);
})
.y0(height)
.y1(function (d) {
return y(d[color]);
});
};
var area2 = function (color) {
return d3.svg.area()
.interpolate("monotone")
.x(function (d) {
return x2(d.date);
})
.y0(height2)
.y1(function (d) {
return y2(d[color]);
});
};
They can be abstracted further, but these are closest to the code you have written.
These functions are used while creating the paths:
focus.selectAll('path')
.data(['red', 'yellow', 'green'])
.enter()
.append('path')
.attr('clip-path', 'url(#clip)')
.attr('d', function (col) {
return area(col)(data);
})
.attr('class', function (col) {
return "path_" + col + " data";
});
// ...
context.selectAll('path')
.data(['red', 'yellow', 'green'])
.enter()
.append('path')
.attr('d', function (col) {
return area2(col)(data);
})
.attr('class', function (col) {
return "path_" + col;
});
The CSS classes seemed to suggest this form of data-join. I also added another class data to the paths which correspond to the time-series plots. This makes it easy to distinguish these <path>s from those meant for the axis.
Finally, in the brush function, recalculate the d attribute for all path.data elements:
function brush() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.selectAll("path.data").attr("d", function (col) {
return area(col)(data);
});
focus.select(".x.axis").call(xAxis);
}
Note that I changed some of the values in data to make all the three colours visible.
Great solution musically_ut, for others having a similar problem. I was able to load additional csv's, like the example found in this link - Focus+Context via Brushing but was really hung up on how to brush all of the lines together, even though they were both rendering properly in area and area2.
Turns out, looking at your code, all I had to do was change focus.select to focus.selectAll like you have. Thanks!
And for anyone working with the original tutorial code, you can add alternate csv's just by copying the following code and pointing to your new csv:
d3.csv("sp501.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("clip-path", "url(#clip)")
.attr("d", area)
.attr("class", "timeLine2");
context.append("path")
.datum(data)
.attr("class", "timeLine2")
.attr("d", area2);
context.append("g")
.attr("class", "x axis2")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
});