#$Id: Plotter.pm 516 2006-09-28 14:59:28Z schroeer $ package Lab::Data::Plotter; use strict; use Lab::Data::Meta; use Data::Dumper; use Time::HiRes qw/gettimeofday tv_interval/; our $VERSION = sprintf("1.%04d", q$Revision$ =~ / (\d+) /); sub new { my $proto = shift; my $class = ref($proto) || $proto; my ($main_meta,$options,@other)=@_; my @other_metas; for my $meta ($main_meta,@other) { unless (ref $meta eq 'Lab::Data::Meta') { for ("",qw/.META META .meta meta/) { if (-e $meta.$_) { $meta=$meta.$_; last; } } die "Metafile $meta does not exist!" unless (-e $meta); $meta=Lab::Data::Meta->new_from_file($meta); } push @other_metas,$meta; } $main_meta=shift @other_metas; my $self = bless { meta => $main_meta, options => $options, other_metas => \@other_metas, }, $class; return $self; } sub start_live_plot { my ($self,$plot,$interval)=@_; $self->{live_plot}->{pipe}=$self->_start_plot($plot); $self->{live_plot}->{plot}=$plot; $self->{live_plot}->{refresh}=$interval; $self->{live_plot}->{last}=[gettimeofday]; } sub update_live_plot { my $self=shift; return unless (defined $self->{live_plot}); return if (($self->{live_plot}->{refresh}) && (tv_interval($self->{live_plot}->{last},[gettimeofday]) < ($self->{live_plot}->{refresh}))); $self->{live_plot}->{last}=[gettimeofday]; $self->_plot($self->{live_plot}->{pipe},$self->{live_plot}->{plot}); } sub stop_live_plot { my $self=shift; return unless (defined $self->{live_plot}); close $self->{live_plot}->{pipe}; undef $self->{live_plot}; } sub plot { my ($self,$plot)=@_; die "Plot what?" unless ($self->{meta} && $plot); my $gpipe=$self->_start_plot($plot); $self->_plot($gpipe,$plot); return $gpipe; } sub _start_plot { my ($self,$plot)=@_; die "plot \"$plot\" undefined" unless (defined($self->{meta}->plot($plot))); my $gpipe; if ($self->{options}->{dump}) { open $gpipe,">".$self->{options}->{dump} or die "cannot open gnuplot dump file ".$self->{options}->{dump}; } else { $gpipe=$self->get_gnuplot_pipe(); } my $gp=""; $gp.="# Encoding of this file\n"; $gp.="set encoding iso_8859_1\n"; if ($self->{options}->{eps}) { $gp.="#\n# Output to file\n"; $gp.="set terminal postscript color enhanced 10\n"; $gp.=qq(set output ").$self->{options}->{eps}.qq("\n); } if ($self->{meta}->plot_type($plot) eq 'pm3d') { $gp.="#\n# Set color plot\n"; $gp.="set pm3d map corners2color c1\n"; $gp.="set view map\n"; if ($self->{meta}->plot_palette($plot)) { $gp.="set palette ".$self->{meta}->plot_palette($plot)."\n"; } } #Quatsch. Aussen über Files loopen, dann über Konstanten if ($self->{meta}->constant()) { $gp.="#\n# Constants\n" ; for (@{$self->{meta}->constant()}) { unless ($self->{options}->{multiple}) { $gp.=($_->{name})."=".($_->{value})."\n"; } else { my $num=0; for my $meta ($self->{meta},@{$self->{othermetas}}) { $gp.=($_->{name})."$num++=".($_->{value})."\n"; } } } } my $xaxis=$self->{meta}->plot_xaxis($plot); my $yaxis=$self->{meta}->plot_yaxis($plot); my $zaxis=$self->{meta}->plot_zaxis($plot); my $cbaxis=$self->{meta}->plot_cbaxis($plot); $gp.="#\n# Axis labels\n"; for my $i (qw/x y z cb/) { my $axisname="plot_".$i."axis"; my $metaaxis=$self->{meta}->$axisname($plot); if (defined $metaaxis) { my $label='"'. ($self->{meta}->axis_label($metaaxis)). ' ('.($self->{meta}->axis_unit($metaaxis)).")". ($self->{options}->{fulllabels} ? ('\n'.$self->{meta}->axis_description($metaaxis)) : '' )."\"\n"; $gp.="set ".$i."label ".$label; } } if (defined $self->{meta}->plot_grid($plot)) { $gp.="#\n# Grid\n"; $gp.="set grid ".($self->{meta}->plot_grid($plot))."\n"; } if ($self->{meta}->plot_time($plot)) { $gp.="#\n# Time axes\n"; for (qw/x y z cb/) { if ($self->{meta}->plot_time($plot) =~ /$_/) { $gp.="set ".$_."data time\n" } } $gp.=qq(set timefmt "%s"\n); } my $gp_help; for (qw/x y z cb/) { my $name="plot_".$_."format"; if ($self->{meta}->$name($plot)) { $gp_help.=qq(set format $_ ").($self->{meta}->$name($plot)).qq("\n); } } $gp.="#\n# Axis format\n".$gp_help if ($gp_help); unless ($self->{options}->{multiple}) { $gp.="#\n# Ranges\n"; my $xmin=(defined $self->{meta}->axis_min($xaxis)) ? $self->{meta}->axis_min($xaxis) : "*"; my $xmax=(defined $self->{meta}->axis_max($xaxis)) ? $self->{meta}->axis_max($xaxis) : "*"; my $ymin=(defined $self->{meta}->axis_min($yaxis)) ? $self->{meta}->axis_min($yaxis) : "*"; my $ymax=(defined $self->{meta}->axis_max($yaxis)) ? $self->{meta}->axis_max($yaxis) : "*"; $gp.="set xrange [$xmin:$xmax]\n"; $gp.="set yrange [$ymin:$ymax]\n"; if ($zaxis) { my $zmin=(defined $self->{meta}->axis_min($zaxis)) ? $self->{meta}->axis_min($zaxis) : "*"; my $zmax=(defined $self->{meta}->axis_max($zaxis)) ? $self->{meta}->axis_max($zaxis) : "*"; $gp.="set zrange [$zmin:$zmax]\n"; } if ($cbaxis) { my $cbmin=(defined $self->{meta}->axis_min($cbaxis)) ? $self->{meta}->axis_min($cbaxis) : "*"; my $cbmax=(defined $self->{meta}->axis_max($cbaxis)) ? $self->{meta}->axis_max($cbaxis) : "*"; $gp.="set cbrange [$cbmin:$cbmax]\n"; $gp.="set zrange [$cbmin:$cbmax]\n" if ($self->{meta}->plot_logscale($plot)); } } if ($self->{meta}->plot_logscale($plot)) { $gp.="#\n# Axes with logscale\n"; $gp.="set logscale ".$self->{meta}->plot_logscale($plot)."\n"; } $gp.="#\n# Title and labels\n"; $gp.=qq(set title ").$self->{meta}->dataset_title()." (".$self->{meta}->sample().")\"\n"; if ($self->{options}->{fulllabels}) { my $h=0.95;my $screen=0.99; my @lines=split "\n",$self->{meta}->dataset_description(); for (@lines) { if ($self->{meta}->plot_type($plot) eq 'pm3d') { $gp.=qq(set label "$_" at screen 0.01, screen $screen\n); } else { $gp.=qq(set label "$_" at graph 0.02, graph $h\n); } $h-=0.04; $screen-=0.025;$screen=0.13 if (abs($screen-0.865)<0.001); } } if ($self->{meta}->plot_label($plot)) { my @labels=$self->{meta}->plot_label($plot); for (@labels) { my $text=$_->{text}; my $x=$_->{x}; my $y=$_->{y}; $gp.=qq(set label "$text" at $x,$y center front\n); } } print $gpipe $gp; return $gpipe; } sub _plot { my ($self,$gpipe,$plot)=@_; my $xaxis=$self->{meta}->plot_xaxis($plot); my $yaxis=$self->{meta}->plot_yaxis($plot); my $zaxis=$self->{meta}->plot_zaxis($plot); my $cbaxis=$self->{meta}->plot_cbaxis($plot); my $xexp=$self->_flatten_exp($xaxis); my $yexp=$self->_flatten_exp($yaxis); my $zexp=$self->_flatten_exp($zaxis) if ($zaxis); my $cbexp=$self->_flatten_exp($cbaxis) if ($cbaxis); my $datafile=$self->{meta}->get_abs_path().$self->{meta}->data_file(); my $pp; if ($self->{meta}->plot_type($plot) eq 'pm3d') { $pp=qq(splot "$datafile" using ($xexp):($yexp):($cbexp) title "$plot"\n); } else { if ($self->{options}->{live_latest}) { my %blocks=$self->{meta}->block(); my @keys=sort(keys %blocks); @keys=splice @keys,-$self->{options}->{live_latest}; $pp="plot "; for (@keys) { $pp.=qq("$datafile" using ($xexp):($yexp) every :::$_::$_ title "$blocks{label}" with lines, ); } $pp=substr $pp,0,(length $pp) -2; } else { $pp=qq(plot "$datafile" using ($xexp):($yexp) title "$plot" with lines\n); } } print $gpipe $pp; } sub _plot_multiple { my ($self,$gpipe,$plot)=@_; my $xaxis=$self->{meta}->plot_xaxis($plot); my $yaxis=$self->{meta}->plot_yaxis($plot); my $zaxis=$self->{meta}->plot_zaxis($plot); my $cbaxis=$self->{meta}->plot_cbaxis($plot); my $xexp=$self->_flatten_exp($xaxis); my $yexp=$self->_flatten_exp($yaxis); my $zexp=$self->_flatten_exp($zaxis) if ($zaxis); my $cbexp=$self->_flatten_exp($cbaxis) if ($cbaxis); my $datafile=$self->{meta}->get_abs_path().$self->{meta}->data_file(); my $pp="#\n# Plot\n"; if ($self->{meta}->plot_type($plot) eq 'pm3d') { $pp.=qq(splot "$datafile" using ($xexp):($yexp):($cbexp) title "$plot"\n); } else { if ($self->{options}->{last_live}) { my %blocks=$self->{meta}->block(); my @keys=sort(keys %blocks); @keys=splice @keys,-$self->{options}->{last_live}; $pp.="plot "; for (@keys) { $pp.=qq("$datafile" using ($xexp):($yexp) every :::$_::$_ title "$blocks{label}" with lines, ); } $pp=substr $pp,0,(length $pp) -2; } else { $pp.=qq(plot "$datafile" using ($xexp):($yexp) title "$plot"\n); } } print $gpipe $pp; } sub _flatten_exp { my ($self,$axis)=@_; $_=$self->{meta}->axis_expression($axis); while (/\$A\d+/) { s/\$A(\d+)/($self->{meta}->axis_expression($1))/e; } while (/\$C\d+/) { s/\$C(\d+)/'$'.($1+1)/e; } $_; } sub available_plots { my $self=shift; my %plots=$self->{meta}->plot(); my @names=(keys %plots); for (@names) { my $xlabel=$self->{meta}->axis_label($plots{$_}->{xaxis}); my $ylabel=$self->{meta}->axis_label($plots{$_}->{yaxis}); $plots{$_}="$ylabel vs. $xlabel"; } return %plots; } sub get_gnuplot_pipe { my $self=shift; my $gpname; if ($^O =~ /MSWin32/) { $gpname="pgnuplot"; } else { $gpname="gnuplot -noraise"; } if (open my $GP,"| $gpname") { my $oldfh = select($GP); $| = 1; select($oldfh); return $GP; } return undef; } 1; __END__ =head1 NAME Lab::Data::Plotter - Plot data with Gnuplot =head1 SYNOPSIS use Lab::Data::Plotter; my $plotter=new Lab::Data::Plotter($metafile); my %plots=$plotter->available_plots(); my @names=keys %plots; $plotter->plot($names[0]); =head1 DESCRIPTION This module can plot data with GnuPlot. It plots data from C<.DATA> files and takes into account the data information in the corresponding C<.META> file. The module also offers the possibility to plot data live, while it is being aquired. =head1 CONSTRUCTOR =head2 new $plotter=new Lab::Data::Plotter($meta,\%options); Creates a Plotter object. C<$meta> is either an object of type L or a filename that points to a C<.META> file. Available options are =over 2 =item dump =item eps =item fulllabels =item last_live =back =head1 METHODS =head2 available_plots my %plots=$plotter->available_plots(); =head2 plot $plotter->plot($plot); =head2 start_live_plot $plotter->start_live_plot($plot); =head2 update_live_plot $plotter->update_live_plot(); =head2 stop_live_plot $plotter->stop_live_plot(); =head1 AUTHOR/COPYRIGHT This is $Id: Plotter.pm 516 2006-09-28 14:59:28Z schroeer $ Copyright 2004-2006 Daniel Schröer (L) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut