How do I read and modify axes on an already-drawn

2020-03-31 04:01发布

问题:

I have a page with several google charts on it, mostly combo charts and line charts. For instance:

chart = new google.visualization.LineChart(chartDiv);

After the page has already drawn, I would like to be able to read the maximum values off of the axes and then redraw the charts so that they all have the same maximum values on their axes (i.e., so that they are easier to compare at a glance). I don't know these maximum values in advance.

I could change the options and redraw the charts with

chart.draw(data, options)

but I'm not sure how to pull the data off of the axes. The chart object has a method called .getChartLayoutInterface() This seems promising but I'm not sure how to use it. For example, this works:

var b = c.getChartLayoutInterface().getChartAreaBoundingBox();

but,

var b = c.getChartLayoutInterface().getVAxisValue();

returns null.

回答1:

You'd have to ask @Sjoerd how getChartLayoutInterface() works, since it doesn't seem there is any documentation online.

In the meantime, what you can do is to get the min and max values of each chart that you're creating, and then take the max and min of the max/min for all charts combined.

Using those max/min values, you can manually set the min/max for each chart (with some nice rounding). To do the rounding nicely, I suggest the following:

// Take the Max/Min of all data values in all graphs
var totalMax = 345;
var totalMin = -123;

// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));

// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);

// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;

// Determine the range of your values
var range = newMax - newMin;

// Define the number of gridlines (default 5)
var gridlines = 5;

// Determine an appropriate gap between gridlines
var interval = range / (gridlines - 1);

// Round that interval up to the exponent of 10
var newInterval = Math.ceil(interval/roundingDec)*roundingDec;

// Re-round your max and min to the new interval
var finalMax = Math.ceil(totalMax/newInterval)*newInterval;
var finalMin = Math.floor(totalMin/newInterval)*newInterval;

This is similar to how Google determines max/min values (sometimes if you have, say, -6.2, and 3.1 as values, it will use 3.1 for gridlines even though it is an odd number to use). But by creating your own rounding, you can get around needing to figure out how google rounds (which is a tricky proposition).

Let me know if you don't understand properly.



回答2:

According to the documentation getVAxisValue function has the following signature:

getVAxisValue(position, optional_axis_index)

Returns the logical vertical value at position, which is an offset from the chart container's top edge. Can be negative.

Example: chart.getChartLayoutInterface().getVAxisValue(300).

Call this after the chart is drawn.

Return Type: number


Since your goal is to read the maximum values off of the axes and then redraw the charts so that they all have the same maximum values on their axes, i would suggest the following solution

calculate min/max values of google.visualization.DataTable:

function calcMinMax(data, colIndex)
{
   var totalMin  = data.getValue(0,colIndex);
   var totalMax = data.getValue(0,colIndex);
   for(var i = 0; i < data.getNumberOfRows();i++){
        if ( data.getValue(i, colIndex) < totalMin  ) {
          totalMin  = data.getValue(i, colIndex);
        }          
        if ( data.getValue(i, colIndex) > totalMax ) {
          totalMax = data.getValue(i, colIndex);
        }
   }
   return {'min': totalMin, 'max': totalMax };
} 

and set viewWindow option:

var vAxisMinMax = calcMinMax(data,1); 
options.vAxis = {'viewWindow': {'min': vAxisMinMax.min, 'max': vAxisMinMax.max}};

Example

google.load('visualization', '1', { packages: ['corechart', 'line'] });
google.setOnLoadCallback(drawBasic);

function drawBasic() {

    var data = new google.visualization.DataTable();
    data.addColumn('number', 'X');
    data.addColumn('number', 'Dogs');

    data.addRows([
      [0, 5], [1, 10], [2, 23], [3, 17], [4, 18], [5, 9],
      [6, 11], [7, 27], [8, 33], [9, 40], [10, 32], [11, 35],
      [12, 30], [13, 40], [14, 42], [15, 47], [16, 44], [17, 48],
      [18, 52], [19, 54], [20, 42], [21, 55], [22, 56], [23, 57],
      [24, 60], [25, 50], [26, 52], [27, 51], [28, 49], [29, 53],
      [30, 55], [31, 60], [32, 61], [33, 59], [34, 62], [35, 65],
      [36, 62], [37, 58], [38, 55], [39, 61], [40, 64], [41, 65],
      [42, 63], [43, 66], [44, 67], [45, 69], [46, 69], [47, 70],
      [48, 72], [49, 68], [50, 66], [51, 65], [52, 67], [53, 70],
      [54, 71], [55, 72], [56, 73], [57, 75], [58, 70], [59, 68],
      [60, 64], [61, 60], [62, 65], [63, 67], [64, 68], [65, 69],
      [66, 70], [67, 72], [68, 75], [69, 80]
    ]);

    var options = {
        hAxis: {
            title: 'Time'
        },
        vAxis: {
            title: 'Popularity'
        }
    };

    var chart = new google.visualization.LineChart(document.getElementById('chart_div'));


    var vAxisMinMax = calcMinMax(data,1); 
    options.vAxis = {'viewWindow': {'min': vAxisMinMax.min, 'max': vAxisMinMax.max}};
   

    chart.draw(data, options);

    
}





function calcMinMax(data, colIndex)
{
   var totalMin  = data.getValue(0,colIndex);
   var totalMax = data.getValue(0,colIndex);
   for(var i = 0; i < data.getNumberOfRows();i++){
        if ( data.getValue(i, colIndex) < totalMin  ) {
          totalMin  = data.getValue(i, colIndex);
        }          
        if ( data.getValue(i, colIndex) > totalMax ) {
          totalMax = data.getValue(i, colIndex);
        }
   }
   return {'min': totalMin, 'max': totalMax };
}
<script type="text/javascript" src="https://www.google.com/jsapi"></script> 
<div id="chart_div"></div>