You are on the Home/Publications & Training/Case Studies/Excel and Google Charts
Google
Web This Site

Using the Google Chart API with Microsoft Excel

Excel by itself supports a range of charting options.  Nonetheless, there are some things it doesn't do well.  One of them is creating dashboard charts such as the one shown below.  Google introduced a Chart API that is designed for charts on web pages.  By combining a set of capabilities that link data in a Excel worksheet with a Google chart, it becomes possible to create charts in Excel that otherwise would be difficult if not impossible.

With the correct configuration of the HTML elements containing the charts, one can easily change the above 2x2 layout to 4x1.

One of the other interesting things one can do is to click-and-drag one of the charts from the web browser window to the Excel worksheet.  This creates a static image of the chart.

The Google documentation for the Chart API is at http://code.google.com/apis/chart/

This document contains code samples that are closer to "proof of concept" quality.  Consequently, the reader should exercise appropriate judgment in using them.

The key to inserting a Google chart into a Excel document is to use the WebBrowser ActiveX control.  Inserting the appropriate HTML elements in the browser document leads to multiple charts in the same browser window.

The rest of this document is organized as follows:

Conceptual Overview

Initial Setup

Inserting charts in the web browser

Responding to events in the browser

Modifying the browser display

 

 

Conceptual Overview

The connection between an Excel worksheet and a Internet browser window is through the WebBrowser ActiveX control.  The WebBrowser control acts both as a container for a HTML document as well as a conduit for VBA to access it.  Through the Document Object Model (DOM) VBA procedures have access to the elements in the HTML document.

The charting code initializes the HTML document with a single DIV element with the ID of AllCharts.  Then, each chart added is inside a new child DIV element of the AllCharts element.  The ID of this new DIV element is simply a unique number.  Also, each DIV element "floats."  This means that as the web browser window is resized, the charts will automatically "flow" within the window.

Finally, each individual DIV is declared in VBA as a 'with events' object.  This allows the VBA code to respond to various chart specific events such as a user double-click.  One could use this to reconfigure an existing chart.

While not part of this document, one could also use a worksheet Change event (or maybe even the Calculate event) to check if a chart created from a worksheet range requires revision.

The prerequisites for this document is a basic understanding of VBA programming, a basic knowledge of event driven programming, and a rudimentary knowledge of the structure of a HTML document.

Pre-requisite reference: The VBA project must contain a reference (Tools | References...) to the Microsoft HTML Object Library.

Initial Setup

Initial setup involves inserting the web browser into the Excel worksheet and writing the routines that map Excel data into the chart codes that the Google Chart API uses.

The code below adds, if necessary, a web browser into the worksheet.  It then navigates to some URL.  For reasons I don't understand, this initial navigation is crucial to making the charts work in the web browser.  In the example, the URL is a Google Chart URL itself.

Sub addBrowser()
    'Application.CommandBars("Control Toolbox").Visible = True
    Dim anOLEObj As OLEObject, BrowserPresent As Boolean
    For Each anOLEObj In ActiveSheet.OLEObjects
        BrowserPresent = BrowserPresent Or TypeName(anOLEObj.Object) = "WebBrowser"
        Next anOLEObj
    If Not BrowserPresent Then
        Set anOLEObj = ActiveSheet.OLEObjects.Add(ClassType:="Shell.Explorer.2", Link:=False, _
            DisplayAsIcon:=False, Left:=461.25, Top:=293.25, Width:=190.5, Height _
            :=95.25)
        anOLEObj.Object.Name = "WebBrowser1"
        End If
    ActiveSheet.Shapes("WebBrowser1").OLEFormat.Object.Object.Navigate2 _
        "http://chart.apis.google.com/chart?cht=p3&chd=s:KW&chs=250x100&chl=Hello|World"
    End Sub

The code below adds the functions needed to map numerical data into one of the special codes that Google uses for its charts.  For more on the role these functions play see the Google Chart API documentation.  Examples of how to use the functions are in the subsequent sections of this document.

Function GoogleChartAPISimpleEncoding(DataToEncode, MaxVal)
    Dim X, Rslt As String
    Const simpleEncoding As String = _
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    Rslt = "s:"
    For Each X In DataToEncode
        Dim CurrVal As Single
        If TypeOf X Is Range Then
            CurrVal = X.Value
        Else
            CurrVal = X
            End If
        If Not IsNumeric(CurrVal) Then
            Rslt = Rslt & "_"
        ElseIf CurrVal >= 0 And CurrVal <= MaxVal Then
            Rslt = Rslt _
                & Mid(simpleEncoding, _
                    (Len(simpleEncoding) - 1) * CurrVal / MaxVal + 1, 1)
        Else
            Rslt = Rslt & "_"
            End If
        Next X
    GoogleChartAPISimpleEncoding = Rslt
    End Function
Function GoogleChartAPITextEncoding(DataToEncode, MaxVal)
    End Function
Function GoogleChartAPIExtendedEncoding(DataToEncode, MaxVal)
    Const ExtendedEncoding As String = _
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-."
    Dim X, Rslt As String
    Rslt = "e:"
    For Each X In DataToEncode
        Dim CurrVal As Single
        If TypeOf X Is Range Then
            CurrVal = X.Value
        Else
            CurrVal = X
            End If
        If Not IsNumeric(CurrVal) Then
            Rslt = Rslt & "__"
        ElseIf CurrVal >= 0 And CurrVal <= MaxVal Then
            CurrVal = Round(CurrVal / MaxVal * 4095, 0)
            Dim Val1, Val2
            Val1 = CurrVal \ 64
            Val2 = CurrVal Mod 64
            Rslt = Rslt & Mid(ExtendedEncoding, Val1 + 1, 1) _
                & Mid(ExtendedEncoding, Val2 + 1, 1)
        Else
            Rslt = Rslt & "__"
            End If
        Next X
    GoogleChartAPIExtendedEncoding = Rslt
    End Function

Inserting charts in the web browser

The code in addCharts below adds four charts to the web browser.  The first one is simply the basic chart "Hello World" chart from the Google documentation.  The second and fourth charts map data in a VBA array.  The third chart uses data in a worksheet range.  As mentioned earlier the code initializes the web browser so that the document in it contains a single DIV HTML element with the ID of AllCharts.  Then, it delegates to the addAChart procedure the task of adding each of the 4 charts.  Finally, it uses the enableEvents procedure to associate each added DIV with a VBA event-enabled variable of type HTMLDivElement.

Note that the 2 module level declarations are needed to persist handling of events inside the HTML document.

Option Explicit
Dim DocEvents As clsWebBrowser, ChartDivs As Collection
Sub addCharts()
    Set DocEvents = New clsWebBrowser
    Set ChartDivs = New Collection
    Set DocEvents.aBrowser = Sheet1.WebBrowser1
    With Sheet1.WebBrowser1
    .Height = 200: .Width = 420
    .Document.body.innerHTML = _
        "<head></head><body><div id='AllCharts'></div></body>"
        End With
    DocEvents.aBrowser_DocumentComplete Nothing, ""
    addAChart Sheet1.WebBrowser1, "cht=p3&chd=s:KW&chs=250x100&chl=Hello|World"
    addAChart Sheet1.WebBrowser1, "cht=lc&chd=" _
        & GoogleChartAPIExtendedEncoding(Array(160, 80, 40, 20, 10, 0), 200) _
        & "&chs=250x100"
    addAChart Sheet1.WebBrowser1, "cht=p3&chd=" _
        & GoogleChartAPISimpleEncoding(Range("H26:H28"), _
            Application.WorksheetFunction.Max(Range("H26:H28").Value)) _
        & "&chs=250x100&chl=Hello|there|World"
    addAChart Sheet1.WebBrowser1, "cht=lc&chd=" _
        & GoogleChartAPIExtendedEncoding(Array(0, 10, 20, 40, 80, 160), 200) _
        & "&chs=250x100"
    enableEvents
    End Sub

The addAChart routine adds a new DIV child in the AllCharts DIV within the WebBrowser document.  Then, it inserts an image tag (IMG) in the new DIV and sets the image source to the appropriate URL to generate the chart.  Note that the DIV floats in the document and the image element has a small margin.

Sub addAChart(ByVal aBrowser As WebBrowser, ByVal ChartURL As String)
    Dim ID As Integer
    With aBrowser.Document
    ID = .getElementsByTagName("div").Length
    With .getElementById("AllCharts")
    .innerHTML = .innerHTML & "<div id='" & ID _
        & "' style='float:left;background-color:00ffff'><img src='" _
        & "http://chart.apis.google.com/chart?" & ChartURL _
        & "' style='margin:""2px""'/></div>"
        End With
        End With
    End Sub

Finally, to establish event handling, there are two things that must happen.  First, the enableEvents routine creates a variable of class clsHTMLDiv, associates each HTML DIV element containing a chart with the "with events" aDiv variable in the class, and persists the variable through the ChartsDiv module level collection variable.

Sub enableEvents()
    Dim I As Integer, aDivObj As clsHTMLDiv, aDiv As HTMLDivElement
    With Sheet1.WebBrowser1.Document
    For I = 1 To .getElementsByTagName("div").Length - 1
        'Skip element zero in the for loop since it should be the "parent" _
         DIV (with ID='AllCharts')
        Set aDivObj = New clsHTMLDiv
        Set aDivObj.aDiv = .getElementById(CStr(I))
        On Error Resume Next
        ChartDivs.Remove CStr(I)
        On Error GoTo 0
        ChartDivs.Add aDivObj, CStr(I)
        Next I
        End With
    End Sub

Second, a WithEvents variable can only exist in a class module.  That is delegated to the clsHTMLDiv class described in the next section..

Responding to events in the browser

In the current implementation, the code in the aDiv_ondblclick event handler (see the clsHTMLDiv class module below) does nothing more than display a message about the ID of the DIV that triggered the event.  However, in a more useful implementation, one could display a userform with the controls required to reconfigure the chart in that particular DIV.

The clsHTMLDiv class module:

Option Explicit
Public WithEvents aDiv As HTMLDivElement
Private Function aDiv_ondblclick() As Boolean
    MsgBox "double click: " & aDiv.ID
    End Function

 

Modifying the browser display

Since the DIV elements are configured to float inside the web browser window, changing the dimensions of the window cause the charts to automatically move in the window.  Below is an example of the browser with the width halved and the height doubled.