DBD::Chart - Perl DBI Driver interface For Rendering Charts and Graphs

SYNOPSIS

Pie Chart

use DBI;
my $dbh = DBI->connect('dbi:Chart:');
    or die "Cannot connect\n";
#
#    example: create a pie chart
#
$dbh->do('CREATE TABLE pie (region CHAR(20), sales FLOAT)');
$sth = $dbh->prepare('INSERT INTO pie VALUES( ?, ?)');
$sth->execute('East', 2756.34);
$sth->execute('Southeast', 3456.78);
$sth->execute('Midwest', 1234.56);
$sth->execute('Southwest', 4569.78);
$sth->execute('Northwest', 33456.78);

$rsth = $dbh->prepare(
"SELECT PIECHART FROM pie
 WHERE WIDTH=400 AND HEIGHT=400 AND
 TITLE = \'Sales By Region\' AND 
 COLOR=(red, green, blue, lyellow, lpurple) AND
 BACKGROUND=lgray AND 
 SIGNATURE=\'Copyright(C) 2001, GOWI Systems, Inc.\'");
$rsth->execute;
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Simple Pie Chart

3-D Bar Chart

#
# multi-range 3-D bar chart
#
$dbh->do('CREATE TABLE bars (quarter SMALLINT, East FLOAT, '.
'Southeast FLOAT, Midwest FLOAT, Southwest FLOAT, Northwest FLOAT)');
$sth = $dbh->prepare('INSERT INTO bars VALUES(?, ?, ?, ?, ?, ?)');
$sth->execute(1, -2756.34, 3456.78, 1234.56, -4569.78, 33456.78);
$sth->execute(2, 2756.34, 3456.78, 1234.56, 4569.78, 33456.78);
$sth->execute(3, 2756.34, 3456.78, -1234.56, 4569.78, 33456.78);
$sth->execute(4, 2756.34, -3456.78, 1234.56, 4569.78, 33456.78);

$rsth = $dbh->prepare(
"SELECT BARCHART FROM bars
WHERE WIDTH=600 AND HEIGHT=400 AND X-AXIS=\'Quarter\' AND
Y-AXIS=\'Revenue\' AND TITLE = \'Quarterly Revenue By Region\' AND
3-D=1 AND SHOWVALUES=1 AND COLOR=(red, green, blue, yellow, dbrown)");
$rsth->execute;
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Bar Chart

Point Graph

#
# scatter graph
#
$dbh->do('CREATE CHART line (Month SMALLINT, sales FLOAT)');
$sth = $dbh->prepare('INSERT INTO line VALUES( ?, ?)');
$sth->execute(1, 2756.34);
$sth->execute(2, 3456.78);
        ...insert some more rows.....
$sth->execute(12, 90.57);

$rsth = $dbh->prepare(
"SELECT POINTGRAPH FROM line
WHERE WIDTH=500 AND HEIGHT=300 AND X-AXIS=\'Sales\' AND
TITLE = \'Sales By Region\' AND COLOR=black AND SHOWGRID=0 AND
SHAPE=filldiamond AND SHOWVALUES=1");
$rsth->execute;
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Scatter Graph

Multi-range Linegraph, with Logo and Signature

#
# multi-range line w/ points, bkground image, and signature
#
$dbh->do('DROP CHART line');
$dbh->do('CREATE CHART line (Month SMALLINT, East FLOAT, ' .
'Southeast float, Midwest float, Southwest float, Northwest float)');
$sth = $dbh->prepare('INSERT INTO line VALUES( ?, ?, ?, ?, ?, ?)');
$sth->execute(1, 2756.34, 3456.90, 1234.99, 1005.34, 2876.34);
$sth->execute(2, 3456.78, undef, 4321.25, 9001.34, 997.68);
        ...insert some more rows.....
$sth->execute(12, 90.57,3456.90, 1234.99, undef, 2876.34);

$rsth = $dbh->prepare(
"SELECT LINEGRAPH FROM line
WHERE WIDTH=? AND HEIGHT=? AND X-AXIS=? AND Y-AXIS=? AND
TITLE = \'Monthly Sales By Region\' AND 
COLOR=(red, green, blue, yellow, lbrown) AND
SHOWPOINTS=1 AND SHOWGRID=1 AND 
SHAPE=(fillcircle, fillsquare, filldiamond, horizcross, diagcross) AND
LOGO=\'gowilogo.png\' AND BACKGROUND=lgray AND 
X-ORIENT=\'VERTICAL\' AND 
SIGNATURE=\'Copyright(C) 2001, GOWI Systems, Inc.\'");
$rsth->execute(400, 400, 'Month', 'Sales');
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Multiline Graph

Multi-range Area Graph

$dbh->do('CREATE CHART line (Month SMALLINT, East FLOAT, ' .
'Southeast float, Midwest float, Southwest float, Northwest float)');
$sth = $dbh->prepare('INSERT INTO line VALUES( ?, ?, ?, ?, ?, ?)');

@month = (1,2,3,4,5,6,7,8,9,10,11,12);
@east = ( 2756.34, 3456.90, 1234.99, 1005.34, 2876.34, 3456.78, undef, 
	4321.25, 9001.34, 997.68, 1234.56, 7783.20);
@seast = ( 5321.11, 3333.33, 876.10, 4569.78, 4326.3,  -7895.44, 4444.44, 
	12345.29, 3456.78, 12094.5, 6666.66, 3322.11);
@midwest = ( 9090.90, 908.57, -2367.4, 3399.55, 5555.55, 887.3, 756.34, 
	1111.11, 2222.22, 8888.88, 9456.3, undef);
@swest = ( 7783.20, 5321.11, 3333.33, 876.10, 12349.56, 12094.5, 6666.66,
	3322.11, 9090.90, 4569.78, 3456.99, 4321.25);
@nwest = ( 9001.34, 997.68, 13456.78, 2367.4, 3399.55, 5555.55, 887.3,
	90.57, 3456.90, 1234.99, undef, 2876.34);
	
$sth->func(1, \@month, chart_bind_param_array);
$sth->func(2, \@east, chart_bind_param_array);
$sth->func(3, \@seast, chart_bind_param_array);
$sth->func(4, \@midwest, chart_bind_param_array);
$sth->func(5, \@swest, chart_bind_param_array);
$sth->func(6, \@nwest, chart_bind_param_array);

%stsary = ();
$sth->func(\%stsary, chart_bind_param_status);

$sth->execute;

$rsth = $dbh->prepare(
"SELECT AREAGRAPH FROM line
WHERE WIDTH=400 AND HEIGHT=400 AND X-AXIS=\'Month\' AND
Y-AXIS=\'Sales\' AND TITLE = \'Monthly Sales By Region\' AND
COLOR=(red, green, blue, yellow, lbrown) AND
SHOWPOINTS=1 AND SHOWGRID=1 AND
SHAPE=(fillcircle, fillsquare, filldiamond, horizcross, diagcross)");
$rsth->execute;
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Area Graph

Log-Log Linegraph

#
# log-log line graph
#
$dbh->do('CREATE CHART line (Month FLOAT, sales FLOAT)');
$sth = $dbh->prepare('INSERT INTO line VALUES( ?, ?)');
for ($i = -3; $i < 13; $i++) {
	$sth->execute(5**$i, exp($i));
}

$rsth = $dbh->prepare(
"SELECT LINEGRAPH FROM line
WHERE WIDTH=450 AND HEIGHT=450 AND X-AXIS=\'5**X\' AND 
Y-AXIS=\'e**X\' AND Y-LOG=1 AND X-LOG=1 AND 
SHOWVALUES=0 AND TITLE = \'Sample Log-Log Linegraph\' AND
COLOR=lred AND SHOWGRID=1 AND SHOWPOINTS=1");
$rsth->execute;
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Log-Log Graph

Candlestick Chart

#
# simple candle graph, directly from another statement handle
#
$tddbh = DBI->connect('dbi:Teradata:dbc', 'dbitst', 'dbitst',
    { PrintError => 1, RaiseError => 0, AutoCommit => 1 });
$tdsth = $tddbh->prepare(
"SELECT TradeDay(VARCHAR(18)), lowprice, highprice
    FROM candle ORDER BY TradeDay");
$tdsth->execute;

$rsth = $dbh->prepare(
"SELECT CANDLESTICK FROM ?
 WHERE WIDTH=? AND HEIGHT=? AND X-AXIS=? AND Y-AXIS=? AND
 TITLE = \'Daily Price Range\' AND COLOR=red AND SHOWGRID=1 AND
 SHAPE=filldiamond AND SHOWPOINTS=1 AND SHOWVALUES=0");
$rsth->execute($tdsth, 300,400, 'Date', 'Price');
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Candlestick Graph

Multirange Symbolic Linegraph

#
#	use same data to render linegraph with symbolic domain
#
$tdsth->execute;

$rsth = $dbh->prepare(
"SELECT LINEGRAPH FROM ?
    WHERE WIDTH=400 AND HEIGHT=400 AND X-AXIS=\'Date\' AND
    Y-AXIS=\'Price\' AND TITLE = \'Daily Price Range\' AND
    COLOR=(red, blue) AND SHOWGRID=1 AND
    SHAPE=(filldiamond, fillsquare) AND SHOWPOINTS=1");

$rsth->execute($tdsth);
$rsth->bind_col(1, \$buf);
$rsth->fetch;
Non-numeric Graph

Multidomain Box & Whisker Chart with Imagemap

#
#	simple boxchart with imagemap
#
@x = ();
@y = ();
$dbh->do('CREATE TABLE samplebox (First integer, Second integer)');
$sth = $dbh->prepare('INSERT INTO samplebox VALUES(?, ?)');
foreach (1..100) { 
	$sth->execute($_, int($_/2)+20);
}
$sth = $dbh->prepare("SELECT BOXCHART, IMAGEMAP FROM samplebox
WHERE WIDTH=500 and HEIGHT=300 AND
	title = 'Sample Box & Whisker Chart' AND
	signature = 'Copyright(C) 2001, Presicient Corp.' AND
	MAPNAME = 'boxsample' AND
	SHOWVALUES = 1 AND
	COLORS=(red, blue) AND
	mapURL = 'http://www.presicient.com/samplemap.pl?plotnum=:PLOTNUM&x=:X&y=:Y&z=:Z'"
	);
$sth->execute;
$row = $sth->fetchrow_arrayref;

open(PIE, '>samplebox.png');
binmode PIE;
print PIE $$row[0];
close PIE;
print MAP '<h2>Boxchart with Imagemap</h2>', "\n";
print MAP '<img src=samplebox.png usemap="#boxsample">',
	$$row[1], "\n";
$dbh->do('DROP TABLE samplebox');
50.5[25.5..75.5] 1 100 45[32.5..57.5] 20 70

Iconic Barchart With Imagemap

#
#	iconic barchart
#
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my @pumpsales = (123, 100, 78, 45, 50, 70, 30, 36, 67, 234, 201, 194);
my @turksales = (123, 70, 65, 35, 40, 70, 90, 80, 67, 134, 301, 250);
$dbh->do('CREATE TABLE iconic (month char(3), pumpkins integer, turkeys integer)');
my $sth = $dbh->prepare('INSERT INTO iconic VALUES(?, ?, ?)');
for (my $i = 0; $i <= $#months; $i++) {
	$sth->execute($months[$i], $pumpsales[$i], $turksales[$i]);
}
$sth = $dbh->prepare("SELECT BARCHART, IMAGEMAP FROM iconic
WHERE WIDTH=820 and HEIGHT=500 AND
	title = 'Monthly Pumpkin vs. Turkey Sales' AND
	signature = 'Copyright(C) 2001, Presicient Corp.' AND
	X-AXIS = 'Month' AND
	Y-AXIS = 'Sales' AND
	icons = ('pumpkin.png', 'turkey.png' ) AND
	keepOrigin = 1 AND
	SHOWGRID = 1 AND
	MAPNAME = 'pumpkins' AND
	MAPURL = 'http://www.presicient.com/samplemap.pl'"	);

$sth->execute;
my $row = $sth->fetchrow_arrayref;

open(BAR, '>iconbar.png');
binmode BAR;
print BAR $$row[0];
close BAR;
print MAP '<p><h2>Iconic Barchart with Imagemap</h2>', "\n";
print MAP '<img src=iconbar.png usemap="#pumpkins">',
	$$row[1], "\n";
$dbh->do('DROP TABLE iconic');
123 100 78 45 50 70 30 36 67 234 201 194 123 70 65 35 40 70 90 80 67 134 301 250 123 100 78 45 50 70 30 36 67 234 201 194 123 70 65 35 40 70 90 80 67 134 301 250

DESCRIPTION

DBI driver abstraction for rendering pie charts, bar charts, box & whisker charts, line, point, area, and candlestick graphs. The default output format is PNG; JPEG, or GIF format can be generated by setting the FORMAT property (see below), assuming the appropriate supporting libraries and modules are installed.
BE ADVISED: This is BETA software, and subject to change at the whim of the author(s).

Why DBD::Chart ? Basically, the idea is to make chart rendering easy for us database geeks...and maybe for other folks, as well. Being able to render a chart from a rowset by simply binding output column buffers as input parameter buffers for a simple INSERT, then just SELECT'ing to get the image, seemed like a nice abstraction.

BTW: This driver is 100% pure Perl.

CURRENT VERSION

Release 0.50

ZIP version
.tar.gz version

CHANGE HISTORY

Release 0.50:

Release 0.43: Release 0.42: Release 0.41: Release 0.40:

Release 0.30:

Release 0.20:

Release 0.10:

DRIVER-SPECIFIC BEHAVIOR

DATA-SOURCE NAME

The dsn string passed to DBI->connect() is simply 'dbi:Chart:'. No username or password is required.

DATA TYPES

For purposes of this discussion, data types are assigned to the following classifications:

Type ClassData Types
NumericINTEGER, SMALLINT, TINYINT, FLOAT, DECIMAL
SymbolicCHAR, VARCHAR
The various type classes are applied as follows:

Note that literal strings are specified with single quotes. Literal strings containing single quotes can escape the quotes using two consecutive quotes, e.g.
'Literal strings can''t avoid escapes!'

Date, Time, Timestamp, and Interval Types

DATE, TIME, and TIMESTAMP types are currently handled as symbolic domain values. Better support for these types is planned for the next release.

Escape clause formatting (i.e., '{d mm/dd/yyy}') will be supported in a future release.

SQL DIALECT

DBD::Chart uses a small subset of SQL to create, populate, render, and discard chart data:

IMAGEMAPS

Imagemaps can be generated by including the IMAGEMAP column as the 2nd field in the SELECTed fieldlist. HTML client-side imagemaps are generated if the MAPTYPE qualifier is 'HTML', or if no qualifier is provided. A Perl compatible array of hashrefs of attributes and coordinates is provided if the MAPTYPE qualifier is 'PERL'.

The resulting HTML imagemap will have the following behaviors based on the type of chart/graph requested, and the values specified (if any) for the MAPURL and MAPSCRIPT qualifiers:

Graph TypeHotspot AreaALT tagSpecial Variable
Values
Piechart Polygon starting from center and
connecting 10° arcs along the circumference of the wedge.
Wedge value followed by wedge percent in parentheses
e.g., "1234(16%)"
  1. :PLOTNUM = 0
  2. :X = <wedge-label>
  3. :Y = <assoc. wedge value>
  4. :Z = <wedge's percent of total>
2-D Barchart Complete area of each bar Range value for the bar
  1. :PLOTNUM = <range number of the bar>
  2. :X = <domain label for the bar>
  3. :Y = <range value for the bar>
  4. :Z = <empty-string>
3-D Barchart
(including 3-axis)
Complete area of top face of each bar Range value for the bar
  1. :PLOTNUM = <range number of the bar>
  2. :X = <domain label for the bar>
  3. :Y = <range value for the bar>
  4. :Z = <Z-axis label value for the bar>
Line, point, area graph 8-pixel diameter circle centered on each datapoint (domain, range) values of the datapoint
  1. :PLOTNUM = <range number of the plot>
  2. :X = <domain value for the datapoint>
  3. :Y = <range value for the datapoint>
  4. :Z = <empty-string>
Candlestick 8-pixel diameter circle centered on
each datapoint, both top and bottom of stick
(domain, range) values of the datapoint
  1. :PLOTNUM = <range number of the plot>
  2. :X = <domain value for the datapoint>
  3. :Y = <range value for the datapoint>
  4. :Z = <'top' | 'bottom'> depending on which endpoint of candlestick is focused.
Box & Whisker the area of the plotted box, and an
8-pixel diameter circle centered on
the lower and upper ends of the whicksers
median[lower quartile..upper quartile] values of the box, and the values of the lower and upper extreme datapoint
  1. :PLOTNUM = <range number of the plot>
  2. :X = <median value>
  3. :Y = <lower quartile value>
  4. :Z = <upper quartile value>

A Perl imagemap will have the following behaviors based on the type of chart/graph requested, and the values specified (if any) for the MAPURL and MAPSCRIPT qualifiers:

Graph TypeAttributes & Values
shape coordinates plotnum X Y Z
Piechart POLY arrayref of (x,y) image pixel coordinates of polygon starting from center and connecting 10° arcs along the circumference of the wedge. 0 wedge label associated wedge value wedge's percent of total
2-D Barchart RECT arrayref of (x,y) image pixel coordinates of rectangle of associated bar range number of the bar domain label range value undef
3-D Barchart
(including 3-axis)
POLY arrayref of (x,y) image pixel coordinates of top face of associated bar range number of the bar domain label for the bar range value for the bar Z-axis label value for the bar
Line, point, area graph CIRCLE arrayref of (xcenter, ycenter, radius) image pixel coordinates of 8-pixel diameter circle centered on the datapoint range number of the plot domain value for the datapoint range value for the datapoint undef
Candlestick CIRCLE arrayref of (xcenter, ycenter, radius) image pixel coordinates of 8-pixel diameter circle centered on
each datapoint, both top and bottom of stick
range number of the plot domain value for the datapoint range value for the datapoint 'top' | 'bottom', depending on which candlestick endpoint is focused.
Box & Whisker RECT, CIRCLE arrayref of (x,y of upper left of box, x.y of lower right of box) image pixel coordinates of the box range number of the plot median value of the box lower quartile value of the box Upper quartile value of the box.

ERROR HANDLING

Any errors generated from improper SQL usage are flagged with an error value of -1, and appropriate text in the errstr. Errors emanating from DBD::Chart::Plot will be flagged in err and errstr with whatever info is returned.

DIAGNOSTICS

DBI provides the trace() function to enable various levels of trace information. DBD::Chart currently doesn't add any traces to this.

DRIVER-SPECIFIC ATTRIBUTES

DRIVER-SPECIFIC FUNCTIONS

The following functions are provided to optimize data movement:
Note:generalized versions of these functions are currently under consideration for addition to the DBI specification.

Note: array binding of output values is not supported, since SELECT only returns a single row at a time, namely, the chart image and (optionally) an imagemap.

PREREQUISITES AND CONFORMANCE

DBD::Chart requires the following (versions are recommended only, cuz they're the version I built with...if you're feeling frisky, you can try older or newer versions.):

Be advised that these modules require the following (non-Perl) libraries:

The following DBI functions are not supported:

DBI->data_sources()
$dbh->prepare_cached()
$sth->table_info()
$dbh->tables()
$dbh->type_info_all()
$dbh->type_info()

KNOWN BUGS

Bug Number Report
Date
Release
Reported in
Description Status Fix
Release
1 2001-Mar-16 0.20 SQL statements with newlines won't prepare() Fixed 0.30
2 2001-May-09 0.30 INTEGER column definition as last column in CREATE TABLE causes an error Fixed 0.40
3 2001-May-09 0.30 FORMAT property not properly handled as string property Fixed 0.40
4 2001-Jun-01 0.40 Y-axis ticks not properly aligned when grid disabled Fixed 0.41
5 2001-Jun-01 0.40 INSERT of string literal value kept quote mark Fixed 0.41
6 2001-Jun-01 0.40 INSERT of string literal used bad column index variable Fixed 0.41
7 2001-Oct-01 0.41 X-ORIENT='HORIZONTAL' didn't work with symbolic domains, candlesticks Fixed 0.42

TO DO List

ACKNOWLEDGEMENTS

Many thanks to all the authors of the various GD, PNG, JPEG, SVG, and zlib modules. Special thanks to all the initial victims, er, users for their comments, complaints, and suggestions.

Special thanks to Peter Scott for the GIF support and a few other fixes!

REFERENCES

AUTHOR

Dean Arnold

COPYRIGHT

Copyright (c) 2001, Dean Arnold, USA
Affiliations:

Permission is granted to use this software according to the terms of the Artistic License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

I'm offering the software AS IS, and ACCEPT NO LIABILITY FOR ANY ERRORS OR OMMISSIONS IN, OR LOSSES INCURRED AS A RESULT OF USING DBD::Chart.

I reserve the right to provide support for this software to individual sites under a separate (possibly fee-based) agreement.

Last updated November 30, 2001