Search code examples
javascriptplotlyplotly.js

plotly.js y-axis same position of zero-line


I have a bunch of scatter-lines that I want to plot. There are some that have values between 0 and 10k and some where the range is -1 -> 1. The value-ranges are different for different applications, so I can not set them to fixed values.

My question is kind of similar to this one: plotly two y-axis same position of zero-line But I have negative values on my second Y-Axis, thus the rangemode='tozero' is not working for me.

I want both axis to share the same X-axis/have the y=0 value on the same position. I made a codepen with my code. https://codepen.io/kesslerf/pen/mdQJQbY

Here is the code: HTML:

<head>
    <!-- Load plotly.js into the DOM -->
    <script src='https://cdn.plot.ly/plotly-2.16.1.min.js'></script>
</head>
<body>
    <div id='ts' class='ts' style="width:1200px; height:800px; background-color: aliceblue"></div>
</body>

And the JS-Part:

var data_all = {
    "b02": {
        "min_value": 562,
        "max_value": 1007,
        "values": [
            {
                "date": "2018-07-01 00:00:00",
                "value": "587"
            },
            {
                "date": "2018-07-04 00:00:00",
                "value": "562"
            },
            {
                "date": "2018-07-16 00:00:00",
                "value": "794"
            },
            {
                "date": "2018-07-19 00:00:00",
                "value": "966"
            },
            {
                "date": "2018-07-24 00:00:00",
                "value": "1007"
            }
        ]
    },
    "b04": {
        "min_value": 1091,
        "max_value": 2549,
        "values": [
            {
                "date": "2018-07-01 00:00:00",
                "value": "1091"
            },
            {
                "date": "2018-07-04 00:00:00",
                "value": "1175"
            },
            {
                "date": "2018-07-16 00:00:00",
                "value": "1553"
            },
            {
                "date": "2018-07-19 00:00:00",
                "value": "2409"
            },
            {
                "date": "2018-07-24 00:00:00",
                "value": "2549"
            }
        ]
    },
    "evi": {
        "min_value": 0,
        "max_value": 1,
        "values": [
            {
                "date": "2018-07-01 00:00:00",
                "value": "1"
            },
            {
                "date": "2018-07-04 00:00:00",
                "value": "1"
            },
            {
                "date": "2018-07-16 00:00:00",
                "value": "0"
            },
            {
                "date": "2018-07-19 00:00:00",
                "value": "0"
            },
            {
                "date": "2018-07-24 00:00:00",
                "value": "0"
            }
        ]
    },
    "ndvi": {
        "min_value": 0,
        "max_value": 1,
        "values": [
            {
                "date": "2018-07-01 00:00:00",
                "value": "1"
            },
            {
                "date": "2018-07-04 00:00:00",
                "value": "0"
            },
            {
                "date": "2018-07-16 00:00:00",
                "value": "0"
            },
            {
                "date": "2018-07-19 00:00:00",
                "value": "0"
            },
            {
                "date": "2018-07-24 00:00:00",
                "value": "0"
            }
        ]
    }
}

var unpack = function(rows, key) {
                return rows.map(function(row) { return row[key]; })
        };

var layout = {
        autosize: false,
        width: 1000,
        height: 420,
        margin: {
                l:50,
                r:150,
                b: 50,
                t: 50,
                pad: 4
                },
        xaxis: {title: 'Datum',
                type: 'date',
                tickformat: '%d-%m-%y'},
        yaxis: {title: 'Wert',
                side: 'left'},
        yaxis2: {
                title: 'yaxis2 title',
                overlaying: 'y',
                side: 'right',
                },
        legend: {
                    x: 1.1,
                    y: 0
                 }
}

var plotly_config = {
        displaylogo: false,
        showSendToCloud: true,
        };


var traces = [];

for (var key1 in data_all){
        var idx_data = data_all[key1]['values'];
        var dates = unpack(idx_data,'date').map(n => n.substr(0,10));
        var values_dates = unpack(idx_data,'value').map(n => parseFloat(n));
        if (key1.substr(0,1) !== 'b') {
            var cur_col = 'red';
                var trace = {
                        x: dates,
                        y: values_dates,
                        name: key1,
                        xaxis: 'x',
                        yaxis: 'y2',
                        type: 'scatter'
                        };
                traces.push(trace);
        }else {                   
                var cur_col = 'blue';
                var trace = {
                        x: dates,
                        y: values_dates,
                        name: key1,
                        xaxis: 'x',
                        yaxis: 'y',
                        type: 'scatter'
                        };
                traces.push(trace);
        }
}


Plotly.newPlot('ts', traces, layout, plotly_config);    


$('.ts')[0].on('plotly_hover', function(data){
        if(data.points[0].data.yaxis=='y'){
                layout.yaxis.linewidth = 5;
        }else if (data.points[0].data.yaxis == 'y2'){
                layout.yaxis2.linewidth = 5;
        }

        Plotly.update('ts',{},layout);
});

$('.ts')[0].on('plotly_unhover', function(data){
        layout.yaxis.linewidth = 1;
        layout.yaxis2.linewidth = 1;
        Plotly.update('ts',{},layout);
});


Solution

  • I just wrote a function to calculate it, added a possibility to use it on zoom/relayout using the new x-range if it is in Date-Format.

    var adjustYAxis = function(traces,layout,new_x_min, new_x_max){
        var y1_max = 0;
        var y1_min = 0;
        var y2_max = 0;
        var y2_min = 0;    
    
        for (let t=0;t<traces.length;t++){
            var curtrac = traces[t];
    
            if(new_x_min && new_x_max){
                var dates_act = curtrac.x.map(n => (new Date(n) > new Date(new_x_min) && new Date(n) < new Date(new_x_max)));
                var relevant_vals = curtrac.y.slice(dates_act.indexOf(true),dates_act.lastIndexOf(true));
            }else{
                relevant_vals = curtrac.y;
            }
            if(traces[t].yaxis === 'y'){
                if (Math.max(...relevant_vals) > y1_max){
                    y1_max = Math.max(...relevant_vals);
                }
                if (Math.min(...relevant_vals) < y1_min){
                    y1_min = Math.min(...relevant_vals);
                }
            }else{
                if (Math.max(...relevant_vals) > y2_max){
                    y2_max = Math.max(...relevant_vals);
                }
                if (Math.min(...relevant_vals) < y2_min){
                    y2_min = Math.min(...relevant_vals);
                }
            }
        }
        if(y1_max > 1){
            y1_max = y1_max - y1_max%250 + 250;
            y2_max = y2_max - y2_max%0.2 + 0.2;
            y2_min = y2_min - y2_min%0.2 - 0.2;
            if(y2_min < y1_min){
                y1_min = -1 * Math.abs(y2_min) * Math.abs(y1_max) / Math.abs(y2_max);    
            }
        }else{
            y1_max = 1;
            y1_min = 0;
        }
    
    
        layout.yaxis.autorange = false;
        layout.yaxis2.autorange = false;
        layout.xaxis.autorange = false;
    
        layout.yaxis.range = [y1_min, y1_max];
        layout.yaxis2.range = [y2_min, y2_max];
        if(new_x_min && new_x_max){
            layout.xaxis.range = [new_x_min, new_x_max];
        }
        layout.shapes[0].y0 = y1_min;
        layout.shapes[0].y1 = y1_max;
    
        return layout;
    };