lib/sparklines.rb in sparklines-0.4.0 vs lib/sparklines.rb in sparklines-0.4.1

- old
+ new

@@ -1,9 +1,8 @@ require 'rubygems' require 'RMagick' -require 'mathn' =begin rdoc A library for generating small unmarked graphs (sparklines). @@ -54,10 +53,11 @@ area discrete pie smooth bar + whisker General Defaults: :type => 'smooth' :height => 14px @@ -72,11 +72,11 @@ Licensed under the MIT license. =end class Sparklines - VERSION = '0.4.0' + VERSION = '0.4.1' @@label_margin = 5.0 @@pointsize = 10.0 class << self @@ -107,21 +107,21 @@ :has_last => false, :label => nil } - # Hack for HashWithIndifferentAccess + # HACK for HashWithIndifferentAccess options_sym = Hash.new options.keys.each do |key| options_sym[key.to_sym] = options[key] end options_sym = defaults.merge(options_sym) # Call the appropriate method for actual plotting. sparkline = self.new(data, options_sym) - if %w(area bar pie smooth discrete).include? options_sym[:type] + if %w(area bar pie smooth discrete whisker).include? options_sym[:type] sparkline.send options_sym[:type] else sparkline.plot_error options_sym end end @@ -131,18 +131,19 @@ File.open( filename, 'wb' ) do |png| png << self.plot( data, options) end end - end + end # class methods def initialize(data=[], options={}) @data = Array(data) @options = options normalize_data end + ## # 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 @@ -162,10 +163,11 @@ # :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. + def area step = @options[:step].to_i height = @options[:height].to_i background_color = @options[:background_color] @@ -229,13 +231,13 @@ @draw.draw(@canvas) @canvas.to_blob end + ## + # A bar graph. - # Draws a bar graph. - # def bar step = @options[:step].to_i height = @options[:height].to_f background_color = @options[:background_color] @@ -258,19 +260,21 @@ @draw.draw(@canvas) @canvas.to_blob end + ## # 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. + def discrete height = @options[:height].to_i upper = @options[:upper].to_i background_color = @options[:background_color] @@ -293,19 +297,20 @@ @draw.draw(@canvas) @canvas.to_blob end + ## # 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. - def pie + def pie diameter = @options[:diameter].to_i background_color = @options[:background_color] create_canvas(diameter, diameter, background_color) @@ -360,10 +365,11 @@ @draw.draw(@canvas) @canvas.to_blob end + ## # Creates a smooth 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 @@ -377,10 +383,11 @@ # :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. + def smooth step = @options[:step].to_i height = @options[:height].to_i background_color = @options[:background_color] @@ -414,11 +421,69 @@ @draw.draw(@canvas) @canvas.to_blob end + ## + # 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 + + def whisker + + # step = @options[:step].to_i + height = @options[:height].to_i + background_color = @options[:background_color] + + create_canvas((@data.size - 1) * 2, height, background_color) + + whisker_color = @options[:whisker_color] || 'black' + exception_color = @options[:exception_color] || 'red' + + 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) ? (@canvas.rows/2.0 - 1).ceil : (@canvas.rows/2.0).floor + + 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 += 2 + end + + @draw.draw(@canvas) + @canvas.to_blob + end + + ## # Draw the error Sparkline. + def plot_error(options={}) create_canvas(40, 15, 'white') @draw.fill('red') @draw.line(0,0,40,15) @@ -437,65 +502,71 @@ else @norm_data = @data.map { |value| value = (value.to_f / @maximum_value) * 100.0 } end end - # * arr - an array of points (represented as two element arrays) + ## + # * :arr - an array of points (represented as two element arrays) + def open_ended_polyline(arr) 0.upto(arr.length - 2) { |i| @draw.line(arr[i][0], arr[i][1], arr[i+1][0], arr[i+1][1]) } end + ## # Create an image to draw on and a drawable to do the drawing with. # - # TODO Refactor into smaller functions + # TODO Refactor into smaller methods + def create_canvas(w, h, bkg_col) @draw = Magick::Draw.new @draw.pointsize = @@pointsize # TODO Use height @canvas = Magick::Image.new(w , h) { self.background_color = bkg_col } - + # Make room for label and last value - if !@options[:label].nil? + unless @options[:label].nil? @options[:has_last] = true @label_width = calculate_width(@options[:label]) @data_last_width = calculate_width(@data.last) # HACK The 7.0 is a severe hack. Must figure out correct spacing @label_and_data_last_width = @label_width + @data_last_width + @@label_margin * 7.0 w += @label_and_data_last_width end - + @canvas = Magick::Image.new(w , h) { self.background_color = bkg_col } @canvas.format = "PNG" - + # Draw label and last value - if !@options[:label].nil? + unless @options[:label].nil? if ENV.has_key?('MAGICK_FONT_PATH') vera_font_path = File.expand_path('Vera.ttf', ENV['MAGICK_FONT_PATH']) @font = File.exists?(vera_font_path) ? vera_font_path : nil else @font = nil end - + @draw.fill = 'black' @draw.font = @font if @font @draw.gravity = Magick::WestGravity @draw.annotate( @canvas, @label_width, 1.0, w - @label_and_data_last_width + @@label_margin, h - calculate_caps_height/2.0, @options[:label]) - + @draw.fill = 'red' @draw.annotate( @canvas, @data_last_width, 1.0, w - @data_last_width - @@label_margin * 2.0, h - calculate_caps_height/2.0, @data.last.to_s) end end - + + ## # Utility to draw a coloured box # Centred on pt, offset off in each direction, fill color is col + def drawbox(pt, offset, color) @draw.stroke 'transparent' @draw.fill(color) @draw.rectangle(pt[0]-offset, pt[1]-offset, pt[0]+offset, pt[1]+offset) end @@ -505,8 +576,7 @@ end def calculate_caps_height @draw.get_type_metrics(@canvas, 'X').height end - end