shutterstock / rickshaw

JavaScript toolkit for creating interactive real-time graphs

Home Page:https://shutterstock.github.io/rickshaw

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Axes disappear when weeks should be displayed

champthemudkip opened this issue · comments

I'm displaying a graph that dynamically deals with a wide range of values, and the x axis is all dates. When the graph is only displaying days of the months, hours, years, or months, it's fine, but between days of the month and months it stops rendering both axes. I know this because I'm using the slider to change how much data is displayed, and over certain ranges the slider stops showing movement and the axes aren't displayed.

I'm using the regular axes functions and epoch seconds in by data. any advice would be appreciated.

the below is the code i'm using to render the axes.

       var xAxis_{{device_strip}} = new Rickshaw.Graph.Axis.Time({
            graph:graph_{{device_strip}},
	        timeFixture: new Rickshaw.Fixtures.Time.Local()
        });
        xAxis_{{device_strip}}.render();

        var yAxis_{{device_strip}} = new Rickshaw.Graph.Axis.Y({
            graph:graph_{{device_strip}},
        });
        yAxis_{{device_strip}}.render();

I had the similar issue; the bug is in the Rickshaw.Fixtures.Time.Local fixture. I cannot totally remember how I fixed it, except that I adapted the Local time fixture by diffing it against the UTC time fixture; here is my implementation:

Rickshaw.namespace('Rickshaw.Fixtures.Time.LocalCustom');

Rickshaw.Fixtures.Time.LocalCustom = function() {

	var self = this;

	this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

	this.units = [
		{
			name: 'decade',
			seconds: 86400 * 365.25 * 10,
			formatter: function(d) { return (parseInt(d.getFullYear() / 10, 10) * 10) }
		}, {
			name: 'year',
			seconds: 86400 * 365.25,
			formatter: function(d) { return d.getFullYear() }
		}, {
			name: 'month',
			seconds: 86400 * 30.5,
			formatter: function(d) { return self.months[d.getMonth()] }
		}, {
			name: 'week',
			seconds: 86400 * 7,
			formatter: function(d) { return self.formatDate(d) }
		}, {
			name: 'day',
			seconds: 86400,
			formatter: function(d) { return self.formatDate(d) }
		}, {
			name: '6 hour',
			seconds: 3600 * 6,
			formatter: function(d) { return self.formatTime(d) }
		}, {
			name: 'hour',
			seconds: 3600,
			formatter: function(d) { return self.formatTime(d) }
		}, {
			name: '15 minute',
			seconds: 60 * 15,
			formatter: function(d) { return self.formatTime(d) }
		}, {
			name: 'minute',
			seconds: 60,
			formatter: function(d) { return d.getMinutes() }
		}, {
			name: '15 second',
			seconds: 15,
			formatter: function(d) { return d.getSeconds() + 's' }
		}, {
			name: 'second',
			seconds: 1,
			formatter: function(d) { return d.getSeconds() + 's' }
		}, {
			name: 'decisecond',
			seconds: 1/10,
			formatter: function(d) { return d.getMilliseconds() + 'ms' }
		}, {
			name: 'centisecond',
			seconds: 1/100,
			formatter: function(d) { return d.getMilliseconds() + 'ms' }
		}
	];

	this.unit = function(unitName) {
		return this.units.filter( function(unit) { return unitName == unit.name } ).shift();
	};

	this.formatDate = function(d) {
		return d3.time.format('%b %e')(d);
	};

	this.formatTime = function(d) {
		return d.toString().match(/(\d+:\d+):/)[1];
	};

	this.ceil = function(time, unit) {

		var date, floor, year, offset;

		if (unit.name == 'month') {

			date = new Date(time * 1000);

			floor = new Date(date.getFullYear(), date.getMonth()).getTime() / 1000;
			if (floor == time) return time;

			year = date.getFullYear();
			var month = date.getMonth();

			if (month == 11) {
				month = 0;
				year = year + 1;
			} else {
				month += 1;
			}

			return new Date(year, month).getTime() / 1000;
		}

		if (unit.name == 'year') {

			date = new Date(time * 1000);

			floor = new Date(date.getUTCFullYear(), 0).getTime() / 1000;
			if (floor == time) return time;

			year = date.getFullYear() + 1;

			return new Date(year, 0).getTime() / 1000;
		}
		offset = new Date(time * 1000).getTimezoneOffset() * 60;
		return Math.ceil((time - offset) / unit.seconds) * unit.seconds + offset;
	};
};

Sorry I can't be more helpful/provide a cleaner solution. I believe the bug was in the block that starts if (unit.name == 'day') { in the definition of this.ceil.

I actually seem to be having a problem with the this.formatDate function. "cannot read property 'format' of undefined". Do you remember getting a similar error? Also, you said you thought the issue was in (unit.name == 'day') { but you didn't actually post your implementation of it, can I see it?
Thanks!

Sorry, the reference to the (unit.name == 'day') block wasn't very helpful. I actually commented that block out, and didn't post it with my code. I noticed that the block is present in the Time.Local fixture and not in the Time fixture.

I don't remember seeing a similar error. What I remember was sticking a bunch of console log statements at each point in the Time.Local fixture, and finding that the reason the axis wasn't appearing was that the renderer was shifting all of the tick marks ~1 month into the future. The error only happened if I looked at time frames longer than a couple days but less than a month. There weren't errors; it was just that the tick marks were (incorrectly) placed outside of the charted window.

Your error might be due to a wonky d3 import; it seems like it's returning undefined for d3.time. I've attached the full source file for my time fixture, including imports and the commented out block. It lives in my project directory, and in the file that renders the chart I use it like so:

import Rickshaw from 'rickshaw';
import d3 from 'd3';
require('../rickshawExtensions/timeFixture.js');

var chart = new Rickshaw.Graph( {
      element: this.chartArea,
      width: width-5,
      height: height-80,
      series: series,
      renderer: 'multi',
      interpolation: 'linear',
      stack: false
    });
    new Rickshaw.Graph.Axis.Time( {
      graph: chart,
      timeFixture: new Rickshaw.Fixtures.Time.LocalCustom()
    } );
chart.render();

If other people have this issue, maybe I'll take whack at fixing the issue and submitting as PR to the real Time.Local fixture. I was under a deadline and just needed to get it working.

timeFixture.js.zip

I tried making the changes you did, but they didn't seem to help. It ended up that I was somehow using d3 v4 instead of v3. I fixed the version issue and the axes are now rendering properly.