Imager-Graph

 view release on metacpan or  search on metacpan

lib/Imager/Graph/Pie.pm  view on Meta::CPAN

The C<data> parameter should be a reference to an array containing the
data the pie graph should present.

The C<labels> parameter is a reference to an array of labels,
corresponding to the values in C<data>.

=back

=head1 FEATURES

As described in L<Imager::Graph> you can enable extra features for
your graph.  The features you can use with pie graphs are:

=over

=item show_callouts_onAll_segments()

Feature: allcallouts.
X<allcallouts>X<features, allcallouts>

all labels are presented as callouts

=cut

sub show_callouts_onAll_segments {
    $_[0]->{'custom_style'}->{'features'}->{'allcallouts'} = 1;
}

=item show_only_label_percentages()

Feature: labelspconly
X<labelspconly>X<features, labelspconly>

only show the percentage, not the labels.

=cut

sub show_only_label_percentages {
    $_[0]->{'custom_style'}->{'features'}->{'labelspconly'} = 1;
}

=item show_label_percentages()

Feature: labelspc
X<labelspc>X<features, labelspc>

adds the percentage of the pie to each label.

=cut

sub show_label_percentages {
    $_[0]->{'custom_style'}->{'features'}->{'labelspc'} = 1;
}

=back

Inherited features:

=over

=item legend

adds a legend to your graph.  Requires the labels parameter

=item labels

labels each segment of the graph.  If the label doesn't fit inside the
segment it is presented as a callout.

=item outline

the pie segments are outlined.

=item dropshadow

the pie is given a drop shadow.

=back

=head1 PIE CHART STYLES

The following style values are specific to pie charts:

Controlling callouts, the C<callout> option:

=over

=item *

color - the color of the callout line and the callout text.

=item *

font, size - font and size of the callout text

=item *

outside - the distance the radial callout line goes outside the pie

=item *

leadlen - the length of the horizontal callout line from the end of
the radial line.

=item *

gap - the distance between the end of the horizontal callout line and
the label.

=item *

inside - the length of the radial callout line within the pie.

=back

The outline, line option controls the color of the pie segment
outlines, if enabled with the C<outline> feature.

Under C<pie>:

=over

=item *

maxsegment - any segment below this fraction of the total of the
segments will be put into the "others" segment.  Default: 0.01

=back

The top level C<otherlabel> setting controls the label for the
"others" segment, default "(others)".

=head1 EXAMPLES

Assuming:

  # from the Netcraft September 2001 web survey
  # http://www.netcraft.com/survey/
  my @data   = qw(17874757  8146372   1321544  811406 );
  my @labels = qw(Apache    Microsoft i_planet  Zeus   );

  my $pie = Imager::Graph::Pie->new;

First a simple graph, normal size, no labels:

  my $img = $pie->draw(data=>\@data)
    or die $pie->error;

label the segments:

  # error handling omitted for brevity from now on
  $img = $pie->draw(data=>\@data, labels=>\@labels, features=>'labels');

just percentages in the segments:

  $img = $pie->draw(data=>\@data, features=>'labelspconly');

add a legend as well:

  $img = $pie->draw(data=>\@data, labels=>\@labels,
                    features=>[ 'labelspconly', 'legend' ]);

and a title, but move the legend down, and add a dropshadow:

  $img = $pie->draw(data=>\@data, labels=>\@labels,
                    title=>'Netcraft Web Survey',
                    legend=>{ valign=>'bottom' },
                    features=>[ qw/labelspconly legend dropshadow/ ]);

something a bit prettier:

  $img = $pie->draw(data=>\@data, labels=>\@labels,
                    style=>'fount_lin', features=>'legend');

suitable for monochrome output:

  $img = $pie->draw(data=>\@data, labels=>\@labels,
                    style=>'mono', features=>'legend');

=cut

# this function is too long
sub draw {
  my ($self, %opts) = @_;

  my $data_series = $self->_get_data_series(\%opts);

  $self->_valid_input($data_series)
    or return;

  my @data = @{$data_series->[0]->{'data'}};

  my @labels = @{$self->_get_labels(\%opts) || []};

  $self->_style_setup(\%opts);

  my $style = $self->{_style};

  my $img = $self->_make_img()
    or return;

  my @chart_box = ( 0, 0, $img->getwidth-1, $img->getheight-1 );
  if ($style->{title}{text}) {
    $self->_draw_title($img, \@chart_box)
      or return;
  }

  my $total = 0;
  for my $item (@data) {
    $total += $item;
  }

  # consolidate any segments that are too small to display
  $self->_consolidate_segments(\@data, \@labels, $total);

  if ($style->{features}{legend} && (scalar @labels)) {
    $self->_draw_legend($img, \@labels, \@chart_box)
      or return;
  }

  # the following code is fairly ugly
  # it attempts to work out a good layout for the components of the chart
  my @info;
  my $index = 0;
  my $pos = 0;
  my @ebox = (0, 0, 0, 0);
  defined(my $callout_outside = $self->_get_number('callout.outside'))
    or return;
  defined(my $callout_leadlen = $self->_get_number('callout.leadlen'))
    or return;
  defined(my $callout_gap = $self->_get_number('callout.gap'))
    or return;
  defined(my $label_vpad = $self->_get_number('label.vpad'))
    or return;
  defined(my $label_hpad = $self->_get_number('label.hpad'))
    or return;
  my $guessradius = 
    int($self->_small_extent(\@chart_box) * $style->{pie}{guessfactor} * 0.5);
  for my $data (@data) {
    my $item = { data=>$data, index=>$index };
    my $size = 2 * PI * $data / $total;
    $item->{begin} = $pos;
    $pos += $size;
    $item->{end} = $pos;
    if (scalar @labels) {
      $item->{text} = $labels[$index];
    }
    if ($style->{features}{labelspconly}) {
      $item->{text} = 
        $style->{label}{pconlyformat}->($data/$total * 100);
    }
    if ($item->{text}) {
      if ($style->{features}{labelspc}) {
        $item->{text} = 
          $style->{label}{pcformat}->($item->{text}, $data/$total * 100);
        $item->{label} = 1;
      }
      elsif ($style->{features}{labelspconly}) {
        $item->{text} = 
          $style->{label}{pconlyformat}->($data/$total * 100);
        $item->{label} = 1;
      }
      elsif ($style->{features}{labels}) {
        $item->{label} = 1;
      }
      $item->{callout} = 1 if $style->{features}{allcallouts};
      if (!$item->{callout}) {
        my @lbox = $self->_text_bbox($item->{text}, 'label')
          or return;
        $item->{lbox} = \@lbox;
        if ($item->{label}) {
          unless ($self->_fit_text(0, 0, 'label', $item->{text}, $guessradius,
                                   $item->{begin}, $item->{end})) {
            $item->{callout} = 1;
          }
        }
      }



( run in 1.299 second using v1.01-cache-2.11-cpan-39bf76dae61 )