widget/chart-widget.js

import Chart from 'chart.js';

import CategoryWidget from './category-widget';

import template from '../../templates/widget/chart-widget.hbs';

/**
 * Widget that shows a bar chart with grouped data fetched using WPS Aggregate
 * @extends CategoryWidget
 */
class ChartWidget extends CategoryWidget {
  /**
   * @param {Object} config - Configuration object
   * @param {String} config.title - Widget title
   * @param {String} config.server - URL of map server
   * @param {Object} config.namespace - Namespace object
   * @param {String} config.namespace.name - Namespace to use
   * @param {String} config.namespace.url - Namespace URL
   * @param {String} config.layerName - Name of the layer to query
   * @param {String} config.property - Field to use in aggregate function
   * @param {Object} config.categories - Categories configuration
   * @param {String} config.categories.property - Property that defines the style to use
   * @param {Object} config.style.values - Object with possible values
   *   and their correspoding style
   * @param {Object} config.chart - Object with chart configuration
   * @param {String} config.chart.type - Chart type. Options: 'bar', 'pie' or 'doughnut'.
   * @param {Object} [config.chart.unit='value'] - Value to show. Options: 'value' or 'percentage'.
   * @param {Object} [config.chart.options] - Object with chart options based on [ChartJS](http://www.chartjs.org/docs/#chart-configuration).
   *   By default, the widget uses a bar chart without legend, Y axis nor gridlines.
   * @param {Function} [config.format] - Function to parse and transform data fetched from server.
   *   This function should return a human friendly value as it will be shown as
   *   the widget current value.
   */
  constructor(config) {
    super(config);
    this.template = template;
    this.chartType = config.chart.type;
    this.chartOptions = config.chart.options || this.getChartOptions();
    this.className = 'chart-widget';
    this.unit = config.chart.unit || 'value';
  }

  /**
   * Builds default chart options based on its type
   * @return {Object} Chart options
   * @private
   */
  getChartOptions() {
    const options = {
      legend: {
        display: false,
      },
      scales: {
        xAxes: [{
          display: false,
        }],
        yAxes: [{
          display: false,
        }],
      },
    };

    if (this.chartType === 'bar') {
      options.scales.yAxes[0].display = true;
    } else if (this.chartType === 'doughnut') {
      options.rotation = Math.PI;
      options.circumference = Math.PI;
    }

    return options;
  }

  /**
   * Renders the widget layout.
   */
  render() {
    super.render();
    this.content.innerHTML = this.template();
    if (this.chartType === 'doughnut') {
      this.unit = 'percentage';
      this.container.classList.add('doughnut-widget');
    }
    this.chart = new Chart(this.content.getElementsByTagName('canvas')[0], {
      type: this.chartType,
      data: {
        labels: this.labels,
        datasets: [{
          data: [],
          backgroundColor: this.colors,
          borderColor: 'rgba(255,255,255,0.1)',
        }],
      },
      options: this.chartOptions,
    });
  }

  /**
   * Reloads the widget value using the formatted value.
   */
  refresh() {
    if (!this.manager.extent) {
      return;
    }

    this.getData((data) => {
      this.parseResponse(data);
      this.chart.data.datasets[0].data = this.value.values.map(value => value[this.unit]);
      this.chart.update();
    });
  }
}

export default ChartWidget;