Silly, fun and useful things to do with Resolver One
  • HOME
  • About
  • Articles
  • Fun
  • Snippets
  • Disclaimer
  • Useful Links

Site by Voidspace

Email Michael

Download Resolver One

RSS Feed RSS Feed

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.

Site Built with rest2web

Creative Commons License

IronPython Wiki

IronPython in Action IronPython in Action

Charting with Gnuplot

Resolver showing images from gnuplot

Contents

  • Introduction
  • Generating the Data
  • A High Level Overview
  • Processing the Data for Gnuplot
  • Creating the Gnuplot Script
  • Launching An External Process

Introduction

This article is about two things:

  • Producing plots of 3 dimensional surfaces using Gnuplot
  • Integating Resolver with an external application

You can downloadthis 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:

  • Download the "Surfaces with Gnuplot" Spreadsheet (zipfile 2.2MB)

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

A worksheet full of numbers...

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 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, which uses gnuplot to produce plots of the interesting parts.

Turning the formula above into a grid of data is very easy:

# Post-formulae user code
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 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:

# Produce the image
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:

def GenerateGnuplotData(sheet):
    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 gnuplot interactive interface

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:

# The template for the gnuplot script
# 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:

def CreateScript(template, scriptPath, data, imagePath):
    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

z = sin(x*x + y*y) / (x*x + y*y)

Gnuplot has to be launched as an external process by Resolver. 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:

from System.Diagnostics import Process

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:

# Create the ImageWorksheet
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.


Hosted by Webfaction

Return to Top

Last edited Mon Dec 10 20:54:31 2007.

Copyright ©2007 Michael Foord. All rights reserved. Design by Elemental Works. Logo by FuchsiaShock. Valid XHTML & CSS.

This work is licensed under a Creative Commons License.