Class PDF::Charts::StdDev
In: lib/pdf/charts/stddev.rb
Parent: Object
Transaction::Simple SimpleTable TechBook Complex Action Procset FontDescriptor FontEncoding Destination Info Catalog Encryption Contents Pages Outline Outlines Annotation Page Font ViewerPreferences Image Hash OHash QuickRef FontMetrics ARC4 StrokeStyle PolygonPoint ImageInfo StdDev lib/pdf/simpletable.rb lib/pdf/techbook.rb lib/pdf/writer.rb lib/pdf/quickref.rb Math lib/pdf/writer/fontmetrics.rb lib/pdf/writer/ohash.rb lib/pdf/writer/arc4.rb lib/pdf/writer/strokestyle.rb lib/pdf/writer/graphics.rb lib/pdf/writer/object.rb lib/pdf/writer/object/image.rb External EN Lang OffsetReader lib/pdf/writer/graphics/imageinfo.rb Graphics lib/pdf/writer/object/outlines.rb lib/pdf/writer/object/destination.rb lib/pdf/writer/object/viewerpreferences.rb lib/pdf/writer/object/fontencoding.rb lib/pdf/writer/object/page.rb lib/pdf/writer/object/contents.rb lib/pdf/writer/object/procset.rb lib/pdf/writer/object/pages.rb lib/pdf/writer/object/info.rb lib/pdf/writer/object/encryption.rb lib/pdf/writer/object/catalog.rb lib/pdf/writer/object/outline.rb lib/pdf/writer/object/fontdescriptor.rb lib/pdf/writer/object/action.rb lib/pdf/writer/object/font.rb lib/pdf/writer/object/annotation.rb Object Writer lib/pdf/charts/stddev.rb Charts PDF dot/m_33_0.png

Creates a standard deviation chart. This is a type of chart that is effective for the display of survey results or other data that can easily be measured in terms of the average and the standard deviation from that average.

The scale of responses is the vertical scale; the average data points and standard deviation values are the horizontal scale.

Methods

new   render_on  

Classes and Modules

Class PDF::Charts::StdDev::Label
Class PDF::Charts::StdDev::Marker
Class PDF::Charts::StdDev::Scale

Constants

DataPoint = Struct.new(:label, :average, :stddev)   A data element.

Attributes

bar  [RW]  The standard deviation bar. A line will be drawn through the dot marker (if drawn) from the upper to lower standard deviation. If nil, the line will not be drawn. This is a PDF::Charts::StdDev::Marker object.
data  [R]  The data used to generate the standard deviation chart. This is an array of DataPoint objects, each containing a label, an average, and the stddev (standard deviation) from that average.
datapoint_width  [RW]  The width of a single datapoint.
dot  [RW]  The dot marker. A filled circle will be drawn with this information. If nil, the dot will not be drawn. This is a PDF::Charts::StdDev::Marker object.
height  [RW]  The height of the chart in PDF user units. Default 200 units.
inner_borders  [RW]  The inner border style. If nil, no inner borders are drawn. This is a PDF::Charts::StdDev::Marker object.
label  [RW]  The label style of the labels if they are displayed. This must be a PDF::Charts::StdDev::Label object.
leading_gap  [RW]  The minimum gap between the chart and the bottom of the page, in PDF user units.
lower_crossbar  [RW]  The lower crossbar. A line will be drawn across the bottom of the standard deviation bar to the width of the dot marker. If dot is nil, then the line will be twice as wide as it is thick. If nil, the lower crossbar will not be drawn. This is a PDF::Charts::StdDev::Marker object.
maximum_width  [RW]  The maximum width of the chart in PDF user units. Default 500 units.
outer_borders  [RW]  The outer border style. If nil, no inner borders are drawn. This is a PDF::Charts::StdDev::Marker object.
scale  [RW]  The scale of the chart. All values must be within this range. This will be a Scale object. It defaults to a scale of 0..6 with a step of 1.
show_labels  [RW]  This will be true if labels are to be displayed.
upper_crossbar  [RW]  The upper crossbar. A line will be drawn across the top of the standard deviation bar to the width of the dot marker. If dot is nil, then the line will be twice as wide as it is thick. If nil, the upper crossbar will not be drawn. This is a PDF::Charts::StdDev::Marker object.

Public Class methods

[Source]

     # File lib/pdf/charts/stddev.rb, line 109
109:   def initialize
110:     @data                       = []
111: 
112:     @scale                      = Scale.new do |scale|
113:       scale.range               = 0..6
114:       scale.step                = 1
115:       scale.style               = PDF::Writer::StrokeStyle.new(0.25)
116:       scale.show_labels         = false
117:       scale.label               = Label.new do |label|
118:         label.text_size         = 8
119:         label.text_color        = Color::RGB::Black
120:         label.pad               = 2
121:         label.decimal_precision = 1
122:       end
123:     end
124:     @leading_gap              = 10
125:     @show_labels              = true
126:     @label                    = Label.new do |label|
127:       label.height            = 25
128:       label.background_color  = Color::RGB::Black
129:       label.text_color        = Color::RGB::White
130:       label.text_size         = 12
131:     end
132: 
133:     @outer_borders            = Marker.new do |marker|
134:       marker.style            = PDF::Writer::StrokeStyle.new(1.5)
135:       marker.color            = Color::RGB::Black
136:     end
137:     @inner_borders            = nil
138: 
139:     @dot                      = Marker.new do |marker|
140:       marker.style            = PDF::Writer::StrokeStyle.new(5)
141:       marker.color            = Color::RGB::Black
142:     end
143:     @bar                      = Marker.new do |marker|
144:       marker.style            = PDF::Writer::StrokeStyle.new(0.5)
145:       marker.color            = Color::RGB::Black
146:     end
147:     @upper_crossbar           = Marker.new do |marker|
148:       marker.style            = PDF::Writer::StrokeStyle.new(1)
149:       marker.color            = Color::RGB::Black
150:     end
151:     @lower_crossbar           = Marker.new do |marker|
152:       marker.style            = PDF::Writer::StrokeStyle.new(1)
153:       marker.color            = Color::RGB::Black
154:     end
155: 
156:     @height                   = 200
157:     @maximum_width            = 500
158:     @datapoint_width          = 35
159: 
160:     yield self if block_given?
161:   end

Public Instance methods

Draw the standard deviation chart on the supplied PDF document.

[Source]

     # File lib/pdf/charts/stddev.rb, line 219
219:   def render_on(pdf)
220:     raise TypeError, PDF::Writer::Lang[:charts_stddev_data_empty] if @data.empty?
221:     data = @data.dup
222:     leftover_data = nil
223: 
224:     loop do
225:       # Set up the scale information.
226:       scale = []
227: 
228:       (@scale.first + @scale.step).step(@scale.last, @scale.step) do |ii|
229:         scale << "%01.#{@scale.label.decimal_precision}f" % ii
230:       end
231: 
232:       scales = PDF::Writer::OHash.new
233:       scale.each_with_index do |gg, ii|
234:         scales[ii] = OpenStruct.new
235:         scales[ii].value = gg
236:       end
237: 
238:       # Add information about the scales' locations to the scales
239:       # hash. Note that the count is one smaller than it should be, so we're
240:       # increasing it. The first scale is the bottom of the chart.
241:       scale_count = scale.size + 1
242: 
243:       label_height_adjuster = 0
244:       label_height_adjuster = @label.height if @show_labels
245: 
246:       chart_area_height = @height - label_height_adjuster
247:       scale_height   = chart_area_height / scale_count.to_f
248: 
249:       scales.each_key do |index|
250:         this_height = scale_height * (index + 1) + @label.height
251:         scales[index].line_height = this_height
252:         if @scale.show_labels
253:           scales[index].label_height = this_height -
254:           (@scale.label.text_size / 3.0)
255:         end
256:       end
257: 
258:       # How many sections do we need in this chart, and how wide will it
259:       # need to be?
260:       chunk_width = @datapoint_width
261:       num_chunks  = data.size
262:       widest_scale_label = 0
263: 
264:       if @scale.show_labels
265:         scales.each_value do |scale|
266:           this_width = pdf.text_width(scale.value, @scale.label.text_size)
267:           widest_scale_label = this_width if this_width > widest_scale_label
268:         end
269:       end
270: 
271:       chart_width = chunk_width * num_chunks
272:       total_width = chart_width + widest_scale_label + @scale.label.pad
273: 
274:         # What happens if the projected width of the chart is too big?
275:         # Figure out how to break the chart in pieces.
276:       if total_width > @maximum_width
277:         max_column_count = 0
278:         base_width = widest_scale_label + @scale.label.pad
279:         (1..(num_chunks + 1)).each do |ii|
280:           if (base_width + (ii * chunk_width)) > @maximum_width
281:             break
282:           else
283:             max_column_count += 1
284:           end
285:         end
286: 
287:         leftover_data = data.slice!(max_column_count, -1)
288: 
289:         num_chunks  = data.size
290:         chart_width = chunk_width * num_chunks
291:         total_width = chart_width + widest_scale_label + @scale.label.pad
292:       end
293: 
294:       chart_y = pdf.y - @height + @leading_gap
295:       chart_y += (@outer_borders.style.width * 2.0) if @outer_borders
296: 
297:       if chart_y < pdf.bottom_margin
298:         pdf.start_new_page
299:         chart_y = pdf.y - @height
300:         chart_y += (@outer_borders.style.width * 2.0) if @outer_borders
301:       end
302: 
303:       chart_x = pdf.absolute_x_middle - (total_width / 2.0) + widest_scale_label
304: 
305:         # Add labels, if needed.
306:       if @show_labels
307:         pdf.save_state
308:         pdf.fill_color! @label.background_color
309:         # Draw a rectangle for each label
310:         num_chunks.times do |ii|
311:           this_x = chart_x + ii * chunk_width
312:           pdf.rectangle(this_x, chart_y, chunk_width, @label.height).fill
313:         end
314: 
315:           # Add a border above the label rectangle.
316:         if @outer_borders
317:           pdf.stroke_style! @outer_borders.style
318:           pdf.line(chart_x, chart_y + @label.height, chart_x + chart_width, chart_y + @label.height).stroke
319:         end
320:         pdf.fill_color! @label.text_color
321: 
322:         data.each_with_index do |datum, ii|
323:           label = datum.label.to_s
324:           label_width = pdf.text_width(label, @label.text_size)
325:           this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0) - (label_width / 2.0)
326:           this_y = chart_y + (@label.height / 2.0) - (@label.text_size / 3.0)
327:           pdf.add_text(this_x, this_y, label, @label.text_size)
328:         end
329:         pdf.restore_state
330:       end
331: 
332:       if @inner_borders
333:         pdf.save_state
334:         pdf.stroke_color! @inner_borders.color
335:         pdf.stroke_style! @inner_borders.style
336:         (num_chunks - 1).times do |ii|
337:           this_x = chart_x + (ii * chunk_width) + chunk_width
338:           pdf.line(this_x, chart_y, this_x, chart_y + @height).stroke
339:         end
340:         pdf.restore_state
341:       end
342: 
343:       pdf.save_state
344:       if @outer_borders
345:         pdf.stroke_color! @outer_borders.color
346:         pdf.stroke_style! @outer_borders.style
347:         pdf.rectangle(chart_x, chart_y, chart_width, @height).stroke
348:       end
349: 
350:       if @scale.style
351:         pdf.save_state
352:         pdf.stroke_style! @scale.style
353:         scales.each_value do |scale|
354:           this_y = chart_y + scale.line_height
355:           pdf.line(chart_x, this_y, chart_x + chart_width, this_y).stroke
356:         end
357:         pdf.restore_state
358:       end
359: 
360:       if @scale.show_labels
361:         pdf.save_state
362:         scales.each_value do |scale|
363:           this_y = chart_y + scale.label_height
364:           label_width = pdf.text_width(scale.value, @scale.label.text_size)
365:           this_x = chart_x - label_width - @scale.label.pad
366:           pdf.fill_color! @scale.label.text_color
367:           pdf.add_text(this_x, this_y, scale.value, @scale.label.text_size)
368:         end
369:         pdf.restore_state
370:       end
371: 
372:       data.each_with_index do |datum, ii|
373:         avg_height    = datum.average * scale_height
374:         stddev_height = datum.stddev * scale_height
375:         this_y        = chart_y + label_height_adjuster + avg_height
376:         this_x        = chart_x + (ii * chunk_width) + (chunk_width / 2.0)
377:         line_top_y    = this_y + (stddev_height / 2.0)
378:         line_bot_y    = this_y - (stddev_height / 2.0)
379: 
380:           # Plot the dot
381:         if @dot
382:           pdf.stroke_color! @dot.color
383:           pdf.stroke_style! @dot.style
384:           pdf.circle_at(this_x, this_y, (@dot.style.width / 2.0)).fill
385:         end
386: 
387:           # Plot the bar
388:         if @bar
389:           pdf.stroke_color! @bar.color
390:           pdf.stroke_style! @bar.style
391:           pdf.line(this_x, line_top_y, this_x, line_bot_y).stroke
392:         end
393: 
394:           # Plot the crossbars
395:         if @upper_crossbar
396:           if @dot
397:             cb_width = @dot.style.width
398:           else
399:             cb_width = @upper_crossbar.style.width
400:           end
401:           pdf.stroke_color! @upper_crossbar.color
402:           pdf.stroke_style! @upper_crossbar.style
403:           pdf.line(this_x - cb_width, line_top_y, this_x + cb_width, line_top_y).stroke
404:         end
405:         if @lower_crossbar
406:           if @dot
407:             cb_width = @dot.style.width
408:           else
409:             cb_width = @lower_crossbar.style.width
410:           end
411:           pdf.stroke_color! @lower_crossbar.color
412:           pdf.stroke_style! @lower_crossbar.style
413: 
414:           pdf.line(this_x - cb_width, line_bot_y, this_x + cb_width, line_bot_y).stroke
415:         end
416:       end
417: 
418:       pdf.restore_state
419: 
420:       pdf.y = chart_y
421: 
422:       break if leftover_data.nil?
423: 
424:       data = leftover_data
425:       leftover_data = nil
426:     end
427: 
428:     pdf.y
429:   end

[Validate]