hesco has asked for the wisdom of the Perl Monks concerning the following question:
Hello all:
I've been poking around for a couple of days looking for a module or script which would dynamically generate a goal-thermometer graph to support a fundraising effort.
I was hoping that GD::Graph might help, but so far, I haven't figured out how to turn its graphs into a thermometer shaped object.
The only thing I've found for this purpose so far is some php code on a Friends of Scouting site. But I'm hoping for a perl module, or script instead.
If anyone here has done this before and can share some sample code, or knows of something on CPAN designed for this job, I would certainly appreciate hearing about it. Failing that, I guess I can start porting the php code to perl, but I'd love to find the ready-made solution, instead.
Thanks,
-- Hugh
UPDATE:
Here is where I am at so far on this project:
package themometer;
use strict;
use warnings;
use GD::Graph::bars;
$GD::Graph::Error::Debug = '5';
my $g = GD::Graph::bars->new(80,180) or die GD::Graph->error;
my @data = (['Your Contributions'],[55]);
$g->set(
y_label => 'Progress toward our Goal (%)',
y_max_value => '100',
y_tick_number => '10',
y_label_skip => '2'
) or die $g->error;
my $gd = $g->plot(\@data) or die $g->error;
unlink 'fundraising_progress_graph.gif';
open(IMG, '>fundraising_progress_graph.gif') or die $!;
binmode IMG;
print IMG $gd->gif;
close IMG;
1;
If it had a bulb filled with mercury at the bottom, this would fulfill my requirement for a thermometer graphic. As it is, this is getting pretty close. Can any of your GD fans advise me how best to add the bulb at the bottom of this image, please?
if( $lal && $lol ) { $life++; }
Re: Seeking GD::Graph like goal-thermometer graph
by vladdrak (Monk) on Dec 18, 2006 at 11:11 UTC
|
This will show a 50x300 white bar filled with blue.
#!/usr/bin/perl
use strict;
use warnings;
use Image::Magick;
my $width = 50;
my $height = 300;
my $fill = 150;
my $image = Image::Magick->new(size => $width."x".$height);
$image->Read("xc:white");
$image->Draw(primitive => "Rectangle",
fill => "blue",
stroke => "blue",
strokewidth => 4,
points => "0,$height $width,$fill");
$image->Write("progress.png");
| [reply] [Watch: Dir/Any] [d/l] |
Re: Seeking GD::Graph like goal-thermometer graph
by SFLEX (Chaplain) on Dec 18, 2006 at 11:29 UTC
|
I have passed by this module " GD-Dashboard - Perl module to create JPEG graphics of meters and dials "
It could have eveything you need
Good Luck! | [reply] [Watch: Dir/Any] |
Re: Seeking GD::Graph like goal-thermometer graph
by zentara (Archbishop) on Dec 18, 2006 at 13:59 UTC
|
I think your best bet, is to get an existing image of a thermometer, load it into GD, then paint a bar onto it. It might take some experimentation to get the pixel positions right. BUT, I just googled, and saw this javascript code from fundraisingknowhow.com You should be able to use it or reverse engineer it.
| [reply] [Watch: Dir/Any] |
|
Btw, the link you showed does do some of the implementation in javascript, but it really embeds some parameters to pass to a flash presentation that takes the "goal" and "current value" parameters. Not as easily reverse-engineerable as at first glance.
The first thing I see when I look at that particular "thermometer" is that you could easily accomplish that effect (assuming that we're showing this on the web) by overlaying two pictures... One, a rectangular 'meter' showing the appropriate amount of 'mercury', and overlaid on top of that a picture that has a transparent cutout of the shape of the thermometer so you can see the mercury through the transparent cutout. The placement (especially the z-index, how far "forward" to set the overlay) could be set via css.
Surely similar to how the fundraisingknowhow folks do it via flash.
Assuming the OP already has the graphing bit down, this solution depends on some HTML + CSS to finish things off.
--chargrill
s**lil*; $*=join'',sort split q**; s;.*;grr; &&s+(.(.)).+$2$1+; $; =
qq-$_-;s,.*,ahc,;$,.=chop for split q,,,reverse;print for($,,$;,$*,$/)
| [reply] [Watch: Dir/Any] [d/l] |
Re: Seeking GD::Graph like goal-thermometer graph
by zentara (Archbishop) on Dec 18, 2006 at 18:48 UTC
|
I hope I get a merit badge for this. :-) This will take the current amount on the commandline and generate the thermometer. It could use some enhancements, but it should get you started.
#!/usr/bin/perl
use warnings;
use strict;
use GD;
use GD::Text::Align;
my $current = shift;
$current ||= 500;
my $goal = 10000;
my $image = new GD::Image(250,500);
my $black = $image->colorAllocate(0,0,0);
my $white = $image->colorAllocate(255,255,255);
my $red = $image->colorAllocate(255,0,0);
my $top = 20;
my $bottom = 400;
my $dif = $bottom - $top;
my $left_text_margin = 65;
#background, set tranparent if you want
$image->filledRectangle( 0, 0, 250, 500, $white );
#$image->rectangle($x1,$y1,$x2,$y2,$color)
$image->rectangle(40,20,60,425,$black);
#$image->filledEllipse($cx,$cy,$width,$height,$color)
$image->filledEllipse(50,450,75,75,$red);
$image->filledRectangle(40,400,60,425,$red);
my $text = new GD::Text::Align( $image,
font => gdLargeFont,
# font => './arial.ttf',
text => $goal.' Our Goal',
color => $black,
valign => "center",
halign => "left",
);
#draw goal at top
$text->draw($left_text_margin,$top);
#draw start at bottom
$text->set_text('0 Start');
$text->draw($left_text_margin,$bottom);
show_current();
open( IMAGE, ">GD-thermometer.png") || die "Couldn't open file: $!\n";
binmode( IMAGE );
print IMAGE $image->png();
close IMAGE;
sub show_current{
my $curpix = 400 - ($dif/$goal)* $current;
#draw text level
$text->set_text("$current Current");
$text->draw($left_text_margin,$curpix);
$image->filledRectangle(40,$curpix ,60,425,$red);
}
| [reply] [Watch: Dir/Any] [d/l] |
|
Zentara:
Thanks, mate!
That is certainly moving me in the right direction. Sorry I have no merit badges to offer you, but perhaps the last three XP I had to share today might help. I also opened a list of your posts and if the XP genies come back around before my browser runs my system out of memory again, I'd be happy to share a few more your way.
My next step will be to make all the image sizes a function of the image's overall deminsions. As it is, when I tried to scale the image down to a usuable size, it cut off most of the content as outside it bounds. This is something I hope to make use of for years to come. Perhaps your code might serve as the basis for a new method / module as GD::Graph::thermometer, if the GD::Graph maintainer will have it. Otherwise I intend to maintain a personal version.
Thanks again. This moves a data cruncher so close to delivering on a graphical requirement that I see light at the end of this tunnel.
-- Hugh
if( $lal && $lol ) { $life++; }
| [reply] [Watch: Dir/Any] |
|
Yeah, I was thinking the same thing about it being a module. I was tempted to make the dimensions all variables based on graphic dimension, but I was in a hurry. Have fun, I'll bet you could make a very useful module out of it, with lots of cool little
features, like color selection, markers for various levels, transparencies, etc.
| [reply] [Watch: Dir/Any] |
Re: Seeking GD::Graph like goal-thermometer graph
by mattr (Curate) on Dec 19, 2006 at 06:20 UTC
|
Hi! I'd like to point you to a GPL thermometer image, alert you to a problem with a module mentioned in this thread, and mention how to do it easily in GD or (probably more quickly) with rsvg. (the image mentioned is available as both PNG and SVG).
First, I tried GD-Dashboard mentioned above many moons ago, on a bug that was 2 years old at the time. I decided instead to roll my own GD-based Gantt-like workflow scheduler and I was very happy with plain GD. So, if you want to try Dashboard note that CPAN says it fails to build and you may have to debug it.
I would say use plain GD, but I also found a neat SVG one.
First, with GD: First find a free thermometer graphic or draw your own. Then use Gimp or another drawing program to find the coordinates of the red rectangle you need to draw to fill the thermometer, and adjust its height based on the thermometer's value. Also make some space above the thermometer to display a big XXX% label (maybe at the top of the thermometer) and get the coordinates of that rectangle too. The rest is pretty straightforward.
As for the graphic and the SVG one I found, I googled for thermometer gpl image (also open clipart might work). Check out this page which provides a script that uses rsvg to rasterize an svg image into a png file. This looks pretty easy to perlize, if you are on linux and install rsvg. At the very least, you can use the png image they show here as a template for your own GD-based thermometer if you don't want to use svg. If you can't view SVG you can get one here at Adobe. It will install an ActiveX component that lets you see the other thermometer in that siteÂs svg chemistry set on this page though it is more chemistry and less PR style.
Here is something like what you need for the svg one for your info (untested but syntax checked) though if you already are using GD then maybe you just want to use the PNG file on the same page. It is nice and easy to read.
# Usage: perl thermoupdate.pl 12000
# in case you are at the 12000 mark.
open(IN,"thermometer_tmpl.svg");
my $buf = <IN>;
close(IN);
my $amt = $ARGV[0];
my $height = 20000;
$n = 10 * $amt/$height; # floating point 0-10
my ($min,$mid,$max) =
('$0','$10,000','$20,000');
$buf =~ s/height="10"/height="$n"/;
$buf =~ s/Foo/$min/;
$buf =~ s/Bar/$mid/;
$buf =~ s/Baz/$max/;
open(OUT,"thermo_live.svg");
print OUT $buf;
close(OUT);
system("rsvg-convert -o thermometer.png thermo_live.svg");
Anyway in the bash script provided replace sed with s/// in perl, and use the system command to run rsvg and it should be doable really in 5 minutes. This will let you easily make a web form or config file to painlessly update the thermometer perhaps automatically. Watch out for when you exceed the boiling point though!
Oh some more links found in some later google results, which you can bend to your purposes.. ugly gd thermometer from php, webthermometer is gpl and has a nice thermo. gif image you can use
| [reply] [Watch: Dir/Any] [d/l] |
|
|