yegle
yegle

Reputation: 5865

How to get the actual vAxis max value of Google Chart API?

I have two Line Charts and each has a rangeFilter (like the one here.)

I want to synchronize the scale of the two charts. That is, when one chart changes the range view, it will change vAxis max value to suit the maximum data in that range, and I want the other chart to change the scale to the same value.

How can I get the vAxis.viewWindow.max in realtime?

Upvotes: 1

Views: 8529

Answers (3)

Bogatyr
Bogatyr

Reputation: 19323

Here's how to do what the original question asked:

chartWrapper.draw();  // initial draw of the chart
var chart = chartWrapper.getChart();
var cli = chart.getChartLayoutInterface();
var bb = cli.getChartAreaBoundingBox();
var vAxisMax = cli.getVAxisValue(bb.top);

Reference: discussion on Google Groups: Google Visualization API.

Upvotes: 0

jmac
jmac

Reputation: 7128

If you are just asking how to determine the vAxis.viewWindow.min (and .max) value, you can do it with the following code:

alert('Min ViewWindow: ' + chart.getOption('vAxis.viewWindow.min') + ', Max: ' + chart.getOption('vAxis.viewWindow.max'));

(given a chart wrapper object named chart).

You can have it tell you the max/min every time you adjust the chart by adding a statechange event to the control wrapper:

  google.visualization.events.addListener(control, 'statechange', function(e) {
    if(e.inProgress == false) {
      alert('Min ViewWindow: ' + chart.getOption('vAxis.viewWindow.min') + ', Max: ' + chart.getOption('vAxis.viewWindow.max'));
    }

This will not actually change anything, mind, it will just report what you want. If on the other hand you want your chart to change max/min viewWindow values depending on the filtered value in the chart, here is my very poor implementation of that:

function drawVisualization() {
  var dashboard = new google.visualization.Dashboard(
    document.getElementById('dashboard'));

  var control = new google.visualization.ControlWrapper({
    'controlType': 'ChartRangeFilter',
    'containerId': 'control',
    'options': {
      // Filter by the date axis.
      'filterColumnIndex': 0,
      'ui': {
        'chartType': 'LineChart',
        'chartOptions': {
          'chartArea': {'width': '90%'},
          'hAxis': {'baselineColor': 'none'}
        },
        // Display a single series that shows the closing value of the stock.
        // Thus, this view has two columns: the date (axis) and the stock value (line series).
        'chartView': {
          'columns': [0, 3]
        },
        // 1 day in milliseconds = 24 * 60 * 60 * 1000 = 86,400,000
        'minRangeSize': 86400000
      }
    },
    // Initial range: 2012-02-09 to 2012-03-20.
    'state': {'range': {'start': new Date(2012, 1, 9), 'end': new Date(2012, 2, 20)}}
  });

  var chart = new google.visualization.ChartWrapper({
    'chartType': 'CandlestickChart',
    'containerId': 'chart',
    'options': {
      // Use the same chart area width as the control for axis alignment.
      'chartArea': {'height': '80%', 'width': '90%'},
      'hAxis': {'slantedText': false},
      'vAxis': {'viewWindow': {'min': 0, 'max': 2000}},
      'legend': {'position': 'none'}
    },
    // Convert the first column from 'date' to 'string'.
    'view': {
      'columns': [
        {
          'calc': function(dataTable, rowIndex) {
            return dataTable.getFormattedValue(rowIndex, 0);
          },
          'type': 'string'
        }, 1, 2, 3, 4]
    }
  });

  var data = new google.visualization.DataTable();
  data.addColumn('date', 'Date');
  data.addColumn('number', 'Stock low');
  data.addColumn('number', 'Stock open');
  data.addColumn('number', 'Stock close');
  data.addColumn('number', 'Stock high');

  // Create random stock values, just like it works in reality.
  var open, close = 300;
  var low, high;
  for (var day = 1; day < 121; ++day) {
    var change = (Math.sin(day / 2.5 + Math.PI) + Math.sin(day / 3) - Math.cos(day * 0.7)) * 150;
    change = change >= 0 ? change + 10 : change - 10;
    open = close;
    close = Math.max(50, open + change);
    low = Math.min(open, close) - (Math.cos(day * 1.7) + 1) * 15;
    low = Math.max(0, low);
    high = Math.max(open, close) + (Math.cos(day * 1.3) + 1) * 15;
    var date = new Date(2012, 0 ,day);
    data.addRow([date, Math.round(low), Math.round(open), Math.round(close), Math.round(high)]);
  }

  // This function determines the maximum and minimum values of the data

  function calculateMinMax(data) {
    // Get max and min values for the data table

    var totalMin = data.getValue(0,1);
    var totalMax = data.getValue(0,1);

    for (var i = 1;i < data.getNumberOfColumns();i++) {
      for (var j = 0;j < data.getNumberOfRows();j++){
        if ( data.getValue(j, i) < totalMin ) {
          totalMin = data.getValue(j, i);
        }          
        if ( data.getValue(j, i) > totalMax ) {
          totalMax = data.getValue(j, i);
        }
      }
    }

    // Calculate grid line axes and min/max settings

    // 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;

    var result = [finalMin, finalMax];

    return result;
  }

  var axisMinMax = calculateMinMax(data);

  alert('Min: ' + axisMinMax[0] + ', Max: ' + axisMinMax[1]);

  chart.setOption('vAxis.viewWindow.min', axisMinMax[0]);
  chart.setOption('vAxis.viewWindow.max', axisMinMax[1]);

  dashboard.bind(control, chart);
  dashboard.draw(data);
  google.visualization.events.addListener(control, 'statechange', function(e) {
    if(e.inProgress == false) {
      var axisMinMax = calculateMinMax(chart.getDataTable());
      chart.setOption('vAxis.viewWindow.min', axisMinMax[0]);
      chart.setOption('vAxis.viewWindow.max', axisMinMax[1]);
      var filteredData = chart.getDataTable();
      chart.draw(filteredData);
    }
  });
}

Upvotes: 4

asgallant
asgallant

Reputation: 26340

Without knowing more specifics about your charts, I can't give you a very detailed answer, but assuming you have two charts (chart1 and chart2) driven by two controls (control1 and control2, respectively) in a Dashboard, and each chart has one data series each, then you could use something like this to sync the scales:

function syncChartScales () {
    var range1 = chart1.getDataTable().getColumnRange(1);
    var range2 = chart2.getDataTable().getColumnRange(1);

    var minMax = {
        min: Math.min(range1.min, range2.min),
        max: Math.max(range1.max, range2.max)
    }

    chart1.setOption('vAxis.minValue', minMax.min);
    chart1.setOption('vAxis.maxValue', minMax.max);
    chart2.setOption('vAxis.minValue', minMax.min);
    chart2.setOption('vAxis.maxValue', minMax.max);

    chart1.draw();
    chart2.draw();
}

google.visualization.events.addListener(control1, 'statechange', syncChartScales);
google.visualization.events.addListener(control2, 'statechange', syncChartScales);

If you have more data series, then it becomes slightly more complicated, as you must get the range of all data series in both charts to find the min/max of them all, and set the vAxis.minValue/maxValue options of both charts to the min/max of all series.

Upvotes: 2

Related Questions