【问题标题】:Dynamic Vertical Axis to Accommodate Trendline(s) in Google Line Chart动态垂直轴以适应 Google 折线图中的趋势线
【发布时间】:2019-11-04 00:22:43
【问题描述】:

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

下面是图表的扩展版本的代码,它显示了线性和二次多项式趋势线,谷歌折线图的 x 轴现在显示为 2120 年。我的问题是我需要 y 轴无论用户选择哪个时间段,动态调整以显示两条趋势线的整体。例如,如果您从日期范围滑块中选择 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>

【问题讨论】:

    标签: javascript linechart google-visualization trendline


    【解决方案1】:

    首先,我不确定为什么图表会绘制一条不可见的趋势线
    这让这有点棘手,因为我们首先要绘制图表,
    为了找到最小和最大 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);
    }
    

    请参阅以下工作 sn-p...
    (运行sn-p时,点击右上角的“整页”)

    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 语句来加载谷歌图表。
    见上面的 sn-p 更新...

    【讨论】:

    • 非常感谢@WhiteHat。我对你的想法感到惊讶。如果你在新西兰,我会很乐意给你买一两杯啤酒。我在单独的 cmets 中确实有几个问题,如下所示:
    • 1.在某些年份,这种趋势确实转为负面。例如 1932 年和 1990 年,多项式趋势线变为负数,而线性趋势线保持正数。因此,在尝试计算这种情况下的最小 y 轴值时,我返回了两条趋势线的 left、width 和 height 属性,然后使用 getVAxisValue 方法返回您指出的相应 y 轴值,但我似乎只得到了无意义的价值观。
    • 更改了上面的答案,注意:我注意到当最小轴值下降到 1000 以下时,选项minValue 会忽略我们的值,并下降到零。改用viewWindow,更正了问题...
    • 问另一个问题可能更容易,我在关注所有 cmets 时遇到了麻烦。我会试着回答几个。 1)对于y轴最小值,我们需要找到线的最底部点,所以我们从顶部开始,并添加高度。 2) 将图表区域的左上角视为位置 0、0。它向左和向下增加。如果该线位于可见图表区域上方,则 y 坐标将为负值。至于 3) 最好是另一个问题——让我们称之为完成
    • 干杯,我去看看。你有没有机会将此标记为已接受?查看投票按钮附近的复选标记...
    猜你喜欢
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多