DIY Graphing Calculator: Google Visualization Grails Plugin

The last time that I had to meet customer requirements for creating and rendering charts, I ended up building a complex, data-driven, dynamic wizard style application which was built on top of a large API wrapper (that I also wrote) around a client component of a third-party chart rendering application (already purchased) that ran on its own server. The final “submit” step in the wizard led to a large chunk of data being handed off to the third-party app server. My application then retrieved the image bytes from that server, and stored those bytes along with the chart metadata in our own database for both immediate rendering and further use. I made several videos (with engaging background music) for user instruction. One of the videos illustrated how the wizard could be used to graph multiple mathematical functions on multiple axes, to serve as a demonstration of how to use some of the more advanced features of the application. It was a tricky business and it involved several detailed steps. The proper parameters needed to be set just to get the scaling and positioning right. If you wanted to make slightly more than basic customizations, such as adding the color maroon to the default color palette, you had to change configuration files on the third-party app server. It was a massive undertaking, and it took several months to deliver a useful and useable application. At that time, there were a few Javascript charting tools that were starting to surface, but nothing rich and complex enough – let alone cheap enough to warrant replacing an already purchased product – to meet our users’ needs. I’ll dispense with the “back in my day” dialog now, as it is not only a dreadfully boring way to start a blog post, but also all of this occurred only 5 years ago. Nevertheless, that amount of time is an eternity in the world of software development. Fast forward to now. Today we have rich web-enabled client-side chart rendering tools that are quite powerful. Many of them can be used for free (double-check the licensing agreements first). These modern tools can create charts that are immensely complex, bind to dynamic server-side data, handle vast quantities of information, and they can be interactive. Charts that can talk to each other…even move.

In this blog post I will walk you through the creation of a function graphing tool in the form of a Grails application that uses Google Chart Tools via the Google Visualization Grails plugin. I will also be using the exp4j library to parse the mathematical expressions and calculate the results. Google Chart Tools provides us with a Javascript API to create and render a multitude of chart types, each with its own configuration options. The Google Visualization Grails plugin further simplifies this by providing us with an easy to use GSP tag library, which ultimately constructs the necessary Javascript Objects and Google API calls. To follow along, you only need to have Grails installed and configured.  Most of these instructions are at the command line level. It should be simple enough to accomplish all of these steps in the IDE of your choosing. A link to the project source code is provided at the bottom of this post.

Let’s dive in!

Fire up a terminal window and navigate to a directory where you would like to create the application, and then type:
grails create-app graphing-calculator

Let’s start by taking care of our dependencies. We’ll grab exp4j first.
cd graphing-calculator/lib
wget http://projects.congrace.de/exp4j/exp4j-0.3.5.jar

Now open BuildConfig.groovy (in grails-app/conf) and add the Google Visualization plugin to the plugins section.

plugins {

	compile ":google-visualization:0.6"
}

That’s all we need for the dependencies. Now it’s on to the fun stuff.
First, let’s create the primary controller that we will be using in this app:
grails create-controller calculator

Open the newly created CalculatorController.groovy file (grails-app/controllers) and add the following:

import de.congrace.exp4j.Calculable
import de.congrace.exp4j.ExpressionBuilder

class CalculatorController {

    def graph() {
        def graphData = [] //data points to be used in drawing the graph
        def expression = "3 * sin(x) - 2 / (x - 2)" //a mathematical function to evaluate
        def graphDataColumns = [['number', 'x'], ['number', 'f(x)=' + expression]] //types and labels for axes

        //evaluate the function on the interval [-5, 5]
        for(def varX=-5.0; varX<=5.0; varX+=0.1) {
            Calculable calc = new ExpressionBuilder(expression)
                    .withVariable("x", varX)
                    .build()

            def result = calc.calculate();

            graphData.add([varX, result]) //add the result to the collection of data points
        }

        //add the following to the model; graphData and graphDataColumns are fed into google visualizations
        [graphData: graphData, graphDataColumns: graphDataColumns, expression: expression]
    }
}

Create the file grails-app/views/calculator/graph.gsp and open it.

First, import the Google Visualization API.

<gvisualization:apiImport/>

Then add the following to the body:

<gvisualization:lineCoreChart hAxis="${new Expando([title: 'x')]}"
                              vAxis="${new Expando([title: 'y', viewWindowMode: 'explicit', viewWindow: new Expando([min: -5, max: 5])])}"
                              curveType="function"
                              elementId="linechart"
                              width="${400}"
                              height="${240}"
                              title="Function Graph"
                              columns="${graphDataColumns}"
                              data="${graphData}" />
<div id="linechart"></div>

Now save it, go back to the command line and type:
grails run-app

After it starts up, open a browser window and go to http://localhost:8080/graphing-calculator/calculator/graph

You should see something similar to the image below. Figure 1

To understand how the chart is rendered, we need to look at the Google Chart Tools documentation for a line chart.

As you can see in the lineCoreChart tag, we pass in the following essential parameters:
elementId, columns, and data. This signifies which element (div) to draw the chart in, the names of the axes, and the data points for those axes respectively.

The width, height, title, curveType, hAxis, and vAxis attributes are all part of the chart’s configuration options. Each chart has many configuration options and you can get really creative with them. Again, please refer to the documentation for details.

Setting curveType=”function” gives us the nice fluid curvy lines that we would see on a nice graphing calculator, rather than straight lines that directly go from point to point. The hAxis and vAxis attributes allow us to define attributes for the horizontal axis and vertical axis respectively.

Note: hAxis and vAxis are of type Object (a Javascript Object). In the above example, we pass an instance of the Groovy Expando class initialized with a Map of the attributes we want to define. The Google Visualization plugin will convert this to the appropriate Javascript Object that the Google Chart Tools API expects. For example, something that ultimately ends up looking like this in Javascript:
{title: 'Hello'}

Could be derived from this:
<gvisualization:lineCoreChart hAxis="${new Expando([title: 'Hello'])}" ... />

In this initial example, for the y-Axis, I wanted to limit the minimum and maximum grid lines that would be drawn. This is done by setting viewWindowMode to explicit, and then specifying values for the viewWindow.min and viewWindow.max attributes. As you can see in the example code, multiple instances of Expando are used to accomplish this.

In your browser, if you view the source, you should see something like this:
visualization.draw(visualization_data, {hAxis: {title: 'x'}, vAxis: {title: 'y', viewWindowMode: 'explicit', viewWindow: {min: -5, max: 5}}, curveType: 'function', width: 400, height: 240, title: 'Function Graph'});

This is Javascript code that the Google Visualization Grails plugin generates in order to create and render the chart via the Google API.

Note: This is very useful for debugging. If you are having problems with the way a chart is rendering (or not rendering at all), simply view the source on the page and verify the accuracy of the content that is being sent along to the Google API.

Now let’s enhance our graphing calculator by allowing the input of mathematical expressions, sticking with just a single variable x.

In the CalculatorController, replace the expression definition with the following:
def expression = params.expression ?: "3 * sin(x) - 2 / (x - 2)"

Then in the browser append the following to the end of the url:
?expression=tan(x)

You should see something similar to this. Figure 2

Enter your own expressions and play around with it a bit. Refer back to the exp4j documentation for information about acceptable input for mathematical expressions.

Step it up!

Now that you’ve seen the basics, let’s leverage the power of Grails to make a tool that’s more useful. It would certainly be nice to enter the expression in a form, define intervals, add additional variables and assign values to variables, etc. We could simply create a form to allow these inputs, but it also might be nice to store all of these inputs so that we can recall them at a later time. Let’s create some domain classes to do this.

grails create-domain-class graph
grails create-domain-class function

The Graph class will hold basic data about the rendering of the Graph itself. The Function class will store the data for a given function, including the mathematical expression.

Let’s open up Graph.groovy and add the following properties:


BigDecimal verticalLowerBound
BigDecimal verticalUpperBound
BigDecimal horizontalLowerBound
BigDecimal horizontalUpperBound
String title
String width
String height

For now, let’s make everything nullable, except for the “title”.

 static constraints = {
        verticalLowerBound nullable: true
        verticalUpperBound nullable: true
        horizontalLowerBound nullable: true
        horizontalUpperBound nullable: true
        width nullable: true
        height nullable: true
}

Let’s design it so that we can graph multiple functions at once. We can do this by defining a one-to-many relationship to the Function class.

static hasMany = [functions: Function]

Now let’s open up the Function class and define a few attributes. For the sake of simplicity we’ll limit ourselves to two variables, x and y.

    
String expression
BigDecimal xValue
BigDecimal yValue
BigDecimal xLowerBound
BigDecimal xUpperBound

And for now, let’s make everything nullable, except for the “expression” field.

static constraints = {
        xLowerBound nullable: true
        xUpperBound nullable: true
        xValue nullable: true
        yValue nullable: true
} 

Now to get rolling quickly, let’s generate controllers and views for our new domain objects by using that nice out-of-the-box Grails CRUD functionality.

grails generate-all graphing.calculator.Graph
grails generate-all graphing.calculator.Function

Note: If you make a mistake or wish to change a domain class later, you can run the above commands to regenerate the controllers and views. Just answer ‘a’ at the prompt to overwrite the existing files.

Now run the app with the grails run-app command and you should be able to do CRUD operations with the new domain classes.

Note: If you are using the Grails default datasource configuration, the data will be stored in an a temporary database, in memory. The database and its data are destroyed when the application is terminated.

Create and persist an instance of Function first by going here:
http://localhost:8080/graphing-calculator/function/create

Enter the following values:
XLower Bound: -5
XUpper Bound: 5
Expression: x^2

And click “Create”

Now let’s create and persist an instance of Graph, by going here:
http://localhost:8080/graphing-calculator/graph/create

And enter the following values:
Vertical Lower Bound: -5
Vertical Upper Bound: 5
Horizontal Lower Bound: -5
Horizontal Upper Bound: 5
Width: 450
Height: 240
Functions: graphing.calculator.Function: 1 (make sure this is selected)
Title: First Function Graph

And click “Create”

Now let’s go back to the CalculatorController and add a method to use the persisted data. To get started, let’s open up the GraphController that Grails generated, and copy the contents of the “show” method, and paste them in a new method within the CalculatorController called graphStoredData.

def graphStoredData(Long id) {
        def graphInstance = Graph.get(id)
        if (!graphInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'graph.label', default: 'Graph'), id])
            redirect(action: "list")
            return
        }

        [graphInstance: graphInstance]
    }

And then let’s add all the content that’s in the graph method, and rework it to use the values stored in the domain classes. The method should look something like this:

 def graphStoredData(Long id) {
        def graphInstance = Graph.get(id)

        if (!graphInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'graph.label', default: 'Graph'), id])
            redirect(action: "list")
            return
        }

        def graphData = [] //data points to be used in drawing the graph
        def function = graphInstance.getFunctions()?.first()
        def expression = function?.expression //a mathematical function to evaluate
        def graphDataColumns = [['number', 'x'], ['number', 'f(x)=' + function?.expression]] //types and labels for axes

        //evaluate the function on the stored interval
        for(def varX=function?.xLowerBound; varX<=function?.xUpperBound; varX+=0.1) {

            Calculable calc = new ExpressionBuilder(expression)
                    .withVariable("x", varX)
                    .build()

            def result = calc.calculate();

            graphData.add([varX, result]) //add the result to the collection of data points
        }

        //add the following to the model; graphData and graphDataColumns are fed into google visualizations
        [graphData: graphData, graphDataColumns: graphDataColumns, expression: expression]
    }

And finally add the instance of the Graph domain class to the model.

[graphData: graphData, graphDataColumns: graphDataColumns, expression: expression, graphInstance: graphInstance]

Now let’s make a copy of graph.gsp and call it graphStoredData.gsp, and open it.

Let’s keep it simple for now, by just replacing most of the literal values with the values from graphInstance.

<gvisualization:lineCoreChart hAxis="${new Expando([title: 'x'])}"
                              vAxis="${new Expando([title: 'y', viewWindowMode: 'explicit',
                                      viewWindow: new Expando([
                                              min: graphInstance?.verticalLowerBound,
                                              max: graphInstance?.verticalUpperBound])])}"
                              curveType="function"
                              elementId="linechart"
                              width="${graphInstance?.width}"
                              height="${graphInstance?.height}"
                              title="${graphInstance?.title}"
                              columns="${graphDataColumns}"
                              data="${graphData}" />

Now, by going to the following url, we should see a nice parabola.
http://localhost:8080/graphing-calculator/calculator/graphStoredData/1/

Figure 3

Play around and add new instances of Graph and Function. Then come back to the above url to view the results. Note that for now, only the first function stored in the Graph instance will be drawn.

Moving along, let’s create another instance of Function, with the same parameters as the first, but change the expression to:
-tan(x)

Now create a new Graph instance with the same parameters as the first Graph instance, but select Function 2 (and only Function 2) in the “Function” combo box .

View the graph and verify that it renders correctly. (you should not see a parabola this time)
http://localhost:8080/graphing-calculator/calculator/graphStoredData/2/

Now let’s put together a multi-function Graph. Create a new Graph instance with the following parameter values:

Vertical Lower Bound: -20
Vertical Upper Bound: 20
Horizontal Lower Bound: -5
Horizontal Upper Bound: 5
Width: 500
Height: 300
Functions (Select both Function 1 and Function 2)
Title: Multi-Function Graph

Go to:
http://localhost:8080/graphing-calculator/calculator/graphStoredData/3/

We still only see the first function drawn, because we haven’t done anything to enable multi-function graphing capabilities. For that, let’s revisit the graphStoredData method of the CalculatorController.

We will need to build up the chart columns and the data from the values of the domain classes. To do this, we’ll need to iterate over all of the instances of the Function class and make the necessary calculations. We can then store the results temporarily in a Map and then iterate over them later to create data points that resemble the following (which the Google API expects):

[point on x-Axis, result of function 1, result of function 2, ... result of function n]

At this point it’s a good idea to remove the expression from the model, since we can no longer guarantee that there will just be one.

Ultimately, the graphStoredData method should end up looking something like this:

def graphStoredData(Long id) {
        def graphInstance = Graph.get(id)

        if (!graphInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'graph.label', default: 'Graph'), id])
            redirect(action: "list")
            return
        }

        def graphData = [] //data points to be used in drawing the graph
        def functions = graphInstance.getFunctions()
        def graphDataColumns = [['number', 'x']] //types and labels for axes
        def allResults = new ArrayList<HashMap>() //store the results for each function's calculations here

        functions?.each() { function ->
            def results = new HashMap()
            graphDataColumns.add(['number', 'f(x)=' + function?.expression]) //add the type and label for each function
            def expression = function?.expression //the mathematical function to evaluate

            //evaluate the function on the stored interval
            for(def varX=function?.xLowerBound; varX<=function?.xUpperBound; varX+=0.1) {

                Calculable calc = new ExpressionBuilder(expression)
                        .withVariable("x", varX)
                        .build()

                def result = calc.calculate();

                results.put(varX, result) //these results will be used to build up the data points
            }

            allResults.add(results)
        }

        //iterate over the results for each function and add the data points to graphData
        def keySet = new TreeSet(allResults.get(0)?.keySet()) //using TreeSet will sort the keys

        //each key represents a point on the x-Axis
        keySet.each() { key ->
            def finalResults = [] //we will build this up as [x, result 1, result2, ...result n]
            finalResults.add(key) //the point on the x-Axis

            for(def i=0; i<allResults.size(); i++) {
                def results = allResults.get(i)
                finalResults.add(results.get(key)) //the result of the calculation for each function
            }

            graphData.add(finalResults)
        }

        //add the following to the model; graphData and graphDataColumns are fed into google visualizations
        [graphData: graphData, graphDataColumns: graphDataColumns, graphInstance: graphInstance]
    }

Now, if we go back to the url again:
http://localhost:8080/graphing-calculator/calculator/graphStoredData/3/

We should see both functions drawn on the graph.
Figure 4

Try adding another Function or two to the Graph instance and viewing it again. It should render correctly.

Finish it up!

The final step in this demonstration application is to bring it all together and incorporate a form into the main page. Most of the necessary changes are pretty trivial. So at this point, I encourage you to download the source code, have a look at it, run the application, and play around with it. You will learn a lot more by doing so. Below is a quick summary of the remaining changes.

1. A new GSP file named createAndDisplay.gsp with the following notable content

a. A <g:select> wired up to Graph.list() directly. The text displayed for each option is the expression of the first Function instance that belongs to the Graph instance. In addition, if the Graph instance has more than one function, an ellipsis (…) is displayed to give the user a hint that the graph drawn will have more than one function.

<g:select name="selectedGraphId"
                          from="${Graph.list()}"
                          optionValue="${{it.functions?.first()?.expression + '' + (it.functions?.size() > 1 ? '...' : '')}}"
                          value="${params?.id ?: ''}"
                          noSelection="['':'---Choose Existing---']"
                          optionKey="id" onchange="this.form.submit()"/>

b. A small form for a Function instance, essentially borrowed straight from the _form.gsp file that was created during the grails generate-all step for the Function domain class.

2. New methods in CalculatorController

Most notably a save() method that creates a new Graph instance with some default parameters. The method then creates a new instance of Function from the parameters supplied on the form in createAndDisplay.gsp. It then attaches that Function instance to the Graph instance and saves it. Finally, it redirects back to createAndDisplay, passing along the new id so that the function can be graphed and rendered in the view. The index() method now redirects to createAndDisplay.

3. Final display changes

a. A div positioned to the right of the form that will display the graph, if the id parameter exists. This is simply done with the <g:include> tag.


<g:if test="${params.id}">
      <g:include action="graphStoredData" params="[id: params.id]"/>
</g:if>

b. Navigation links added to chartAndDisplay.gsp so that the user can easily access the CRUD functionality for the Graph and Function domain objects.

 

After all is said and done, we have ourselves a usable function graphing app!
Figure 5

Final Challenge!

Lastly, I would like to present you with a challenge. Build a Combo Chart that shows a curved line which is representative of some mathematical function, and bars that represent the area underneath the curve of that function using the <gvisualization:comboCoreChart/> tag. Nerd out and have fun with it!

Download the source from the project page on bitbucket:
https://bitbucket.org/sconnelly/graphing-calculator

One thought on “DIY Graphing Calculator: Google Visualization Grails Plugin

  1. Julie Chi says:

    a good job.

  2. Gedion says:

    Very interesting. Could not put it down last until I could no longer keep my eyes open anymore.

    Concise and clear. As a newbie to groovy and google chart tools, I was able to follow this walk through line by line and implement it on a newly created groovy app.

    In the coming week, I’m going to take on your combo challenge.

    There is one syntax typo in this page. In the first section, in graph.gsp, the brackets do not match.

    <gvisualization:lineCoreChart hAxis="${new Expando([title: 'x')]}" should be <gvisualization:lineCoreChart hAxis="${new Expando([title: 'x'])}"

  3. Steve Connelly says:

    Thanks Gedion. Let us know how it goes with the challenge!

    I’ll try to get that typo fixed.

  4. Jon Polaski says:

    Fantastic article!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*