在 Google 折线图中容纳趋势线的动态垂直轴

Dynamic Vertical Axis to Accommodate Trendline(s) in Google Line Chart

我是编码新手,但在过去的几个月里,我摸索着创建了一个网站,该网站利用 Google 折线图和嵌入式线性趋势线来显示历史平均海平面以及新西兰和太平洋周围不同地点的平均海平面上升速度。每个位置都有自己的 Google 折线图和线性趋势线,以显示用户 select 编辑期间的平均海平面变化率。我现在想扩展每个 Google 折线图的功能,使线性和多项式趋势线都延伸到 2120 年(它们目前只显示到 2018 年),即使计算它们的可用数据也是如此使用到 2018 年的观测数据。这将允许用户预测到 2020 年的海平面高度。我意识到这个解释可能令人困惑,所以请访问我的网站 www.sealevel.nz 以查看现有图表我希望这有助于理解我的问题。

下面是图表扩展版本的代码,显示了线性和二次多项式趋势线,Google 折线图的 x 轴现在显示 2120 年。我的问题是我需要 y 轴动态调整以显示两条趋势线的整体,无论用户 selects 在哪个时间段。例如,如果您 select 日期范围滑块中的 1971 年和 2018 年,则两条趋势线分别在 2017 年(线性)和 2031 年(多项式)被截断。我需要能够看到到 2120 年为止的趋势线及其值。

请见谅我的编码技能新手。我的代码:

<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://unpkg.com/mathjs/dist/math.min.js"></script>
<script type="text/javascript">

google.load('visualization', 'current', {'packages':['controls','corechart']});
google.setOnLoadCallback(initialize);
function initialize() {
  var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1vn1iuhsG33XzFrC4QwkTdUnxOGdcPQOj-cuaEZeX-eA/edit#gid=0');
  query.send(drawDashboard);
}
function drawDashboard(response) {
  var data = response.getDataTable();
//Asign units of 'mm' to data.
    var formatMS = new google.visualization.NumberFormat({
    pattern: '# mm'
  });
  // format into data mm..
  for (var colIndex = 1; colIndex < data.getNumberOfColumns(); colIndex++) {
    formatMS.format(data, colIndex);
  }
 var YearPicker = new google.visualization.ControlWrapper({
    'controlType': 'NumberRangeFilter',
    'containerId': 'filter_div',
    'options': {
      'filterColumnLabel': 'Year',
        'ui': {
       cssClass: 'filter-date',
          'format': { pattern: '0000' },
      'labelStacking': 'vertical',
      'allowTyping': false,
      'allowMultiple': false    
      }
    },
  });
  var MSLChart = new google.visualization.ChartWrapper({
    'chartType': 'LineChart',
    'containerId': 'chart_div',
    'options': {  
    'fontSize': '14', 
    'title': 'Timbucktoo Annual Mean Sea Level Summary',
        hAxis: {title: 'Year', format:'0000', maxValue: 2120},
        vAxis: {title: 'Height above Chart Datum (mm)', format:'0000'},
        'height': 600,
    chartArea: {height: '81%', width: '85%', left: 100},
    'legend': {'position': 'in', 'alignment':'end', textStyle: {fontSize: 13} },
    colors: ['blue'],
    trendlines: {
            0: {
                type: 'polynomial',
                degree: 2,
                color: 'green',
                visibleInLegend: true,
            },
            1: {
                type: 'linear',
                color: 'black',
                visibleInLegend: true,
            },
        },
        series: {
            0: { visibleInLegend: true },
            1: { visibleInLegend: false },
        },    
    },
    'view': {'columns': [0,1,2]}
  });

  var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div')).
    bind(YearPicker, MSLChart).

  draw(data)
 </script>

首先,我不确定为什么图表会绘制一条不可见的趋势线
这让这有点棘手,因为我们首先要绘制图表,
为了找到最小和最大 y 轴值。

但是我们可以使用图表 methods 来找到最大值。

首先,我们得到图表的布局界面。

var chartLayout = MSLChart.getChart().getChartLayoutInterface();

因为我们使用的是 ChartWrapper,所以我们必须从包装器 (MSLChart.getChart()) 中获取图表。

接下来,我们使用方法getBoundingBox 来查找每一行的最小值和最大值。

var yAxisCoords = {min: null, max: null};
var lineIndex = 0;
var boundsLine = chartLayout.getBoundingBox('line#' + lineIndex);
do {
  yAxisCoords.max = yAxisCoords.max || boundsLine.top;
  yAxisCoords.max = Math.min(yAxisCoords.max, boundsLine.top);
  yAxisCoords.min = yAxisCoords.min || (boundsLine.top + boundsLine.height);
  yAxisCoords.min = Math.max(yAxisCoords.min, (boundsLine.top + boundsLine.height));
  lineIndex++;
  boundsLine = chartLayout.getBoundingBox('line#' + lineIndex);
} while (boundsLine !== null);

然后我们使用方法getVAxisValue来确定每个y轴值应该是什么,
在 y 轴上设置 viewWindow,然后重新绘制图表。

MSLChart.setOption('vAxis.viewWindow.max', chartLayout.getVAxisValue(yAxisCoords.max));
MSLChart.setOption('vAxis.viewWindow.min', chartLayout.getVAxisValue(yAxisCoords.min));
MSLChart.draw();

我们在一个函数中完成所有这些。
我们在图表包装器上使用一次性 'ready' 事件进行第一次计算。
再一次,在图表上。

google.visualization.events.addOneTimeListener(MSLChart, 'ready', filterChange);

function filterChange() {
  // get chart layout
  var chartLayout = MSLChart.getChart().getChartLayoutInterface();

  // get y-axis bounds
  var yAxisCoords = {min: null, max: null};
  var lineIndex = 0;
  var boundsLine = chartLayout.getBoundingBox('line#' + lineIndex);
  do {
    yAxisCoords.max = yAxisCoords.max || boundsLine.top;
    yAxisCoords.max = Math.min(yAxisCoords.max, boundsLine.top);
    yAxisCoords.min = yAxisCoords.min || (boundsLine.top + boundsLine.height);
    yAxisCoords.min = Math.max(yAxisCoords.min, (boundsLine.top + boundsLine.height));
    lineIndex++;
    boundsLine = chartLayout.getBoundingBox('line#' + lineIndex);
  } while (boundsLine !== null);

  // re-draw chart
  MSLChart.setOption('vAxis.viewWindow.max', chartLayout.getVAxisValue(yAxisCoords.max));
  MSLChart.setOption('vAxis.viewWindow.min', chartLayout.getVAxisValue(yAxisCoords.min));
  MSLChart.draw();
  google.visualization.events.addOneTimeListener(MSLChart.getChart(), 'ready', filterChange);
}

请参阅以下工作片段...
(当你 运行 片段时,点击右上角的 "full page")

google.charts.load('current', {
  packages: ['controls']
}).then(initialize);

function initialize() {
  var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1vn1iuhsG33XzFrC4QwkTdUnxOGdcPQOj-cuaEZeX-eA/edit#gid=0');
  query.send(drawDashboard);
}

function drawDashboard(response) {
  var data = response.getDataTable();

  //Asign units of 'mm' to data.
  var formatMS = new google.visualization.NumberFormat({
    pattern: '# mm'
  });

  // format into data mm..
  for (var colIndex = 1; colIndex < data.getNumberOfColumns(); colIndex++) {
    formatMS.format(data, colIndex);
  }

  var YearPicker = new google.visualization.ControlWrapper({
    controlType: 'NumberRangeFilter',
    containerId: 'filter_div',
    options: {
      filterColumnLabel: 'Year',
      ui: {
        cssClass: 'filter-date',
        format: {pattern: '0000'},
        labelStacking: 'vertical',
        allowTyping: false,
        allowMultiple: false
      }
    },
  });

  var MSLChart = new google.visualization.ChartWrapper({
    chartType: 'LineChart',
    containerId: 'chart_div',
    dataTable: data,
    options: {
      fontSize: '14',
      title: 'Timbucktoo Annual Mean Sea Level Summary',
      hAxis: {title: 'Year', format: '0000', maxValue: 2120},
      vAxis: {title: 'Height above Chart Datum (mm)', format:'###0'},
      height: 600,
      chartArea: {height: '81%', width: '85%', left: 100},
      legend: {position: 'in', alignment: 'end', textStyle: {fontSize: 13}},
      colors: ['blue'],
      trendlines: {
        0: {
          type: 'polynomial',
          degree: 2,
          color: 'green',
          visibleInLegend: true,
        },
        1: {
          type: 'linear',
          color: 'black',
          visibleInLegend: true,
        },
      },
      series: {
        0: { visibleInLegend: true },
        1: { visibleInLegend: false },
      },
    },
    view: {columns: [0,1,2]}
  });

  google.visualization.events.addOneTimeListener(MSLChart, 'ready', filterChange);

  function filterChange() {
    // get chart layout
    var chartLayout = MSLChart.getChart().getChartLayoutInterface();

    // get y-axis bounds
    var yAxisCoords = {min: null, max: null};
    var lineIndex = 0;
    var boundsLine = chartLayout.getBoundingBox('line#' + lineIndex);
    do {
      yAxisCoords.max = yAxisCoords.max || boundsLine.top;
      yAxisCoords.max = Math.min(yAxisCoords.max, boundsLine.top);
      yAxisCoords.min = yAxisCoords.min || (boundsLine.top + boundsLine.height);
      yAxisCoords.min = Math.max(yAxisCoords.min, (boundsLine.top + boundsLine.height));
      lineIndex++;
      boundsLine = chartLayout.getBoundingBox('line#' + lineIndex);
    } while (boundsLine !== null);

    // re-draw chart
    MSLChart.setOption('vAxis.viewWindow.max', chartLayout.getVAxisValue(yAxisCoords.max));
    MSLChart.setOption('vAxis.viewWindow.min', chartLayout.getVAxisValue(yAxisCoords.min));
    MSLChart.draw();
    google.visualization.events.addOneTimeListener(MSLChart.getChart(), 'ready', filterChange);
  }

  var dashboard = new google.visualization.Dashboard(
    document.getElementById('dashboard_div')
  ).bind(YearPicker, MSLChart).draw(data);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="dashboard_div">
  <div id="chart_div"></div>
  <div id="filter_div"></div>
</div>


注意:您似乎在使用旧的 load 语句来加载 google 图表。
请参阅上面的更新片段...