class PDF::Charts::StdDev
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.
Constants
- DataPoint
A data element.
- VERSION
Attributes
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.
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.
The width of a single datapoint.
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.
The height of the chart in PDF
user units. Default 200 units.
The inner border style. If nil
, no inner borders are drawn. This is a PDF::Charts::StdDev::Marker
object.
The label style of the labels if they are displayed. This must be a PDF::Charts::StdDev::Label
object.
The minimum gap between the chart and the bottom of the page, in PDF
user units.
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.
The maximum width of the chart in PDF
user units. Default 500 units.
The outer border style. If nil
, no inner borders are drawn. This is a PDF::Charts::StdDev::Marker
object.
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.
This will be true
if labels are to be displayed.
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
# File lib/pdf/charts/stddev.rb 110 def initialize 111 @data = [] 112 113 @scale = Scale.new do |scale| 114 scale.range = 0..6 115 scale.step = 1 116 scale.style = PDF::Writer::StrokeStyle.new(0.25) 117 scale.show_labels = false 118 scale.label = Label.new do |label| 119 label.text_size = 8 120 label.text_color = Color::RGB::Black 121 label.pad = 2 122 label.decimal_precision = 1 123 end 124 end 125 @leading_gap = 10 126 @show_labels = true 127 @label = Label.new do |label| 128 label.height = 25 129 label.background_color = Color::RGB::Black 130 label.text_color = Color::RGB::White 131 label.text_size = 12 132 end 133 134 @outer_borders = Marker.new do |marker| 135 marker.style = PDF::Writer::StrokeStyle.new(1.5) 136 marker.color = Color::RGB::Black 137 end 138 @inner_borders = nil 139 140 @dot = Marker.new do |marker| 141 marker.style = PDF::Writer::StrokeStyle.new(5) 142 marker.color = Color::RGB::Black 143 end 144 @bar = Marker.new do |marker| 145 marker.style = PDF::Writer::StrokeStyle.new(0.5) 146 marker.color = Color::RGB::Black 147 end 148 @upper_crossbar = Marker.new do |marker| 149 marker.style = PDF::Writer::StrokeStyle.new(1) 150 marker.color = Color::RGB::Black 151 end 152 @lower_crossbar = Marker.new do |marker| 153 marker.style = PDF::Writer::StrokeStyle.new(1) 154 marker.color = Color::RGB::Black 155 end 156 157 @height = 200 158 @maximum_width = 500 159 @datapoint_width = 35 160 161 yield self if block_given? 162 end
Public Instance Methods
Draw the standard deviation chart on the supplied PDF
document.
# File lib/pdf/charts/stddev.rb 220 def render_on(pdf) 221 raise TypeError, PDF::Writer::Lang[:charts_stddev_data_empty] if @data.empty? 222 data = @data.dup 223 leftover_data = nil 224 225 loop do 226 # Set up the scale information. 227 scale = [] 228 229 (@scale.first + @scale.step).step(@scale.last, @scale.step) do |ii| 230 scale << "%01.#{@scale.label.decimal_precision}f" % ii 231 end 232 233 scales = PDF::Writer::OHash.new 234 scale.each_with_index do |gg, ii| 235 scales[ii] = OpenStruct.new 236 scales[ii].value = gg 237 end 238 239 # Add information about the scales' locations to the scales 240 # hash. Note that the count is one smaller than it should be, so we're 241 # increasing it. The first scale is the bottom of the chart. 242 scale_count = scale.size + 1 243 244 label_height_adjuster = 0 245 label_height_adjuster = @label.height if @show_labels 246 247 chart_area_height = @height - label_height_adjuster 248 scale_height = chart_area_height / scale_count.to_f 249 250 scales.each_key do |index| 251 this_height = scale_height * (index + 1) + @label.height 252 scales[index].line_height = this_height 253 if @scale.show_labels 254 scales[index].label_height = this_height - 255 (@scale.label.text_size / 3.0) 256 end 257 end 258 259 # How many sections do we need in this chart, and how wide will it 260 # need to be? 261 chunk_width = @datapoint_width 262 num_chunks = data.size 263 widest_scale_label = 0 264 265 if @scale.show_labels 266 scales.each_value do |scale| 267 this_width = pdf.text_width(scale.value, @scale.label.text_size) 268 widest_scale_label = this_width if this_width > widest_scale_label 269 end 270 end 271 272 chart_width = chunk_width * num_chunks 273 total_width = chart_width + widest_scale_label + @scale.label.pad 274 275 # What happens if the projected width of the chart is too big? 276 # Figure out how to break the chart in pieces. 277 if total_width > @maximum_width 278 max_column_count = 0 279 base_width = widest_scale_label + @scale.label.pad 280 (1..(num_chunks + 1)).each do |ii| 281 if (base_width + (ii * chunk_width)) > @maximum_width 282 break 283 else 284 max_column_count += 1 285 end 286 end 287 288 leftover_data = data.slice!(max_column_count, -1) 289 290 num_chunks = data.size 291 chart_width = chunk_width * num_chunks 292 total_width = chart_width + widest_scale_label + @scale.label.pad 293 end 294 295 chart_y = pdf.y - @height + @leading_gap 296 chart_y += (@outer_borders.style.width * 2.0) if @outer_borders 297 298 if chart_y < pdf.bottom_margin 299 pdf.start_new_page 300 chart_y = pdf.y - @height 301 chart_y += (@outer_borders.style.width * 2.0) if @outer_borders 302 end 303 304 chart_x = pdf.absolute_x_middle - (total_width / 2.0) + widest_scale_label 305 306 # Add labels, if needed. 307 if @show_labels 308 pdf.save_state 309 pdf.fill_color! @label.background_color 310 # Draw a rectangle for each label 311 num_chunks.times do |ii| 312 this_x = chart_x + ii * chunk_width 313 pdf.rectangle(this_x, chart_y, chunk_width, @label.height).fill 314 end 315 316 # Add a border above the label rectangle. 317 if @outer_borders 318 pdf.stroke_style! @outer_borders.style 319 pdf.line(chart_x, chart_y + @label.height, chart_x + chart_width, chart_y + @label.height).stroke 320 end 321 pdf.fill_color! @label.text_color 322 323 data.each_with_index do |datum, ii| 324 label = datum.label.to_s 325 label_width = pdf.text_width(label, @label.text_size) 326 this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0) - (label_width / 2.0) 327 this_y = chart_y + (@label.height / 2.0) - (@label.text_size / 3.0) 328 pdf.add_text(this_x, this_y, label, @label.text_size) 329 end 330 pdf.restore_state 331 end 332 333 if @inner_borders 334 pdf.save_state 335 pdf.stroke_color! @inner_borders.color 336 pdf.stroke_style! @inner_borders.style 337 (num_chunks - 1).times do |ii| 338 this_x = chart_x + (ii * chunk_width) + chunk_width 339 pdf.line(this_x, chart_y, this_x, chart_y + @height).stroke 340 end 341 pdf.restore_state 342 end 343 344 pdf.save_state 345 if @outer_borders 346 pdf.stroke_color! @outer_borders.color 347 pdf.stroke_style! @outer_borders.style 348 pdf.rectangle(chart_x, chart_y, chart_width, @height).stroke 349 end 350 351 if @scale.style 352 pdf.save_state 353 pdf.stroke_style! @scale.style 354 scales.each_value do |scale| 355 this_y = chart_y + scale.line_height 356 pdf.line(chart_x, this_y, chart_x + chart_width, this_y).stroke 357 end 358 pdf.restore_state 359 end 360 361 if @scale.show_labels 362 pdf.save_state 363 scales.each_value do |scale| 364 this_y = chart_y + scale.label_height 365 label_width = pdf.text_width(scale.value, @scale.label.text_size) 366 this_x = chart_x - label_width - @scale.label.pad 367 pdf.fill_color! @scale.label.text_color 368 pdf.add_text(this_x, this_y, scale.value, @scale.label.text_size) 369 end 370 pdf.restore_state 371 end 372 373 data.each_with_index do |datum, ii| 374 avg_height = datum.average * scale_height 375 stddev_height = datum.stddev * scale_height 376 this_y = chart_y + label_height_adjuster + avg_height 377 this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0) 378 line_top_y = this_y + (stddev_height / 2.0) 379 line_bot_y = this_y - (stddev_height / 2.0) 380 381 # Plot the dot 382 if @dot 383 pdf.stroke_color! @dot.color 384 pdf.stroke_style! @dot.style 385 pdf.circle_at(this_x, this_y, (@dot.style.width / 2.0)).fill 386 end 387 388 # Plot the bar 389 if @bar 390 pdf.stroke_color! @bar.color 391 pdf.stroke_style! @bar.style 392 pdf.line(this_x, line_top_y, this_x, line_bot_y).stroke 393 end 394 395 # Plot the crossbars 396 if @upper_crossbar 397 if @dot 398 cb_width = @dot.style.width 399 else 400 cb_width = @upper_crossbar.style.width 401 end 402 pdf.stroke_color! @upper_crossbar.color 403 pdf.stroke_style! @upper_crossbar.style 404 pdf.line(this_x - cb_width, line_top_y, this_x + cb_width, line_top_y).stroke 405 end 406 if @lower_crossbar 407 if @dot 408 cb_width = @dot.style.width 409 else 410 cb_width = @lower_crossbar.style.width 411 end 412 pdf.stroke_color! @lower_crossbar.color 413 pdf.stroke_style! @lower_crossbar.style 414 415 pdf.line(this_x - cb_width, line_bot_y, this_x + cb_width, line_bot_y).stroke 416 end 417 end 418 419 pdf.restore_state 420 421 pdf.y = chart_y 422 423 break if leftover_data.nil? 424 425 data = leftover_data 426 leftover_data = nil 427 end 428 429 pdf.y 430 end