Add xScale parameter to Graph #346

Closed
wants to merge 2 commits into
from

Projects

None yet

3 participants

Contributor
rbu commented Nov 13, 2013

By creating Rickshaw.Graph({xScale: d3.time.scale()}), users can now
instantiate a Graph with a linear scale that has Date objects as
domain values. This way, a regular Axis.X can render time using D3's
time formatter:

var xAxis = new Rickshaw.Graph.Axis.X({
    graph: graph,
    tickFormat: graph.x.tickFormat()
});
@rbu rbu Add xScale parameter to Graph
By creating Rickshaw.Graph({xScale: d3.time.scale()}), users can now
instantiate a Graph with a linear scale that has Date objects as
domain values. This way, a regular Axis.X can render time using D3's
time formatter:

    var xAxis = new Rickshaw.Graph.Axis.X({
        graph: graph,
        tickFormat: graph.x.tickFormat()
    });
52fdc40
Contributor
rbu commented Nov 13, 2013

We've started using actual d3.time.scale as x axis, as this allows for some more advanced features of D3. I'm curious whether the Axis.X.Time is actually still needed, or can be replaced completely with a d3 scale and the regular axis?

We'd also like to get rid of setting the tickFormatter explicitly, as d3 should do the right thing without setting a tick formatter at all. Not sure if this can be done without extending the API of Axis.X?

Third point, is there any chance to change Rickshaw's assumption that x time values are in seconds? d3 decided to go with microseconds, and combining a d3 time scale and axis works fine, but this breaks the HoverDetail (and others?).
You can obviously do

var hoverDetail = new Rickshaw.Graph.HoverDetail({
    graph: graph,
    xFormatter: function(x) {
        return new Date(x).toUTCString();
    }
});

But it would be interesting to synchronize data formats between d3 and Rickshow in the mid term (even if this is a backwards-incompatible change). What do you think?

@rbu rbu Ensure Graph creates a copy of the xScale before modifying it
xScale is coming from the configuration dictionary which may be
referenced by the Graph creator, or shared with other Graphs. We need to
ensure we copy the scale so that our mutations do not change the object
given to us.
a326825
Contributor
rbu commented Nov 26, 2013

We've noticed that handing in a scale through the config will create a mutable "global" object, and this creates weird interactions where other graphs (such as the mini-previews from the Minimap) will copy the xScale and modify it, affecting the original graph.
On a general note, have you encountered mutable objects in configuration state before, and what is the policy on this, if any?
For example, should mutable config parameters be disallowed (and how do you enforce this?), should objects always make a copy of the config state, or should third parties that need to mimic the object configuration not blindly copy references (then how do you do a minimap)?

@dchester dchester closed this in 958c043 Dec 2, 2013
Contributor
dchester commented Dec 2, 2013

I'm curious whether the Axis.X.Time is actually still needed, or can be replaced completely with a d3 scale and the regular axis?

There's a probable future where Axis.Time is a subclass of Axis.X with an option to use ticks from d3.time.

Third point, is there any chance to change Rickshaw's assumption that x time values are in seconds?

I'd like to see if we can get d3.time and Rickshaw to play more nicely together, but I'd also like to see if we can find a way to do that that doesn't involve Rickshaw changing the assumption that x time values are in seconds.

We'd also like to get rid of setting the tickFormatter explicitly, as d3 should do the right thing without setting a tick formatter at all.

To your point, this would be easier if Rickshaw had millisecond-based epochs. But given that that's not the case, setting tickFormatter seems like the right way to go.

We've noticed that handing in a scale through the config will create a mutable "global" object...

Can you give more details about where/how this causes trouble? In some cases like the "minimap" this seems like a feature more than a bug, since the preview graph inherits the same scale so everything matches up.

I am also interested in this feature. I'd like to show a time-series updating roughly once-per second, and showing a window of samples of about 1-2 minutes. I'd like to also see a timestamp containing the full date and time underneath the fine-grained seconds ticks.

I tried the suggestions above (i.e. use xScale: d3.time.scale() in graph, use Date objects as the x component of a data series element, and use Rickshaw.Graph.Axis.X for the x axis). I'm getting the following error:

[Error] x and y properties of points should be numbers instead of object and number
    (anonymous function) (rickshaw.min.js, line 1)
    forEach
    validateSeries (rickshaw.min.js, line 1)
    initialize (rickshaw.min.js, line 1)
    Graph (rickshaw.min.js, line 1)
    (anonymous function) (r3.html, line 51)
    sampleDummyData (r3.html, line 46)

I would greatly appreciate any ideas on what is going wrong. Here is the source code I am using:

<!doctype html> 
<link rel="stylesheet" href="rickshaw/rickshaw.min.css">
<script src="d3/d3.v3.min.js"></script> 
<script src="rickshaw/rickshaw.min.js"></script> 

<style>
#chart_container {
        position: relative;
        font-family: Arial, Helvetica, sans-serif;
}
#chart {
        position: relative;
        left: 40px;
}
#y_axis {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 40px;
}
</style>


<div id="chart_container">
        <div id="y_axis"></div>
        <div id="chart"></div>
</div>


<script> 


var seriesData = []; 
var max_series_len = 100;

var sampleDummyData = function(f) {
    var d = new Date;
    var milliseconds = (new Date).getTime();
    var epochSecs = milliseconds / 1000;
    if (seriesData.length > max_series_len) {
    seriesData.shift();
    }

    // doesn't work
    seriesData.push({x:d,y: Math.abs(Math.sin(epochSecs))});

    // works, but doesn't format as time.
    // seriesData.push({x:epochSecs,y: Math.abs(Math.sin(epochSecs))});
    f();
}

sampleDummyData(function () {

    var graph = new Rickshaw.Graph( {
    element: document.querySelector("#chart"), 
    width: 300, 
    height: 200,
    renderer: 'line',
    xScale: d3.time.scale(),
    series: [{
        name: 'throughput',
            color: 'steelblue',
            data: seriesData
    }]
    });
    var x_axis = new Rickshaw.Graph.Axis.X( { 
    graph: graph, 
    tickFormat: graph.x.tickFormat()
    } );

    var y_axis = new Rickshaw.Graph.Axis.Y( {
        graph: graph,
        orientation: 'left',
        element: document.getElementById('y_axis'),
    } );

    graph.render();

    setInterval( function() {
    sampleDummyData(function () { graph.render(); });
    }, 1000 );
});


</script> 
@dwt dwt deleted the dwt:x-time-scale branch Jan 14, 2014
Contributor
rbu commented Feb 5, 2014

@dchester:

Can you give more details about where/how this causes trouble? In some cases like the "minimap" this seems like a feature more than a bug, since the preview graph inherits the same scale so everything matches up.

I've opened PR #387 about this.

Contributor
rbu commented Feb 5, 2014

@AndreasVoellmy, have you tried converting the Date objects to integers as milliseconds?

(new Date()) * 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment