Left-click for next slide. Move the mouse cursor to the bottom right of the page; additional controls will appear. This slide is based on the S5 presentation system.
| Server | Client |
|---|---|
| Many diverse tools available, lots of architecture choices. | Fewer choices (less developed area, browser limitations). |
| Can develop a single codebase to serve all clients. | Browser compatibility complicates development. |
| Easier to secure. | Harder to secure; client side code is also easy to copy. |
| Dynamic image data generation can strain resources. | Client-side programs reduce server load. |
| Loading large images can rapidly consume bandwidth. | Loading large client programs can rapidly consume bandwidth (but a well-designed network protocol can conserve bandwidth). |
| Low interactivity, simple solution. | Can be highly interactive, but more complex. |
Use a charting package like JFreeChart to automate the presentation of common data that is found in any organization.
Here is a simulated survey, illustrated using JFreeChart.

1. Initialize. Remember to run the program with the -Djava.awt.headless=true option if X is not installed on your server.
import java.io.*;
import java.awt.image.BufferedImage;
import org.jfree.chart.*;
import org.jfree.data.general.DefaultPieDataset;
...
2. Insert the data.
...
DefaultPieDataset data = new DefaultPieDataset();
for (int ii = 0; ii < values.length; ++ii) {
data.setValue(labels[ii], values[ii]);
}
JFreeChart chart = ChartFactory.createPieChart3D
(new String(name), data, true, true, true);
//Make the chart translucent.
chart.getPlot().setForegroundAlpha(this._opacity);
BufferedImage buffer = chart.createBufferedImage(this._width,
this._height);
return ChartUtilities.encodeAsPNG(buffer);
//ChartUtilities.writeBufferedImageAsPNG(ostream, buffer);
//ChartUtilities.saveChartAsPNG(new File("survey.png"), chart,
// this._width, this._height);
...
Not all graphics fit into typical categories (such as pie or bar charts). Low-level libraries like GD are very useful for creating non-standard visual representations.
Suppose you want to show the location of various items (products in a warehouse, trucks on the road, etc.) on a map. This is easy to implement with GD. Here is an example, written in Python.

1. Initialize.
import gd
import cStringIO
image = gd.image('map.png')
tag_color = image.colorAllocate((255, 0, 0))
txt_color = image.colorAllocate((0, 0, 0))
2. Mark item's location with a circular pattern.
if (itemname):
for ii in range(30, 5, -5):
image.arc((item_x, item_y),
(ii, ii), 0, 360, tag_color)
image.fillToBorder((item_x, item_y),
tag_color, tag_color)
image.string(gd.gdFontGiant, (0, 0),
'Location of '+itemname, txt_color)
3. Generate the generated image in memory, and return it.
capture = cStringIO.StringIO()
try:
image.writeGif(capture)
return ("image/gif", capture.getvalue())
finally:
capture.close()
With Graphviz, you can regularly regenerate "Visio-style" diagrams (including very large ones) to reflect current data.
Here is an organizational chart generator, using Graphviz. This is a Python program, which utilizes the Python bindings supplied by Graphviz 2.8.

import os
import time
import sys
#Replace with your own location, or append to PYTHONPATH
sys.path.append('/usr/local/lib/graphviz/python')
import gv
g = gv.graph('Orgchart')
#Set default node properties
proto = gv.protonode(g)
gv.setv(proto, 'shape', 'box')
gv.setv(proto, 'fillcolor', 'blue')
gv.setv(proto, 'style', 'filled')
gv.setv(proto, 'fontcolor', 'white')
2. Specify all superior subordinate relationships. Set employee titles, and mark all the important people.
for boss, underling in bossof:
rel = gv.edge(g, boss, underling)
for someone in important:
gv.setv(gv.findnode(g, someone), 'fillcolor', 'red')
for lord in title:
gv.setv(gv.findnode(g, lord), 'label',
lord+'\n('+title[lord]+')')
3. Specify the layout, then render the graph. Currently, the Graphviz Python bindings will only render to a named file. As a workaround, you can create temporary filenames (make sure to do so securely, and to clean up the file after you are done). There are also alternative Python bindings, which are listed on the Graphviz website.
gv.layout(g, 'dot') gv.render(g, 'gif', 'orgchart.gif')
Dynamic web content has evolved greatly since the early days. Instead of inefficient CGIs, a powerful high-level language can now run directly in the web server's context (e.g. Apache with Python/mod_python, Perl/mod_perl or Java/Tomcat). Sophisticated frameworks such as Zope and JBoss exist to support complex applications.
Nevertheless, dynamic content remains a difficult problem. Scalability is always an issue. You also need to configure your web server appropriately. This often means coordinating between departments and limiting the variety of tools. Too many tools results in a complex server environment, which can harm stability and performance.
On the other hand, serving static content is a far easier. You should always consider solutions which convert dynamic content into static content. Fortunately, visualization applications can often use this approach.
It is important to realize that you do not need to update your diagrams until the underlying data changes. Between such changes, the diagram -- which is just an ordinary image file -- can be used by the web server many times. From the web server's point of view, the application is static. Optionally, you can still use dynamic techniques on the web server to control access to the static images.
With wz_jsgraphics you can develop cross-browser, interactive graphical applications now.
This is a variation on example 2. The new code uses an image from the server, but draws the item marker on the client, using wz_jsgraphics. Enter items "cubevan11" or "flatbed28" in the demo's "Item" textbox.

1a. The ItemFinder class, constructor.
var ItemFinder = Class.create();
ItemFinder.prototype = {
initialize: function(url, markf) {
this._url = url;
this._markf = markf;
},
...
...
getitem: function(name) {
markf = this._markf;
new Ajax.Request(this._url+name, {
method: 'get',
onSuccess: function(r) {
coords = r.responseText.split(',',2);
markf(Number(coords[0]), Number(coords[1]));
},
onFailure: function(e) {
alert('Error (make sure item exists)');
}});
}
}
ItemFinder retrieves the item's location from the server without redrawing the page. After receiving the item's coordinates, ItemFinder passes them on to a callback function for actual drawing. Note that the network protocol is very simple -- just the two coordinates of the item, separated by a comma.
See also the pure JavaScript animation at http://www.openlight.com/visualizing.html
2. Load the necessary libraries Note the <div> at the bottom. The drawing will take place there.
... <script src="/s/prototype.js" type="text/javascript"></script> <script src="/s/wz_jsgraphics.js" type="text/javascript"></script> <script src="/s/itemfinder.js" type="text/javascript"></script> </head> <body> <div id="map"> <img src="map.png" alt="Item Location" width="283" height="320"> </div> ...
3. The controls which send user-generated events to the application.
...
<div>
<label>Item: </label><input id="item" type="text" size="15"/>
<button onclick="finder.getitem($F('item'))">Get Location</button>
</div>
...
...
<script type="text/javascript">
var canvas = new jsGraphics("map");
canvas.setColor("#00ff00");
canvas.setStroke(2);
var finder = new ItemFinder('items/', marksquares);
function marksquares(x, y) {
canvas.clear();
for (var ii = 32; ii > 0; ii -= 8) {
canvas.drawRect(x-ii/2, y-ii/2, ii, ii);
}
canvas.paint();
}
</script>
...
| Generic | Specialized |
|---|---|
| Harder to design, extensible | Easier to design, but less extensible |
| Difficult for competitors to copy | Easier to copy -- most functionality is on the client |
| Require lots of interaction with the server | Can operate with little server input |
Flash is not just for dumb movies -- it is an advanced programming environment. Flash-based clients can be very sophisticated, and browser support is excellent.
Replace all paths to where MTASC resides on your system. The header option creates a Flash "movie" from scratch. Specify the file where your main resides; the compiler will find the other source code files automatically.
/opt/bin/mtasc -cp /opt/lib/mtasc/ -version 6 -strict \ -swf canvas.swf -header 800:600:20 -main FdUI.as
We finish with a more complex example, which embodies a lot of the concepts covered in this talk. Here is a complete Open Source monitoring application (written by the presenter). You can download all the source code and view the demo at http://www.openlight.com/fdui

class Canvas {
...
public function draw_poly(moviename:String, points:Array,
color:Number):Void {
var movie:MovieClip = this.parent[moviename];
with(movie) {
beginFill(color,100);
lineStyle(0,color,100);
moveTo(points[0][0],points[0][1]);
for (var ii:Number = 1; ii < points.length; ++ii) {
lineTo(points[ii][0],points[ii][1]);
}
lineTo(points[0][0],points[0][1]);
endFill();
}
}
...
You can create movie clips as your ActionScript program runs. Movie clips layer on top of one another. If you set one clip to be partially transparent, the one underneath will show through.
class Canvas {
...
public function clear(moviename:String):Void {
this.parent.createEmptyMovieClip(moviename,
Number(this.names[moviename]));
}
...
}
ActionScript supports interfaces, similar to Java. Here is the Protocol interface from Flightdeck-UI Online. Making the protocol replaceable is very important for extensibility, and greatly eases security patching.
interface Protocol {
public function init(hostdata:LoadVars, id:String):Void;
public function input(hostdata:LoadVars):Object;
public function output(hostdata:LoadVars):Void;
public function remote_log(msg:String, level:String):Void;
}
Even with client-based visualization, the server usually supplies the underlying data. Hence, the "Relative Static" approach also works here. Flightdeck-UI Online uses a separate daemon process to periodically regenerate the data, and save it to a file.
$ cat demo.txt reset\needles\2|poly\needles\697.0\501.5\691.0\498.5\ ...
Here, you can see the server's instructions to draw polygons into the "needles" movie clip of the Flash client. This updates the gauges.
Protocol version check.
class Proto_X1 implements Protocol{
...
public function input(hostdata:LoadVars):Object {
if (!Number(hostdata.proto_x1)) {
this.remote_log("Wrong protocol version; expected x1")
return null;
...
In addition to the dedicated data generator daemon, Flightdeck-UI Online uses Apache with a mod_python script to control the client. The code below shuts off outdated clients.
def handler(req):
...
ver = fields.getfirst('ver','').strip()
...
if not versions.has_key(ver): #!!! Length-limit this?
req.write('command='+Client.shutoff_panel('Version Expired ...
...
Of course, the client must allow the server to control it. During initialization, the Flightdeck-UI Online client registers a "stop" command ("stop_cmd", below) for this purpose.
public function FdUI() {
...
var protocol:Protocol = new Proto_X1({test:[this,"test_cmd"],
rclr:[this,"regclr_cmd"],
rsnd:[this,"sndreg_cmd"],
snd:[this,"sndplay_cmd"],
arc:[this,"arc_cmd"],
poly:[this,"poly_cmd"],
label:[this,"label_cmd"],
...
stop:[this,"stop_cmd"]},
this.version,
this.max_command_len);
...