How to Resize D3 Line Charts - javascript

I have a question about resizing D3 multi line charts. I followed Building Responsive Visualizations with D3.js. But I am having trouble updating the elements inside of my chart. I am successfully updating the axis, but not the lines themselves. I also am unsure of how to update the dots and text in the graph.
Thanks for your help!
Here is the code I have so far:
<svg id="graph"></svg>
<script>
// Set the dimensions of the canvas / graph
var margin = 40,
width = 960 - margin*2,
height = 500 - margin*2;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(8);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.average_ticnum); });
var valueline2 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.average_fatiguenum); });
var valueline3 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.average_stressnum); });
var valueline4 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.sum_nf_sugars); });
var valueline5 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.sum_nf_total_carbohydrate); });
// Adds the svg canvas
var graph = d3.select("#graph")
.attr("width", width + margin*2)
.attr("height", height + margin*2)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")");
// Get the data
d3.json("progress_output.php?useremail=<?php echo $useremail; ?>", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.average_ticnum = +d.average_ticnum;
d.fatiguenum = +d.average_fatiguenum;
d.stressnum = +d.average_stressnum;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0,12]);
// Add the valueline path.
graph.append("path")
.attr("class", "line")
.style("stroke", "#891f83")
.attr("id","ticLine")
.attr("d", valueline(data));
graph.append("path")
.attr("class", "line")
.style("stroke", "#7db6e3")
.attr("id","fatigueLine")
.attr("d", valueline2(data));
graph.append("path")
.attr("class", "line")
.style("stroke", "#36376a")
.attr("id","stressLine")
.attr("d", valueline3(data));
graph.append("path")
.attr("class", "line")
.style("stroke", "#9bcf81")
.attr("id","sugarLine")
.attr("d", valueline4(data));
graph.append("path")
.attr("class", "line")
.style("stroke", "#efa465")
.attr("id","carbLine")
.attr("d", valueline5(data));
// Add the Dots
graph.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("id","ticDots")
.attr("r", 3.5)
.style("fill", "#891f83")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.average_ticnum); });
graph.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.style("fill", "#7db6e3")
.attr("id","fatigueDots")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.average_fatiguenum); });
graph.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.style("fill", "#36376a")
.attr("id", "stressDots")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.average_stressnum); });
graph.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("id","sugarDots")
.attr("r", 3.5)
.style("fill", "#9bcf81")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.sum_nf_sugars); });
graph.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.style("fill", "#efa465")
.attr("id","carbDots")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.sum_nf_total_carbohydrate); });
// Add the X Axis
graph.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
graph.append("g")
.attr("class", "y axis")
.call(yAxis);
//Add the Tic title
graph.append("text")
.attr("x", 15)
.attr("y", 0)
.attr("class", "legend")
.attr("id", "ticText")
.style("color", "#891f83")
.style("fill", "#891f83")
.style("cursor", "pointer")
.style("font-family", "cooperbook")
.on("click", function(){
// Determine if current line is visible
var active = ticLine.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements
width = 200;
height = 200;
d3.select("#ticLine").style("opacity", newOpacity);
d3.selectAll("#ticDots").style("opacity", newOpacity);
// Update whether or not the elements are active
ticLine.active = active;
})
.text("Tic Severity");
//Add the fatigue title
graph.append("text")
.attr("x", 115)
.attr("y", 0)
.attr("class", "legend")
.style("color", "#7bd6e3")
.style("fill", "#7bd6e3")
.style("cursor", "pointer")
.style("font-family", "cooperbook")
.on("click", function(){
// Determine if current line is visible
var active = fatigueLine.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements
d3.select("#fatigueLine").style("opacity", newOpacity);
d3.selectAll("#fatigueDots").style("opacity", newOpacity);
// Update whether or not the elements are active
fatigueLine.active = active;
})
.text("Fatigue");
//Add the Stress title
graph.append("text")
.attr("x", 190)
.attr("y", 0)
.attr("class", "legend")
.style("color", "#36376a")
.style("fill", "#36376a")
.style("cursor", "pointer")
.style("font-family", "cooperbook")
.on("click", function(){
// Determine if current line is visible
var active = stressLine.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements
d3.select("#stressLine").style("opacity", newOpacity);
d3.selectAll("#stressDots").style("opacity", newOpacity);
// Update whether or not the elements are active
stressLine.active = active;
})
.text("Stress");
//Add the sugar title
graph.append("text")
.attr("x", 250)
.attr("y", 0)
.attr("class", "legend")
.style("color", "#9bcf81")
.style("fill", "#9bcf81")
.style("cursor", "pointer")
.style("font-family", "cooperbook")
.on("click", function(){
// Determine if current line is visible
var active = sugarLine.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements
d3.select("#sugarLine").style("opacity", newOpacity);
d3.selectAll("#sugarDots").style("opacity", newOpacity);
// Update whether or not the elements are active
sugarLine.active = active;
})
.text("Sugars");
//Add the carb title
graph.append("text")
.attr("x", 320)
.attr("y", 0)
.attr("class", "legend")
.style("color", "#efa465")
.style("fill", "#efa465")
.style("cursor", "pointer")
.style("font-family", "cooperbook")
.on("click", function(){
// Determine if current line is visible
var active = carbLine.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements
d3.select("#carbLine").style("opacity", newOpacity);
d3.selectAll("#carbDots").style("opacity", newOpacity);
// Update whether or not the elements are active
carbLine.active = active;
})
.text("Carbohydrates");
});
function resize() {
/* Find the new window dimensions */
var width = parseInt(d3.select("#graph").style("width")) - margin*2,
height = parseInt(d3.select("#graph").style("height")) - margin*2;
/* Update the range of the scale with new width/height */
x.range([0, width]).nice(d3.time.year);
y.range([height, 0]).nice();
/* Update the axis with the new scale */
graph.select('.x.axis')
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
graph.select('.y.axis')
.call(yAxis);
/* Force D3 to recalculate and update the line */
graph.selectAll('.line')
.attr("d", valueline(data));
}
d3.select(window).on('resize', resize);

Related

Adding circles to multi line graph with dropdown on d3.js

I am trying to add circles to the data points on the following line graph example: https://bl.ocks.org/ProQuestionAsker/8382f70af7f4a7355827c6dc4ee8817d
To generate the circles I have used the following:
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3)
.attr("color", "pink")
.attr("cx", function(d) { return x(d.Month); })
.attr("cy", function(d) { return y(+d.Sales); });
However, as seen here, all the circles for each every fruit. I would like only the circles for selected fruits to appear, as per the lines.
Many thanks
James
You see the circles for each and every fruit because you're not filtering the data based on the dropdown selection.
Here's a snippet doing that data filtering and appending the dots:
var dataAsCsv = `Month,Sales,Fruit,Year
Jan,87,strawberry,2016
Feb,3,strawberry,2016
Mar,89,strawberry,2016
Apr,56,strawberry,2016
May,1,strawberry,2016
Jun,17,strawberry,2016
Jul,59,strawberry,2016
Aug,43,strawberry,2016
Sep,16,strawberry,2016
Oct,94,strawberry,2016
Nov,99,strawberry,2016
Dec,53,strawberry,2016
Jan,93,grape,2016
Feb,8,grape,2016
Mar,95,grape,2016
Apr,62,grape,2016
May,5,grape,2016
Jun,24,grape,2016
Jul,62,grape,2016
Aug,49,grape,2016
Sep,18,grape,2016
Oct,101,grape,2016
Nov,103,grape,2016
Dec,53,grape,2016
Jan,94,blueberry,2016
Feb,15,blueberry,2016
Mar,95,blueberry,2016
Apr,64,blueberry,2016
May,11,blueberry,2016
Jun,33,blueberry,2016
Jul,64,blueberry,2016
Aug,53,blueberry,2016
Sep,27,blueberry,2016
Oct,103,blueberry,2016
Nov,108,blueberry,2016
Dec,62,blueberry,2016
Jan,80,strawberry,2015
Feb,0,strawberry,2015
Mar,71,strawberry,2015
Apr,51,strawberry,2015
May,3,strawberry,2015
Jun,11,strawberry,2015
Jul,56,strawberry,2015
Aug,34,strawberry,2015
Sep,12,strawberry,2015
Oct,75,strawberry,2015
Nov,94,strawberry,2015
Dec,46,strawberry,2015
Jan,76,grape,2015
Feb,0,grape,2015
Mar,78,grape,2015
Apr,58,grape,2015
May,10,grape,2015
Jun,22,grape,2015
Jul,47,grape,2015
Aug,36,grape,2015
Sep,18,grape,2015
Oct,86,grape,2015
Nov,98,grape,2015
Dec,40,grape,2015
Jan,79,blueberry,2015
Feb,0,blueberry,2015
Mar,78,blueberry,2015
Apr,49,blueberry,2015
May,5,blueberry,2015
Jun,31,blueberry,2015
Jul,62,blueberry,2015
Aug,49,blueberry,2015
Sep,7,blueberry,2015
Oct,86,blueberry,2015
Nov,100,blueberry,2015
Dec,46,blueberry,2015`;
// Set the margins
var margin = {top: 60, right: 100, bottom: 20, left: 80},
width = 850 - margin.left - margin.right,
height = 370 - margin.top - margin.bottom;
// Parse the month variable
var parseMonth = d3.timeParse("%b");
var formatMonth = d3.timeFormat("%b");
var formatYear = d3.timeFormat("%Y");
var parseYear = d3.timeParse("%Y");
// Set the ranges
var x = d3.scaleTime().domain([parseMonth("Jan"), parseMonth("Dec")]).range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// Define the line
var valueLine = d3.line()
.x(function(d) { return x(d.Month); })
.y(function(d) { return y(+d.Sales); })
// Create the svg canvas in the "graph" div
var svg = d3.select("#graph")
.append("svg")
.style("width", width + margin.left + margin.right + "px")
.style("height", height + margin.top + margin.bottom + "px")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")")
.attr("class", "svg");
var data = d3.csvParse(dataAsCsv);
// Format the data
data.forEach(function(d) {
d.Month = parseMonth(d.Month);
d.Sales = +d.Sales;
d.Fruit = d.Fruit;
d.Year = formatYear(parseYear(+d.Year));
});
var nest = d3.nest()
.key(function(d){
return d.Fruit;
})
.key(function(d){
return d.Year;
})
.entries(data)
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.Month; }));
y.domain([0, d3.max(data, function(d) { return d.Sales; })]);
// Set up the x axis
var xaxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "x axis")
.call(d3.axisBottom(x)
.ticks(d3.timeMonth)
.tickSize(0, 0)
.tickFormat(d3.timeFormat("%B"))
.tickSizeInner(0)
.tickPadding(10));
// Add the Y Axis
var yaxis = svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y)
.ticks(5)
.tickSizeInner(0)
.tickPadding(6)
.tickSize(0, 0));
// Add a label to the y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - 60)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Monthly Sales")
.attr("class", "y axis label");
svg.append('g').classed('data-points', true);
// Create a dropdown
var fruitMenu = d3.select("#fruitDropdown")
fruitMenu
.append("select")
.selectAll("option")
.data(nest)
.enter()
.append("option")
.attr("value", function(d){
return d.key;
})
.text(function(d){
return d.key;
})
// Function to create the initial graph
var initialGraph = function(fruit){
// Filter the data to include only fruit of interest
var selectFruit = nest.filter(function(d){
return d.key == fruit;
})
var selectFruitGroups = svg.selectAll(".fruitGroups")
.data(selectFruit, function(d){
return d ? d.key : this.key;
})
.enter()
.append("g")
.attr("class", "fruitGroups")
var initialPath = selectFruitGroups.selectAll(".line")
.data(function(d) { return d.values; })
.enter()
.append("path")
initialPath
.attr("d", function(d){
return valueLine(d.values)
})
.attr("class", "line")
svg.select('g.data-points').selectAll("dot")
.data(data.filter(function(d) {
return d.Fruit === fruit;
}))
.enter().append("circle").classed('dot', true)
.attr("r", 3)
.style("fill", "pink").style('stroke', '#000')
.attr("cx", function(d) { return x(d.Month); })
.attr("cy", function(d) { return y(+d.Sales); });
}
// Create initial graph
initialGraph("strawberry")
// Update the data
var updateGraph = function(fruit){
// Filter the data to include only fruit of interest
var selectFruit = nest.filter(function(d){
return d.key == fruit;
})
// Select all of the grouped elements and update the data
var selectFruitGroups = svg.selectAll(".fruitGroups")
.data(selectFruit)
// Select all the lines and transition to new positions
selectFruitGroups.selectAll("path.line")
.data(function(d){
return (d.values);
})
.transition()
.duration(1000)
.attr("d", function(d){
return valueLine(d.values)
});
var circles = svg.select('g.data-points').selectAll(".dot")
.data(data.filter(function(d) {
return d.Fruit === fruit;
}));
circles
.enter().append("circle")
.merge(circles).classed('data-point', true)
.attr("r", 3)
.style("fill", "pink").style('stroke', '#000')
.transition().duration(1000)
.attr("cx", function(d) { return x(d.Month); })
.attr("cy", function(d) { return y(+d.Sales); });
}
// Run update function when dropdown selection changes
fruitMenu.on('change', function(){
// Find which fruit was selected from the dropdown
var selectedFruit = d3.select(this)
.select("select")
.property("value")
// Run update function with the selected fruit
updateGraph(selectedFruit)
});
.line {
fill: none;
stroke: #EF5285;
stroke-width: 2px;
}
<script src="https://d3js.org/d3.v4.js"></script>
<div id = "fruitDropdown"></div>
<div id="graph"></div>
Important code changes:
Instead of appending circles directly to the SVG, I've created a group <g class="data-points"></g> that holds all the dots.
svg.append('g').classed('data-points', true);
Enter/update/exit all dots within the above group in both functions i.e. initialGraph and updateGraph
InitialGraph:
svg.select('g.data-points').selectAll("dot")
.data(data.filter(function(d) {
return d.Fruit === fruit;
}))
.enter().append("circle").classed('dot', true)
.attr("r", 3)
.style("fill", "pink").style('stroke', '#000')
.attr("cx", function(d) { return x(d.Month); })
.attr("cy", function(d) { return y(+d.Sales); });
UpdateGraph:
var circles = svg.select('g.data-points').selectAll(".dot")
.data(data.filter(function(d) {
return d.Fruit === fruit;
}));
circles
.enter().append("circle")
.merge(circles).classed('data-point', true)
.attr("r", 3)
.style("fill", "pink").style('stroke', '#000')
.transition().duration(1000)
.attr("cx", function(d) { return x(d.Month); })
.attr("cy", function(d) { return y(+d.Sales); });
Observe the data filtering based on the fruit selected bound to the circles and the transition applied in order to match the transition for the lines.
Always use style for applying fill and not attr. It's a good practice. Adding color:pink wouldn't change the color of the circles but fill would. In addition, I've added a stroke in order to make them visible even with a pink color. You can always change that though.
I would suggest you to add a code snippet every time you ask a question and not provide links. That would be easier for anyone to debug and help fix errors.
Hope this helps. :)

d3js: Update data array and change graph

I have a d3 script for which the data that I have is as shown below:
var data = [{name: "A", rank: 0, student_percentile: 100.0},
{name: "B", rank: 45, student_percentile: 40.3},
{name: "C", rank: 89, student_percentile: 89.7},
{name: "D", rank: 23, student_percentile: 10.9},
{name: "E", rank: 56, student_percentile: 30.3}];
This data array has been fetched from the server.
I have a d3 script given below:
function d3Data(){
data = data.sort(function(x,y){
return d3.ascending(+x.rank, +y.rank);
});
var size = document.getElementById("range").value;
console.log(size);
data = data.slice(0,size);
d3(data);
}
function d3(data){
var margin = 40,
width = 600,
height = 400;
console.log(data);
var xscale = d3.scaleLinear()
.domain(
d3.extent(data, function(d) { return +d.student_percentile; })
)
.nice()
.range([0, width]);
var yscale = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return +d.rank; }))
.nice()
.range([height, 0]);
var xAxis = d3.axisBottom().scale(xscale);
var yAxis = d3.axisLeft().scale(yscale);
var svg = d3.select('.chart')
.append('svg')
.attr('class', 'chart')
.attr("width", width + margin + margin)
.attr("height", height + margin + margin + 10 )
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("opacity", 0.3)
.attr("r", 20)
.style("fill", "blue")
.attr("cx", function(d) {
return xscale(+d.student_percentile);
})
.attr("cy", function(d) {
return yscale(+d.rank);
})
.on('mouseover', function(d, i) {
d3.select(this)
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("r", 32)
.style("fill", "orange")
.style("cursor", "pointer")
.attr("text-anchor", "middle");
texts.filter(function(e) {
return e.rank === d.rank;
})
.attr("font-size", "20px")
})
.on('mouseout', function(d, i) {
d3.select(this).transition()
.style("opacity", 0.3)
.attr("r", 20)
.style("fill", "blue")
.style("cursor", "default");
texts.filter(function(e) {
return e.rank === d.rank;
})
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("font-size", "10px")
});
var texts = svg.selectAll(null)
.data(data)
.enter()
.append('text')
.attr("x", function(d) {
return xscale(+d.student_percentile);
})
.attr("text-anchor", "middle")
.attr("y", function(d) {
return yscale(+d.rank);
})
.text(function(d) {
return +d.student_percentile;
})
.attr("pointer-events", "none")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "red");
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin) + ")")
.style("text-anchor", "middle")
.text("Percentile");
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rank");
$('circle').tipsy({
console.log("tipsy");
gravity: 'w',
html: true,
title: function() {
var d = this.__data__;
return d.name + '<br/>' + d.rank;
}
});
}
$(document).ready(function(){
d3Data();
});
function rangeVal(){
d3Data();
}
function fadeOut() {
svg.selectAll("circle")
.transition()
.style("opacity", 0.3)
.attr("r", 20)
.style("fill", "blue");
}
function handleMouseOver() {
d3.select(this)
.attr("r", 32)
.style("fill", "orange");
}
I call the function d3Data when the document gets loaded and also when button is clicked (rangeVal is the function that is called on button click). On button click, I want to apply different filters on data and then make the graph again. Currently what is happening is I am getting multiple graphs on button click but the existing graph is not getting updated. The current output is as shown: multiple graphs
I just want d3Data() function to update original data array every time button is clicked and then make the graph again. How can I do that?
Each an every time depend upon the data SVG is newly created. So you have to remove the SVG Before Creation
//d3.select("Your Id Name or Your Class Name").select("svg").remove();
In Your Code, I changed follow as
d3.select('.chart').select("svg").remove();
var svg = d3.select('.chart')
I found the solution. I had to make two changes. In function d3data, I was updating the same array again and again, so the data was not getting updated correctly and before calling d3(), I had to remove existing graph.
function d3Data(){
data_sorted = data.sort(function(x,y){
return d3.ascending(+x.rank, +y.rank);
}); // update array and put it in another variable
var size = document.getElementById("range").value;
console.log(size);
data_sliced = data_sorted.slice(0,size);
d3.select('.chart').html(""); //this to remove existing graph
d3(data_sliced);
}

Data not being redrawn after being filtered on d3 bar chart

I've been trying to add a filter to a bar chart. The bar is removed when I click on the legend, but when I try to reactivate the bar is not being redrawn.
Here is a plnk;
http://plnkr.co/edit/GZtErHGdq8GbM2ZawQSD?p=preview
I can't seem to work out the issue - if anyone could lend a hand?
Thanks
JS code;
// load the data
d3.json("data.json", function(error, data) {
var group = [];
function updateData() {
group = [];
var organization = data.organizations.indexOf("Org1");
var dateS = $("#selectMonth").text()
for (var country = 0; country < data.countries.length; country++) {
var date = data.dates.indexOf(dateS);
group.push({
question: data.organizations[organization],
label: data.countries[country],
value: data.values[organization][country][date]
});
}
}
function draw(create) {
updateData();
x.domain(group.map(function(d) {
return d.label;
}));
y.domain([0, 100]);
// add axis
// Add bar chart
var bar = svg.selectAll("rect")
.data(group);
if (create) {
bar
.enter().append("rect")
.attr('x', function(d) {
return x(d.label);
})
.attr('y', function(d) {
return y(d.value);
})
.attr('width', x.rangeBand())
.attr('height', function(d) {
return height - y(d.value);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value");
}
bar
.transition()
.duration(750)
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
// Existing code to draw y-axis:
var legendGroups = d3.select("#legend")
.selectAll(".legendGroup")
.data(group, function(d) {
return d.label; // always try and use a key function to uniquely identify
});
var enterGroups = legendGroups
.enter()
.append("g")
.attr("class", "legendGroup");
legendGroups
.exit()
.remove();
legendGroups
.attr("transform", function(d, i) {
return "translate(10," + (10 + i * 15) + ")"; // position the whole group
});
enterGroups.append("text")
.text(function(d) {
return d.label;
})
.attr("x", 15)
.attr("y", 10);
enterGroups
.append("rect")
.attr("width", 10)
.attr("height", 10)
.attr("fill", function(d) {
return "#bfe9bc";
})
.attr("class", function(d, i) {
return "legendcheckbox " + d.label.replace(/\s|\(|\)|\'|\,|\.+/g, '')
})
.on("click", function(d) {
d.active = !d.active;
d3.select(this).attr("fill", function(d) {
if (d3.select(this).attr("fill") == "#cccccc") {
return "#bfe9bc";
} else {
return "#cccccc";
}
})
var result = group.filter(function(d) {
return $("." + d.label.replace(/\s|\(|\)|\'|\,+/g, '')).attr("fill") != "#cccccc"
})
x.domain(result.map(function(d) {
return d.label;
}));
bar
.select(".x.axis")
.transition()
.call(xAxis);
bar
.data(result, function(d) {
return d.label.replace(/\s|\(|\)|\'|\,|\.+/g, '')
})
.enter()
.append("rect")
.attr("class", "bar")
bar
.transition()
.attr("x", function(d) {
return x(d.label);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
bar
.data(result, function(d) {
return d.label.replace(/\s|\(|\)|\'|\,|\.+/g, '')
})
.exit()
.remove()
});
}
The bar disappear because you totally remove it with
.remove()
What you can do is hide the element when not selected like that :
d3.selectAll('.graph').selectAll("rect").attr("visibility", function(e) {
return e.active ? "hidden" : "";
})
See http://plnkr.co/edit/2UWaZOsffkw1vkdIJuSA?p=preview

d3 lines between dots not working

I have made a scatter plot using d3 and want to connect the dots with lines, I have tried using what people have written online to connect the lines but it does not seem to be working.
function makeGraph(sampleData){
console.log(sampleData);
var vis = d3.select("#svgVisualize");
yMax = d3.max(sampleData, function (point) {return point.y;});
//step 1 : scale the data
xRange = d3.scale.ordinal().domain(sampleData.map(function (d) { return d.x; })).rangePoints([0, 700]);
yRange = d3.scale.linear().range([400, 40]).domain([0, yMax]);
//step 2: scale the axis
xAxis = d3.svg.axis().scale(xRange);
yAxis = d3.svg.axis().scale(yRange).orient("left");
//Step3: append the x and y axis
vis.append('svg:g')
.call(xAxis)
.attr("transform", "translate(90,400)")
.append("text")
.text("Build Model")
.attr("y", 70)
.attr("x", 150);
vis.append('svg:g')
.call(yAxis)
.attr("transform", "translate(90,0)")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x", -130)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of Users");
var circles = vis.selectAll("circle").data(sampleData);
circles
.enter()
.insert("circle")
.attr("cx", function (d) {return xRange(d.x);})
.attr("cy", function (d) { return yRange(d.y); })
.attr("r", 4)
.attr("transform", "translate(90,0)")
.style("fill", "blue");
var lineFunction = vis.line()
.x(function (d) {
return d.x;
})
.y(function (d) {
return d.y;
})
.interpolate("linear");
vis.append("path")
.attr("d", lineFunction(sampleData))
.style("stroke-width", 0.5)
.style("stroke", "rgb(6,120,155)")
.style("fill", "none")
.on("mouseover", function () {
d3.select(this)
.style("stroke", "orange");
})
.on("mouseout", function () {
d3.select(this)
.style("stroke", "rgb(6,120,155)");
});
}
If anyone could help that would be great, I'm still new to d3
Your example is working fine, I had to do the following
Change the line that creates a path generator to d3.svg.line (just like Gerardo's answer)
Create a group for your circles and the path so that you don't have to add .attr("transform", "translate(90,0)") to both of them
Use a data join for the path (although it's not required)
function makeGraph(sampleData) {
var svg = d3.select('#svgVisualize')
yMax = d3.max(sampleData, function(point) {
return point.y;
});
xRange = d3.scale.ordinal().domain(sampleData.map(function(d) {
return d.x;
})).rangePoints([0, 700]);
yRange = d3.scale.linear().range([400, 40]).domain([0, yMax]);
//step 2: scale the axis
xAxis = d3.svg.axis().scale(xRange);
yAxis = d3.svg.axis().scale(yRange).orient("left");
//Step3: append the x and y axis
svg.append('svg:g')
.call(xAxis)
.attr("transform", "translate(90,400)")
.append("text")
.text("Build Model")
.attr("y", 70)
.attr("x", 150);
svg.append('svg:g')
.call(yAxis)
.attr("transform", "translate(90,0)")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -80)
.attr("x", -130)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of Users");
// data
var g = svg.append('g')
.attr('class', 'data')
.attr("transform", "translate(90,0)")
var circles = g.selectAll("circle").data(sampleData);
circles
.enter()
.insert("circle")
.attr("cx", function(d) {
return xRange(d.x);
})
.attr("cy", function(d) {
return yRange(d.y);
})
.attr("r", 4)
.style("fill", "blue");
var lineFunction = d3.svg.line()
.x(function(d) { return xRange(d.x); })
.y(function(d) { return yRange(d.y); })
.interpolate("linear");
var path = g.selectAll('path').data([sampleData])
.enter()
.append('path')
.attr("d", lineFunction)
.style("stroke-width", 0.5)
.style("stroke", "rgb(6,120,155)")
.style("fill", "none")
.on("mouseover", function() {
d3.select(this)
.style("stroke", "orange");
})
.on("mouseout", function() {
d3.select(this)
.style("stroke", "rgb(6,120,155)");
});
}
makeGraph([
{x: 0, y: 100},
{x: 100, y: 100},
{x: 200, y: 200},
{x: 300, y: 100}
])
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="svgVisualize" width="900" height="500" style="position: relative; left: 2%;"></svg>
The line function has to be like this:
var lineFunction = d3.svg.line()
.x(function(d) { return xRange(d.x); })
.y(function(d) { return yRange(d.y); })
.interpolate("linear");

D3 transitions with multiple graphs

I'm trying to add a drop down menu that selects which measure to graph. I have 8 graphs, all graphing the same measure but by different ethnicities. Below is the code, any thoughts on what I'm doing wrong? Right now i get the error exit() is not a function.
Ok i've made some progress with the following, however it's still a little wonky. The graphs are changing but are going off the charts - the yAxis is rescaling to the max of all of the graphs, not the local one.:
function updateGraphs(newData) {
d3.selectAll("svg").each(function(d, i){
eachRace = d.values;
svg = d3.select(this);
yMax = d3.max(eachRace, function(d) { return d[newData]; });
yScale = d3.scale.linear().domain([0, yMax*1.25]).range([height/8, 0]).nice();
yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(5);
line = d3.svg.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return yScale(d[newData]); })
d3.transition().duration(1000).selectAll(".line")
.attr("id", function(d) { return d.key ;})
.attr('class', 'line')
.attr('opacity', .8)
.attr('d', function(d) { return line(d.values); })
d3.selectAll(".y.axis")
.call(yAxis);
});
}
var svgContainer = d3.select("body").selectAll("svg")
.data(data2)
svgContainer.enter()
.append("svg")
.attr("width", 150)
.attr("height", 400)
.attr("transform", "translate(" + margin.left + "," + margin.top+ ")");
d3.selectAll("svg").each(function(d, i){
var eachRace = d.values;
var svg = d3.select(this);
var yMax = d3.max(eachRace, function(d) { return d.app; });
var yScale = d3.scale.linear().domain([0, yMax*1.25]).range([height/8, 0]).nice();
var yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(5);
var line = d3.svg.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return yScale(d.app); })
svg.append("path")
.attr("id", function(d) { return d.key ;})
.attr('class', 'line')
.attr('opacity', .8)
.attr('d', function(d) { return line(d.values); })
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
Thanks JSBob for working with me. I've got it working with:
function updateGraphs(newData) {
d3.selectAll("svg").each(function(d, i){
eachRace = d.values;
svg = d3.select(this);
yMax = d3.max(eachRace, function(d) { return d[newData]; });
yScale = d3.scale.linear().domain([0, yMax*1.25]).range([height/8, 0]).nice();
yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(5);
console.log(yMax);
line = d3.svg.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return yScale(d[newData]); })
svg.transition().duration(1000).select(".line")
.attr("id", function(d) { return d.key ;})
.attr('class', 'line')
.attr('opacity', .8)
.attr('d', function(d) { return line(d.values); });
svg.transition().duration(1000).select(".y.axis")
.call(yAxis);
});
}

Categories