设置Google数字范围过滤器下限的最大值

问题描述 投票:1回答:1

我有一个交互式的Google折线图,它可以显示用户选择的两个不同年份之间的历史海平面数据。该图表还显示所选期间的线性趋势线和多项式趋势线。可用的历史数据范围介于1904年至2018年之间。但是,用户可以选择1904年到2120年之间的任何开始年和结束年。如果选择了2018年之后的结束年份,则图表将显示直到2018年的可用历史数据,然后将两条趋势线延伸以显示用户选择的年份之前的预测海平面。

这似乎可以正常工作,直到选定的两个年份都超过了2018年,即抛出错误(“无法读取属性'top'为null”的2020和2056),因为它无法从不包含以下内容的期间计算和绘制趋势线观察数据。目前,我正在使用一个错误处理程序来解决此问题,该错误处理程序将在发生这种情况时启动,并显示一条警告消息,提示用户他们不能选择大于2018年的开始年份。然后重新加载页面,并且数字范围过滤器默认返回分别到1904年和2018年的开始和结束年度,这并不理想。我想做的是限制用户选择不超过2018年的开始年份,但是似乎没有在数字范围过滤器控件中进行此操作的选项/设置。有什么想法吗?

我的代码:

<html>

<script src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">

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 data into 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: {
      maxValue:2120,
      filterColumnLabel: 'Year',
      ui: {
        cssClass: 'filter-date',
        format: {pattern: '0000'},
        labelStacking: 'vertical',
        allowTyping: false,
        allowMultiple: false
      }
    },
    "state": {"lowValue": 1904, "highValue": 2018},
  });

  google.visualization.events.addListener(YearPicker, 'statechange', filterChange);

  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'},
      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);
   try {
    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);
     }
     catch (error) {alert("Please choose a start year less than or equal to 2018");
     window.location.reload(false);
     exit;
     }
    var state = YearPicker.getState();
      var EndYear = state.highValue;
    // re-draw chart
    MSLChart.setOption('vAxis.viewWindow.max', chartLayout.getVAxisValue(yAxisCoords.max));
    MSLChart.setOption('vAxis.viewWindow.min', chartLayout.getVAxisValue(yAxisCoords.min));
    MSLChart.setOption('hAxis.viewWindow.max', EndYear);
    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>
  <div id="dashboard_div">
    <div id="chart_div"></div>
    <div id="filter_div"></div>
  </div>
</html>
javascript google-visualization controls google-chartwrapper trendline
1个回答
0
投票

为防止错误,在filterChange功能中,更改do...while语句,

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

仅是while语句

while (boundsLine !== null) {
  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);
};

do...while假定将始终绘制至少一条线。


我们不能阻止用户选择超过2018年的开始年份,但是我们可以立即重置开始年份。

google.visualization.events.addListener(YearPicker, 'statechange', function () {
  var state = YearPicker.getState();
  state.lowValue = Math.min(2018, state.lowValue);
  YearPicker.setState({
    lowValue: state.lowValue,
    highValue: state.highValue
  });
  YearPicker.draw();
  filterChange();
});

请参阅以下工作片段...

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 data into 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: {
      maxValue: 2120,
      filterColumnLabel: 'Year',
      ui: {
        cssClass: 'filter-date',
        format: {pattern: '0000'},
        labelStacking: 'vertical',
        allowTyping: false,
        allowMultiple: false
      }
    },
    state: {lowValue: 1904, highValue: 2018},
  });

  google.visualization.events.addListener(YearPicker, 'statechange', function () {
    var state = YearPicker.getState();
    state.lowValue = Math.min(2018, state.lowValue);
    YearPicker.setState({
      lowValue: state.lowValue,
      highValue: state.highValue
    });
    YearPicker.draw();
    filterChange();
  });

  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'},
      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);

    while (boundsLine !== null) {
      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);
    };

    var state = YearPicker.getState();
    var EndYear = state.highValue;

    // re-draw chart
    MSLChart.setOption('vAxis.viewWindow.max', chartLayout.getVAxisValue(yAxisCoords.max));
    MSLChart.setOption('vAxis.viewWindow.min', chartLayout.getVAxisValue(yAxisCoords.min));
    MSLChart.setOption('hAxis.viewWindow.max', EndYear);
    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>

编辑

没有用于修改趋势线工具提示的选项,但是我们可以在'onmouseover'事件期间手动更改它。

首先,我们需要使用html工具提示,默认情况下它们是svg。添加此选项...

  tooltip: {
    isHtml: true
  },

然后将'onmouseover'事件添加到图表中,我们可以在包装器的'ready'事件中执行此操作。

google.visualization.events.addOneTimeListener(MSLChart, 'ready', function () {
  google.visualization.events.addListener(MSLChart.getChart(), 'onmouseover', function (props) {
    // ensure trendline tooltip
    if (props.column === 0) {
      // get year value
      var year = data.getValue(props.row, 0);

      // get tooltip, remove width
      var tooltip = MSLChart.getChart().getContainer().getElementsByTagName('ul');
      tooltip[0].parentNode.style.width = null;

      // get tooltip labels
      var tooltipLabels = MSLChart.getChart().getContainer().getElementsByTagName('span');

      // set year
      tooltipLabels[0].innerHTML = year;

      // remove formula
      tooltipLabels[1].innerHTML = '';

      // set height value
      var height = parseFloat(tooltipLabels[2].innerHTML.split(' ')[2].replace(',', '')).toFixed(0);
      tooltipLabels[2].innerHTML = height + ' mm';
    }
  });
});

请参阅以下工作片段...

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 data into 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: {
      maxValue: 2120,
      filterColumnLabel: 'Year',
      ui: {
        cssClass: 'filter-date',
        format: {pattern: '0000'},
        labelStacking: 'vertical',
        allowTyping: false,
        allowMultiple: false
      }
    },
    state: {lowValue: 1904, highValue: 2018},
  });

  google.visualization.events.addListener(YearPicker, 'statechange', function () {
    var state = YearPicker.getState();
    state.lowValue = Math.min(2018, state.lowValue);
    YearPicker.setState({
      lowValue: state.lowValue,
      highValue: state.highValue
    });
    YearPicker.draw();
    filterChange();
  });

  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'},
      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'],
      tooltip: {
        isHtml: true
      },
      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);

    while (boundsLine !== null) {
      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);
    };

    var state = YearPicker.getState();
    var EndYear = state.highValue;

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

  google.visualization.events.addOneTimeListener(MSLChart, 'ready', function () {
    google.visualization.events.addListener(MSLChart.getChart(), 'onmouseover', function (props) {
      // ensure trendline tooltip
      if (props.column === 0) {
        // get year value
        var year = data.getValue(props.row, 0);

        // get tooltip, remove width
        var tooltip = MSLChart.getChart().getContainer().getElementsByTagName('ul');
        tooltip[0].parentNode.style.width = null;

        // get tooltip labels
        var tooltipLabels = MSLChart.getChart().getContainer().getElementsByTagName('span');

        // set year
        tooltipLabels[0].innerHTML = year;

        // remove formula
        tooltipLabels[1].innerHTML = '';

        // set height value
        var height = parseFloat(tooltipLabels[2].innerHTML.split(' ')[2].replace(',', '')).toFixed(0);
        tooltipLabels[2].innerHTML = height + ' mm';
      }
    });
  });

  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>
© www.soinside.com 2019 - 2024. All rights reserved.