d3.js Multi-line graph with zoom : line error - javascript

It is my version of "Multi-line graph 4: Toggle" from http://bl.ocks.org/d3noob/e99a762017060ce81c76 but I ran into some problems pls help.
At first the initial graph is correct,
after the zooms, the line would become one which used data for both line and rotated.
I think it is the zoomed functions is not doing right, when I used "d.value" the browser would say "d. is not define"
Here are the codes:
// https://github.com/mbostock/d3/wiki/Ordinal-Scales#category10
var colors = d3.scale.category10();
var margin = {top: 20, right: 30, bottom: 80, left: 85},
width = 900 - margin.left - margin.right,
height = 570 - margin.top - margin.bottom;
// Kind of defining the length and the directions of the axis
var x = d3.scale.linear()
.range([0, width]);
// Since the origin is on the left corner, the y axis of the svg system points down
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.tickPadding(10) // Distance between axis and tick note
.tickSubdivide(true)
.tickFormat(d3.format(".0"))
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickPadding(10)
.tickSize(-width)
.tickSubdivide(true)
.tickFormat(d3.format(".3e")) // https://github.com/mbostock/d3/wiki/Formatting#d3_format
.orient("left");
var valueline = d3.svg.line()
.x(function(d) { return x(d.samples); })
.y(function(d) { return y(d.measurements); });
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([0.1, 50])
.on("zoom", zoomed);
// Adding svg canvas
var svg = d3.select("body").append("svg")
.call(zoom)
.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
data=[{"SAMPLES":"1","MEASUREMENTS":"2","ID":"ch1"},{"SAMPLES":"2","MEASUREMENTS":"3","ID":"ch1"},{"SAMPLES":"1","MEASUREMENTS":"4","ID":"ch2"},{"SAMPLES":"3","MEASUREMENTS":"5","ID":"ch1"},{"SAMPLES":"2","MEASUREMENTS":"6","ID":"ch2"}];
data.forEach(function(d) {
d.samples = +d.SAMPLES;
d.measurements = +d.MEASUREMENTS;
});
console.log(data);
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.samples; }));
y.domain([0, d3.max(data, function(d) { return d.measurements; })]);
// Creating X axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Drawing notations
svg.append("g")
.attr("class", "x axis")
.append("text")
.attr("class", "axis-label")
.attr("x", (width - margin.left)/2)
.attr("y", height + margin.top + 45)
.text('Samples');
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", (-margin.left) + 10)
.attr("x", -height/2)
.text('Volts');
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
// Nest the entries by channel id (ID)
var dataNest = d3.nest()
.key(function(d) {return d.ID;})
.entries(data);
// set the colour scale
var color = d3.scale.category10();
// Auto spacing for the legend
legendSpace = width/dataNest.length;
// Loop through each IDs / key to draw the lines and the legend labels
dataNest.forEach(function(d,i) {
svg.append("path")
.attr("class", "line")
.attr("clip-path", "url(#clip)")
.style("stroke", function() { // Add the colours dynamically
return d.color = color(d.key);
})
.attr("id", 'tag'+d.key.replace(/\s+/g, '')) // assign ID
.attr("d", valueline(d.values))
.style("stroke", function(){return d.color = color(d.key);});
// Adding legends
svg.append("text")
// Setting coordinates and classes
.attr("x", (legendSpace/2)+i*legendSpace)
.attr("y", height + (margin.bottom/2)+ 5)
.attr("class", "legend")
// Setting colors
.style("fill",function(){
return d.color = color(d.key);
})
// Setting 'click' events
.on("click", function(){
// Determine if current line is visible
var active = d.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements based on the ID
d3.select("#tag"+d.key.replace(/\s+/g, ''))
.transition().duration(600)
.style("opacity", newOpacity);
// Update whether or not the elements are active
d.active = active;
})
.text(function() {
if (d.key == '28-00043b6ef8ff') {return "Inlet";}
if (d.key == '28-00043e9049ff') {return "Ambient";}
if (d.key == '28-00043e8defff') {return "Outlet";}
else {return d.key;}
})
})
// Zoom specific updates
function zoomed() {
svg.select(".x.axis")
.transition().duration(500)
.call(xAxis);
svg.select(".y.axis")
.transition().duration(500)
.call(yAxis);
svg.selectAll('path.line')
.transition().duration(500)
.attr('d', valueline(data));
}
body {
font: 12px Arial;
margin: 50px;
}
.axis path {
fill: none;
stroke: #bbb;
stroke-width: 2;
shape-rendering: crispEdges;
}
.axis text {
fill: #555;
}
.axis line {
fill: none;
stroke-width: 1;
stroke: #e7e7e7;
shape-rendering: crispEdges;
}
.axis .axis-label {
font-size: 14px;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: middle;
}

You need to bind your paths to your data, so you can call valueLine with the correct data for the path when zooming.
Use d3 data and enter functions when adding a new path:
// choose all .line objects and append a path which is not already binded
// by comparing its data to the current key
svg.selectAll(".line").data([d], function (d) {
return d.key;
})
.enter().append("path")
.attr("class", "line")
Then when you zoom, change the d attribute path by calling valueLine with the path's correct values:
svg.selectAll('path.line')
.transition().duration(500)
.attr('d', function(d) {
return valueline(d.values)
});
Plunker

Related

D3 shift ticks to align with axis

I'm trying to create a d3 line chart that displays monthly data. The chart draws but the ticks on the x-axis are shifted and don't align with the data points drawn. As you can see from the screenshot, the ticks are shifted right from the data points.
I've tried figuring out how to transform-translate the ticks but without luck. Thanks!
Here is a screenshot.
Here is my code:
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 50, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleBand().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.average); });
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#chart")
.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
var data = [{"month":"january","average":0},{"month":"february","average":0},{"month":"march","average":0},{"month":"april","average":9.0},{"month":"may","average":892.0},{"month":"june","average":10.0},{"month":"july","average":92.0},{"month":"august","average":9281.0},{"month":"september","average":8402.0},{"month":"october","average":823213.0},{"month":"november","average":82.0},{"month":"december","average":0}];
x.domain(data.map(function(d) { return d.month; }));
y.domain([0, d3.max(data, function(d) { return d.average; })]);
// Add the valueline path.
console.log(valueline);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the X Axis
svg.append("g")
.attr("class", "e4rAxis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
// xAxisElement.selectAll(".tick").attr("transform", "translate(0,-5)");
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.month); })
.attr("cy", function(d) { return y(d.average); });
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.axis path, .axis line {
fill: none;
shape-rendering: crispEdges;
}
.line {
}
.area {
fill: steelblue;
opacity: 0.5;
}
.dot {
fill: steelblue;
stroke: steelblue;
stroke-width: 1.5px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500" id="chart"></svg>
There are 2 ways to fix your issue.
Hard code and set some padding for your line and circles so they align with your x axis.
// define the line
var valueline = d3.line().x(function(d) {
return x(d.month) + 38; //set x padding of 38
})
.y(function(d) {
return y(d.average);
});
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) {
return x(d.month) + 38;
})
.attr("cy", function(d) {
return y(d.average);
});
JSFiddle - https://jsfiddle.net/L5dctwsz/
Use d3.scaleTime since you're dealing with month names which will solve the issue.
Set it like the following:
var x = d3.scaleTime().range([0, width]);
Parse the month and then set the domain:
// parse the date / time
var parseMonth = d3.timeParse("%B");
data.forEach(function(d) {
d.month = parseMonth(d.month);
});
x.domain(d3.extent(data, function(d) {
return d.month;
}));
JSFiddle - https://jsfiddle.net/pm7gLn2u/

Fixed center with dynamic axis scaling

EDIT:
I have edited my code. Now you can directly run the code inside stackoverflow.
Last day, I started with D3 and having some problems in fixing the dynamic axis. I am new to D3 :)
I am trying to build the following.
I am trying to plot some 2d streaming data with D3. Data comes in through web sockets. So, as the data comes in I plot the new points. I have been somewhat succesful in that but my axis are not behaving as expected.
As the new data comes in, I rescale my x and y axis. Forexample if (20,100) comes in the following plot then I will scale my y-axis to accomadate 100. But if you look closely the point where x and y axis meet is not on 0. Initially it was on (0,0) but after rescaling it went way below. How can I fix the center a (0,0) while dynamically rescaling my axis ?
Can you please help me. Here is my code.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
div.bar {
display: inline-block;
width: 20px;
height: 75px; /* We'll override this later */
background-color: teal;
}
/* Format X and Y Axis */
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<svg width="960" height="500"></svg>
<br/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var dataset = []; // Initialize empty array
var numDataPoints = 15; // Number of dummy data points
for(var i=0; i<numDataPoints; i++) {
dataset.push([getRandomInt(-80,80), getRandomInt(-60,60)]); // Add new number to array
}
var width = 1020;
var height = 840;
var xScale = d3.scale.linear().domain([d3.min(dataset, function(d) { return d[0]; } ), d3.max(dataset, function(d) { return d[0]; })])
.range([20, width - 20 * 2])
.nice();
var yScale = d3.scale.linear().domain([d3.min(dataset, function(d) { return d[1]; } ), d3.max(dataset, function(d) { return d[1]; })])
.range([height - 20, 20])
.nice();
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yAxis = d3.svg.axis().scale(yScale).orient("left");
var svg = d3.select("svg")
.attr("height",height)
.attr("width", width)
.append("g")
.attr("transform","translate(20,20)")
.attr("width", width- 40)
.attr("height", height- 40);
// x-axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
//.attr("transform", "translate(0," + (height - 20) + ")")
//.style("position", "fixed")
.call(xAxis);
//y-axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
//.attr("transform", "translate(" + 20 + ",0)")
//.style("position", "fixed")
.call(yAxis);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle") // Add circle svg
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) { // Circle's Y
return yScale(d[1]);
})
.attr("r", 2)
.call(updatePlotWithSocketData);
function updatePlotWithSocketData() {
console.log(dataset.length);
for(var i=0; i<numDataPoints; i++) {
dataset.push([getRandomInt(-200,200), getRandomInt(-100,100)]); // Add new number to array
}
// Update scale domains
xScale.domain([d3.min(dataset, function(d) { return d[0]; } ), d3.max(dataset, function(d) { return d[0]; })]);
yScale.domain([d3.min(dataset, function(d) { return d[1]; } ), d3.max(dataset, function(d) { return d[1]; })]);
xScale.range([20, width - 20 * 2]).nice();
yScale.range([height - 20, 20]).nice();
// Update old points to the new scale
svg.selectAll("circle")
.transition()
.duration(1000)
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) {
return yScale(d[1]); // Circle's Y
});
// Update circles
svg.selectAll("circle")
.data(dataset) // Update with new data
.enter()
.append("circle")
.transition() // Transition from old to new
.duration(1000) // Length of animation
.each("start", function() { // Start animation
d3.select(this) // 'this' means the current element
.attr("fill", "red") // Change color
.attr("r", 5); // Change size
})
.delay(function(d, i) {
return i / dataset.length * 500; // Dynamic delay (i.e. each item delays a little longer)
})
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) {
return yScale(d[1]); // Circle's Y
})
.each("end", function() { // End animation
d3.select(this) // 'this' means the current element
.transition()
//.duration(500)
.attr("fill", "black") // Change color
.attr("r", 2); // Change radius
});
/* force.on("tick", function() {
nodes[0].x = width / 2;
nodes[1].y = height / 2;
}); */
/* var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yAxis = d3.svg.axis().scale(yScale).orient("left"); */
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
// Update Y Axis
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
setTimeout(updatePlotWithSocketData, 3000);
}
// Socker code
/*
var socket;
var registered = false;
function startDataReceptionFromSocket() {
console.log("opening socket");
//on http server use document.domain instead od "localhost"
//Start the websocket client
socket = new WebSocket("ws://localhost:8887/");
//When the connection is opened, login.
socket.onopen = function() {
console.log("Opened socket.");
//register the user
};
socket.onmessage = function(a) {
//process the message here
console.log("received message socker => : " + a.data);
var message = JSON.parse(a.data);
console.log("message =>" + message.message);
var numbers = message.message.split(" ")
dataset.push([numbers[0],numbers[1]]);
updatePlotWithSocketData();
}
socket.onclose = function() { console.log("Closed socket."); };
socket.onerror = function() { console.log("Error during transfer."); };
}
// On document load, open the socket connection
(function() {
startDataReceptionFromSocket();
})();*/
</script>
It seems to me that the solution is setting the translate again when you update the axes.
Right now, in your code, the domains (for the x and y scales) are changing, but the axes are translated using the original xScale(0) and yScale(0) positions. This demo reproduces the problem:
var margin = 10;
var width = 250, height = 250;
var svg = d3.select("#mydiv")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scale.linear()
.range([margin, width - margin])
.domain([-10, 10]);
var yScale = d3.scale.linear()
.range([margin, height - margin])
.domain([-10, 10]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.append("g").attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
d3.select("#btn").on("click", randomize);
function randomize(){
xScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
yScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
svg.selectAll(".x.axis")
.transition()
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.call(yAxis);
}
.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>
<button id="btn">Randomize</button>
<div id="mydiv"></div>
Now the solution. The next demo has the same code, but updating the positions like this:
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
Check the demo:
var margin = 10;
var width = 250, height = 250;
var svg = d3.select("#mydiv")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scale.linear()
.range([margin, width - margin])
.domain([-10, 10]);
var yScale = d3.scale.linear()
.range([margin, height - margin])
.domain([-10, 10]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.append("g").attr("class", "y axis")
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
d3.select("#btn").on("click", randomize);
function randomize(){
xScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
yScale.domain([Math.floor(Math.random()*50)*(-1),Math.floor(Math.random()*50)]);
svg.selectAll(".x.axis")
.transition()
.duration(1000)
.attr("transform", "translate(0," + yScale(0) + ")")
.call(xAxis);
svg.selectAll(".y.axis")
.transition()
.duration(1000)
.attr("transform", "translate(" + xScale(0) + ",0)")
.call(yAxis);
}
.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>
<button id="btn">Randomize</button>
<div id="mydiv"></div>

d3js multigraph auto update

I could really use some help fixing a d3js related problem.. I did everything I know and googled everywhere but still can’t figure out the solution. I’ve got a simple database as shown below.
when executing the code it reads all the data and everything is fine, when it’s doing it’s refresh it’s updating the temperature graph but not the humidity... I tried many things including adding valueline2 to the update function but it still doesn't work... Any help would be much appreciated thanks.
dtg | temperature | hum
2016-03-02 09:14:00 23 40
2016-03-02 09:10:00 22 45
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 14px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
/* Addon 5,6,7 - part 1*/
.grid .tick {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 60, bottom: 50, left: 50},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Parse the date / time
/* var parseDate = d3.time.format("%d-%b-%y").parse; */
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").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(6);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(8);
// Define the line
var valueline = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.dtg); })
.y(function(d) { return y(d.temperature); });
// Define the 2nd line -- Addon 9 part 1
var valueline2 = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.dtg); })
.y(function(d) { return y(d.hum); });
// Adds the svg canvas
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 + ")");
// Addon 5,6,7 - part 2
function make_x_axis() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(8)
}
function make_y_axis() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(8)
}
// Get the data
d3.json("data.php", function(error, data) {
data.forEach(function(d) {
d.dtg = parseDate(d.dtg);
d.temperature = +d.temperature;
d.hum = +d.hum; // Addon 9 part 3
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.dtg; }));
y.domain([0, d3.max(data, function(d) { return Math.max(d.temperature, d.hum)+5; })]);
// y.domain([0, d3.max(data, function(d) { return d.temperature; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the valueline2 path - Addon 9 part 2
svg.append("path")
.attr("class", "line")
.style("stroke", "red")
.attr("d", valueline2(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Addon 4
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
// Addon 5,6,7 - part 3
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat("")
)
svg.append("g")
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
)
});
var inter = setInterval(function() {
updateData();
}, 5000);
// ** Update data section (Called from the onclick)
function updateData() {
// Get the data again
d3.json("data.php", function(error, data) {
data.forEach(function(d) {
d.dtg = parseDate(d.dtg);
d.temperature = +d.temperature;
d.hum = +d.hum;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.dtg; }));
y.domain([0, d3.max(data, function(d) { return Math.max(d.temperature, d.hum) + 5; })]); // Addon 9 part 4
// Select the section we want to apply our changes to
var svg = d3.select("body").transition();
// Make the changes
svg.select(".line").duration(750).attr("d", valueline(data));
svg.select("x.axis").duration(750).call(xAxis);
svg.select("y.axis").duration(750).call(yAxis);
});
}
</script>
</body>
You need to give different names to your different lines. Right now both lines only have the same class: ".line".
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("id", "line1") //new id
.attr("d", valueline(data));
// Add the valueline2 path - Addon 9 part 2
svg.append("path")
.attr("class", "line")
.attr("id", "line2") //new id
.style("stroke", "red")
.attr("d", valueline2(data));
Then in the update, you need to actually update both lines:
// Make the changes
svg.select("#line1").duration(750).attr("d", valueline(data)); //update line 1
svg.select("#line2").duration(750).attr("d", valueline2(data)); //update line 2
svg.select("x.axis").duration(750).call(xAxis);
svg.select("y.axis").duration(750).call(yAxis);
Another more "D3-friendly" approach would be to give the temperature data to your first path, the humidity data to the second one, and then call a variant of valueline on each path, each one with their respective data.

d3 append a svg with constant area

I have a svg element to which I want to append an area whose end-points are constant/calculated already. Now If I call this area by passing data to it and then plotting it as function over x-axis it works fine and I also see the color filled-area. However if I mark all end points eg: x0,x1,y0,y1 then the area does not show or doesnt fill color. Is it possible to append constant area to svg and if yes then how? The previous question was deviating so I was asked to make a new question which is specific.Previous Question
Created a fiddle here to show my problem : Problem
var percentArea = d3.svg.area()
// do not want to use this .x(function(d){return x(d.date);})
.x0(0)
.x1(width)
.y0(0)
.y1(height);
svg.append("path")
.datum(data)
.attr("class","area")
.attr("d",percentArea);
Note if possible i do not want to pass data by setting datum here as end points are constant.
I wouldn't try to use the d3.svg.area() path generator for what you're trying to do. If all you want is a background fill you should be able to do that with a simple rectangle. Here's my attempt from your fiddle:
svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.style("fill", "orange");
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
var percentArea = d3.svg.area()
// do not want to use this .x(function(d){return x(d.date);})
.x0(0)
.x1(width)
.y0(0)
.y1(height);
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 + ")");
d3.tsv("data.tsv", function(error, data) {
data = [
{
"date":"1-May-12",
"close":582.13
},
{
"date":"30-Apr-12",
"close":583.98
},
{
"date":"27-Apr-12",
"close":603
},
{
"date":"26-Apr-12",
"close":607.7
},
{
"date":"25-Apr-12",
"close":610
},
{
"date":"24-Apr-12",
"close":560.28
},
{
"date":"23-Apr-12",
"close":571.7
},
{
"date":"20-Apr-12",
"close":572.98
},
{
"date":"19-Apr-12",
"close":587.44
},
{
"date":"18-Apr-12",
"close":608.34
},
{
"date":"17-Apr-12",
"close":609.7
},
{
"date":"16-Apr-12",
"close":580.13
},
{
"date":"13-Apr-12",
"close":605.23
},
{
"date":"12-Apr-12",
"close":622.77
}
];
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
//svg.append("path")
// .datum(data)
// .attr("class", "area")
/// .attr("d", area);
svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.style("fill", "orange");
//THIS PART MAKES AREA OF CONSTANT SIZE BUT NEED TO PASS DATA TO IT.
svg.append("path")
.datum(data)
.attr("class","area")
.attr("d",percentArea);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
});
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.area {
fill: steelblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div class="plotgraph"></div>

D3 Line Chart Show/Hide Element, Variable Not Defined Error?

I'm using code similar to this one, and actually plugged in this code to see if I get the same error and I do. This is the code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: start;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 40, bottom: 70, left: 50},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale().range([0, width]);
var y0 = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxisLeft = d3.svg.axis().scale(y0)
.orient("left").ticks(5);
var yAxisRight = d3.svg.axis().scale(y1)
.orient("right").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y0(d.close); });
var valueline2 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y1(d.open); });
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 + ")");
var data = [
{"date":"9-Apr-12","close":436,"open":9.04},
{"date":"7-Apr-12","close":221,"open":4.02},
{"date":"5-Apr-12","close":113,"open":9.02},
{"date":"4-Apr-12","close":64,"open":32.05},
{"date":"3-Apr-12","close":29,"open":46.03},
{"date":"2-Apr-12","close":18,"open":51.03}
];
// Get the data
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
d.open = +d.open;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y0.domain([0, d3.max(data, function(d) {
return Math.max(d.close); })]);
y1.domain([0, d3.max(data, function(d) {
return Math.max(d.open); })]);
svg.append("path")
.attr("class", "line")
.attr("id", "blueLine")
.attr("d", valueline(data));
svg.append("path")
.attr("class", "line")
.style("stroke", "red")
.attr("id", "redLine")
.attr("d", valueline2(data));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// edit the Y Axis Left
svg.append("g")
.attr("class", "y axis")
.style("fill", "steelblue")
.attr("id", "blueAxis")
.call(yAxisLeft);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + " ,0)")
.style("fill", "red")
.attr("id", "redAxis")
.call(yAxisRight);
// Add the blue line title
svg.append("text")
.attr("x", 0)
.attr("y", height + margin.top + 10)
.attr("class", "legend")
.style("fill", "steelblue")
.on("click", function(){
// Determine if current line is visible
var active = blueLine.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements
d3.select("#blueLine").style("opacity", newOpacity);
d3.select("#blueAxis").style("opacity", newOpacity);
// Update whether or not the elements are active
blueLine.active = active;
})
.text("Blue Line");
// Add the red line title
svg.append("text")
.attr("x", 0)
.attr("y", height + margin.top + 30)
.attr("class", "legend")
.style("fill", "red")
.on("click", function(){
// Determine if current line is visible
var active = redLine.active ? false : true ,
newOpacity = active ? 0 : 1;
// Hide or show the elements
d3.select("#redLine").style("opacity", newOpacity);
d3.select("#redAxis").style("opacity", newOpacity);
// Update whether or not the elements are active
redLine.active = active;
})
.text("Red Line");
</script>
</body>
I'm getting errors saying that 'blueline' and 'redline' are not defined.
Where do I define those?
I've looked at similar code where the html, css, and js are separated into their own files which is how I've done it also, and other than what I see in the code above I don't find those variables used anyplace else or defined beyond what is there.
I made a plunk with the code you have there and everything works just fine. Let me explain a little of what is going on though.
Instead of using HTML that is already there, D3 will often generate its own (in fact it has to generate its own to do the visualizations). You see this with statements like the following:
svg.append("path")
.attr("class", "line")
.attr("id", "blueLine")
.attr("d", valueline(data));
D3 just created a path element and appended it to the svg element it created earlier. It gives this particular element an id of blueLine which it uses later to apply styles. It did not exist in any HTML prior to this script running. D3 created it.

Categories