Sybase iAnywhere SQL AAnywhere Mobile and Embedded Database

Peering Behind the Browser


Exploring the World of Data Behind the Tags

header image

HTML5′s Canvas with SQL Anywhere

March 16th, 2009 · No Comments

A few months back, I did a series of posts that used SQL Anywhere to create SVG graphs that were displayed in the browser. Today I would like to explore another browser technology for creating graphics, the HTML canvas tag. While the canvas tag is part of the developing HTML5 spec, it has already been implemented in most browsers (with the notable exception being Internet Explorer). The main differences between SVG and Canvas are:

  1. SVG uses vector graphics, Canvas uses raster graphics. This means that SVG is better when the scale is unknown, or when you need the ability to manipulate specific parts of the image. Canvas is better with a fixed size, and when you want to paint arbitrary pixels.
  2. SVG is described in a declarative, XML markup language. Canvas is created procedurally in JavaScript.

For the series on SVG, we created the declarative SVG markup on the server and then displayed it statically on the client. For this demonstration of the canvas, we will be creating a dynamic, animated graph that polls the server for values. In the end, the graph will look like a scrolling version of:

sawtooth3.GIF

The HTML canvas is used by first declaring a canvas tags in the HTML source. The developer then uses a JavaScript API to paint pixels on the image. It may sounds somewhat limiting, but it can be quite powerful. As an example the entire Bespin front-end is rendered entirely in the canvas. Or, for a more fun example, here is a demonstration of using the HTML canvas for a Doom-like ray-caster written entirely in JavaScript.

The goal of this demo is to create a scrolling performance graph that displays some arbitrary value in the SQL Anywhere server. The first thing we need is to create a couple of services. The first will return the HTML page containing the canvas. The second is the procedure that we will poll to get the graph values.

1
2
3
4
CREATE SERVICE "root" TYPE 'RAW' 
  AUTHORIZATION OFF USER DBA URL OFF AS CALL sp_root();
CREATE SERVICE "getGraphData" TYPE 'RAW' 
  AUTHORIZATION OFF USER DBA URL OFF AS CALL sp_getGraphData();

The getGraphData service in turn calls the sp_getGraphData procedure. We will design our JavaScript such that it expects back an arbitrary piece of JSON containing up to three values to graph simultaneously. For example, a valid object would be:

1
2
3
{ cpu         : 10,
  memory      : 15,
  dirty_pages : 5 }

For our first example, we will use something simple and meaningless (a random number between 0 and 100):

1
2
3
4
CREATE PROCEDURE "sp_getGraphData"()
BEGIN
  SELECT '{rand:' || (RAND() * 100) || '}';
END;

The JavaScript that creates the graph is quite simple, and is shown below. After the HTML is loaded, the getData() function is called. getData makes immediate Ajax request to the getGraphData service, and gets a JSON object in response. The Ajax callback function takes the response and adds it to moving queue of data to graph. It then iterates through the queue, updating the canvas to display the new graph. Once the graph is drawn, it sleeps for a configurable amount of time and then polls again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ***** START OF CONFIG PARAMS ***** //
var WIDTH = 300;               // also must change HTML canvas width
var HEIGHT= 50;               // also must change HTML cnavas height
var SAMPLE_RATE_IN_MS = 50;
var MAX = 59;
var MIN = 0;
// ***** END OF CONFIG PARAMS ***** //
 
var norm = HEIGHT/(MAX - MIN);
var queue = [];
var colour_map = ["rgb(0,0,255)", "rgb(0,255,0)", "rgb(255,0,0)"];
 
function getData() {
  jQuery.getJSON( 'getGraphData', null, function (data) {
    // Get reference to the canvas
    var canvas = document.getElementById('canvasGraph');
    var ctx, colour, pos, index, item;
 
    // Add new value onto our "queue"
    if (queue.push(data) > WIDTH) {
      // if we have more than WIDTH elements, delete the oldest
      delete queue[queue.length - (WIDTH + 1)]
    }
    if (queue.length > 2) {
      if (canvas.getContext){
        ctx = canvas.getContext('2d');
        ctx.clearRect(0,0,WIDTH,HEIGHT)       // Clear the drawing area
        colour = 0;                           // Set first colour
        // Iterate through all the items in the data object
        // and draw the graph line with normalization
        for (item in queue[queue.length - 1])
        {
          ctx.strokeStyle = colour_map[colour];
          pos = WIDTH;
          index = queue.length - 1;
          ctx.beginPath();
          ctx.moveTo(pos, HEIGHT-((queue[index][item]-MIN)*norm));
          while(pos > 1 && index > 1)
          {
            pos = pos - 1;
            index = index - 1;
            ctx.lineTo(pos, HEIGHT-((queue[index][item]-MIN)*norm));
          }
          ctx.stroke();
          colour++;
        }
      }
    }
    // Wait for SAMPLE_RATE_IN_MS, then get more data
    setTimeout(getData, SAMPLE_RATE_IN_MS);
  });   
}

To use this graph for something meaningful you simply need to do two things:

  1. Define the sp_getGraphData() to return some meaningful data
  2. Set the WIDTH, HEIGHT, SAMPLE_RATE_IN_MS, MAX, and MIN constants

For example, to create a graph of the current second value (MIN=0, MAX=59) on a 100×400 graph, with a 1 second resolution, we have to: (view full SQL code)

  1. Create the appropriate sp_getGraphData()
    1
    2
    3
    4
    
    CREATE PROCEDURE "sp_getGraphData"()
    BEGIN
      SELECT '{second:' || (SELECT SECOND(NOW())) || '}';
    END;
  2. Set the proper config settings in the JavaScript
    1
    2
    3
    4
    5
    6
    7
    
    // ***** START OF CONFIG PARAMS ***** //
    var WIDTH = 400;               // also must change HTML canvas width
    var HEIGHT= 100;               // also must change HTML cnavas height
    var SAMPLE_RATE_IN_MS = 1000;
    var MAX = 59;
    var MIN = 0;
    // ***** END OF CONFIG PARAMS ***** //

The result (as you would expect), is a sawtooth graph:
sawtooth.GIF
If we change the resolution to 50 ms, the result is:
sawtooth2.GIF
Now it appears as a stepped sawtooth. This makes sense since we are sampling many more times a second.

The lowest poll rate that I tried was 10ms, and neither Chrome nor Firefox broke a sweat keeping up with the graph. There are a number of limitations with this simplistic graph (notably no scale or labels on the output), but it is easily expandable.

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks

Tags: Technology