$( function() { var csv_text = 'date,sugar\n\ 2011-01-13T15:42:08,108\n\ 2011-01-14T15:42:08,108\n\ 2011-01-15T15:42:08,108\n\ 2011-01-16T15:42:08,108\n\ 2011-01-17T15:42:08,108\n\ 2011-02-15T20:58:25,121\n\ 2011-02-16T20:58:25,121\n\ 2011-02-17T20:58:25,121\n\ 2011-02-18T20:58:25,121\n\ 2011-02-19T20:58:25,121\n\ 2011-02-20T20:58:25,121\n\ 2011-02-21T20:58:25,121\n\ 2011-03-08T16:55:07,100\n\ 2011-03-08T18:29:12,91\n\ 2011-03-08T19:30:37,94\n\ 2011-03-08T21:34:27,126\n\ 2011-03-09T08:38:01,242\n'; var json = [ ]; json = d3.csv.parse(csv_text); json.forEach(fix_row); var url = './sugars.csv'; myChart.draw_graph(json); d3.csv(url, myChart.update_data); } ); // make these things debuggable from console // var my = { }; function create ( ) { function my ( ) { } var zoomer, x, y ; var width; my.width = function (_) { if (!arguments.length == 0) return width; width = _; return my; } my.prev = function ( ) { var ex = x.domain( ); var end = ex[0]; var start = ex[0] - (ex[1] - ex[0]); x.domain([start, end]); zoomer.x(x); my.render( ); }; my.next = function ( ) { var ex = x.domain( ); var start = ex[1]; var end = new Date(start.getTime( ) + (ex[1] - ex[0])); x.domain([start, end]); zoomer.x(x); my.render( ); }; my.draw_graph = draw_graph; my.update_data = update_data; return my; function draw_graph(data) { var results, chart, dots, margin = 100, w = 8, h = 500, width = $('#lab').width( ), height = $('#lab').height( ), xAxis, yAxis zoom = 40 ; var scaleExtent = [ 0.0101, 200]; // allow some granularity between our notion of scale and d3's // my.zScale = d3.scale.linear( ).domain(scaleExtent).rangeRound( [0, 1000] ); // but force only 8 layout switches // my.zSwitching = d3.scale.linear( ).domain([0,1000]).rangeRound([0,8]); console.log( $('#lab'), $('#lab').width( ) ); $('#lab #test').remove( ); $('#lab').append( $('#clone').clone(true).attr('id', 'test') ); chart = d3.select( '#test' ).append( 'svg' ) .attr( 'class', 'chart' ) .attr( 'width', width ) .attr( 'height', h ) .append('g'); d3.select('svg g') .attr('class', 'container') .attr('transform', 'translate(50, 50)'); var first = d3.time.day.round(data[0].date), last = d3.time.day.round(d3.time.day.offset(first, 7)) ; my.first = first; my.last = last; my.range = d3.time.day.range(first, last); console.log(first, last); x = d3.time.scale() .domain( [first, last] ) .range( [0, width - margin] ) ; // y = d3.scale.linear() y = d3.scale.log() .domain( [30, d3.max( data, function( d ) { return d.sugar; } )] ) .rangeRound( [1, h - margin] ); ; my.x = x; my.y = y; // safety bars var safeties = { low: 70, high: 140, x: x.range()[0], y: (h - margin) - y(140) + .5, width: (width), height: y(140) - y(70) + .5 }; var upper = { low: 140, high: 180, x: x.range()[0], y: (h - margin) - y(180) + .5, width: (width), height: y(180) - y(140) + .5 }; var bars = chart.append('g') .attr('class', 'safety'); bars.append('rect') .attr('class', 'safe-sugar') ; bars.append('rect') .attr('class', 'upper') ; // Bars dots = chart.append('g') .attr('class', 'dots'); dots.selectAll( 'circle' ) .data( data ) .enter().append( 'circle' ) .attr( 'r', '.5ex') .append('title') .attr('class', 'values') // .attr( 'width', w ) // .attr( 'height', function( d ) { return y( d.population ) } ) ; // Axis xAxis = d3.svg.axis() .scale(x) .ticks(my.range.length) .tickSize(12, 1, 1) ; // .tickFormat(d3.time.format('%m/%d/%y')) //.tickSize(25, 18); yAxis = d3.svg.axis() .scale( d3.scale.log() .domain( [30, d3.max( data, function( d ) { return d.sugar || 0; } )] ) .rangeRound( [h - margin, 1] ) .base(12) ) .tickValues([36, 40, 60, 70, 80, 120, 160, 180, 200, 220, 260, 300, 350, 400]) .tickFormat(d3.format("d")) .tickSize(6, 3, 1) .orient('right'); chart.append('g') .attr('class', 'x axis') .attr('transform', 'translate(0, ' + (h - margin) + ')'); chart.append('g') .attr('transform', 'translate('+ (width - margin) + ', 0)') .attr('class', 'y axis'); zoomer = d3.behavior.zoom() // By supplying only .x() any pan/zoom can only alter the x scale. y // scale remains fixed. .x(x) .scaleExtent( scaleExtent ) // .scale( 34 ) .on("zoom",render) ; d3.select("svg").call(zoomer); function extent_lines (selection) { // var begin = x.invert(x(x.invert(width)) - width + margin); // var begin = x.domain( )[0]; var begin = x.invert(0); var end = x.invert(width - margin); var length = y.domain( ); d3.select('.start').text(begin); var diff = moment(begin).from(moment(end)).replace(' ago', ''); d3.select('.range').text(diff); d3.select('.end').text((end)); selection.selectAll('line.begin-line') .data([{x: 0, begin: begin}]) .enter( ) .append('line') .attr('class', 'begin-line') ; selection.select('line.begin-line') .attr('x1', 0) .attr('x2', 0) .attr('y1', y(length[0])) .attr('y2', y(length[1])) .style('stroke-dasharray', '3, 3') .attr('stroke', 'grey') ; selection.selectAll('line.end-line') .data([{x: 0, end: end}]) .enter( ) .append('line') .attr('class', 'end-line') ; selection.select('line.end-line') .attr('x1', width - margin) .attr('x2', width - margin) .attr('y1', y(length[0])) .attr('y2', y(length[1])) .style('stroke-dasharray', '3, 3') .attr('stroke', 'grey') ; return selection; } console.log('f', data.slice(-1).pop( )); var tail = data.slice(-1).pop( ).date; var maxDataWidth = width * (d3.time.day.round(first, tail) / my.range.length - 1); console.log(new Date(tail).getTime( )); var tenth = (tail - first.getTime( )) * .10; var offset = tail var panExtent = { }; panExtent = {x: [ // 0 // x(new Date(first.getTime( ) - tenth)) // x(first) * -1 - margin // x(first) x(first) - margin , // width * 40 maxDataWidth // x(tail) * 2 // x(tail + (tail - first.getTime( ) / 2 )) // x(new Date(tail)) // my.range.length * width * 40 // x(new Date(tail)) // x.invert(new Date(tail.getTime( ))) // width * 20 // (x(last) + x(first) ) // x(last) + x(margin * 2) // (x(last) + x(first)) * 2 // x(width) // width + margin ], y: y.range( ) }; console.log('PANEXTENT', panExtent.x, new Date(tail).getTime( ) - first.getTime( ), tail, x(tail)); function render ( ) { // if the zoom level changes, consider resizing the scales. // var old_zoom = zoom; var t = zoomer.translate( ); // var limit = panLimit(zoomer); // console.log(t, 'constrain?'); // console.log('domain', x.domain( ), x(x.domain( )[0]), x(x.domain( )[1]) ); console.log('constrain?', t, zoomer.scale( )); // console.log('minx', x(first), x.invert(0), x(x.invert(0))); console.log('maxx', x.invert(width-margin), x(x.invert(width))); if (first > x.invert(0)) { t[0] = 0; console.log('constraining LEFT!!!', t); } if (tail < x.invert(width - (margin*2))) { // t[0] = Math.max(t[0], (x(first) - x(tail) + (width )); // t[0] = Math.max(t[0], (x(first) - x(tail) + (width + (margin * 3/2) ))); // t[0] = Math.max(t[0], (x(first) - x(tail) + (margin + margin + x(last) - x(first)))); // t[0] = Math.max(t[0], (x(first) - x(tail) + (margin + margin + x(last) - x(first)))); var maxW = x(first) - x(tail); var pgW = x(first) - x(tail) - t[0] + margin + margin; // t[0] = Math.max(t[0], ((maxW + (x(last) - x(first) )))); t[0] = Math.max(t[0], pgW); // t[0] = Math.max(t[0], margin - width); console.log('constraining RIGHT!!!', t, maxW, pgW); } // t = limit; // console.log(t, 'constrained'); zoomer.translate(t); d3.select('.container').call(extent_lines); if (d3.event) { // zoom = my.zScale( d3.event.scale ); // var i = my.zSwitching(zoom); } bars.selectAll("rect.safe-sugar") .attr( 'x', safeties.x) .attr( 'y', safeties.y) .attr( 'width', safeties.width - (margin * .5)) .attr( 'height', safeties.height) ; bars.selectAll("rect.upper") .attr( 'x', upper.x) .attr( 'y', upper.y) .attr( 'width', upper.width - (margin * .5)) .attr( 'height', upper.height) ; dots.selectAll("circle") .attr( 'cx', function( d, i ) { return x( d.date ) - .5; } ) .attr( 'cy', function( d ) { return (h - margin) - y( d.sugar ) + .5 } ) ; dots.selectAll("title.values") .text( function (d) { return [d.date, d.sugar].join(' '); }); chart.select(".x.axis").call(xAxis); chart.select(".y.axis").call(yAxis); } my.render = render; // Call this to place initially render(); } function update_data(rows) { // console.log('XXX', this, arguments); if (rows) { rows.forEach(fix_row); draw_graph(rows); } } } function fix_row(row, i) { row.date = d3.time.format.iso.parse(row.date); row.sugar = parseInt(row.sugar); // console.log('rows each', this, arguments); }