Related
Below is the code I used. This code is working fine to me, but problem is when the out put comes for the below code, some part of the graph doesn't show in x axis. Some parts are hidden because of the x axis length is not enough to show. So I decided to make this graph scrollable and also I tried a code to make this scrollable.But It didnt work.
please help me to sort out this.
I used this reference :- http://computationallyendowed.com/blog/2013/01/21/bounded-panning-in-d3.html
graph :
HTML CODE:
<div class="row">
<div class="col-sm-12">
<div class="lineChart1" style=" overflow: scroll">
<svg width="960" height="500" style=" overflow: scroll"></svg>
</div>
</div>
</div>
JAVASCRIPT CODE:
function createLineChart() {
var number=1;
var data = [ { label: "Execution 1 - buddhika#gmail.com",
x: ["1","2","2","3","3","4","4","5","5","6","6","7","7","8","8","9","9","10","10","11","11","12","12"],
y: ["3","3","3","3","3","3","2","2","3","3","3","3","3","3","3","3","3","3","2","2","3","3","3","3"] }] ;
var xy_chart = d3_xy_chart()
.width(960)
.height(500)
.xlabel("TCS")
.ylabel("STATUS");
var svg = d3.select(".lineChart" + number).append("svg")
.datum(data)
.call(xy_chart);
function d3_xy_chart() {
var width = 640,
height = 480,
xlabel = "X Axis Label",
ylabel = "Y Axis Label";
function chart(selection, svg) {
selection.each(function (datasets) {
//
// Create the plot.
//
var margin = {top: 20, right: 80, bottom: 30, left: 50},
innerwidth = width - margin.left - margin.right,
innerheight = height - margin.top - margin.bottom;
var x_scale = d3.scale.linear()
.range([0, innerwidth])
.domain([d3.min(datasets, function (d) {
return d3.min(d.x);
}),
d3.max(datasets, function (d) {
return d3.max(d.x);
})]);
var y_scale = d3.scale.linear()
.range([innerheight, 0])
.domain([d3.min(datasets, function (d) {
return d3.min(d.y);
}),
d3.max(datasets, function (d) {
return d3.max(d.y);
})]);
var color_scale = d3.scale.category10()
.domain(d3.range(datasets.length));
var x_axis = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickFormat(function (d, i) {
if (d % 1 == 0) {
return parseInt(d)
} else {
return " "
}
});
var y_axis = d3.svg.axis()
.scale(y_scale)
.orient("left")
.tickFormat(function (d, i) {
if (d == "1") {
return "NOT EXECUTED"
} else if (d == "2") {
return "FAILED"
} else if (d == "3") {
return "PASSED"
} else {
return " "
}
});
var x_grid = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickSize(-innerheight)
.tickFormat("");
var y_grid = d3.svg.axis()
.scale(y_scale)
.orient("left")
.tickSize(-innerwidth)
.tickFormat("");
var draw_line = d3.svg.line()
.interpolate("linear")
.x(function (d) {
return x_scale(d[0]);
})
.y(function (d) {
return y_scale(d[1]);
});
var svg = d3.select(this)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_grid);
svg.append("g")
.attr("class", "y grid")
.call(y_grid);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_axis)
.append("text")
.attr("dy", "-.71em")
.attr("x", innerwidth)
.style("text-anchor", "end")
.text(xlabel);
svg.append("g")
.attr("class", "y axis")
.call(y_axis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.style("text-anchor", "end")
.text(ylabel);
var data_lines = svg.selectAll(".d3_xy_chart_line")
.data(datasets.map(function (d) {
return d3.zip(d.x, d.y);
}))
.enter().append("g")
.attr("class", "d3_xy_chart_line");
data_lines.append("path")
.attr("class", "line")
.attr("d", function (d) {
return draw_line(d);
})
.attr("stroke", function (_, i) {
return color_scale(i);
});
data_lines.append("text")
.datum(function (d, i) {
return {name: datasets[i].label, final: d[d.length - 1]};
})
.attr("transform", function (d) {
return ( "translate(" + x_scale(d.final[0]) + "," +
y_scale(d.final[1]) + ")" );
})
.attr("x", 3)
.attr("dy", ".35em")
.attr("fill", function (_, i) {
return color_scale(i);
})
.text(function (d) {
return d.name;
});
// scrolling code START
var xscale = d3.scale.linear().domain([0, 12]).range([0, 12]),
yscale = d3.scale.linear().domain([0, 100]).range([innerheight, 0]);
var line = d3.svg.line()
.x(function(d) { return xscale(d[0]); })
.y(function(d) { return yscale(d[1]); })
.interpolate('basis');
svg.append('g')
.datum(datasets)
.append('path')
.attr('class', 'data')
.attr('d', line);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 1])
.x(xscale)
.on('zoom', function() {
svg.select('.data').attr('d', line)
});
svg.call(zoom);
// Scrolling code END
});
}
i found the issue in here. issue is in json data what you provided. that all json data stored as strings not as integers. all integer numbers with in the double quotes. that mean it become as a string. when the max number taking for the x axis from the json data, it take the 9 as max number. it happen because 9 is the max number when numbers are strings. because of that graph not going to continue after 9. but expected value is 12. no need the scrolling code here. simply can make the graph scrollable by adding css entry in div ("overflow: scroll")
So I've found a half dozen posts regarding this error but none seem to resolve my issue.
The data as it comes from firebase:
data = [{percent:24,year:1790},....]
var parseDate = d3.time.format("%Y").parse;
// so I want to convert the year to a 'date' for d3 time scale
data.forEach(function(d) {
d.year = parseDate(d.year.toString());
d.percent = +d.percent;
});
which then the data looks like
console.log(data);
[{percent:24,year:Fri Jan 01 1790 00:00:00 GMT-0500 (EST)}...]
my x scale
var x = d3.time.scale().range([0, this.width]);
my x domain
x.domain(d3.extent(data, function(d){return d.year; }));
my line
var line = d3.svg.line()
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.percent); });
then my chart (there is a base chart that this is extending)
this.chart.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
Which when added has the exception ...
Error: <path> attribute d: Expected number,
UPDATE
So here is the working code using sample data the code is more or less a copy paste from d3 example site using a csv instead of tsv
This works fine
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatDate = d3.time.format("%Y");
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 line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
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.csv("./data/debt-public-percent-gdp.csv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
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 ($)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
function type(d) {
d.date = formatDate.parse(d.date);
d.close = +d.close;
return d;
}
This does not work
Using the same datasource here is my 'Base Chart'
export class BaseChart{
constructor(data,elem,config = {}){
var d3 = require('d3');
this.data = data;
this.svg = d3.select('#'+elem).append('svg');
this.margin = {
left: 30,
top: 30,
right: 30,
bottom: 30,
};
let width = config.width ? config.width : 600;
let height = config.height ? config.height : 300;
this.svg.attr('height', height);
this.svg.attr('width', width);
this.width = width - this.margin.left - this.margin.right;
this.height = height - this.margin.top - this.margin.bottom;
this.chart = this.svg.append('g')
.attr('width', this.width)
.attr('height', this.height)
.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
}
}
Chart that Extends the Base Chart
export class FedDebtPercentGDP extends BaseChart{
constructor(data, elem, config){
super(data, elem, config);
var x = d3.time.scale().range([0, this.width]);
var y = d3.scale.linear().range([this.height, 0]);
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
this.chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + this.height + ")")
.call(xAxis);
//
this.chart.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 ($)");
this.chart.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
}
}
The code that calls the chart
d3.csv('./data/debt-public-percent-gdp.csv', type, function(error, data) {
new FedDebtPercentGDP(data, "debt-to-gdp", {width: '100%'});
});
The data
date,close
1790,30
1791,29
1792,28
1793,24
1794,22
1795,19
1796,16
1797,17
1798,16
1799,16
1800,15
1801,13
1802,14
1803,14
1804,13
1805,11
1806,10
1807,10
1808,9
1809,7
1810,6
1811,6
1812,7
1813,8
1814,9
1815,10
1816,10
1817,8
1818,7
1819,7
1820,8
1821,9
1822,8
1823,8
1824,8
1825,7
1826,6
1827,6
1828,5
1829,4
1830,3
1831,2
1832,1
1833,0
1834,0
1835,0
1836,0
1837,0
1838,1
1839,0
1840,0
1841,1
1842,1
1843,2
1844,1
1845,1
1846,1
1847,2
1848,2
1849,3
1850,2
...
There are quite a few problems with the code that you've provided. I guess you did not paste it all. The excerpt you pasted definitely does not have this.chart defined, for example, so it is not possible to reconstruct your error message.
That being said, it is still possible to identify the main problem:
The error message says it all: as you have parsed d.year and turned it into a string, but the path needs a number as an argument there is a 'type mismatch'-kind of error in your code.
You can see how your data looks like in the console log that you've provided. year is turned into a string.
I would recommend leaving d.year as it is, and if you really need, you can create a new attribute called for example date if you need the date in that format. So if it is needed for displaying the date, you can use that one, while year can be used for path calculation in its original format.
I'm trying to build a multi series bar chart using d3 but running into problems due to the sparse nature of the dataset.
I want to force the x-axis to have a tick for every day, even if there is no data. The test data I have can have data points that are weeks apart so I'm expecting wide areas with no bars - which is fine.
I thought I could force the xAxis to use a set of predefined ticks using the tickValues array, but these leads to very strange display of overlaying the text for each day on top of days that do have some data.
I've included a screenshot of what I mean.
I get the feeling I'm supposed to do something when calculating the width of the bars but can't figure out what that might be.
Code:
var data = [];
var tickValues = [];
var max = _.max(chartData.tabular, function(assessment) { return assessment.dateUTC; });
var min = _.min(chartData.tabular, function(assessment) { return assessment.dateUTC; });
var iter = moment.twix(min.dateUTC, max.dateUTC).iterate("days");
while(iter.hasNext()){
var momentObj = iter.next();
var assessment = _.find(chartData.tabular, {'date': momentObj.format('DD/MM/YYYY')});
tickValues.push(momentObj.valueOf());
if(assessment != null){
if(assessment.type == 'calculated'){
data.push({date: momentObj.valueOf(), calculated: assessment.score, manual: null});
}
if(assessment.type == 'manual'){
data.push({date: momentObj.valueOf(), calculated: null, manual: assessment.score});
}
}
}
log(data);
var margin = {top: 20, right: 55, bottom: 30, left: 40},
width = $('#cahai-chart').width() - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickValues(tickValues)
.tickFormat(function(d){return d3.time.format('%d/%m/%y')(new Date(d))});
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#001c9c","#101b4d","#475003","#9c8305","#d3c47c"]);
var svg = d3.select("#cahai-chart 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 labelVar = 'date';
var varNames = d3.keys(data[0]).filter(function (key) { return key !== labelVar;});
color.domain(varNames);
data.forEach(function (d) {
var y0 = 0;
d.mapping = varNames.map(function (name) {
return {
name: name,
label: d[labelVar],
y0: y0,
y1: y0 += +d[name]
};
});
d.total = d.mapping[d.mapping.length - 1].y1;
});
x.domain(data.map(function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.total; })]);
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("Score");
var selection = svg.selectAll(".series")
.data(data)
.enter().append("g")
.attr("class", "series")
.attr("transform", function (d) { return "translate(" + x(d.date) + ",0)"; });
selection.selectAll("rect")
.data(function (d) { return d.mapping; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function (d) { return y(d.y1); })
.attr("height", function (d) { return y(d.y0) - y(d.y1); })
.style("fill", function (d) { return color(d.name); })
.style("stroke", "grey")
.on("mouseover", function (d) { showPopover.call(this, d); })
.on("mouseout", function (d) { removePopovers(); })
var legend = svg.selectAll(".legend")
.data(varNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(55," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 10)
.attr("width", 10)
.attr("height", 10)
.style("fill", color)
.style("stroke", "grey");
legend.append("text")
.attr("x", width - 12)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) { return d; });
function removePopovers () {
$('.popover').each(function() {
$(this).remove();
});
}
function showPopover (d) {
$(this).popover({
title: d.name,
placement: 'auto top',
container: 'body',
trigger: 'manual',
html : true,
content: function() {
return "Date: " + d3.time.format('%d/%m/%y')(new Date(d.label)) +
"<br/>Score: " + d3.format(",")(d.value ? d.value: d.y1 - d.y0); }
});
$(this).popover('show')
}
An ordinal scale will always show as many ticks are there are values in the domain. You just need to pass the full array of dates as the domain.
Replace this line
x.domain(data.map(function (d) { return d.date; }));
with this
x.domain(tickValues);
It looks like you have everything else set up correctly, so this will space the bars out along the axis and make them slimmer.
I'm trying to build this trend component that is able to zoom and pan in data fetched with d3.json. First off, here's my code:
<script>
var margin = { top: 20, right: 80, bottom: 20, left: 50 },
width = $("#trendcontainer").width() - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var format = d3.time.format("%Y-%m-%d").parse;
var x = d3.time.scale()
.domain([-width / 2, width / 2])
.range([0, width]);
var y = d3.scale.linear()
.domain([-height / 2, height / 2])
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width);
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.value);
});
var svg = d3.select(".panel-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 + ")")
.call(zoom);
d3.json('#Url.Action("DataBlob", "Trend", new {id = Model.Unit.UnitId, runId = 5})', function(error, json) {
$('#processing').hide();
color.domain(d3.keys(json[0]).filter(function(key) {
return key !== "Time" && key !== "Id";
}));
data.forEach(function(d) { var date = new Date(parseInt(d.Time.substr(6))); d.Time = date; });
var instruments = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.Time,
value: +d[name]
};
})
};
});
x.domain(d3.extent(data, function(d) { return d.Time; }));
y.domain([
d3.min(instruments, function(c) {
return d3.min(c.values, function(v) {
return v.value;
});
}),
d3.max(instruments, function(c) {
return d3.max(c.values, function(v) {
return v.value;
});
})
]);
svg.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var instrument = svg.selectAll(".instrument")
.data(instruments)
.enter().append("g")
.attr("class", "instrument");
instrument.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) { return color(d.name); });
instrument.append("text")
.datum(function(d) {
return {
name: d.name,
value: d.values[d.values.length - 1]
};
})
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.value) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
svg.select(".line")
.attr("class", "line")
.attr("d", line);
};
var make_x_axis = function() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5);
};
var make_y_axis = function() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
};
</script>
Problem here being that the zooming / panning does not interact with my lines. The just stay the same, 'below' the zoomable / pannable grid. Also, one of the lines disappear when trying to zoom / pan and my console says the following:
Error: Problem parsing d="" - referring to the following snippet, last line:
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
svg.select(".line")
.attr("class", "line")
.attr("d", line);
};
Here's the content of my json result from the controller:
[{"Weight":0.0,"Speed":59.9,"Depth":362.24000,"Time":"2014-04-09T10:01:23","Id":0},{"Weight":10.0,"Speed":59.9,"Depth":394.07000,"Time":"2014-04-09T10:01:56","Id":1},{"Weight":971.0,"Speed":70.1,"Depth":425.84650,"Time":"2014-04-09T10:02:28","Id":2},{"Weight":0.0,"Speed":-29.9,"Depth":422.07465,"Time":"2014-04-09T10:03:00","Id":3},{"Weight":1321.0,"Speed":-21.6,"Depth":406.32840,"Time":"2014-04-09T10:03:32","Id":4},{"Weight":-6.0,"Speed":-30.0,"Depth":390.57880,"Time":"2014-04-09T10:04:04","Id":5},{"Weight":3.0,"Speed":59.9,"Depth":404.50380,"Time":"2014-04-09T10:04:36","Id":6},{"Weight":609.0,"Speed":59.9,"Depth":435.79380,"Time":"2014-04-09T10:05:08","Id":7},{"Weight":1.0,"Speed":59.9,"Depth":467.95280,"Time":"2014-04-09T10:05:40","Id":8},{"Weight":-2149.0,"Speed":34.6,"Depth":498.61060,"Time":"2014-04-09T10:06:12","Id":9},{"Weight":2.0,"Speed":59.9,"Depth":529.83060,"Time":"2014-04-09T10:06:44","Id":10}]
Trend looks like this in my view now, but the actual lines don't zoom or pan. Only the overlaying grid (black lines) does;
For simplicitys sake, I've considered just starting over, following the original example found here, but I really struggle with placing json-loaded data into this.
Hopefully, someone can help me figure this out :)
Thanks to Lars, I was finally able to solve this. Ultimately, I had to do some changes to my controller in addition to using this fiddle which assigns the scales to the zoom behaviour after setting the domains, so it returns a json string to my view like this:
string json = JsonConvert.SerializeObject(Model.Trend.ToArray());
return Json(json, JsonRequestBehavior.AllowGet);
This was due to the fact that some errors appeared when zooming / panning on the array returned without being serialized before returned to the view.
Also had to do the following for it to work:
d3.json('#Url.Action("DataBlob", "Trend", new {id = Model.Unit.UnitId, runId = 5})', function(error, tmparray) {
var json = JSON.parse(tmparray);
...
)};
If this step is skipped, my data will not be displayed properly for some reason, being squeezed to the left side of my graph.
I have a graph that correctly plots several lines based on a sensor's serial number. The problem is that I need it to transition to a new dataset and the data will overlap. Here's what I have so far......
var thresholdTemp = 72;
var minutes = 5;
var transInterval;
//Main function to create a graph plot
function plot(date1, date2, interval) {
var data;
//If we define a date search parameter then we don't want to have it load interactive
if (date1 == undefined && date2==undefined) {
data = loadMinutesJSON(minutes);
} else {
data = searchJSON(date1, date2, interval);
}
var margin = {top: 20, right: 80, bottom: 60, left: 50},
width = 960 - margin.left - margin.right ,
height = 500 - margin.top - margin.bottom;
//Re-order the data to be more usable by the rest of the script
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
//Set the X domain to exist within the date and times given
var x = d3.time.scale().domain([getMinDate(data), getMaxDate(data)]).range([0, (width)]);
//Y Axis scale set to start at 45 degrees and go up to 30 degrees over highest temp
var y = d3.scale.linear()
.domain([
45,
getMaxTemp(data) + 10
])
.range([height, 0]);
//Set up the line colors based on serial number, this generates a color based on an ordinal value
var color = d3.scale.category20().domain(d3.keys(data).filter(function(key) { return key;}));
//.domain(d3.keys(data[0]).filter(function(key) { return key === 'serial';}));
//Define where the X axis is
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%b %d %H:%M:%S"));
//Define where the Y axis is
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
//When called creates a line with the given datapoints
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.date);})
.y(function(d) { return y(d.reading); });
//An extra line to define a maximum temperature threshold
var threshold = d3.svg.line()
.x(function(d) { return x(d.date);})
.y(function(d) { return y(thresholdTemp); });
//Append the SVG to the HTML element
var svg = d3.select("#plot").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 + ")");
//Define the clipping boundaries
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", (width + margin.left + margin.right))
.attr("height", height + margin.top + margin.bottom);
//Add the X axis
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", ".15em")
.attr("transform" , function (d) {return "rotate(-35)"});
//Add the Y axis and a label denoting it as temperature in F
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)");
//Create the lines based on serial number
var serial = svg.selectAll(".serial")
.data(data);
var serials = serial.enter().append("g")
.attr("class", "serial");
//Add the extra line for a threshold and align it with the current time
var threshElement = svg.selectAll(".thresh")
.data(data)
.enter().append("g")
.attr("class", ".thresh");
//Add the path to draw lines and clipping so they stay within bounds
var path = serial.append("path")
.attr("clip-path", "url(#clip)")
.attr("class", "line")
.attr("d", function (d) {return line(d.values);})
.style("stroke", function(d,i) {return color(i);});
//Custom path to add a line showing the temperature threshold
var threshpath = threshElement.append("path")
.attr("clip-path", "url(#clip)")
.attr("class", "line")
.attr("d", function(d) { return threshold(d.values);})
.style("stroke", "red");
//Add a label to the end of the threshold line denoting it as the threshold
threshElement.append("text")
.attr("transform", "translate(" + x(getMaxDate(data)) + "," + y(thresholdTemp) + ")")
.attr("x", 3)
.attr("dy", ".35em")
.text("Threshold");
serial.exit().remove();
//Add the legend
//plotLegend(data);
//Load in the new data and add it to the SVG
function transition() {
data = loadMinutesJSON(minutes);
x.domain([getMinDate(data), getMaxDate(data)]);
y.domain([45, getMaxTemp(data) + 10]);
d3.select(".x.axis")
.transition()
.duration(500).call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform" , function (d) {return "rotate(-35)"});
d3.select(".y.axis").transition().duration(500).call(yAxis);
serial.data(data).enter().append("g").attr("class", "serial");
d3.selectAll("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values);})
.style("stroke", function(d,i) { return color(i);});
}
if(date1 == undefined && date2 == undefined) {
//Set the transition loop to run every 30 seconds
transInterval = setInterval(transition,30000);
}
}
//Set the new time in minutes and re-draw the graph
function setMinutes(newminutes) {
if (transInterval) {
clearInterval(transInterval);
}
d3.selectAll("svg").remove();
console.log("Setting new minutes " + newminutes);
minutes=newminutes;
plot();
}
//Search the database for data between 2 dates and create a new plot of it
function searchDB(date1, date2, intervalInMinutes) {
if (transInterval) {
clearInterval(transInterval);
}
d3.selectAll("svg").remove();
plot(date1, date2, intervalInMinutes);
console.log("Processing Search ");
}
//Quick function to determine the maximum date in a dataset
function getMaxDate(data) {
var arr = [];
for (x in data) {
arr.push(d3.max(data[x].values, function(d) { return d.date;}));
}
return d3.max(arr);
//return d3.max(data[0].values, function(d) { return d.date;});
}
//Calculate the minimum data
function getMinDate(data) {
var arr = [];
for (x in data) {
arr.push(d3.min(data[x].values, function(d) { return d.date;}));
}
return d3.min(arr);
//return d3.min(data[0].values, function(d) { return d.date;});
}
//Calculate the upper maximum temperature
function getMaxTemp(data) {
var arr = [];
for (x in data) {
arr.push(d3.max(data[x].values, function(d) { return d.reading;}));
}
return d3.max(arr);
//return d3.max(data, function(d) { return d3.max(data.values, function(d) {return d.reading})});
}
Here's examples of the data:
First
[{"serial":"2D0008017075F210","values":[{"date":"2013-08-23T20:43:46.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:44:16.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:44:46.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:16.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:47.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:17.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:47.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:17.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:47.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:17.000Z","reading":76.1,"elevation":null,"room":null,"system":null}]},{"serial":"1D00080170496D10","values":[{"date":"2013-08-23T20:43:46.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:44:16.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:44:46.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:16.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:47.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:17.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:47.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:17.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:47.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:17.000Z","reading":73.4,"elevation":null,"room":null,"system":null}]},{"serial":"380008017037ED10","values":[{"date":"2013-08-23T20:43:46.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:44:16.000Z","reading":75.2,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:44:46.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:16.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:47.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:17.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:47.000Z","reading":75.2,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:17.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:47.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:17.000Z","reading":74.3,"elevation":null,"room":null,"system":null}]}]
And then transitioning to:
[{"serial":"2D0008017075F210","values":[{"date":"2013-08-23T20:44:46.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:16.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:47.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:17.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:47.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:17.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:47.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:17.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:47.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:49:17.000Z","reading":76.1,"elevation":null,"room":null,"system":null}]},{"serial":"1D00080170496D10","values":[{"date":"2013-08-23T20:44:46.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:16.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:47.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:17.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:47.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:17.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:47.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:17.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:47.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:49:17.000Z","reading":73.4,"elevation":null,"room":null,"system":null}]},{"serial":"380008017037ED10","values":[{"date":"2013-08-23T20:44:46.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:16.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:45:47.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:17.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:46:47.000Z","reading":75.2,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:17.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:47:47.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:17.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:48:47.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"date":"2013-08-23T20:49:17.000Z","reading":74.3,"elevation":null,"room":null,"system":null}]}]
UPDATE:
I've modified the code quite a bit, but the jsfiddle is here http://jsfiddle.net/8cguF/1/
I'm not sure why the fiddle doesn't work, I tested it on my webserver and it works fine. But what I'm trying to do is construct a graph with the data1 variable in there and then transition it to the data2 variable.