Resolver Hacks contains information and code for doing fun and useful things with Resolver One. This site is created and maintained by Michael Foord, not by Resolver Systems. Please read the disclaimer before using any of the code on this site.
Follow Michael on:
Charting with Gnuplot

Contents
Introduction
This article is about two things:
- Producing plots of 3 dimensional surfaces using Gnuplot
- Integrating Resolver One with an external application
You can download this demo as a zipfile. It is quite big because it includes the gnuplot binary needed to create the image, plus the Python support module for generating the data file:
Gnuplot is an advanced charting program, capable of producing an enormous range of different plots. The images it produces aren't as glitzy as other charting packages, but for plotting 3D surfaces there is no competition.
In this example we fill a grid with data representing the 'Z-coordinate', generated from a formula. We save this data as a file that Gnuplot can read, and then launch gnuplot with a generated script to produce the image.
Generating the Data

The formula we will use to generate the data is:
z = sin(x*x + y*y) / (x*x + y*y)
The dimensions of the shape are from -3 to 3 on the X and Y axes.
Although this might seem to be a frivolous example, Resolver One is in production use doing something very similar - generating images of surfaces that represent risk contours from simulations of power station explosions. The data is imported into Resolver One, which uses gnuplot to produce plots of the interesting parts.
Turning the formula above into a grid of data is very easy:
sheet = workbook['Surface Data']
from math import sin
for x in range(1, 50):
u = (6.0 / 50) * x - 3
for y in range(1, 50):
v = (6.0 / 50) * y - 3
try:
sheet[x, y] = sin(u*u + v*v) / (u*u + v*v)
except ZeroDivisionError:
sheet[x, y] = 1
u and v are the 'real' data points. Each X and Y coordinate in Resolver One represents one unit on a scale from -3 to 3, divided into fifty points. The ZeroDivisionError traps the point at which u and v are both zero.
A High Level Overview
Gnuplot can be quite tricky to work with. It has its own scripting language, and the Windows version doesn't even report errors! If you get it wrong, it just fails to produce any output, without telling you why. In another section I'll write up a few things I've found useful when working with gnuplot.
Once we have populated a worksheet with data, our example spreadsheet creates the image by calling a series of functions:
data = plot.GenerateGnuplotData(sheet)
plot.CreateScript(template, scriptPath, data, imagePath)
plot.LaunchGnuplot(gnuplotPath, scriptPath, fontPath)
# Create the ImageWorksheet
plotImage = Image.FromFile(imagePath)
workbook.AddImageWorksheet('Surface Data Plot', plotImage)
These steps perform the following actions:
- Turn the data into something gnuplot can understand
- Generate the gnuplot script
- Launch gnuplot
- Load the image
- Create the image worksheet
Lets look at each of those steps in a bit more detail.
Processing the Data for Gnuplot
In order to generate an image, we must turn the data into something that Gnuplot can understand. Gnuplot wants the data in the following format:
x0 y0 z(0, 0) x1 y0 z(1, 0) x2 y0 z(2, 0) ... xN y0 z(N, 0) x0 y1 z(0, 1) x1 y1 z(1, 1) x2 y1 z(2, 1) ... xN y1 z(N, 1) ... x0 yN z(0, N) x1 yN z(1, N) x2 yN z(2, N) ... xN yN z(N, N)
This is each coordinate (x, y, z) on a separate line, with every 'row' (new y coordinate) separated with an extra newline.
This is produced from the data in the worksheet, using the GenerateGnuplotData function in the plot module:
out = []
for y in range(1, sheet.MaxRow + 1):
row = []
for x in range(1, sheet.MaxCol + 1):
value = sheet[x, y]
u = (6.0 / 50) * x - 3
v = (6.0 / 50) * y - 3
row.append('%s %s %s' % (u, v, value))
out.append(row)
return '\n\n '.join('\n '.join(row) for row in out)
GenerateGnuplotData returns a string.
Creating the Gnuplot Script

The image is produced by launching gnuplot with a script. Gnuplot has its own scripting language to configure how it produces its output. This can be quite tricky to get to grips with, and isn't helped by the fact that the windows version doesn't report any problems (in the 4.2 version anyway). If you have an error in your script it just refuses to generate the output.
If you run the gnuplot program (wgnuplot.exe) on its own, you get the 'interactive' interface shown in the image above. You can experiment with the commands in this interface, and it does give you useful error messages if you do something wrong.
The template for the gnuplot script is in the Pre-constants user code of the spreadsheet:
# Experiment with this!
template = r"""
set terminal png nocrop enhanced font verdana 12 size 640,480
set title "Resolver Gnuplot Demo"
set xlabel "X axis"
set xlabel offset character -2, -2, 0
set ylabel "Y axis"
set ylabel offset character 2, -2, 0
set zlabel "Z axis"
set zlabel offset character 1, 0, 0
set output '%s'
set contour both
set view 45, 30
set style data lines
set hidden3d
splot "-" title "The Data"
%s
"""
Let me quickly take you through these commands:
set terminal png nocrop enhanced font verdana 12 size 640,480
This tells gnuplot that we want a '.png' file as the output. Text is to be produced using the Verdana font, and the image size will be 640x480 pixels.
set title "Resolver Gnuplot Demo"
Set the title on the image.
Set the axis labels and the label locations:
set xlabel "X axis" set xlabel offset character -2, -2, 0 set ylabel "Y axis" set ylabel offset character 2, -2, 0 set zlabel "Z axis" set zlabel offset character 1, 0, 0
set output '%s'
This sets the filename (and path) for the output image. The '%s' will be filled in with the real path later.
set contour both
This tells gnuplot to draw contour lines on both the surface and on the base of the image. Alternative options are set contour surface and set contour base. You can also remove this line to get rid of the contour lines.
set view 45, 30
This is the angle that the view will be produced from. The format is set view <rot_x>, <rot_z>. See the view command documentation for full details. Another interesting option is set view map.
set style data lines
Tell gnuplot to turn our data points into lines.
set hidden3d
Tell gnuplot to do hidden line removal.
splot "-" title "The Data"
splot is the command for drawing 3D plots. The "-" tells gnuplot that we are embedding the data in the script (it will replace the following '%s'). I'm sure you can guess what the 'title' is for...
The script is written out by the CreateScript function in the plot module:
h = open(scriptPath, 'w')
h.write(template % (imagePath.replace('\\', '/'), data))
h.close()
scriptPath is the file to save the script as. data is the string returned by GenerateGnuplotData. imagePath is the location to save the image in. Note that the imagePath must use '/' as the directory separator, or gnuplot gets very confused. !#$%&!*
Launching An External Process

Gnuplot has to be launched as an external process by Resolver One. It then generates the image which we can put into an image worksheet. Launching programs from IronPython code is easy - see Launching Sub-Processes.
The LaunchGnuplot function actually does the work. Here I've added comments so that you can see what it is doing:
def LaunchGnuplot(gnuplotPath, scriptPath, fontPath):
proc = Process()
# The program to launch
proc.StartInfo.FileName = gnuplotPath
# The arguments - which is our script
# If the script has spaces in the path, it must be in quotes
proc.StartInfo.Arguments = '"%s"' % scriptPath
# Set environment variables needed by gnuplot
proc.StartInfo.EnvironmentVariables['GDFONTPATH'] = fontPath
proc.StartInfo.EnvironmentVariables['GNUPLOT_FONTPATH'] = fontPath
# Needed when setting environment variables
proc.StartInfo.UseShellExecute = False
# Launch gnuplot
proc.Start()
# Wait until gnuplot has finished
proc.WaitForExit()
Once gnuplot has exited, it has generated an image. Putting this in an image worksheet is straightforward:
plotImage = Image.FromFile(imagePath)
workbook.AddImageWorksheet('Surface Data Plot', plotImage)
And that's it. This technique can be adapted for integrating Resolver with any external program that generates or processes data.
Last edited Sun Mar 8 01:17:10 2009.

IronPython in Action