What does Reset Axes do?

I’m looking for documentation on Reset Axes as I’m completely confused about its behaviour.

I have some large datasets, my default is to plot around 5000 or so points per curve so I resample to that many points, they are stored in “new_data” and I call Plotly.react(PLOTTER, new_data, layout, config) to update. Whatever a user zooms on the x-axis then I trap that zoom request with “onclick”, recompute new_data so that new curves again have around 5000 points, and call REACT again. Very clean and swift user experience.

If I click “Reset Axes”, however, then all bets are off. I can’t work out how Plotly is computing the x-axis range over which to go back to. Sometimes it picks x-axis with no data at all, which is frustrating. Does Plotly store somewhere what it thinks the original axes should have been? Or is it recomputing on the fly what a reset should do.

I had (erroneously) assumed that it would always go back to the axes for the very first call to Plotly.newplot. Maybe some variables therein are getting overwritten…

Thanks in advance, T.

Alternatively to “Random React” is is possible to trap clicking on the little House symbol so that I can, say, have it call newplot with the original layout?

Something like this appears to work: You can use config to delete existing resetScale2D and recreate your own version that can relate back to the original code…

        var config = {                         //let's make this a global
        	  responsive: true,
           displayModeBar: true, 
    	    modeBarButtonsToRemove: ["resetScale2d"],
    	    modeBarButtonsToAdd: [
    	      name: 'resetaxes',
              _cat: 'resetscale',
             title: function (gd) {
                       return _(gd, 'Reset axes');
                    },
              attr: 'zoom',
               val: 'reset',
              icon: Icons.home,
             click: function(gd) {
                       layout.xaxis.range = range_;                              // Use a stored global range
                       layout.xaxis.rangeslider = {range: range_};     // Update rangeslider too 
                       Plotly.newplot(gd, data_, layout_, config);       //Use the original data and layout
                  }
            ]

Ignore. Doesnt work. :frowning:

I did find that this is the code used for resetting the axes though. Maybe some way to adapt that

function handleCartesian(gd, ev) {
  var button = ev.currentTarget;
  var astr = button.getAttribute('data-attr');
  var val = button.getAttribute('data-val') || true;
  var fullLayout = gd._fullLayout;
  var aobj = {};
  var axList = axisIds.list(gd, null, true);
  var allSpikesEnabled = fullLayout._cartesianSpikesEnabled;
  var ax, i;
  if (astr === 'zoom') {
    var mag = val === 'in' ? 0.5 : 2;
    var r0 = (1 + mag) / 2;
    var r1 = (1 - mag) / 2;
    var axName;
    for (i = 0; i < axList.length; i++) {
      ax = axList[i];
      if (!ax.fixedrange) {
        axName = ax._name;
        if (val === 'auto') {
          aobj[axName + '.autorange'] = true;
        } else if (val === 'reset') {
          if (ax._rangeInitial0 === undefined && ax._rangeInitial1 === undefined) {
            aobj[axName + '.autorange'] = true;
          } else if (ax._rangeInitial0 === undefined) {
            aobj[axName + '.autorange'] = ax._autorangeInitial;
            aobj[axName + '.range'] = [null, ax._rangeInitial1];
          } else if (ax._rangeInitial1 === undefined) {
            aobj[axName + '.range'] = [ax._rangeInitial0, null];
            aobj[axName + '.autorange'] = ax._autorangeInitial;
          } else {
            aobj[axName + '.range'] = [ax._rangeInitial0, ax._rangeInitial1];
          }

          // N.B. "reset" also resets showspikes
          if (ax._showSpikeInitial !== undefined) {
            aobj[axName + '.showspikes'] = ax._showSpikeInitial;
            if (allSpikesEnabled === 'on' && !ax._showSpikeInitial) {
              allSpikesEnabled = 'off';
            }
          }
        } else {
          var rangeNow = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])];
          var rangeNew = [r0 * rangeNow[0] + r1 * rangeNow[1], r0 * rangeNow[1] + r1 * rangeNow[0]];
          aobj[axName + '.range[0]'] = ax.l2r(rangeNew[0]);
          aobj[axName + '.range[1]'] = ax.l2r(rangeNew[1]);
        }
      }
    }
  } else {
    // if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y'
    if (astr === 'hovermode' && (val === 'x' || val === 'y')) {
      val = fullLayout._isHoriz ? 'y' : 'x';
      button.setAttribute('data-val', val);
    }
    aobj[astr] = val;
  }
  fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
  Registry.call('_guiRelayout', gd, aobj);
}

Finally, success!! Exactly what I wanted. Two “small” issues. One was that I had to call newPlot() not newplot() - tricky, eh? The other is that newPlot seems to ignore the value assigned to layout.xaxis.range but loves the value assigned to gd._fullLayout.xaxis.range which in my case I have hard coded to a variable PLOTTER…

        var config = {                         //let's make this a global
        	  responsive: true,
           displayModeBar: true, 
    	    modeBarButtonsToRemove: ["resetScale2d","select2d", "lasso2d", "zoomIn2d", "zoomOut2d", "autoScale2d", "toggleSpikelines"],
    	    modeBarButtonsToAdd: [ {
    	      name: 'resetaxes',
              _cat: 'resetscale',
             title: 'Home axes', 
              attr: 'zoom',
               val: 'reset',
              icon: Plotly.Icons.home,                                  // Same icon as before
             click: function() { 
                       layout.xaxis.range = range_;                  // Use stored global, but I think this value is ignored
                       layout.xaxis.rangeslider = {range: range_};   // Update rangeslider too, is this used?
                       PLOTTER._fullLayout.xaxis.range = range_;     // THIS DOES THE WORK
                       bounds[0] = Date.parse(range_[0]);
                       bounds[1] = Date.parse(range_[1]); 
                       new_data = interactiveZoom(bounds[0], bounds[1],Np); // Get my data back for the original range
                       Plotly.newPlot(PLOTTER, new_data, layout)
                  }}
            ]
        }

I’ve been working on plots with large datasets, typically around 5000 points per curve. When users zoom in on the x-axis, I’ve got it covered using the Plotly.react function for smooth updates. However, things get murky when it comes to the “Reset Axes” feature. It seems like the x-axis range selected after a reset is inconsistent and sometimes even chooses a range with no data, which is quite frustrating.

Hi rileykyrie, I had posted a possible solution a few days later. Basically use config to hijack the default call to reset axes and instead call newPlot with whatever you deem appropriate. One bit of trickery was that newPlot ignores a value assigned to layout.xaxis.range but instead you can set gd._fullLayout.xaxis.range to the range that you want, and then call newPlot.

The full code is submitted. It has one “bug”, which I’ve decided is actually a nice feature which is that it turns the “Home” button into something which correctly zooms on the x-axis but doesn’t change at all the y-axis. However, if you then move the mouse into white-space and double click, then that tidies up everything. Kinda nice to have both options…

Regards,