Просмотр исходного кода

graph: better generation of y-axis ticks for log-scale

If there are too many ticks generated for the y-axis (which can occur
for log scale 2, with a small y-min and a large max), then the ticks
will be regenerated using larger jumps between the ticks.

This also handles the case when y-min is set to 0. Previously, y-min of
0 was ignored as zero is not a valid value for log scale. Now the tick
generator approximates zero by setting min to 0.1.

Ref #8244

Ref #8516
Daniel Lee 8 лет назад
Родитель
Сommit
1b79e17970

+ 34 - 4
public/app/plugins/panel/graph/graph.ts

@@ -506,6 +506,8 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
           return;
         }
 
+        const minSetToZero = axis.min === 0;
+
         if (axis.min < Number.MIN_VALUE) {
           axis.min = null;
         }
@@ -556,10 +558,17 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
         }
 
         if (Number.isFinite(min) && Number.isFinite(max)) {
-          axis.ticks = [];
-          var nextTick;
-          for (nextTick = min; nextTick <= max; nextTick *= axis.logBase) {
-            axis.ticks.push(nextTick);
+          if (minSetToZero) {
+            axis.min = 0.1;
+            min = 1;
+          }
+
+          axis.ticks = generateTicksForLogScaleYAxis(min, max, axis.logBase);
+          if (minSetToZero) {
+            axis.ticks.unshift(0.1);
+          }
+          if (axis.ticks[axis.ticks.length - 1] > axis.max) {
+            axis.max = axis.ticks[axis.ticks.length - 1];
           }
           axis.tickDecimals = decimalPlaces(min);
         } else {
@@ -567,7 +576,28 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
           delete axis.min;
           delete axis.max;
         }
+      }
+
+      function generateTicksForLogScaleYAxis(min, max, logBase) {
+        let ticks = [];
+
+        var nextTick;
+        for (nextTick = min; nextTick <= max; nextTick *= logBase) {
+          ticks.push(nextTick);
+        }
+
+        const maxNumTicks = Math.ceil(ctrl.height/25);
+        const numTicks = ticks.length;
+        if (numTicks > maxNumTicks) {
+          const factor = Math.ceil(numTicks/maxNumTicks) * logBase;
+          ticks = [];
+
+          for (nextTick = min; nextTick <= (max * factor); nextTick *= factor) {
+            ticks.push(nextTick);
+          }
+        }
 
+        return ticks;
       }
 
       function decimalPlaces(num) {

+ 54 - 0
public/app/plugins/panel/graph/specs/graph_specs.ts

@@ -176,6 +176,60 @@ describe('grafanaGraph', function() {
     });
   });
 
+  // y-min set 0 is a special case for log scale,
+  // this approximates it by setting min to 0.1
+  graphScenario('when logBase is log 10 and y-min is set to 0 and auto min is > 0.1', function(ctx) {
+    ctx.setup(function(ctrl, data) {
+      ctrl.panel.yaxes[0].logBase = 10;
+      ctrl.panel.yaxes[0].min = '0';
+      data[0] = new TimeSeries({
+        datapoints: [[2000,1],[4 ,2],[500,3],[3000,4]],
+        alias: 'seriesAutoscale',
+      });
+      data[0].yaxis = 1;
+    });
+
+    it('should set min to 0.1 and add a tick for 0.1 and tickDecimals to be 0', function() {
+      var axisAutoscale = ctx.plotOptions.yaxes[0];
+      expect(axisAutoscale.transform(100)).to.be(2);
+      expect(axisAutoscale.inverseTransform(-3)).to.be(0.001);
+      expect(axisAutoscale.min).to.be(0.1);
+      expect(axisAutoscale.max).to.be(10000);
+      expect(axisAutoscale.ticks.length).to.be(6);
+      expect(axisAutoscale.ticks[0]).to.be(0.1);
+      expect(axisAutoscale.ticks[5]).to.be(10000);
+      expect(axisAutoscale.tickDecimals).to.be(0);
+    });
+  });
+
+  graphScenario('when logBase is log 2 and y-min is set to 0 and num of ticks exceeds max', function(ctx) {
+    ctx.setup(function(ctrl, data) {
+      const heightForApprox5Ticks = 125;
+      ctrl.height = heightForApprox5Ticks;
+      ctrl.panel.yaxes[0].logBase = 2;
+      ctrl.panel.yaxes[0].min = '0';
+      data[0] = new TimeSeries({
+        datapoints: [[2000,1],[4 ,2],[500,3],[3000,4], [10000,5], [100000,6]],
+        alias: 'seriesAutoscale',
+      });
+      data[0].yaxis = 1;
+    });
+
+    it('should regenerate ticks so that if fits on the y-axis', function() {
+      var axisAutoscale = ctx.plotOptions.yaxes[0];
+      expect(axisAutoscale.min).to.be(0.1);
+      expect(axisAutoscale.ticks.length).to.be(8);
+      expect(axisAutoscale.ticks[0]).to.be(0.1);
+      expect(axisAutoscale.ticks[7]).to.be(262144);
+      expect(axisAutoscale.max).to.be(262144);
+      expect(axisAutoscale.tickDecimals).to.be(0);
+    });
+
+    it('should set axis max to be max tick value', function() {
+      expect(ctx.plotOptions.yaxes[0].max).to.be(262144);
+    });
+  });
+
   graphScenario('dashed lines options', function(ctx) {
     ctx.setup(function(ctrl) {
       ctrl.panel.lines = true;