mirror of https://github.com/apache/lucene.git
add sparklines pie charts by the facet counts
git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@504694 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e3991438b3
commit
667aba96f2
|
@ -0,0 +1,30 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'sparklines_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class SparklinesController; def rescue_action(e) raise e end; end
|
||||
|
||||
class SparklinesControllerTest < Test::Unit::TestCase
|
||||
|
||||
#fixtures :data
|
||||
|
||||
def setup
|
||||
@controller = SparklinesController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_index
|
||||
get :index, :results => "1,2,3,4,5", :type => 'bar', :line_color => 'black'
|
||||
assert_response :success
|
||||
assert_equal 'image/png', @response.headers['Content-Type']
|
||||
end
|
||||
|
||||
# TODO Replace this with your actual tests
|
||||
def test_show
|
||||
get :show
|
||||
assert_response :success
|
||||
assert_equal 'image/png', @response.headers['Content-Type']
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2006 Geoffrey Grosenbach
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,15 @@
|
|||
== Sparklines Plugin
|
||||
|
||||
Make tiny graphs.
|
||||
|
||||
Also has a "sparklines" generator for copying a controller and functional test to your app.
|
||||
|
||||
./script/generate sparklines
|
||||
|
||||
See examples at http://nubyonrails.com/pages/sparklines
|
||||
|
||||
== Author
|
||||
|
||||
Geoffrey Grosenbach boss@topfunky.com http://nubyonrails.com
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/test_*.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'CalendarHelper'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
author: topfunky
|
||||
summary: A library for making small graphs, a la Edward Tufte. A generator and helper are also included.
|
||||
homepage: http://nubyonrails.com/pages/sparklines
|
||||
plugin: http://topfunky.net/svn/plugins/sparklines
|
||||
license: MIT
|
||||
version: 0.4.0
|
||||
rails_version: 1.1+
|
13
client/ruby/flare/vendor/plugins/sparklines/generators/sparklines/sparklines_generator.rb
vendored
Normal file
13
client/ruby/flare/vendor/plugins/sparklines/generators/sparklines/sparklines_generator.rb
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
class SparklinesGenerator < Rails::Generator::Base
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
m.directory File.join("app/controllers")
|
||||
m.directory File.join("test/functional")
|
||||
|
||||
m.file "controller.rb", File.join("app/controllers/sparklines_controller.rb")
|
||||
m.file "functional_test.rb", File.join("test/functional/sparklines_controller_test.rb")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
40
client/ruby/flare/vendor/plugins/sparklines/generators/sparklines/templates/controller.rb
vendored
Normal file
40
client/ruby/flare/vendor/plugins/sparklines/generators/sparklines/templates/controller.rb
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
class SparklinesController < ApplicationController
|
||||
|
||||
# Handles requests for sparkline graphs from views.
|
||||
#
|
||||
# Params are generated by the sparkline_tag helper method.
|
||||
#
|
||||
def index
|
||||
# Make array from comma-delimited list of data values
|
||||
ary = []
|
||||
if params.has_key?('results') && !params['results'].nil?
|
||||
params['results'].split(',').each do |s|
|
||||
ary << s.to_i
|
||||
end
|
||||
end
|
||||
|
||||
send_data( Sparklines.plot( ary, params ),
|
||||
:disposition => 'inline',
|
||||
:type => 'image/png',
|
||||
:filename => "spark_#{params[:type]}.png" )
|
||||
end
|
||||
|
||||
|
||||
# Use this type of method for sparklines that can be cached. (Doesn't work with the helper.)
|
||||
#
|
||||
# To make caching easier, add a line like this to config/routes.rb:
|
||||
# map.sparklines "sparklines/:action/:id/image.png", :controller => "sparklines"
|
||||
#
|
||||
# Then reference it with the named route:
|
||||
# image_tag sparklines_url(:action => 'show', :id => 42)
|
||||
def show
|
||||
send_data(Sparklines.plot(
|
||||
[42, 37, 89, 74, 70, 50, 40, 30, 40, 50],
|
||||
:type => 'bar', :above_color => 'orange'
|
||||
),
|
||||
:disposition => 'inline',
|
||||
:type => 'image/png',
|
||||
:filename => "sparkline.png")
|
||||
end
|
||||
|
||||
end
|
30
client/ruby/flare/vendor/plugins/sparklines/generators/sparklines/templates/functional_test.rb
vendored
Normal file
30
client/ruby/flare/vendor/plugins/sparklines/generators/sparklines/templates/functional_test.rb
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'sparklines_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class SparklinesController; def rescue_action(e) raise e end; end
|
||||
|
||||
class SparklinesControllerTest < Test::Unit::TestCase
|
||||
|
||||
#fixtures :data
|
||||
|
||||
def setup
|
||||
@controller = SparklinesController.new
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_index
|
||||
get :index, :results => "1,2,3,4,5", :type => 'bar', :line_color => 'black'
|
||||
assert_response :success
|
||||
assert_equal 'image/png', @response.headers['Content-Type']
|
||||
end
|
||||
|
||||
# TODO Replace this with your actual tests
|
||||
def test_show
|
||||
get :show
|
||||
assert_response :success
|
||||
assert_equal 'image/png', @response.headers['Content-Type']
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
ActionView::Base.send :include, SparklinesHelper
|
|
@ -0,0 +1,582 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'RMagick'
|
||||
|
||||
=begin rdoc
|
||||
|
||||
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}[http://bitworking.org/projects/sparklines].
|
||||
|
||||
Requires the RMagick image library.
|
||||
|
||||
==Authors
|
||||
|
||||
{Dan Nugent}[mailto:nugend@gmail.com] Original port from Python Sparklines library.
|
||||
|
||||
{Geoffrey Grosenbach}[mailto:boss@topfunky.com] -- http://nubyonrails.topfunky.com
|
||||
-- Conversion to module and further maintenance.
|
||||
|
||||
==General Usage and Defaults
|
||||
|
||||
To use in a script:
|
||||
|
||||
require 'rubygems'
|
||||
require 'sparklines'
|
||||
Sparklines.plot([1,25,33,46,89,90,85,77,42],
|
||||
: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
|
||||
bar
|
||||
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.
|
||||
|
||||
=end
|
||||
class Sparklines
|
||||
|
||||
VERSION = '0.4.1'
|
||||
|
||||
@@label_margin = 5.0
|
||||
@@pointsize = 10.0
|
||||
|
||||
class << self
|
||||
|
||||
# Does the actual plotting of the graph.
|
||||
# Calls the appropriate subclass based on the :type argument.
|
||||
# Defaults to 'smooth'
|
||||
def plot(data=[], options={})
|
||||
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',
|
||||
|
||||
:has_min => false,
|
||||
:has_max => false,
|
||||
:has_last => false,
|
||||
|
||||
:label => nil
|
||||
}
|
||||
|
||||
# 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 whisker).include? options_sym[:type]
|
||||
sparkline.send options_sym[:type]
|
||||
else
|
||||
sparkline.plot_error options_sym
|
||||
end
|
||||
end
|
||||
|
||||
# Writes a graph to disk with the specified filename, or "sparklines.png"
|
||||
def plot_to_file(filename="sparklines.png", data=[], options={})
|
||||
File.open( filename, 'wb' ) do |png|
|
||||
png << self.plot( data, options)
|
||||
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
|
||||
#
|
||||
# :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.
|
||||
|
||||
def area
|
||||
|
||||
step = @options[:step].to_i
|
||||
height = @options[:height].to_i
|
||||
background_color = @options[:background_color]
|
||||
|
||||
create_canvas((@norm_data.size - 1) * step + 4, height, background_color)
|
||||
|
||||
upper = @options[:upper].to_i
|
||||
|
||||
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.to_blob
|
||||
end
|
||||
|
||||
##
|
||||
# A bar graph.
|
||||
|
||||
def bar
|
||||
step = @options[:step].to_i
|
||||
height = @options[:height].to_f
|
||||
background_color = @options[:background_color]
|
||||
|
||||
create_canvas(@norm_data.length * step + 2, height, background_color)
|
||||
|
||||
upper = @options[:upper].to_i
|
||||
below_color = @options[:below_color]
|
||||
above_color = @options[:above_color]
|
||||
|
||||
i = 1
|
||||
@norm_data.each_with_index do |r, index|
|
||||
color = (r >= upper) ? above_color : below_color
|
||||
@draw.stroke('transparent')
|
||||
@draw.fill(color)
|
||||
@draw.rectangle( i, @canvas.rows,
|
||||
i + step - 2, @canvas.rows - ( (r / @maximum_value) * @canvas.rows) )
|
||||
i += step
|
||||
end
|
||||
|
||||
@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]
|
||||
step = @options[:step].to_i
|
||||
|
||||
create_canvas(@norm_data.size * step - 1, height, background_color)
|
||||
|
||||
below_color = @options[:below_color]
|
||||
above_color = @options[:above_color]
|
||||
|
||||
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_i,
|
||||
i, (@canvas.rows - r/(101.0/(height-4))).to_i)
|
||||
i += step
|
||||
end
|
||||
|
||||
@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
|
||||
diameter = @options[:diameter].to_i
|
||||
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.to_blob
|
||||
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.to_blob
|
||||
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.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
|
||||
#
|
||||
# :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.
|
||||
|
||||
def smooth
|
||||
|
||||
step = @options[:step].to_i
|
||||
height = @options[:height].to_i
|
||||
background_color = @options[:background_color]
|
||||
|
||||
create_canvas((@norm_data.size - 1) * step + 4, 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]
|
||||
|
||||
@draw.stroke(line_color)
|
||||
coords = []
|
||||
i=0
|
||||
@norm_data.each do |r|
|
||||
coords.push [ 2 + i, (height - 3 - r/(101.0/(height-4))) ]
|
||||
i += step
|
||||
end
|
||||
|
||||
open_ended_polyline(coords)
|
||||
|
||||
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.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)
|
||||
@draw.line(0,15,40,0)
|
||||
|
||||
@draw.draw(@canvas)
|
||||
@canvas.to_blob
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize_data
|
||||
@maximum_value = @data.max
|
||||
if @options[:type].to_s == 'pie'
|
||||
@norm_data = @data
|
||||
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)
|
||||
|
||||
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 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
|
||||
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
|
||||
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
|
||||
|
||||
def calculate_width(text)
|
||||
@draw.get_type_metrics(@canvas, text.to_s).width
|
||||
end
|
||||
|
||||
def calculate_caps_height
|
||||
@draw.get_type_metrics(@canvas, 'X').height
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
# Provides a tag for embedding sparklines graphs into your Rails app.
|
||||
#
|
||||
module SparklinesHelper
|
||||
|
||||
# Call with an array of data and a hash of params for the Sparklines module.
|
||||
#
|
||||
# sparkline_tag [42, 37, 43, 182], :type => 'bar', :line_color => 'black'
|
||||
#
|
||||
# You can also pass :class => 'some_css_class' ('sparkline' by default).
|
||||
def sparkline_tag(results=[], options={})
|
||||
url = { :controller => 'sparklines',
|
||||
:results => results.join(',') }
|
||||
options = url.merge(options)
|
||||
|
||||
%(<img src="#{ url_for options }" class="#{options[:class] || 'sparkline'}" alt="Sparkline Graph" />)
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue