All eazyBI for Jira eazyBI for Confluence Private eazyBI

Custom chart example - Google Maps
eazyBI for Jira eazyBI for Confluence

This example will describe how to add a Google Maps based custom chart type to eazyBI.

eazyBI by default includes similar OpenStreetMap and MapQuest interactive maps in the Map chart.

This is just an example of how to add custom charts to eazyBI.


On this page:

Create custom JavaScript files

This example will use Google Maps API and additional MarkerClusterer library. At first, create the directory JIRA_HOME/data/eazybi/custom/javascripts.

Then download https://github.com/googlemaps/js-marker-clusterer and save it in this directory as markerclusterer.js.

Then create custom_charts.js file in this directory with the following implementation of Google Maps based chart type:

Eazy.resultsViews.push({
  name: "Google Maps",
  key: "custom_google_maps",
  view: "CustomGoogleMapsView",
  icon: "fa fa-map-marker"
});
CustomGoogleMapsView = Eazy.ChartView.extend({
  infoWindowTemplate: Eazy.template('<div class="content">' +
    '<div><strong>{{title}}</strong></div>' +
    '{{#data}}<div>{{name}}: {{formattedValue}}</div>{{/data}}' +
    '</div>'),
  loadAPI: function() {
    if (window.google && google.maps) {
      return true;
    } else if (!this.constructor.apiLoading) {
      this.constructor.apiLoading = true;
      $("head").append("<script async defer src=\"https://maps.googleapis.com/maps/api/js\" type=\"text/javascript\">");
      return false;
    } else {
      return false;
    }
  },
  render: function() {
    if (!this.loadAPI()) {
      // retry after 1 second as Google Maps API will probably be loaded then
      _.delay(_.bind(this.render, this), 1000);
      return;
    }
    var queryResult = this.model.queryResult;
    this.categories = queryResult.chartCategories("row");
    this.series = queryResult.chartSeries("column");
    if (this.series.length < 2) {
      this.$el.html("Please use latitude and longitude as first two columns.");
      return this;
    }
    var $map = this.$(".google_maps_container");
    if ($map.length && this.map) {
      this.clearMarkers();
    } else {
      this.$el.html('<div class="google_maps_container">');
      $map = this.$(".google_maps_container");
      $map.height(500);
      this.map = new google.maps.Map($map[0], {
        // do not set center and zoom as will be positioned automatically using fitBounds
        mapTypeId: google.maps.MapTypeId.ROADMAP
      });
    }
    this.displayMarkers();
    Eazy.PopupMenuView.removeAll();
    return this;
  },
  displayMarkers: function() {
    var self = this,
        bounds = new google.maps.LatLngBounds();
    this.markers = [];
    _.each(this.categories, function(members, ci) {
      var lat = self.series[0].data[ci].y,
          lng = self.series[1].data[ci].y;
      if (lat && lng) {
        var title = _.invoke(members, "get", "caption").join(", "),
            marker = new google.maps.Marker({
              position: new google.maps.LatLng(lat, lng),
              map: self.map,
              title: title
            });
        self.markers.push(marker);
        var data = [];
        _.each(self.series, function(serie, si) {
          data.push({
            name: _.invoke(serie.name, "get", "caption").join(", "),
            formattedValue: self.series[si].data[ci].name.formattedValue
          });
        });
        var infowindow = new google.maps.InfoWindow({
          content: self.infoWindowTemplate({
            title: title,
            data: data
          })
        });
        google.maps.event.addListener(marker, 'click', function() {
          infowindow.open(self.map, marker);
        });
        bounds.extend(marker.position);
      }
    });
    this.map.fitBounds(bounds);
    this.markerCluster = new MarkerClusterer(this.map, this.markers);
  },
  clearMarkers: function() {
    _.each(this.markers, function(marker) {
      marker.setMap(null);
    });
    this.markerCluster.clearMarkers();
  }
});

This chart assumes that you have selected events or objects on the rows and you would like to show them as markers on Google Maps. The first two columns should contain latitude and longitude of these markers, other column values will be shown in the popup when clicking on the marker. The Google Maps API library will be dynamically loaded from https://maps.googleapis.com/maps/api/js during the first rendering of the chart.

In the beginning of this file, you can see how you can add custom chart types to the list of available report and charts types in Eazy.resultsViews:

  • name will be the display name of the chart type in the Analyze tab.
  • key is the internal key that will be saved in the report definition. It is recommended to use the prefix custom_ to avoid conflicts with the standard chart type keys.
  • view specifies the JavaScript view function that will be used for rendering the results.
  • icon specifies the CSS classes that will be used for the chart type icon. You can use Font Awesome icon names there.

You can also modify the Eazy.resultsViews array if you would like to remove any of the standard chart types that you do not need.

Include custom assets

Now create JIRA_HOME/data/eazybi/custom/layouts/_custom_head.html.haml file and include custom JavaScript files that you need for your custom charts:

- if include_custom_report_assets?
  = custom_javascript_include 'markerclusterer', 'custom_charts'

include_custom_report_assets? will return true when it is necessary to load assets for custom charts (when showing reports and dashboards).

Test and debug

After each change of the _custom_head.html.haml or custom_charts.js you can reload the browser page and the new version of the files will be loaded. You can use console.log in the JavaScript code to print debug messages in the console.