domingo, 30 de abril de 2017

Raphael JS Pie Chart and Spring MVC

Raphael JS is a lightweight JavaScript framework which allows you to draw vector graphics in a browser. In this post, I will introduce you a basic example in order to explain how to create a Pie Chart from a given set of data which will be provided by a Spring Controller and a Ajax calling.

Required Software to Run Example:
  • Java 7
  • Eclipse Mars
  • Apache Tomcat 8.0
  • Maven
  • Raphael JS
Project Structure in Netbeans

POM.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.rolandopalermo.web</groupId>
 <artifactId>spring-raphael</artifactId>
 <packaging>war</packaging>
 <version>0.0.1-SNAPSHOT</version>
 <name>spring-raphael Maven Webapp</name>
 <url>http://maven.apache.org</url>
 <dependencies>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>4.3.5.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>4.3.5.RELEASE</version>
  </dependency>
  <!-- JSON -->
  <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.7.3</version>
  </dependency>
  <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.7.3</version>
  </dependency>
 </dependencies>
 <build>
  <finalName>spring-raphael</finalName>
 </build>
</project>
Pie.js
Raphael.fn.pieChart = function(cx, cy, r, values, labels, stroke) {
 var paper = this;
 var rad = Math.PI / 180;// degrees to radians
 var chart = this.set();
 var angle = 0, total = 0, start = 0;
 // Draw a sector
 function sector(cx, cy, r, startAngle, endAngle, params) {
  console.log(params.fill);
  var x1 = cx + r * Math.cos(-startAngle * rad), x2 = cx + r
    * Math.cos(-endAngle * rad), y1 = cy + r
    * Math.sin(-startAngle * rad), y2 = cy + r
    * Math.sin(-endAngle * rad);
  return paper.path(
    [ "M", cx, cy, "L", x1, y1, "A", r, r, 0,
      +(endAngle - startAngle > 180), 0, x2, y2, "z" ]).attr(
    params);
 }

 process = function(j) {
  var value = values[j], angleplus = 360 * value / total, popangle = angle
    + (angleplus / 2), color = Raphael.hsb(start, .75, 1), ms = 500, delta = 30, bcolor = Raphael
    .hsb(start, 1, 1), p = sector(cx, cy, r, angle, angle
    + angleplus, {
   fill : "90-" + bcolor + "-" + color,
   stroke : stroke,
   "stroke-width" : 3
  }), txt = paper.text(cx + (r + delta + 55) * Math.cos(-popangle * rad),
    cy + (r + delta + 25) * Math.sin(-popangle * rad), labels[j])
    .attr({
     fill : bcolor,
     stroke : "none",
     opacity : 0,
     "font-size" : 20
    });
  p.mouseover(function() {
   p.stop().animate({
    transform : "s1.1 1.1 " + cx + " " + cy
   }, ms, "elastic");
   txt.stop().animate({
    opacity : 1
   }, ms, "elastic");
  }).mouseout(function() {
   p.stop().animate({
    transform : ""
   }, ms, "elastic");
   txt.stop().animate({
    opacity : 0
   }, ms);
  });
  angle += angleplus;
  chart.push(p);
  chart.push(txt);
  start += .1;
 };
 for (var i = 0, ii = values.length; i < ii; i++) {
  total += values[i];
 }
 for (i = 0; i < ii; i++) {
  process(i);
 }
 return chart;
};

$(function() {
 $.ajax({
  method: "POST",
        url: 'get-full-report',
        dataType: 'json',
        crossDomain: true,
        beforeSend: function (xhr) {
            //Set authentication headers
        },
        success: function (data, textStatus, jqXHR) {
         if (data.error) {
                alert(data.message);
            } else {
             var values = [], labels = [];
             $.each(data, function(i, item) {
              values.push(parseInt(data[i].percentageOfUse, 10));
              labels.push(data[i].name);
             });
             Raphael("holder", 700, 700).pieChart(350, 350, 200, values, labels, "#fff");
            }
        },
        error: function (xhr) { // if error occured
            alert('No se pudo procesar la solicitud.');
        },
        complete: function () {
        },
        xhrFields: {
            withCredentials: true
        }
 });
});
Spring Controller
package com.rolandopalermo.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.rolandopalermo.web.bean.Language;
import com.rolandopalermo.web.util.ReportUtil;
import com.rolandopalermo.web.util.ResponseList;

@Controller
public class HomeController {

 @RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET)
 public ModelAndView home() {
  ModelAndView model = new ModelAndView();
  model.setViewName("index");
  return model;
 }

 @RequestMapping(value = { "get-full-report" }, method = RequestMethod.POST)
 @CrossOrigin(origins = "*", allowCredentials = "true")
 @ResponseBody
 public String getReport() {
  String jsonResponse = "";
  ResponseList<Language> list = ReportUtil.generateReport();
  if (list == null || list.isEmpty()) {
   jsonResponse = "{"error" : true, "message": "No available data"}";
  } else {
   jsonResponse = list.toString();
  }
  return jsonResponse;
 }

}

And your output should look like:


Download the full source code from Github and enjoy it!