I have some D3 javascript that works just fine. Here is the code if it helps. The trouble is, it loads in place of the javascript, which means I have to put the javascript in the right place in the HTML to position the graph.
what I want to be able to do is have this:
<div class="graph"></div>
and have the javascript inject the graph into that div.
Can anyone pleasse help me understand how to do this.
<!-- 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: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").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(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// 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 + ")");
// Get the data
d3.csv("test3.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(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);
});
var inter = setInterval(function() {
updateData();
}, 5000);
// ** Update data section (Called from the onclick)
function updateData() {
// Get the data again
d3.csv("test3.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Select the section we want to apply our changes to
var svg = d3.select("body").transition();
// Make the changes
svg.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
svg.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
</script>
This line:
d3.select("body")
.append("svg")
is telling d3 to append the svg to the body of the DOM. Try:
d3.select(".graph")
.append("svg")
Which finds your div by class name and appends the svg to that.
Related
As stated by the title, I'm trying to dynamically update multiple line charts using D3. I have combined these two examples (http://bl.ocks.org/d3noob/6bd13f974d6516f3e491 and http://bl.ocks.org/d3noob/5987480). Only the first chart is updating with the second remaining static.
Here is the code:
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
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;
}
</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: 20, bottom: 30, left: 50},
width = 400 - margin.left - margin.right,
height = 220 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%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(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.temperature); });
// Adds the svg canvas
var chart1 = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.csv("output.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.temperature = +d.temperature;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.temperature; })]);
// Add the valueline path.
chart1.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
chart1.append("g")
.attr("class", "y axis")
.call(yAxis);
});
// Adds the svg canvas
var chart2 = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.csv("output2.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.humidity = +d.humidity;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.humidity; })]);
// Add the valueline path.
chart2.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
chart2.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
chart2.append("g")
.attr("class", "y axis")
.call(yAxis);
});
//2 graph sample code ends here
var TemperatureInterval = setInterval(function() {
updateTemperatureData();
}, 5000);
var HumidityInterval = setInterval(function() {
updateHumidityData();
}, 7000);
function updateTemperatureData() {
// Get the data again
d3.csv("output2.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.temperature = +d.temperature;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.temperature; })]);
// Select the section we want to apply our changes to
var chart1 = d3.select("body").transition();
// Make the changes
chart1.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
chart1.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
chart1.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
function updateHumidityData() {
// Get the data again
d3.csv("output2.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.humidity = +d.humidity;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.humidity; })]);
// Select the section we want to apply our changes to
var chart2 = d3.select("body").transition();
// Make the changes
chart2.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
chart2.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
chart2.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
</script>
</body>
Thanks.
Here is a possible approach you can try:
// Adds the svg canvas
var chart1 = d3.select("body")
.append("svg")
.attr("class", "chart-1") // Be a little more specific and give a class or identifier to the chart
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var chart2 = d3.select("body")
.append("svg")
.attr("class", "chart-2") // Be a little more specific and give a class or identifier to the chart
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Add placeholders for the path and axis:
// Chart1 placeholders
chart1.append('path').attr('class', 'line');
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
chart1.append("g")
.attr("class", "y axis");
// Chart2 placeholders
chart2.append('path').attr('class', 'line');
chart2.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
chart2.append("g")
.attr("class", "y axis");
Since we don't have a data source we must simulate one:
function fetchData(selector) {
console.log('fetching');
return new Promise(function(resolve, reject) {
// Create new array, each time a different sized one with random values
var dataLength = Math.floor(Math.random() * 40) + 1;
var data = [];
for (var i = 0; i < dataLength; i++) {
data.push({
date: randomDate('2016-01-01', '2016-12-01'),
temperature: Math.floor(Math.random() * 40) + 1
});
}
setTimeout(function() { // Adding timeout to simulate latency
// resolve our promise with the newly created data and the selector of the chart we want to update
resolve({
data: data,
selector: selector
});
}, 4000)
})
}
function randomDate() { // helper function
var startDate = new Date(2012, 0, 1).getTime();
var endDate = new Date(2015, 0, 1).getTime();
var spaces = (endDate - startDate);
var timestamp = Math.round(Math.random() * spaces);
timestamp += startDate;
return new Date(timestamp);
}
Then let us create a new function which will update our charts:
function updateChart(resolved) { // receiving the resolved object from our promise
var data = resolved.data;
var selector = resolved.selector;
data.forEach(function(d) {
d.date = d.date;
d.temperature = +d.temperature;
});
// sort dates so we don't have issue of line screwing up
data.sort(function(a, b) {
return a.date - b.date;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.temperature;
})]);
// Since both charts have the same behaviour we can do this
var selection = d3.select("body").select(selector).transition();
// Add the valueline path.
selection.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
selection.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
selection.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
}
And finally set our update functions:
var HumidityInterval = setInterval(function() {
fetchData('.chart-1').then(updateChart);
}, 7000);
var TemperatureInterval = setInterval(function() {
fetchData('.chart-2').then(updateChart);
}, 7000);
Here is a plnkr with the working code if you have any doubts:
http://plnkr.co/edit/XlyME4tYlhoW4OgRW1dx?p=preview
I'm using angularjs to get data from facebook
I have written following code.
At the backend,it is giving proper output but for $scope.chart variable but at the front end when I log the data it is showing date:null.I'm uploading images for resprctive data.Backend Data Imagefrontend data showing date Null
<script>
function mychart(){
var dom_el = document.querySelector('[ng-controller="myCtrl"]');
var ng_el = angular.element(dom_el);
var ng_el_scope = ng_el.scope();
var data = ng_el_scope.chart;
//var data = chart;
console.log(data);
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").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(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// 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 + ")");
// Get the data
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(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);
}
</script>
Here is my entire JS code, I'm really just getting started based on some example.
I would like to be able to catch a click event, wether on the whole drawing, wether on the chart line and get y value for it.
onMouseOver and onMouseOut work as intended for now.
Thank you.
//Example may be interesting: http://bl.ocks.org/byrongibson/5232838
var parseDate = d3.time.format("%d-%b-%y").parse;
var svg, d3, x, y, valueline, xAxis, yAxis, width, height;
function CreateSvg()
{
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50};
width = 600 - margin.left - margin.right;
height = 270 - margin.top - margin.bottom;
// Set the ranges
x = d3.time.scale().range([0, width]);
y = d3.scale.linear().range([height, 0]);
// Define the axes
xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
//var rect = d3.selectAll("rect");
// Adds the svg canvas
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 + ")").on("click", click)
;
}
function GetData(data)
{
data.forEach(function(d)
{
d.date = parseDate(d.date);
d.close = +d.close;
});
data.reverse();
data.push({date:parseDate('3-May-12'), close:Math.random() * 1200});
data.push({date:parseDate('4-May-12'), close:Math.random() * 1200});
data.reverse();
return data;
}
function Draw()
{
d3.csv("data.csv", function(error, data) {
data = GetData(data);
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
svg.text("");
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data))
.on("mouseover", onMouseOver)
.on("mouseout", onMouseOut)
.on("click", click)
;
// 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);
});
}
function click() {
alert("onclick");
}
function onMouseOver(){
alert("mouseOver");
};
function onMouseOut(){
alert("mouseOut");
};
CreateSvg();
Draw();
Draw();
I am trying to create a line graph using D3. The data for the graph is stored in an XML file like this:
XML Code:
<record>
<date_value>1376092800000</date_value>
</record>
JavaScript Code:
var record=xmlDoc.getElementsByTagName("record");
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").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(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// 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 + ")");
// Get the data
d3.xml("values.xml", "application/xml", function(data) {
for(var d = 0; d < record.length; d++){
d.date = parseDate(record[d].getElementsByTagName("date_value")[0].childNodes[0].nodeValue);
d.close += 1;
};
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(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);
});
I'm having trouble with the part where I get the data. The x value is the date value and the y value should be increased by one each time. With the current code I have no errors, but only the y axis is showing with no data. I would appreciate any suggestions. Thanks!
I am new to d3.js and am trying to graph three lines on the same plot. I'm reading from a tsv file with four columns: time, x, y, and z. This is accelerometer data. I want to plot the x,y,z columns vs time but can't seem to get it. Any suggestions?
function graph(){
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// 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(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.x); });
var valueline2 = d3.svg.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.y); })
var valueline3 = d3.svg.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.z); });
// 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 + ")");
// Get the data
d3.tsv("data/data2.tsv", function(error, data) {
data.forEach(function(d) {
d.time = +d.time;
d.x = +d.x;
d.y = +d.y;
d.z = +d.z;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return Math.max(d.x, d.y, d.z); })]);
// Add the valueline path.
svg.append("path") // Add the valueline path.
.attr("class", "line")
.attr("d", valueline(data));
svg.append("path") // Add the valueline path.
.attr("class", "line")
.style("stroke", "red")
.attr("d", valueline2(data));
svg.append("path") // Add the valueline path.
.attr("class", "line")
.style("stroke", "green")
.attr("d", valueline3(data));
// Add the X Axis
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});}
You can add each line one at a time to the canvas but it's not very d3 or efficient. It's much better to organise your data in the appropriate way. So the the main issue is the way that the data's been prepared. In the example that Lar's pointed to the data is nested using this block of code:
var series = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
time: d.time,
score: +d[name]
};
})
};
});
And what this does is to create an array of 3 objects. Each object has a key value pair: name and
an array. In each array is another object which contains the time and score information for plotting. It is this last array of object that is passed to the line generator.
I've created a fiddle, here, that's heavily based on the example that Lar's has pointed to and it's commented throughout. The trick at this stage is to inspect the elements and use console.log
Best of luck.