Recipes by Category

App Distribution (2) Bundle logic, interface and services for distribution. App Logic (37) The Apex programming language, workflow and formulas for logic. Collaboration (5) The Salesforce Chatter collaboration platform. Database (29) Data persistence, reporting and analytics. Integration (33) Web Service APIs and toolkits for integration. Security (9) Platform, application and data security. Tools (4) Force.com tooling User Interface (36) Visualforce MVC and metadata-drive user interfaces. Web Sites (12) Public web sites and apps with optional user registration and login.
Beta Feedback
Cookbook Home » Integrating Visualforce and Google Charts

Integrating Visualforce and Google Charts

Post by Developer Force  (2010-07-16)

Status: Certified
Level: novice

Problem

You want to create a Visualforce page that can render data using Google Charts.

Solution

Use the <apex:form> tag to capture information about the chart. Then, construct the URL that passes the data to the Google Charts API through a custom controller.

Discussion

Google Charts provides a way to dynamically render data through different visualizations. Combined with Visualforce, the Google Charts can offer more flexibility and distribution potential than using a dashboard. Since the charts are generated through a URL, the visualizations can be shared and embedded wherever images are permitted.

There are two prerequisites before using the Google Charts API. The first is to determine how to encode the data. The Google Charts API has three data encoding types—text, simple, and extended. For this example, we'll only use the simple encoding. The second is to decide what type of chart to use. For this example, a user will choose between a bar graph or a line chart.

The custom controller has two important functions—init() and create()—that correspond to the requirements above:
  • The function init() takes a numeric value and converts it to Google Chart's simple data encoding type. For more information, see Simple Encoding Data Format in the Google Charts API documentation.
  • The function create() constructs the URL that makes the request to the Google Charts API.
The following code represents the controller for the Visualforce page:
/* This class contains the encoding algorithm for use with the 
   Google chartAPI. */  
    
   
public class GoogleDataEncoding { 
    // Exceptions to handle any erroneous data  
    
    public class EncodingException extends Exception {}
    public class UnsupportedEncodingTypeException 
           extends Exception {}  

    /* The encoding map which takes an integer key and returns the 
       respective encoding value as defined by Google. 
       This map is initialized in init() */  
    
      private Map<Integer, String> encodingMap { get; set; }
    
   /* The maximum encoding value supported for the given encoding 
      type. This value is set during init() */  
    
    private Integer encodingMax { get; set; }
    
    /* The minimum encoding value supported for the given encoding 
       type. This value is set during init() */  
    
    private Integer encodingMin { get; set; }
    
    /* The encoding type according to Google's API. Only SIMPLE 
       is implemented. */  
    
    public enum EncodingType { TEXT, SIMPLE, EXTENDED }
    
    /* The minimum value to use in the generation of an encoding 
       value. */  
    
    public Integer min { get; private set; }
    
    /* The maximum value to use in the generation of an encoding 
       value. */  
    
    public Integer max { get; private set; }
    
    // The encoding type according to the API defined by Google  
    
    public EncodingType eType { get; private set; }       
    
    // Corresponds to the data set provided by the page   
    
    public String dataSet { get; set; }
    
    // Corresponds to the type of graph selected on the page   
    
    public String graph { get; set; }
    
    // The URL that renders the Google Chart  
    
    public String chartURL { get; set; }  

    // Indicates whether the chart should be displayed   
    
    public Boolean displayChart { get; set; }
    
    public GoogleDataEncoding() {
        min = 0;
        max = 61;
        eType = EncodingType.SIMPLE;
        displayChart = false;
        init();
    } 
    
    public PageReference create() {
        String[] dataSetList = dataSet.split(',', 0);
        String mappedValue = 'chd=s:';
        
        chartURL = 'http://chart.apis.google.com/chart?chs=600x300'
         + '&amp;chtt=Time+vs|Distance&amp;chxt=x,y,x,y' 
         + '&amp;chxr=0,0,10,1|1,0,65,5'
         + '&amp;chxl=2:|Seconds|3:|Meters';
        
        if (graph.compareTo('barChart') == 0)
        {
            chartURL += '&amp;cht=bvs';
        }
        else if (graph.compareTo('lineChart') == 0)
        {
            chartURL += '&amp;cht=ls';
        }
        else
        {
            throw new EncodingException('An unsupported chart type' 
                + 'was selected: ' + graph + ' does not exist.');
        }
        
        for(String dataPoint : dataSetList)
        {
            mappedValue += 
               getEncode(Integer.valueOf(dataPoint.trim()));
        }
        
        chartURL += '&amp;' + mappedValue;
        displayChart = true;
        return null;
    }

    
    /* This method returns the encoding type parameter value that 
       matches the specified encoding type. */  
    
   public static String getEncodingDescriptor(EncodingType t) {
        if(t == EncodingType.TEXT) return 't';
        else if(t == EncodingType.SIMPLE) return 's';
        else if(t == EncodingType.EXTENDED) return 'e';
        else return '';
    }  
    
    /* This method takes a given number within the declared 
       range of the encoding class and encodes it according to the 
       encoding type. If the value provided fall outside of the 
       declared range, an EncodingException is thrown. */  
    
    public String getEncode(Integer d) {    
        if(d > max || d < min) {
            throw new EncodingException('Value provided ' + d 
                + ' was outside the declared min/max range (' 
                + min + '/' + max + ')');         
        } 
        else {
            return encodingMap.get(d);
        }
    }  
    
    /* This method initializes the encoding map which is then 
       stored for expected repetitious use to minimize statement 
       invocation. */  
    
    private void init() {
        if(eType == EncodingType.SIMPLE) {
            encodingMax = 61;
            encodingMin = 0;
            encodingMap = new Map<Integer, String>();
            encodingMap.put(0,'A');
            encodingMap.put(1,'B');
            encodingMap.put(2,'C');
            encodingMap.put(3,'D');
            encodingMap.put(4,'E');
            encodingMap.put(5,'F');
            encodingMap.put(6,'G');
            encodingMap.put(7,'H');
            encodingMap.put(8,'I');
            encodingMap.put(9,'J');
            encodingMap.put(10,'K');
            encodingMap.put(11,'L');
            encodingMap.put(12,'M');
            encodingMap.put(13,'N');
            encodingMap.put(14,'O');
            encodingMap.put(15,'P');
            encodingMap.put(16,'Q');
            encodingMap.put(17,'R');
            encodingMap.put(18,'S');
            encodingMap.put(19,'T');
            encodingMap.put(20,'U');
            encodingMap.put(21,'V');
            encodingMap.put(22,'W');
            encodingMap.put(23,'X');
            encodingMap.put(24,'Y');
            encodingMap.put(25,'Z');
            encodingMap.put(26,'a');
            encodingMap.put(27,'b');
            encodingMap.put(28,'c');
            encodingMap.put(29,'d');
            encodingMap.put(30,'e');
            encodingMap.put(31,'f');
            encodingMap.put(32,'g');
            encodingMap.put(33,'h');
            encodingMap.put(34,'i');
            encodingMap.put(35,'j');
            encodingMap.put(36,'k');
            encodingMap.put(37,'l');
            encodingMap.put(38,'m');
            encodingMap.put(39,'n');
            encodingMap.put(40,'o');
            encodingMap.put(41,'p');
            encodingMap.put(42,'q');
            encodingMap.put(43,'r');
            encodingMap.put(44,'s');
            encodingMap.put(45,'t');
            encodingMap.put(46,'u');
            encodingMap.put(47,'v');
            encodingMap.put(48,'w');
            encodingMap.put(49,'x');
            encodingMap.put(50,'y');
            encodingMap.put(51,'z');
            encodingMap.put(52,'0');
            encodingMap.put(53,'1');
            encodingMap.put(54,'2');
            encodingMap.put(55,'3');
            encodingMap.put(56,'4');
            encodingMap.put(57,'5');
            encodingMap.put(58,'6');
            encodingMap.put(59,'7');
            encodingMap.put(60,'8');
            encodingMap.put(61,'9');
        }
    } 
}
The Visualforce page needs two input elements: one for the chart type, and one for the data set. Below is a sample page that constructs the form to collect this information:
<apex:page controller="GoogleDataEncoding">
    <apex:form >
        <apex:pageBlock 
               title="Create a Google Chart for Time and Distance">
            <apex:outputLabel 
               value="Enter data set, seperated by commas: " 
               for="dataInput"/><br/>
            <apex:inputTextArea 
               id="dataInput" title="First Data Point" 
               value="{!dataSet}" rows="3" cols="50"/><br/>
            <apex:selectRadio value="{!graph}" 
               layout="pageDirection">
                <apex:selectOption itemValue="barChart" 
               itemLabel="Horizontal Bar Chart"/>
                <apex:selectOption itemValue="lineChart" 
               itemLabel="Line Chart"/>
            </apex:selectRadio>            
            <apex:commandButton action="{!create}" 
               value="Create"/>
        </apex:pageBlock>
    </apex:form>
    <apex:image url="{!chartURL}" alt="Sample chart" 
               rendered="{!displayChart}"/>
</apex:page>
For a sample, enter the following sequence of numbers: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55. Your page should render the following:

Share

Recipe Activity - Please Log in to write a comment

Is there any other way to specify at server side, as keith pointed out the important aspect of limit.

by Jitendra Zaa  (2011-11-18)

nice recipe. it is what I want

voted as verified by Suzan Yu  (2011-06-03)

Just a note of caution that the URL can only be up to 2k in length after encoding so there is a hard limit to how much data you can pass. With charts such as http://code.google.com/apis/chart/docs/gallery/graphviz.html this limit is very easy to hit.

I couldn't find a way in Apex to achieve the server-side solution described here http://code.google.com/apis/chart/docs/post_requests.html which is disappointing.

by Keith Clarke  (2011-01-24)

X

Vote to Verify a Recipe

Verifying a recipe is a way to give feedback to others and broaden your own understanding of the capabilities on Force.com. When you verify a recipe, please make sure the code runs, and the functionality solves the articulated problem as expected.

Please make sure:
  • All the necessary pieces are mentioned
  • You have tested the recipe in practice
  • Have sent any suggestions for improvements to the author

Please Log in to verify a recipe

You have voted to verify this recipe.