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             package Excel::Writer::XLSX::Chart::Scatter;
2              
3             ###############################################################################
4             #
5             # Scatter - A class for writing Excel Scatter charts.
6             #
7             # Used in conjunction with Excel::Writer::XLSX::Chart.
8             #
9             # See formatting note in Excel::Writer::XLSX::Chart.
10             #
11             # Copyright 2000-2019, John McNamara, jmcnamara@cpan.org
12             #
13             # Documentation after __END__
14             #
15              
16             # perltidy with the following options: -mbl=2 -pt=0 -nola
17              
18 34     34   3340 use 5.008002;
  34         124  
19 34     34   186 use strict;
  34         67  
  34         686  
20 34     34   159 use warnings;
  34         74  
  34         989  
21 34     34   183 use Carp;
  34         89  
  34         2239  
22 34     34   254 use Excel::Writer::XLSX::Chart;
  34         88  
  34         42662  
23              
24             our @ISA = qw(Excel::Writer::XLSX::Chart);
25             our $VERSION = '1.03';
26              
27              
28             ###############################################################################
29             #
30             # new()
31             #
32             #
33             sub new {
34              
35 34     34 0 893 my $class = shift;
36 34         165 my $self = Excel::Writer::XLSX::Chart->new( @_ );
37              
38 34   100     173 $self->{_subtype} = $self->{_subtype} || 'marker_only';
39 34         75 $self->{_cross_between} = 'midCat';
40 34         69 $self->{_horiz_val_axis} = 0;
41 34         66 $self->{_val_axis_postion} = 'b';
42 34         66 $self->{_smooth_allowed} = 1;
43 34         62 $self->{_requires_category} = 1;
44              
45             # Set the available data label positions for this chart type.
46 34         72 $self->{_label_position_default} = 'right';
47             $self->{_label_positions} = {
48 34         215 center => 'ctr',
49             right => 'r',
50             left => 'l',
51             above => 't',
52             below => 'b',
53             # For backward compatibility.
54             top => 't',
55             bottom => 'b',
56             };
57              
58 34         88 bless $self, $class;
59 34         139 return $self;
60             }
61              
62              
63             ###############################################################################
64             #
65             # combine()
66             #
67             # Override parent method to add a warning.
68             #
69             sub combine {
70              
71 0     0 1 0 my $self = shift;
72 0         0 my $chart = shift;
73              
74 0         0 carp 'Combined chart not currently supported with scatter chart ' .
75             'as the primary chart';
76 0         0 return;
77             }
78              
79              
80             ##############################################################################
81             #
82             # _write_chart_type()
83             #
84             # Override the virtual superclass method with a chart specific method.
85             #
86             sub _write_chart_type {
87              
88 66     66   143 my $self = shift;
89              
90             # Write the c:scatterChart element.
91 66         219 $self->_write_scatter_chart( @_ );
92             }
93              
94              
95             ##############################################################################
96             #
97             # _write_scatter_chart()
98             #
99             # Write the element.
100             #
101             sub _write_scatter_chart {
102              
103 66     66   126 my $self = shift;
104 66         199 my %args = @_;
105              
106 66         128 my @series;
107 66 100       190 if ( $args{primary_axes} ) {
108 33         231 @series = $self->_get_primary_axes_series;
109             }
110             else {
111 33         283 @series = $self->_get_secondary_axes_series;
112             }
113              
114 66 100       223 return unless scalar @series;
115              
116 34         82 my $style = 'lineMarker';
117 34         86 my $subtype = $self->{_subtype};
118              
119             # Set the user defined chart subtype.
120              
121 34 100       195 if ($subtype eq 'marker_only') {
122 24         78 $style = 'lineMarker';
123             }
124              
125 34 100       156 if ($subtype eq 'straight_with_markers') {
126 3         14 $style = 'lineMarker';
127             }
128              
129 34 100       168 if ($subtype eq 'straight') {
130 3         5 $style = 'lineMarker';
131 3         11 $self->{_default_marker} = { type => 'none' };
132             }
133              
134 34 100       130 if ($subtype eq 'smooth_with_markers') {
135 2         4 $style = 'smoothMarker';
136             }
137              
138 34 100       116 if ($subtype eq 'smooth') {
139 2         11 $style = 'smoothMarker';
140 2         12 $self->{_default_marker} = { type => 'none' };
141             }
142              
143             # Add default formatting to the series data.
144 34         149 $self->_modify_series_formatting();
145              
146 34         152 $self->xml_start_tag( 'c:scatterChart' );
147              
148             # Write the c:scatterStyle element.
149 34         143 $self->_write_scatter_style( $style );
150              
151             # Write the series elements.
152 34         143 $self->_write_ser( $_ ) for @series;
153              
154             # Write the c:axId elements
155 34         393 $self->_write_axis_ids( %args );
156              
157 34         141 $self->xml_end_tag( 'c:scatterChart' );
158             }
159              
160              
161             ##############################################################################
162             #
163             # _write_ser()
164             #
165             # Over-ridden to write c:xVal/c:yVal instead of c:cat/c:val elements.
166             #
167             # Write the element.
168             #
169             sub _write_ser {
170              
171 62     62   219 my $self = shift;
172 62         118 my $series = shift;
173 62         177 my $index = $self->{_series_index}++;
174              
175 62         252 $self->xml_start_tag( 'c:ser' );
176              
177             # Write the c:idx element.
178 62         388 $self->_write_idx( $index );
179              
180             # Write the c:order element.
181 62         333 $self->_write_order( $index );
182              
183             # Write the series name.
184 62         357 $self->_write_series_name( $series );
185              
186             # Write the c:spPr element.
187 62         336 $self->_write_sp_pr( $series );
188              
189             # Write the c:marker element.
190 62         392 $self->_write_marker( $series->{_marker} );
191              
192             # Write the c:dPt element.
193 62         330 $self->_write_d_pt( $series->{_points} );
194              
195             # Write the c:dLbls element.
196 62         313 $self->_write_d_lbls( $series->{_labels} );
197              
198             # Write the c:trendline element.
199 62         309 $self->_write_trendline( $series->{_trendline} );
200              
201             # Write the c:errBars element.
202 62         451 $self->_write_error_bars( $series->{_error_bars} );
203              
204             # Write the c:xVal element.
205 62         272 $self->_write_x_val( $series );
206              
207             # Write the c:yVal element.
208 62         229 $self->_write_y_val( $series );
209              
210             # Write the c:smooth element.
211 62 100 100     362 if ( $self->{_subtype} =~ /smooth/ && !defined $series->{_smooth} ) {
212             # Default is on for smooth scatter charts.
213 6         39 $self->_write_c_smooth( 1 );
214             }
215             else {
216 56         442 $self->_write_c_smooth( $series->{_smooth} );
217             }
218              
219 62         199 $self->xml_end_tag( 'c:ser' );
220             }
221              
222              
223             ##############################################################################
224             #
225             # _write_plot_area()
226             #
227             # Over-ridden to have 2 valAx elements for scatter charts instead of
228             # catAx/valAx.
229             #
230             # Write the element.
231             #
232             sub _write_plot_area {
233              
234 31     31   93 my $self = shift;
235              
236 31         144 $self->xml_start_tag( 'c:plotArea' );
237              
238             # Write the c:layout element.
239 31         369 $self->_write_layout( $self->{_plotarea}->{_layout}, 'plot' );
240              
241             # Write the subclass chart type elements for primary and secondary axes.
242 31         149 $self->_write_chart_type( primary_axes => 1 );
243 31         118 $self->_write_chart_type( primary_axes => 0 );
244              
245             # Write c:catAx and c:valAx elements for series using primary axes.
246             $self->_write_cat_val_axis(
247             x_axis => $self->{_x_axis},
248             y_axis => $self->{_y_axis},
249             axis_ids => $self->{_axis_ids},
250 31         319 position => 'b',
251             );
252              
253 31         78 my $tmp = $self->{_horiz_val_axis};
254 31         75 $self->{_horiz_val_axis} = 1;
255             $self->_write_val_axis(
256             x_axis => $self->{_x_axis},
257             y_axis => $self->{_y_axis},
258             axis_ids => $self->{_axis_ids},
259 31         264 position => 'l',
260             );
261 31         78 $self->{_horiz_val_axis} = $tmp;
262              
263             # Write c:valAx and c:catAx elements for series using secondary axes.
264             $self->_write_cat_val_axis(
265             x_axis => $self->{_x2_axis},
266             y_axis => $self->{_y2_axis},
267             axis_ids => $self->{_axis2_ids},
268 31         185 position => 'b',
269             );
270 31         64 $self->{_horiz_val_axis} = 1;
271             $self->_write_val_axis(
272             x_axis => $self->{_x2_axis},
273             y_axis => $self->{_y2_axis},
274             axis_ids => $self->{_axis2_ids},
275 31         174 position => 'l',
276             );
277              
278             # Write the c:spPr element for the plotarea formatting.
279 31         131 $self->_write_sp_pr( $self->{_plotarea} );
280              
281 31         102 $self->xml_end_tag( 'c:plotArea' );
282             }
283              
284              
285             ##############################################################################
286             #
287             # _write_x_val()
288             #
289             # Write the element.
290             #
291             sub _write_x_val {
292              
293 62     62   127 my $self = shift;
294 62         132 my $series = shift;
295 62         130 my $formula = $series->{_categories};
296 62         123 my $data_id = $series->{_cat_data_id};
297 62         148 my $data = $self->{_formula_data}->[$data_id];
298              
299 62         226 $self->xml_start_tag( 'c:xVal' );
300              
301             # Check the type of cached data.
302 62         318 my $type = $self->_get_data_type( $data );
303              
304             # TODO. Can a scatter plot have non-numeric data.
305              
306 62 100       220 if ( $type eq 'str' ) {
307              
308             # Write the c:numRef element.
309 2         29 $self->_write_str_ref( $formula, $data, $type );
310             }
311             else {
312              
313             # Write the c:numRef element.
314 60         359 $self->_write_num_ref( $formula, $data, $type );
315             }
316              
317 62         226 $self->xml_end_tag( 'c:xVal' );
318             }
319              
320              
321             ##############################################################################
322             #
323             # _write_y_val()
324             #
325             # Write the element.
326             #
327             sub _write_y_val {
328              
329 62     62   124 my $self = shift;
330 62         116 my $series = shift;
331 62         198 my $formula = $series->{_values};
332 62         131 my $data_id = $series->{_val_data_id};
333 62         165 my $data = $self->{_formula_data}->[$data_id];
334              
335 62         210 $self->xml_start_tag( 'c:yVal' );
336              
337             # Unlike Cat axes data should only be numeric.
338              
339             # Write the c:numRef element.
340 62         235 $self->_write_num_ref( $formula, $data, 'num' );
341              
342 62         199 $self->xml_end_tag( 'c:yVal' );
343             }
344              
345              
346             ##############################################################################
347             #
348             # _write_scatter_style()
349             #
350             # Write the element.
351             #
352             sub _write_scatter_style {
353              
354 34     34   81 my $self = shift;
355 34         67 my $val = shift;
356              
357 34         99 my @attributes = ( 'val' => $val );
358              
359 34         122 $self->xml_empty_tag( 'c:scatterStyle', @attributes );
360             }
361              
362              
363             ##############################################################################
364             #
365             # _modify_series_formatting()
366             #
367             # Add default formatting to the series data unless it has already been
368             # specified by the user.
369             #
370             sub _modify_series_formatting {
371              
372 34     34   73 my $self = shift;
373 34         73 my $subtype = $self->{_subtype};
374              
375             # The default scatter style "markers only" requires a line type.
376 34 100       113 if ( $subtype eq 'marker_only' ) {
377              
378             # Go through each series and define default values.
379 24         60 for my $series ( @{ $self->{_series} } ) {
  24         72  
380              
381             # Set a line type unless there is already a user defined type.
382 44 100       174 if ( !$series->{_line}->{_defined} ) {
383             $series->{_line} = {
384 42         220 width => 2.25,
385             none => 1,
386             _defined => 1,
387             };
388             }
389             }
390             }
391             }
392              
393              
394             ##############################################################################
395             #
396             # _write_d_pt_point()
397             #
398             # Write an individual element. Override the parent method to add
399             # markers.
400             #
401             sub _write_d_pt_point {
402              
403 3     3   5 my $self = shift;
404 3         5 my $index = shift;
405 3         5 my $point = shift;
406              
407 3         9 $self->xml_start_tag( 'c:dPt' );
408              
409             # Write the c:idx element.
410 3         12 $self->_write_idx( $index );
411              
412 3         9 $self->xml_start_tag( 'c:marker' );
413              
414             # Write the c:spPr element.
415 3         9 $self->_write_sp_pr( $point );
416              
417 3         9 $self->xml_end_tag( 'c:marker' );
418              
419 3         6 $self->xml_end_tag( 'c:dPt' );
420             }
421              
422              
423             1;
424              
425              
426             __END__