File Coverage

blib/lib/Excel/Writer/XLSX/Chart/Scatter.pm
Criterion Covered Total %
statement 128 132 96.9
branch 22 22 100.0
condition 5 5 100.0
subroutine 15 16 93.7
pod 1 2 50.0
total 171 177 96.6


line stmt bran cond sub pod time code
1              
2             ###############################################################################
3             #
4             # Scatter - A class for writing Excel Scatter charts.
5             #
6             # Used in conjunction with Excel::Writer::XLSX::Chart.
7             #
8             # See formatting note in Excel::Writer::XLSX::Chart.
9             #
10             # Copyright 2000-2021, John McNamara, jmcnamara@cpan.org
11             #
12             # Documentation after __END__
13             #
14              
15             # perltidy with the following options: -mbl=2 -pt=0 -nola
16              
17             use 5.008002;
18 34     34   2986 use strict;
  34         99  
19 34     34   148 use warnings;
  34         59  
  34         554  
20 34     34   141 use Carp;
  34         56  
  34         674  
21 34     34   128 use Excel::Writer::XLSX::Chart;
  34         55  
  34         1674  
22 34     34   166  
  34         47  
  34         33949  
23             our @ISA = qw(Excel::Writer::XLSX::Chart);
24             our $VERSION = '1.09';
25              
26              
27             ###############################################################################
28             #
29             # new()
30             #
31             #
32              
33             my $class = shift;
34             my $self = Excel::Writer::XLSX::Chart->new( @_ );
35 34     34 0 772  
36 34         128 $self->{_subtype} = $self->{_subtype} || 'marker_only';
37             $self->{_cross_between} = 'midCat';
38 34   100     151 $self->{_horiz_val_axis} = 0;
39 34         84 $self->{_val_axis_postion} = 'b';
40 34         71 $self->{_smooth_allowed} = 1;
41 34         64 $self->{_requires_category} = 1;
42 34         69  
43 34         73 # Set the available data label positions for this chart type.
44             $self->{_label_position_default} = 'right';
45             $self->{_label_positions} = {
46 34         83 center => 'ctr',
47             right => 'r',
48 34         256 left => 'l',
49             above => 't',
50             below => 'b',
51             # For backward compatibility.
52             top => 't',
53             bottom => 'b',
54             };
55              
56             bless $self, $class;
57             return $self;
58 34         83 }
59 34         114  
60              
61             ###############################################################################
62             #
63             # combine()
64             #
65             # Override parent method to add a warning.
66             #
67              
68             my $self = shift;
69             my $chart = shift;
70              
71 0     0 1 0 carp 'Combined chart not currently supported with scatter chart ' .
72 0         0 'as the primary chart';
73             return;
74 0         0 }
75              
76 0         0  
77             ##############################################################################
78             #
79             # _write_chart_type()
80             #
81             # Override the virtual superclass method with a chart specific method.
82             #
83              
84             my $self = shift;
85              
86             # Write the c:scatterChart element.
87             $self->_write_scatter_chart( @_ );
88 66     66   122 }
89              
90              
91 66         171 ##############################################################################
92             #
93             # _write_scatter_chart()
94             #
95             # Write the <c:scatterChart> element.
96             #
97              
98             my $self = shift;
99             my %args = @_;
100              
101             my @series;
102             if ( $args{primary_axes} ) {
103 66     66   89 @series = $self->_get_primary_axes_series;
104 66         179 }
105             else {
106 66         123 @series = $self->_get_secondary_axes_series;
107 66 100       172 }
108 33         216  
109             return unless scalar @series;
110              
111 33         217 my $style = 'lineMarker';
112             my $subtype = $self->{_subtype};
113              
114 66 100       203 # Set the user defined chart subtype.
115              
116 34         70 if ($subtype eq 'marker_only') {
117 34         66 $style = 'lineMarker';
118             }
119              
120             if ($subtype eq 'straight_with_markers') {
121 34 100       143 $style = 'lineMarker';
122 24         55 }
123              
124             if ($subtype eq 'straight') {
125 34 100       143 $style = 'lineMarker';
126 3         20 $self->{_default_marker} = { type => 'none' };
127             }
128              
129 34 100       136 if ($subtype eq 'smooth_with_markers') {
130 3         4 $style = 'smoothMarker';
131 3         7 }
132              
133             if ($subtype eq 'smooth') {
134 34 100       102 $style = 'smoothMarker';
135 2         4 $self->{_default_marker} = { type => 'none' };
136             }
137              
138 34 100       117 # Add default formatting to the series data.
139 2         4 $self->_modify_series_formatting();
140 2         7  
141             $self->xml_start_tag( 'c:scatterChart' );
142              
143             # Write the c:scatterStyle element.
144 34         128 $self->_write_scatter_style( $style );
145              
146 34         114 # Write the series elements.
147             $self->_write_ser( $_ ) for @series;
148              
149 34         118 # Write the c:axId elements
150             $self->_write_axis_ids( %args );
151              
152 34         161 $self->xml_end_tag( 'c:scatterChart' );
153             }
154              
155 34         343  
156             ##############################################################################
157 34         103 #
158             # _write_ser()
159             #
160             # Over-ridden to write c:xVal/c:yVal instead of c:cat/c:val elements.
161             #
162             # Write the <c:ser> element.
163             #
164              
165             my $self = shift;
166             my $series = shift;
167             my $index = $self->{_series_index}++;
168              
169             $self->xml_start_tag( 'c:ser' );
170              
171 62     62   111 # Write the c:idx element.
172 62         99 $self->_write_idx( $index );
173 62         126  
174             # Write the c:order element.
175 62         183 $self->_write_order( $index );
176              
177             # Write the series name.
178 62         317 $self->_write_series_name( $series );
179              
180             # Write the c:spPr element.
181 62         258 $self->_write_sp_pr( $series );
182              
183             # Write the c:marker element.
184 62         330 $self->_write_marker( $series->{_marker} );
185              
186             # Write the c:dPt element.
187 62         260 $self->_write_d_pt( $series->{_points} );
188              
189             # Write the c:dLbls element.
190 62         317 $self->_write_d_lbls( $series->{_labels} );
191              
192             # Write the c:trendline element.
193 62         276 $self->_write_trendline( $series->{_trendline} );
194              
195             # Write the c:errBars element.
196 62         252 $self->_write_error_bars( $series->{_error_bars} );
197              
198             # Write the c:xVal element.
199 62         314 $self->_write_x_val( $series );
200              
201             # Write the c:yVal element.
202 62         286 $self->_write_y_val( $series );
203              
204             # Write the c:smooth element.
205 62         181 if ( $self->{_subtype} =~ /smooth/ && !defined $series->{_smooth} ) {
206             # Default is on for smooth scatter charts.
207             $self->_write_c_smooth( 1 );
208 62         181 }
209             else {
210             $self->_write_c_smooth( $series->{_smooth} );
211 62 100 100     333 }
212              
213 6         39 $self->xml_end_tag( 'c:ser' );
214             }
215              
216 56         324  
217             ##############################################################################
218             #
219 62         184 # _write_plot_area()
220             #
221             # Over-ridden to have 2 valAx elements for scatter charts instead of
222             # catAx/valAx.
223             #
224             # Write the <c:plotArea> element.
225             #
226              
227             my $self = shift;
228              
229             $self->xml_start_tag( 'c:plotArea' );
230              
231             # Write the c:layout element.
232             $self->_write_layout( $self->{_plotarea}->{_layout}, 'plot' );
233              
234 31     31   68 # Write the subclass chart type elements for primary and secondary axes.
235             $self->_write_chart_type( primary_axes => 1 );
236 31         156 $self->_write_chart_type( primary_axes => 0 );
237              
238             # Write c:catAx and c:valAx elements for series using primary axes.
239 31         321 $self->_write_cat_val_axis(
240             x_axis => $self->{_x_axis},
241             y_axis => $self->{_y_axis},
242 31         123 axis_ids => $self->{_axis_ids},
243 31         109 position => 'b',
244             );
245              
246             my $tmp = $self->{_horiz_val_axis};
247             $self->{_horiz_val_axis} = 1;
248             $self->_write_val_axis(
249             x_axis => $self->{_x_axis},
250 31         250 y_axis => $self->{_y_axis},
251             axis_ids => $self->{_axis_ids},
252             position => 'l',
253 31         63 );
254 31         69 $self->{_horiz_val_axis} = $tmp;
255              
256             # Write c:valAx and c:catAx elements for series using secondary axes.
257             $self->_write_cat_val_axis(
258             x_axis => $self->{_x2_axis},
259 31         227 y_axis => $self->{_y2_axis},
260             axis_ids => $self->{_axis2_ids},
261 31         62 position => 'b',
262             );
263             $self->{_horiz_val_axis} = 1;
264             $self->_write_val_axis(
265             x_axis => $self->{_x2_axis},
266             y_axis => $self->{_y2_axis},
267             axis_ids => $self->{_axis2_ids},
268 31         146 position => 'l',
269             );
270 31         52  
271             # Write the c:spPr element for the plotarea formatting.
272             $self->_write_sp_pr( $self->{_plotarea} );
273              
274             $self->xml_end_tag( 'c:plotArea' );
275 31         131 }
276              
277              
278             ##############################################################################
279 31         154 #
280             # _write_x_val()
281 31         111 #
282             # Write the <c:xVal> element.
283             #
284              
285             my $self = shift;
286             my $series = shift;
287             my $formula = $series->{_categories};
288             my $data_id = $series->{_cat_data_id};
289             my $data = $self->{_formula_data}->[$data_id];
290              
291             $self->xml_start_tag( 'c:xVal' );
292              
293 62     62   100 # Check the type of cached data.
294 62         96 my $type = $self->_get_data_type( $data );
295 62         121  
296 62         115 # TODO. Can a scatter plot have non-numeric data.
297 62         155  
298             if ( $type eq 'str' ) {
299 62         186  
300             # Write the c:numRef element.
301             $self->_write_str_ref( $formula, $data, $type );
302 62         270 }
303             else {
304              
305             # Write the c:numRef element.
306 62 100       187 $self->_write_num_ref( $formula, $data, $type );
307             }
308              
309 2         9 $self->xml_end_tag( 'c:xVal' );
310             }
311              
312              
313             ##############################################################################
314 60         336 #
315             # _write_y_val()
316             #
317 62         136 # Write the <c:yVal> element.
318             #
319              
320             my $self = shift;
321             my $series = shift;
322             my $formula = $series->{_values};
323             my $data_id = $series->{_val_data_id};
324             my $data = $self->{_formula_data}->[$data_id];
325              
326             $self->xml_start_tag( 'c:yVal' );
327              
328             # Unlike Cat axes data should only be numeric.
329 62     62   102  
330 62         106 # Write the c:numRef element.
331 62         143 $self->_write_num_ref( $formula, $data, 'num' );
332 62         99  
333 62         110 $self->xml_end_tag( 'c:yVal' );
334             }
335 62         194  
336              
337             ##############################################################################
338             #
339             # _write_scatter_style()
340 62         182 #
341             # Write the <c:scatterStyle> element.
342 62         169 #
343              
344             my $self = shift;
345             my $val = shift;
346              
347             my @attributes = ( 'val' => $val );
348              
349             $self->xml_empty_tag( 'c:scatterStyle', @attributes );
350             }
351              
352              
353             ##############################################################################
354 34     34   56 #
355 34         53 # _modify_series_formatting()
356             #
357 34         89 # Add default formatting to the series data unless it has already been
358             # specified by the user.
359 34         391 #
360              
361             my $self = shift;
362             my $subtype = $self->{_subtype};
363              
364             # The default scatter style "markers only" requires a line type.
365             if ( $subtype eq 'marker_only' ) {
366              
367             # Go through each series and define default values.
368             for my $series ( @{ $self->{_series} } ) {
369              
370             # Set a line type unless there is already a user defined type.
371             if ( !$series->{_line}->{_defined} ) {
372 34     34   73 $series->{_line} = {
373 34         80 width => 2.25,
374             none => 1,
375             _defined => 1,
376 34 100       143 };
377             }
378             }
379 24         52 }
  24         63  
380             }
381              
382 44 100       125  
383             ##############################################################################
384 42         188 #
385             # _write_d_pt_point()
386             #
387             # Write an individual <c:dPt> element. Override the parent method to add
388             # markers.
389             #
390              
391             my $self = shift;
392             my $index = shift;
393             my $point = shift;
394              
395             $self->xml_start_tag( 'c:dPt' );
396              
397             # Write the c:idx element.
398             $self->_write_idx( $index );
399              
400             $self->xml_start_tag( 'c:marker' );
401              
402             # Write the c:spPr element.
403 3     3   4 $self->_write_sp_pr( $point );
404 3         4  
405 3         5 $self->xml_end_tag( 'c:marker' );
406              
407 3         8 $self->xml_end_tag( 'c:dPt' );
408             }
409              
410 3         8  
411             1;
412 3         8  
413              
414              
415 3         23  
416             =head1 NAME
417 3         8  
418             Scatter - A class for writing Excel Scatter charts.
419 3         7  
420             =head1 SYNOPSIS
421              
422             To create a simple Excel file with a Scatter chart using Excel::Writer::XLSX:
423              
424             #!/usr/bin/perl
425              
426             use strict;
427             use warnings;
428             use Excel::Writer::XLSX;
429              
430             my $workbook = Excel::Writer::XLSX->new( 'chart.xlsx' );
431             my $worksheet = $workbook->add_worksheet();
432              
433             my $chart = $workbook->add_chart( type => 'scatter' );
434              
435             # Configure the chart.
436             $chart->add_series(
437             categories => '=Sheet1!$A$2:$A$7',
438             values => '=Sheet1!$B$2:$B$7',
439             );
440              
441             # Add the worksheet data the chart refers to.
442             my $data = [
443             [ 'Category', 2, 3, 4, 5, 6, 7 ],
444             [ 'Value', 1, 4, 5, 2, 1, 5 ],
445             ];
446              
447             $worksheet->write( 'A1', $data );
448              
449             __END__
450              
451             =head1 DESCRIPTION
452              
453             This module implements Scatter charts for L<Excel::Writer::XLSX>. The chart object is created via the Workbook C<add_chart()> method:
454              
455             my $chart = $workbook->add_chart( type => 'scatter' );
456              
457             Once the object is created it can be configured via the following methods that are common to all chart classes:
458              
459             $chart->add_series();
460             $chart->set_x_axis();
461             $chart->set_y_axis();
462             $chart->set_title();
463              
464             These methods are explained in detail in L<Excel::Writer::XLSX::Chart>. Class specific methods or settings, if any, are explained below.
465              
466             =head1 Scatter Chart Subtypes
467              
468             The C<Scatter> chart module also supports the following sub-types:
469              
470             markers_only (the default)
471             straight_with_markers
472             straight
473             smooth_with_markers
474             smooth
475              
476             These can be specified at creation time via the C<add_chart()> Worksheet method:
477              
478             my $chart = $workbook->add_chart(
479             type => 'scatter',
480             subtype => 'straight_with_markers'
481             );
482              
483             =head1 EXAMPLE
484              
485             Here is a complete example that demonstrates most of the available features when creating a chart.
486              
487             #!/usr/bin/perl
488              
489             use strict;
490             use warnings;
491             use Excel::Writer::XLSX;
492              
493             my $workbook = Excel::Writer::XLSX->new( 'chart_scatter.xlsx' );
494             my $worksheet = $workbook->add_worksheet();
495             my $bold = $workbook->add_format( bold => 1 );
496              
497             # Add the worksheet data that the charts will refer to.
498             my $headings = [ 'Number', 'Batch 1', 'Batch 2' ];
499             my $data = [
500             [ 2, 3, 4, 5, 6, 7 ],
501             [ 10, 40, 50, 20, 10, 50 ],
502             [ 30, 60, 70, 50, 40, 30 ],
503              
504             ];
505              
506             $worksheet->write( 'A1', $headings, $bold );
507             $worksheet->write( 'A2', $data );
508              
509             # Create a new chart object. In this case an embedded chart.
510             my $chart = $workbook->add_chart( type => 'scatter', embedded => 1 );
511              
512             # Configure the first series.
513             $chart->add_series(
514             name => '=Sheet1!$B$1',
515             categories => '=Sheet1!$A$2:$A$7',
516             values => '=Sheet1!$B$2:$B$7',
517             );
518              
519             # Configure second series. Note alternative use of array ref to define
520             # ranges: [ $sheetname, $row_start, $row_end, $col_start, $col_end ].
521             $chart->add_series(
522             name => '=Sheet1!$C$1',
523             categories => [ 'Sheet1', 1, 6, 0, 0 ],
524             values => [ 'Sheet1', 1, 6, 2, 2 ],
525             );
526              
527             # Add a chart title and some axis labels.
528             $chart->set_title ( name => 'Results of sample analysis' );
529             $chart->set_x_axis( name => 'Test number' );
530             $chart->set_y_axis( name => 'Sample length (mm)' );
531              
532             # Set an Excel chart style. Colors with white outline and shadow.
533             $chart->set_style( 10 );
534              
535             # Insert the chart into the worksheet (with an offset).
536             $worksheet->insert_chart( 'D2', $chart, 25, 10 );
537              
538             __END__
539              
540              
541             =begin html
542              
543             <p>This will produce a chart that looks like this:</p>
544              
545             <p><center><img src="http://jmcnamara.github.io/excel-writer-xlsx/images/examples/scatter1.jpg" width="483" height="291" alt="Chart example." /></center></p>
546              
547             =end html
548              
549              
550             =head1 AUTHOR
551              
552             John McNamara jmcnamara@cpan.org
553              
554             =head1 COPYRIGHT
555              
556             Copyright MM-MMXXI, John McNamara.
557              
558             All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.