Class: Sparklines
- Inherits:
-
Object
- Object
- Sparklines
- Defined in:
- lib/sparklines.rb
Overview
A library for generating small unmarked graphs (sparklines).
Can be used to write an image to a file or make a web service with Rails or other Ruby CGI apps.
Idea and much of the outline for the source lifted directly from Joe Gregorio’s Python Sparklines web service script.
Requires the RMagick image library.
Authors
Dan Nugent Original port from Python Sparklines library.
Geoffrey Grosenbach – nubyonrails.topfunky.com – Conversion to module and further maintenance.
General Usage and Defaults
To use in a script:
require ‘rubygems’ require ‘sparklines’ Sparklines.plot(, :type => ‘discrete’, :height => 20)
An image blob will be returned which you can print, write to STDOUT, etc.
For use with Ruby on Rails, see the sparklines plugin:
http://nubyonrails.com/pages/sparklines
In your view, call it like this:
<%= sparkline_tag [1,2,3,4,5,6] %>
Or specify details:
<%= sparkline_tag [1,2,3,4,5,6],
:type => 'discrete',
:height => 10,
:upper => 80,
:above_color => 'green',
:below_color => 'blue' %>
Graph types:
area
discrete
pie
smooth
bullet
whisker
General Defaults:
:type => 'smooth'
:height => 14px
:upper => 50
:above_color => 'red'
:below_color => 'grey'
:background_color => 'white'
:line_color => 'lightgrey'
License
Licensed under the MIT license.
Constant Summary collapse
- VERSION =
'0.5.2'
- @@label_margin =
5.0
- @@pointsize =
10.0
Class Method Summary collapse
-
.plot(data = [], options = {}) ⇒ Object
Does the actual plotting of the graph.
-
.plot_to_file(filename = "sparklines.png", data = [], options = {}) ⇒ Object
Writes a graph to disk with the specified filename, or “sparklines.png”.
-
.plot_to_image(data = [], options = {}) ⇒ Object
Plots a sparkline and returns a Magic::Image object.
Instance Method Summary collapse
-
#area ⇒ Object
Creates a continuous area sparkline.
-
#bar ⇒ Object
A bar graph.
-
#bullet ⇒ Object
A bullet graph, a la Stephen Few in “Information Dashboard Design.”.
-
#discrete ⇒ Object
Creates a discretized sparkline.
-
#initialize(data = [], options = {}) ⇒ Sparklines
constructor
class methods.
-
#pie ⇒ Object
Creates a pie-chart sparkline.
-
#plot_error(options = {}) ⇒ Object
Draw the error Sparkline.
-
#smooth ⇒ Object
Creates a smooth line graph sparkline.
-
#whisker ⇒ Object
Creates a whisker sparkline to track on/off type data.
Constructor Details
#initialize(data = [], options = {}) ⇒ Sparklines
class methods
154 155 156 157 158 |
# File 'lib/sparklines.rb', line 154 def initialize(data=[], ={}) @data = Array(data) @options = normalize_data end |
Class Method Details
.plot(data = [], options = {}) ⇒ Object
Does the actual plotting of the graph. Calls the appropriate subclass based on the :type argument. Defaults to ‘smooth’.
Returns a blob.
139 140 141 |
# File 'lib/sparklines.rb', line 139 def plot(data=[], ={}) plot_to_image(data, ).to_blob end |
.plot_to_file(filename = "sparklines.png", data = [], options = {}) ⇒ Object
Writes a graph to disk with the specified filename, or “sparklines.png”.
146 147 148 149 150 |
# File 'lib/sparklines.rb', line 146 def plot_to_file(filename="sparklines.png", data=[], ={}) File.open( filename, 'wb' ) do |png| png << self.plot( data, ) end end |
.plot_to_image(data = [], options = {}) ⇒ Object
Plots a sparkline and returns a Magic::Image object.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/sparklines.rb', line 88 def plot_to_image(data=[], ={}) defaults = { :type => 'smooth', :height => 14, :upper => 50, :diameter => 20, :step => 2, :line_color => 'lightgrey', :above_color => 'red', :below_color => 'grey', :background_color => 'white', :share_color => 'red', :remain_color => 'lightgrey', :min_color => 'blue', :max_color => 'green', :last_color => 'red', :std_dev_color => '#efefef', :has_min => false, :has_max => false, :has_last => false, :has_std_dev => false, :label => nil } # HACK for HashWithIndifferentAccess = Hash.new .keys.each do |key| [key.to_sym] = [key] end = defaults.merge() # Call the appropriate method for actual plotting. sparkline = self.new(data, ) if %w(area bar bullet pie smooth discrete whisker).include? [:type] sparkline.send [:type] else sparkline.plot_error end end |
Instance Method Details
#area ⇒ Object
Creates a continuous area sparkline. Relevant options.
:step - An integer that determines the distance between each point on the sparkline. Defaults to 2.
:height - An integer that determines what the height of the sparkline will be. Defaults to 14
:upper - An integer that determines the threshold for colorization purposes. Any value less than upper will be colored using below_color, anything above and equal to upper will use above_color. Defaults to 50.
:has_min - Determines whether a dot will be drawn at the lowest value or not. Defaults to false.
:has_max - Determines whether a dot will be drawn at the highest value or not. Defaults to false.
:has_last - Determines whether a dot will be drawn at the last value or not. Defaults to false.
:min_color - A string or color code representing the color that the dot drawn at the smallest value will be displayed as. Defaults to blue.
:max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green.
:last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red.
:above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red.
:below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/sparklines.rb', line 185 def area step = @options[:step].to_f height = @options[:height].to_f background_color = @options[:background_color] create_canvas((@norm_data.size - 1) * step + 4, height, background_color) upper = @options[:upper].to_f has_min = @options[:has_min] has_max = @options[:has_max] has_last = @options[:has_last] min_color = @options[:min_color] max_color = @options[:max_color] last_color = @options[:last_color] below_color = @options[:below_color] above_color = @options[:above_color] coords = [[0,(height - 3 - upper/(101.0/(height-4)))]] i=0 @norm_data.each do |r| coords.push [(2 + i), (height - 3 - r/(101.0/(height-4)))] i += step end coords.push [(@norm_data.size - 1) * step + 4, (height - 3 - upper/(101.0/(height-4)))] # TODO Refactor! Should take a block and do both. # # Block off the bottom half of the image and draw the sparkline @draw.fill(above_color) @draw.define_clip_path('top') do @draw.rectangle(0,0,(@norm_data.size - 1) * step + 4,(height - 3 - upper/(101.0/(height-4)))) end @draw.clip_path('top') @draw.polygon(*coords.flatten) # Block off the top half of the image and draw the sparkline @draw.fill(below_color) @draw.define_clip_path('bottom') do @draw.rectangle(0,(height - 3 - upper/(101.0/(height-4))),(@norm_data.size - 1) * step + 4,height) end @draw.clip_path('bottom') @draw.polygon(*coords.flatten) # The sparkline looks kinda nasty if either the above_color or below_color gets the center line @draw.fill('black') @draw.line(0,(height - 3 - upper/(101.0/(height-4))),(@norm_data.size - 1) * step + 4,(height - 3 - upper/(101.0/(height-4)))) # After the parts have been masked, we need to let the whole canvas be drawable again # so a max dot can be displayed @draw.define_clip_path('all') do @draw.rectangle(0,0,@canvas.columns,@canvas.rows) end @draw.clip_path('all') drawbox(coords[@norm_data.index(@norm_data.min)+1], 1, min_color) if has_min == true drawbox(coords[@norm_data.index(@norm_data.max)+1], 1, max_color) if has_max == true drawbox(coords[-2], 1, last_color) if has_last == true @draw.draw(@canvas) @canvas end |
#bar ⇒ Object
A bar graph.
Also takes :target option (a line will be drawn) and :upper (values under will be drawn in :below_color, above in :above_color).
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/sparklines.rb', line 258 def step = @options[:step].to_f height = @options[:height].to_f width = ((@norm_data.size - 1) * step).to_f background_color = @options[:background_color] create_canvas(@norm_data.length * step + 2, height, background_color) upper = @options[:upper].to_f below_color = @options[:below_color] above_color = @options[:above_color] target = @options.has_key?(:target) ? @options[:target].to_f : nil target_color = @options[:target_color] || 'white' i = 1 # raise @norm_data.to_yaml max_normalized = @norm_data.max @norm_data.each_with_index do |r, index| color = (@data[index] >= upper) ? above_color : below_color @draw.stroke('transparent') @draw.fill(color) = @canvas.rows - ( (r.to_f / max_normalized.to_f) * @canvas.rows) @draw.rectangle( i, @canvas.rows, i + step - 2, ) i += step end unless target.nil? normalized_target_value = ((target.to_f - @minimum_value)/(@maximum_value - @minimum_value)) * 100.0 adjusted_target_value = (height - 3 - normalized_target_value/(101.0/(height-4))).to_i @draw.stroke(target_color) open_ended_polyline([[-5, adjusted_target_value], [width + 5, adjusted_target_value]]) end @draw.draw(@canvas) @canvas end |
#bullet ⇒ Object
A bullet graph, a la Stephen Few in “Information Dashboard Design.”
-
data - A single value for the thermometer part of the bullet. Represents the current value.
-
options - a hash
:good - Numeric. Maximum value that will be shown on the graph. Required.
:height - Numeric. Defaults to 15. Should be a multiple of three.
:width - This graph expands to any specified width. Defaults to 100.
:bad - Numeric. A darker shade background will be drawn up to this point.
:satisfactory - Numeric. A medium background will be drawn up to this point.
:target - Numeric value. A thin vertical bar will be drawn.
:good_color - Color for the rightmost section of the bullet.
:satisfactory_color - Color for the middle background of the bullet.
:bad_color - Color for the lowest, leftmost section.
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
# File 'lib/sparklines.rb', line 572 def bullet height = @options[:height].to_f @graph_width = @options.has_key?(:width) ? @options[:width].to_f : 100.0 good_color = @options.has_key?(:good_color) ? @options[:good_color] : '#eeeeee' satisfactory_color = @options.has_key?(:satisfactory_color) ? @options[:satisfactory_color] : '#bbbbbb' bad_color = @options.has_key?(:bad_color) ? @options[:bad_color] : '#999999' bullet_color = @options.has_key?(:bullet_color) ? @options[:bullet_color] : 'black' @thickness = height/3.0 create_canvas(@graph_width, height, good_color) @value = @norm_data @good_value = @options[:good].to_f @graph_height = @options[:height] qualitative_range_colors = [satisfactory_color, bad_color] [:satisfactory, :bad].each_with_index do |indicator, index| next unless @options.has_key?(indicator) @draw = @draw.fill(qualitative_range_colors[index]) indicator_width_x = @graph_width * (@options[indicator].to_f / @good_value) @draw = @draw.rectangle(0, 0, indicator_width_x.to_i, @graph_height) end if @options.has_key?(:target) @draw = @draw.fill(bullet_color) target_x = @graph_width * (@options[:target].to_f / @good_value) half_thickness = (@thickness / 2.0).to_i = 1.0 @draw = @draw.rectangle(target_x.to_i, half_thickness, (target_x + ).to_i, @thickness * 2 + half_thickness) end # Value @draw = @draw.fill(bullet_color) @draw = @draw.rectangle(0, @thickness.to_i, @graph_width * (@data.first.to_f / @good_value), (@thickness * 2.0).to_i) @draw.draw(@canvas) @canvas end |
#discrete ⇒ Object
Creates a discretized sparkline
:height - An integer that determines what the height of the sparkline will be. Defaults to 14
:upper - An integer that determines the threshold for colorization purposes. Any value less than upper will be colored using below_color, anything above and equal to upper will use above_color. Defaults to 50.
:above_color - A string or color code representing the color to draw values above or equal the upper value. Defaults to red.
:below_color - A string or color code representing the color to draw values below the upper value. Defaults to gray.
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/sparklines.rb', line 308 def discrete height = @options[:height].to_f upper = @options[:upper].to_f background_color = @options[:background_color] step = @options[:step].to_f width = @norm_data.size * step - 1 create_canvas(@norm_data.size * step - 1, height, background_color) below_color = @options[:below_color] above_color = @options[:above_color] std_dev_color = @options[:std_dev_color] drawstddevbox(width,height,std_dev_color) if @options[:has_std_dev] == true i = 0 @norm_data.each do |r| color = (r >= upper) ? above_color : below_color @draw.stroke(color) @draw.line(i, (@canvas.rows - r/(101.0/(height-4))-4).to_f, i, (@canvas.rows - r/(101.0/(height-4))).to_f) i += step end @draw.draw(@canvas) @canvas end |
#pie ⇒ Object
Creates a pie-chart sparkline
:diameter - An integer that determines what the size of the sparkline will be. Defaults to 20
:share_color - A string or color code representing the color to draw the share of the pie represented by percent. Defaults to red.
:remain_color - A string or color code representing the color to draw the pie not taken by the share color. Defaults to lightgrey.
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/sparklines.rb', line 348 def pie diameter = @options[:diameter].to_f background_color = @options[:background_color] create_canvas(diameter, diameter, background_color) share_color = @options[:share_color] remain_color = @options[:remain_color] percent = @norm_data[0] # Adjust the radius so there's some edge left in the pie r = diameter/2.0 - 2 @draw.fill(remain_color) @draw.ellipse(r + 2, r + 2, r , r , 0, 360) @draw.fill(share_color) # Special exceptions if percent == 0 # For 0% return blank @draw.draw(@canvas) return @canvas elsif percent == 100 # For 100% just draw a full circle @draw.ellipse(r + 2, r + 2, r , r , 0, 360) @draw.draw(@canvas) return @canvas end # Okay, this part is as confusing as hell, so pay attention: # This line determines the horizontal portion of the point on the circle where the X-Axis # should end. It's caculated by taking the center of the on-image circle and adding that # to the radius multiplied by the formula for determinig the point on a unit circle that a # angle corresponds to. 3.6 * percent gives us that angle, but it's in degrees, so we need to # convert, hence the muliplication by Pi over 180 arc_end_x = r + 2 + (r * Math.cos((3.6 * percent)*(Math::PI/180))) # The same goes for here, except it's the vertical point instead of the horizontal one arc_end_y = r + 2 + (r * Math.sin((3.6 * percent)*(Math::PI/180))) # Because the SVG path format is seriously screwy, we need to set the large-arc-flag to 1 # if the angle of an arc is greater than 180 degrees. I have no idea why this is, but it is. percent > 50? large_arc_flag = 1: large_arc_flag = 0 # This is also confusing # M tells us to move to an absolute point on the image. We're moving to the center of the pie # h tells us to move to a relative point. We're moving to the right edge of the circle. # A tells us to start an absolute elliptical arc. The first two values are the radii of the ellipse # the third value is the x-axis-rotation (how to rotate the ellipse if we wanted to [could have some fun # with randomizing that maybe), the fourth value is our large-arc-flag, the fifth is the sweep-flag, # (again, confusing), the sixth and seventh values are the end point of the arc which we calculated previously # More info on the SVG path string format at: http://www.w3.org/TR/SVG/paths.html path = "M#{r + 2},#{r + 2} h#{r} A#{r},#{r} 0 #{large_arc_flag},1 #{arc_end_x},#{arc_end_y} z" @draw.path(path) @draw.draw(@canvas) @canvas end |
#plot_error(options = {}) ⇒ Object
Draw the error Sparkline.
615 616 617 618 619 620 621 622 623 624 |
# File 'lib/sparklines.rb', line 615 def plot_error(={}) create_canvas(40, 15, 'white') @draw.fill('red') @draw.line(0,0,40,15) @draw.line(0,15,40,0) @draw.draw(@canvas) @canvas end |
#smooth ⇒ Object
Creates a smooth line graph sparkline.
:step - An integer that determines the distance between each point on the sparkline. Defaults to 2.
:height - An integer that determines what the height of the sparkline will be. Defaults to 14
:has_min - Determines whether a dot will be drawn at the lowest value or not. Defaults to false.
:has_max - Determines whether a dot will be drawn at the highest value or not. Defaults to false.
:has_last - Determines whether a dot will be drawn at the last value or not. Defaults to false.
:has_std_dev - Determines whether there will be a standard deviation bar behind the smooth graph or not. Defaults to false.
:min_color - A string or color code representing the color that the dot drawn at the smallest value will be displayed as. Defaults to blue.
:max_color - A string or color code representing the color that the dot drawn at the largest value will be displayed as. Defaults to green.
:last_color - A string or color code representing the color that the dot drawn at the last value will be displayed as. Defaults to red.
:std_dev_color - A string or color code representing the color that the standard deviation bar behind the smooth graph will be displayed as. Defaults to #efefef
:underneath_color - A string or color code representing the color that will be used to fill in the area underneath the line. Optional.
:target - A 1px horizontal line will be drawn at this value. Useful for showing an average.
:target_color - Color of the target line. Defaults to white.
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/sparklines.rb', line 435 def smooth step = @options[:step].to_f height = @options[:height].to_f width = ((@norm_data.size - 1) * step).to_f background_color = @options[:background_color] create_canvas(width, height, background_color) min_color = @options[:min_color] max_color = @options[:max_color] last_color = @options[:last_color] has_min = @options[:has_min] has_max = @options[:has_max] has_last = @options[:has_last] line_color = @options[:line_color] has_std_dev = @options[:has_std_dev] std_dev_color = @options[:std_dev_color] target = @options.has_key?(:target) ? @options[:target].to_f : nil target_color = @options[:target_color] || 'white' drawstddevbox(width,height,std_dev_color) if has_std_dev == true @draw.stroke(line_color) coords = [] i=0 @norm_data.each do |r| coords.push [ i, (height - 3 - r/(101.0/(height-4))) ] i += step end if @options[:underneath_color] closed_polygon(height, width, coords) else open_ended_polyline(coords) end unless target.nil? normalized_target_value = ((target.to_f - @minimum_value)/(@maximum_value - @minimum_value)) * 100.0 adjusted_target_value = (height - 3 - normalized_target_value/(101.0/(height-4))).to_i @draw.stroke(target_color) open_ended_polyline([[-5, adjusted_target_value], [width + 5, adjusted_target_value]]) end drawbox(coords[@norm_data.index(@norm_data.min)], 2, min_color) if has_min == true drawbox(coords[@norm_data.index(@norm_data.max)], 2, max_color) if has_max == true drawbox(coords[-1], 2, last_color) if has_last == true @draw.draw(@canvas) @canvas end |
#whisker ⇒ Object
Creates a whisker sparkline to track on/off type data. There are five states: on, off, no value, exceptional on, exceptional off. On values create an up whisker and off values create a down whisker. Exceptional values may be colored differently than regular values to indicate, for example, a shut out. No value produces an empty row to indicate a tie.
-
results - an array of integer values between -2 and 2. -2 is exceptional down, -1 is regular down, 0 is no value, 1 is up, and 2 is exceptional up.
-
options - a hash that takes parameters
:height - height of the sparkline
:whisker_color - the color of regular whiskers; defaults to black
:exception_color - the color of exceptional whiskers; defaults to red
:step - Spacing for whiskers. Includes the whisker itself. Default 2.
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 |
# File 'lib/sparklines.rb', line 506 def whisker step = @options[:step].to_i height = @options[:height].to_f background_color = @options[:background_color] create_canvas(@data.size * step - 1, height, background_color) whisker_color = @options[:whisker_color] || 'black' exception_color = @options[:exception_color] || 'red' on_row = (@canvas.rows/2.0 - 1).ceil off_row = (@canvas.rows/2.0).floor i = 0 @data.each do |r| color = whisker_color if ( (r == 2 || r == -2) && exception_color ) color = exception_color end y_mid_point = (r >= 1) ? on_row : off_row y_end_point = y_mid_point if ( r > 0) y_end_point = 0 end if ( r < 0 ) y_end_point = @canvas.rows end @draw.stroke( color ) @draw.line( i, y_mid_point, i, y_end_point ) i += step end @draw.draw(@canvas) @canvas end |