0

I recently started working on D3.js. I am developing a interactive multiple line graph with dual x-axis. Please take a look at my work, JSFIDDLE is here : http://jsfiddle.net/dalapati/cdkn14j3/1/

    data1 = [
                    {"date": 1357717800000,"value": "5.6"}, 
                    {"date": 1357718400000,"value": "5.6"}, 
                    {"date": 1357719000000,"value": "6"}, 
                    {"date": 1357719600000,"value": "5.1"},
                    {"date": 1357720200000,"value": "5.3"},
                    //{"date": 1357720800000,"value": "5.4"}
                ];

    data2 = [
                    {"date": 1357714800000,"value": "5.2"},
                    {"date": 1357715400000,"value": "5.2"},
                    {"date": 1357716000000,"value": "5.2"}, 
                    {"date": 1357716600000,"value": "5.1"},
                    {"date": 1357717200000,"value": "5.5"}, 
            ]      

// date manipulation to format UTC to js Date obj           
    data1.forEach(function(d){ d.time = new Date(d.time * 1000);});
    data2.forEach(function(d){ d.time = new Date(d.time * 1000);});

// helpers and constants        
var margin = {"top": 50, "right": 50, "bottom": 50, "left": 100, "axis": 55};
var width = 1500 - margin.left - margin.right;
var height = 580 - margin.top - margin.bottom;
var timeFormat = d3.time.format("%X");

// find data range
var x1Domain = d3.extent(data1, function(d){ return d.date; });
var x2Domain = d3.extent(data2, function(d){ return d.date; });

var x1Min = d3.min(data1, function(d){ return Math.min(d.date); });
var x1Max = d3.max(data1, function(d){ return Math.max(d.date); });

var x2Min = d3.min(data2, function(d){ return Math.min(d.date); });
var x2Max = d3.max(data2, function(d){ return Math.max(d.date); });

var y1Min = d3.min(data1, function(d){ return Math.min(d.value); });
var y1Max = d3.max(data1, function(d){ return Math.max(d.value); });

var y2Min = d3.min(data2, function(d){ return Math.min(d.value); });
var y2Max = d3.max(data2, function(d){ return Math.max(d.value); });

var yMin = (y1Min < y2Min) ? y1Min:y2Min;
var yMax = (y1Max > y2Max) ? y1Max:y2Max;

// scales
var x1Scale = d3.time.scale()
                    .domain(d3.extent(data1, function (d) {
                        return d.date;}))
                    .range([0, width]);
var x2Scale = d3.time.scale()
                    .domain(d3.extent(data2, function (d) {
                        return d.date;}))
                    .range([0, width]);
var yScale = d3.scale.linear()
                    .domain([yMin,yMax]).range([height, 0]); 

// set up axes
var x1Axis = d3.svg.axis()
                    .scale(x1Scale)
                    .orient("bottom")
                    .ticks(5)
                    .tickPadding(5)
                    .tickFormat(timeFormat);
var x2Axis = d3.svg.axis()
                    .scale(x2Scale)
                    .orient("top")
                    .ticks(5)
                    .tickPadding(5)
                    .tickFormat(timeFormat);
var yAxis = d3.svg.axis()
                    .scale(yScale)
                    .orient("left")
                    .ticks(5);
var make_y_axis = function () {
                            return d3.svg.axis()
                                .scale(yScale)
                                .orient("left")
                                .ticks(5);
                        };

// Set up chart type
// create a line function that can convert data into x and y points
var line1 = d3.svg.line().interpolate("basis")
                .x(function (d) {
                    return x1Scale(d.date);
                })
                .y(function (d) {
                    return yScale(d.value);
                });
var line2 = d3.svg.line().interpolate("basis")
                .x(function (d) {
                    return x2Scale(d.date);
                })
                .y(function (d) {
                    return yScale(d.value);
                });


//   Create Zoom feature
var zoomBottom = d3.behavior.zoom()
                    .x(x1Scale)
                    //.y(yScale);
                    .scaleExtent([1,10]);
var zoom = d3.behavior.zoom()
                    .x(x2Scale)
                    //.y(yScale)
                    .scaleExtent([1,10])
                    .on("zoom",zoomed);


// Create Drag behaviour
var drag = d3.behavior.drag()
            .origin(function(d){return d;})
            .on("dragstart", dragstarted)
            .on("drag", dragged)
            .on("dragend", dragended)


// create svg container
var svg = d3.select('#chart')
                .append("svg:svg")
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)
                .append("svg:g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
                .call(zoom);

    svg.append("svg:rect")
                .attr("width", width)
                .attr("height", height)
                .attr("class", "plot");

function draw(){
 //Draw Axes
    svg.append("svg:g")
        .attr("class", "x axis axisBottom")
        .attr("transform", "translate(0, " + height + ")")
        .call(x1Axis);

    svg.append("svg:g")
        .attr("class", "x axis axisTop")
        .attr("transform", "translate(0, 0)")
        .call(x2Axis);

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

// grid plot
    svg.append("g")
        .attr("class", "y grid")
        .call(make_y_axis()
        .tickSize(-width, 0, 0)
        .tickFormat(""));

 // add lines
// do this AFTER the axes above so that the line is above the tick-lines
var clip = svg.append("svg:clipPath")
                    .attr("id", "clip")
                    .append("svg:rect")
                    .attr("x1Scale", 0)
                    .attr("x2Scale", 0)
                    .attr("yScale", 0)
                    .attr("width", width)
                    .attr("height", height);

var chartBody = svg.append("g")
        .attr("clip-path", "url(#clip)");

chartBody.append("svg:path")
        .datum(data1)
        .attr("class", "data1")
        .attr("d", line1(data1))
        .attr("cursor", "move")
        .call(drag);

    chartBody.append("svg:path")
        .datum(data2)
        .attr("class", "data2")
        .attr("d", line2(data2))
        .attr("cursor", "move")
        .call(drag);
    }  

    draw();  
/************************** ADDING ZOOMING FEATURE****************************************/

function zoomed() {
        //console.log(d3.event.translate);
        //console.log(d3.event.scale);        
        zoomBottom.scale(zoom.scale()).translate(zoom.translate());
        svg.select(".axisBottom").call(x1Axis);
        svg.select(".axisTop").call(x2Axis);
        svg.select(".y .axis").call(yAxis);
        svg.select(".grid").call(make_y_axis().tickSize(-width,0,0).tickFormat(""));
        svg.select(".data1").attr("d",line1(data1));
        svg.select(".data2").attr("d",line2(data2));
    }

/***************** Adding Dragging feature*****************************************************/

function dragstarted(d){
    d3.event.sourceEvent.stopPropagation();
    d3.select(this).classed("dragging", true);
}

function dragged(d){
    var lineToMove = d3.event.x;
    console.log(lineToMove);
    d3.select(this)
        .attr("cx", d[0] = d3.event.sourceEvent.x);
}

function dragended(d){
    d3.select(this).classed("dragging", false);
}

As you see that there are two different line graphs, taken from different data sources and drawn with respect to two different x-axis. I am trying to implement dragging behavior to each line graph, where If I select one graph and drag it and keeping the other graph constant, the respective axis values should also get updated.

But when I try to drag the line graph, I am getting a NAN error. I am not sure how to solve this error. Do anyone have any idea where I am going wrong.

Thanks in advance.

Dinesh
  • 9
  • 7

1 Answers1

2

The problem is when you set the origin, because it expect to an object with x and y.

Try:

.origin(function(){ var t = d3.select(this); return { x: t.attr('x') , y: t.attr('y') } })

reference How to set the Origin (drag.origin) for drag behavior in d3 JavaScript library

Community
  • 1
  • 1
Bruno Queiroz
  • 367
  • 2
  • 9
  • Hi Bruno Queiroz, Thank you for the reply. I appreciate your answer. I tried the code snippet that you have posted. The NaN error problem is solved, but still when I try to drag the graph the values are not getting updated. Is there anything else that you can suggest me to achieve the dragging behavior in my graph. Here is the updated Jsfiddle: http://jsfiddle.net/dalapati/cdkn14j3/2/. Thank you! – Dinesh Sep 25 '14 at 23:18
  • Actually if you open the console and start dragging a line, you'll see that the value for lineToMove is changing, but this variable is not being used anywhere in the code – Bruno Queiroz Sep 25 '14 at 23:43
  • The "lineTOMove" variable was created to see the output on the console. But it is assigned to attribute "cx". Ok, in the code I posted I did not update the lineToMove variable which is equal to "d3.event.sourceEvent.x". I guess it is not causing the error here. What I understood is, initially when I zoom the graph the zooming feature is working as expected. Later, when I try to drag any one of the line graph the drag feature is not working, but from the console I can see that the event values are getting updated. I am not sure what's the bug here. – Dinesh Sep 26 '14 at 00:02
  • Check it out http://jsfiddle.net/cdkn14j3/4/ i'm not sure it solves your problem, but it corrects some bugs you had, you still have to find the best approach to update lines data though. – Bruno Queiroz Sep 26 '14 at 00:34
  • Yes, it clears the bugs and it also solves a part my problem, where now I can drag a graph. But I am actually trying to move the graph along with its respective axis. Yes, I should figure out a right approach to solve it. Thanks a lot for helping me. – Dinesh Sep 26 '14 at 15:05
  • Hi Bruno, I am still working on the same project and I got stuck at one point. I was able to drag the line graph along with the its axis, but later when I try to drag the same line graph it resets back to the original position and then the dragging is applied. I see that the problem here is the rescaling of the axis after the dragging behaviour, but I am not sure how exactly it can be done. My work so far: http://jsfiddle.net/dalapati/cdkn14j3/6/ . If the problem is not about the rescaling axis, then how can I solve this issue. Please share your idea to solve this problem. Thank you – Dinesh Oct 09 '14 at 23:16