Graph-ChartSVG

 view release on metacpan or  search on metacpan

lib/Graph/ChartSVG.pm  view on Meta::CPAN

package  Graph::ChartSVG::Layer;
use Moose;

has 'data'    => ( isa => 'Graph::ChartSVG::Data',    is => 'rw', required => 0 );
has 'glyph'   => ( isa => 'Graph::ChartSVG::Glyph',   is => 'rw', required => 0 );
has 'overlay' => ( isa => 'Graph::ChartSVG::Overlay', is => 'rw', required => 0 );

1;

package  Graph::ChartSVG::Data;
use Moose;

has 'data_set'  => ( isa => 'ArrayRef',       is => 'rw', required => 0 );
has 'type'      => ( isa => 'Str',            is => 'rw', required => 0, default => 'line' );
has 'thickness' => ( isa => 'Num',            is => 'rw', required => 0, default => 1 );
has 'color'     => ( isa => 'Str | ArrayRef', is => 'rw', required => 0, default => '00000000' );
has 'opacity'   => ( isa => 'Num',            is => 'rw', required => 0, default => 1 );
has 'max'       => ( isa => 'Int',            is => 'rw', required => 0 );
has 'last'      => ( isa => 'Int',            is => 'rw', required => 0 );
has 'label'     => ( isa => 'Str',            is => 'rw', required => 0 );
has 'offset'    => ( isa => 'Num',            is => 'rw', required => 0, default => 0 );
has 'scale'     => ( isa => 'Num | ArrayRef', is => 'rw', required => 0, default => 1 );

1;

package Graph::ChartSVG::Frame;
use Moose;

has 'type'      => ( isa => 'Str', is => 'rw', required => 0, default => 'line' );
has 'thickness' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'color'     => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );

1;

package  Graph::ChartSVG::Glyph;
use Moose;

has 'x'    => ( isa => 'Str', is => 'rw', required => 1, default => 0 );
has 'y'    => ( isa => 'Str', is => 'rw', required => 1, default => 0 );
has 'type' => ( isa => 'Str', is => 'rw', required => 0, default => 'line' );
has 'filled'    => ( isa => 'Bool',     is => 'rw', required => 0 );
has 'color'     => ( isa => 'Str',      is => 'rw', required => 0, default => '00000000' );
has 'anchor'    => ( isa => 'Str',      is => 'rw', required => 0, default => 'start' );
has 'data_set'  => ( isa => 'ArrayRef', is => 'rw', required => 0 );
has 'thickness' => ( isa => 'Num',      is => 'rw', required => 0, default => 1 );
has 'font'           => ( isa => 'Str', is => 'rw' );
has 'size'           => ( isa => 'Num', is => 'rw' );
has 'font_weight'    => ( isa => 'Str', is => 'rw' );
has 'stretch'        => ( isa => 'Str', is => 'rw' );
has 'letter_spacing' => ( isa => 'Num', is => 'rw' );
has 'word_spacing'   => ( isa => 'Num', is => 'rw' );
has 'label'          => ( isa => 'Str', is => 'rw', required => 0 );

1;

package  Graph::ChartSVG::Border;
use Moose;

has 'left'   => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'right'  => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'top'    => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'bottom' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );

1;

package  Graph::ChartSVG::Label;

lib/Graph/ChartSVG.pm  view on Meta::CPAN

has 'label2'    => ( isa => 'Graph::ChartSVG::Label', is => 'rw', required => 0 );

1;

package  Graph::ChartSVG::Grid;
use Moose;

has 'x'      => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'y'      => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'y_up'   => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'y_down' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'x_up'   => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'x_down' => ( isa => 'Graph::ChartSVG::Grid_def', is => 'rw', required => 0 );
has 'debord' => ( isa => 'Graph::ChartSVG::Border',   is => 'rw', required => 0, default => sub { Graph::ChartSVG::Border->new } );

1;

package  Graph::ChartSVG::Overlay;
use Moose;

has 'type'     => ( isa => 'Str', is => 'rw', required => 0, default => 'v' );
has 'debord_1' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'debord_2' => ( isa => 'Num', is => 'rw', required => 0, default => 0 );
has 'data_set' => ( isa => 'HashRef', is => 'rw', required => 0 );
has 'color' => ( isa => 'Str', is => 'rw', required => 0, default => '00000000' );

1;

package Graph::ChartSVG;
use Moose;

use constant PI => 4 * atan2( 1, 1 );

use SVG;
use SVG::Parser qw(SAX=XML::LibXML::SAX::Parser);

use List::Util qw( max  min sum );
# use Math::Complex;
# use Compress::Zlib;
use Hash::Merge qw( merge );
use Data::Serializer;

# use Carp::Clan;
use Carp;
#use Data::Dumper;
# use Devel::Size qw(size total_size);
# use Clone qw(clone);

use MIME::Base64;

use vars qw( $VERSION );

$VERSION = '2.07';

has 'active_size' => ( isa => 'ArrayRef', is => 'rw', required => 0 );
has 'total_size'  => ( isa => 'ArrayRef', is => 'rw', required => 0 );
has 'bg_color'    => ( isa => 'Str',      is => 'rw', required => 0, default => 'ffffffff' );
has 'frame'       => ( isa => 'Graph::ChartSVG::Frame',    is => 'rw', required => 0, default => sub { Graph::ChartSVG::Frame->new } );
has 'grid'        => ( isa => 'Graph::ChartSVG::Grid',     is => 'rw', required => 0 );
#has 'reticle'     => ( isa => 'HashRef',  is => 'rw', required => 0 );
has 'overlay' => ( isa => 'Graph::ChartSVG::Overlay',  is => 'rw', required => 0 );
has 'glyph'   => ( isa => 'ArrayRef', is => 'rw', required => 0 );
#has 'layer'    => ( isa => 'ArrayRef', is => 'rw' ,default => sub { [ Layer->new]} );
has 'layer'   => ( isa => 'ArrayRef[Layer]', is => 'rw' );
has 'image'   => ( isa => 'Str',             is => 'rw' );
has 'svg_raw' => ( isa => 'Str',             is => 'rw' );
has 'border'  => ( isa => 'Graph::ChartSVG::Border',          is => 'rw', required => 0, default => sub { Graph::ChartSVG::Border->new } );
has 'tag' => ( isa => 'Bool', is => 'rw', required => 0 );
#has 'tag' => ( isa => 'Tag', is => 'rw', required => 0 );
#has 'tag' =>  ( isa => 'HashRef', is => 'rw', required => 0 );

sub Tag
{
    my $self = shift;
    my $args = shift;
    if ( exists $self->{ tag } )
    {
        $self->{ tag } = merge( $self->{ tag }, $args );
    }
    else
    {
        $self->{ tag } = $args;
    }
    bless $self;
}

sub label
{
    my $self  = shift;
    my $label = shift;
    foreach my $l ( 1 .. ( scalar @{ $self->{ Layer } } ) )
    {

        my $m = $l - 1;

        if (   defined $self->{ Layer }->[$m]
            && exists $self->{ Layer }->[$m]->{ label }
            && $self->{ Layer }->[$m]->{ label } eq $label )
        {
# carp "$l =>  ".Dumper($self->{ Layer }->[$m]->{ data_set });
            return wantarray
              ? ( $self->{ Layer }->[$m]->{ data_set }, $m )
              : $self->{ Layer }->[$m]->{ data_set };
        }
    }
    return ();
}

sub move
{
    my $self = shift;
    my $from = shift;
    my $to   = shift;
    my $type = 'Layer';
    my @tmp;
    if ( exists $self->{ $type } )
    {
        my $elem = splice @{ $self->{ $type } }, $from, 1;
        @tmp = ( @{ $self->{ $type } }[ 0 .. ( $to - 1 ) ], $elem, @{ $self->{ $type } }[ ( $to ) .. $#{ $self->{ $type } } ] );
    }

lib/Graph/ChartSVG.pm  view on Meta::CPAN

                        id    => 'ellipse_' . $layer_ind . '_' . $ind,
                        style => \%style,
#                         transform => "matrix(1,0,0,-1," . $self->border->left . "," . ( $self->total_size->[1] - $Y - $self->border->bottom ) . ")",
                    );
                    $ind++;
                }
            }
            elsif ( exists $layer->{ type } && $layer->{ type } eq 'image' )
            {
                my $image_nbr = 1;
                foreach my $set ( @{ $layer->data_set } )
                {
                    my $raw_img = $set->{ image };
                    my $img64   = encode_base64( $raw_img, "\n" );
                    my $tag     = $svg->image(
                        x => $set->{ x }  || 0,
                        y => -$set->{ y } || 0,
                        width     => $set->{ width },
                        height    => $set->{ height },
                        id        => 'image_' . $layer_ind . '_' . $image_nbr,
                        '-href'   => "data:image/png;base64," . $img64,
                        transform => "matrix(1,0,0,1," . $X . "," . $Y . ")",
                    );
                    $image_nbr++;
                }
# $tag = $svg->image(
# x=>100, y=>100,
# width=>300, height=>200,
# '-href'=>"image.png", #may also embed SVG, e.g. "image.svg"
# id=>'image_1'
# );

            }
        }

#######################################
############## Overlay ################
#######################################
        if ( ( ref $layer ) eq 'Graph::ChartSVG::Overlay' )
        {
            $layer_goup = $data_goup->group( id => "data_$layer_ind" );
            my $ind       = 0;
            my $color_hex = $layer->{ color };
            if ( $layer->{ type } eq 'v' )
            {
                foreach my $start ( keys %{ $layer->{ data_set } } )
                {
                    my $stop = $layer->{ data_set }->{ $start };
                    my $k    = $layer_goup->rectangle(
                        x      => $start,
                        y      => -$layer->{ debord_1 },
                        width  => $stop - $start,
                        height => $self->{ active_size }->[1] + $layer->{ debord_1 } + $layer->{ debord_2 },
                        style  => {

                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill-rule' => 'nonzero'
                        },
                        id => 'v_overlay_' . $layer_ind . '_' . $ind
                    );
                    $ind++;
                }
            }
            if ( $layer->{ type } eq 'h' )
            {
                foreach my $start ( keys %{ $layer->{ data_set } } )
                {
                    my $stop = $layer->{ data_set }->{ $start };
                    my $k    = $layer_goup->rectangle(
                        x      => -$layer->{ debord_1 },
                        y      => $start,
                        width  => $self->{ active_size }->[0] + $layer->{ debord_1 } + $layer->{ debord_2 },
                        height => $stop - $start,
                        style  => {
                            'opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill' => '#' . ( unpack "a6", $color_hex ) || 0,
                            'fill-opacity' => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                            'fill-rule' => 'nonzero'
                        },
                        id => 'h_overlay_' . $layer_ind . '_' . $ind
                    );
                    $ind++;
                }
            }
        }
        $layer_ind++;
    }

    my $info_data_group = $svg->group( id => "info_data" );
    my $obj = Data::Serializer->new( 'compress' => 1 );
    my $tag = $obj->serialize( \@list_data );
    $info_data_group->comment( $tag );

#######################################
## grid
#######################################
    if ( defined $self->grid )
    {
        if ( defined $self->grid->x )
        {
            my $x_grid_group = $svg->group( id => "x_grid" );
            my $x_grid_text = $x_grid_group->group(
                id        => "x_grid_text",
                transform => "matrix(1,0,0,1, 0," . ( $self->border->bottom ) . " )"
            );
            my $thickness = $self->grid->x->thickness || 1;
            my $color_hex = $self->grid->x->color;
            my $max_length_x;
            $max_length_x = max( map( length, @{ $self->grid->x->label->text } ) )
              if ( defined( $self->grid->x->label )
                && ( ref( $self->grid->x->label->text ) eq 'ARRAY' ) );
            my $max_length_x2;
            $max_length_x2 = max( map( length, @{ $self->grid->x->label2->text } ) )
              if ( defined( $self->grid->x->label2 )
                && ( ref( $self->grid->x->label2->text ) eq 'ARRAY' ) );

            for ( my $nbr = $self->grid->x->number - 1 ; $nbr >= 0 ; $nbr-- )
            {
                my $val = $nbr * ( ( ( $self->active_size->[1] ) / ( $self->grid->x->number - 1 ) ) );
                my $text_indx = $self->grid->x->number - $nbr - 1;

                my $tag = $x_grid_group->line(
                    id    => 'x_grid_' . $nbr,
                    x1    => $self->border->left - $self->grid->debord->left,
                    y1    => $self->border->bottom + $val,
                    x2    => $self->border->left + $self->active_size->[0] + $self->grid->debord->right,
                    y2    => $self->border->bottom + $val,
                    style => {
                        'fill'            => 'none',
                        'opacity'         => eval( hex( ( unpack "a6 a2", $color_hex )[1] ) / 255 ) || 1,
                        'stroke'          => '#' . ( unpack "a6", $color_hex ) || 0,
                        'stroke-width'    => $thickness,
                        'stroke-linecap'  => 'butt',
                        'stroke-linejoin' => 'miter',
                        'stroke-opacity'  => 1
                    },
                    transform => "matrix(1,0,0,-1," . ( 0 ) . "," . ( $self->total_size->[1] ) . ")",
                );

                if ( defined $self->grid->x->label && defined $self->grid->x->label->text->[$text_indx] )

lib/Graph/ChartSVG.pm  view on Meta::CPAN

# carp Dumper($sib2);
#
# }
#
# }
    $self;
}

# __PACKAGE__->meta->make_immutable;
#
1;

=head1 METHODS
	
	OO interface

=head2 Graph::ChartSVG->new

=over

Create a new Chart 

possible parameters are :

=back

=head3  active_size 

=over 

=back

    an array ref with x,y size of the active graph ( without the reserved border for label )
    
 
=head3  bg_color
 
=over 

=back

  an hex color for the global background color
 
=head3      frame
 
=over 

=back

  a Frame object to surround the active part of the graph

=head3      grid
 
=over 

=back

  a Grid oject to add to the graph
  
  
=head3     overlay
 
=over 

=back

 a Overlay to add on top of the graph ( useful to enhance a period in alarm )
  
=head3     layer
 
=over

=back

 a Layer object
  
=head3     border
 
=over 

=back

 a Border object ( = some extra space to fit aroubd the active graph to allow label.
                This increase the actual_size and create the total_size)
 
=head3      tag
 
=over 

=back

 a Tag objet ( if missing create a automatically incremented one )
 
=head3      glyph
 
=over 

=back

 a Glyph object to add on the graph ( like a arrow to point at the end of the current data )
    


my $graph = Graph::ChartSVG->new( active_size => \@size, bg_color => 'FCF4C6', frame => $f, png_tag => 1 );


=over 

=back

=head2 Frame->new

=over

=back
 
=head3      color
 
=over 

=back



( run in 2.735 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )