class Birdwatcher::Punchcard

Constants

DAYS
FONT_FACE
HOURS
LEFT_PADDING
TOP_PADDING
WIDTH

Public Class Methods

new(timestamps) click to toggle source
# File lib/birdwatcher/punchcard.rb, line 15
def initialize(timestamps)
  @timestamps = timestamps
end

Public Instance Methods

generate(destination) click to toggle source
# File lib/birdwatcher/punchcard.rb, line 19
def generate(destination)
  @data_log  = Hash.new { |h, k| h[k] = Hash.new }
  final_data = []
  @timestamps.each do |timestamp|
    day  = timestamp.strftime("%a")
    hour = timestamp.strftime("%H").to_i
    @data_log[day][hour] = (@data_log[day][hour] || 0) + 1
  end
  @data_log.each do |d, hour_pair|
    hour_pair.each do |h, value|
      glr = @data_log[d][h] * 1.0
      glr /= max_value
      glr *= max_range
      glrb = get_weight(glr)
      final_data.push([glrb, get_x_y_from_day_and_hour(d, h)])
    end
  end
  final_data.each do |x|
    draw_circle(x[1], x[0])
  end
  surface.write_to_png(destination)
end

Private Instance Methods

all_values() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 97
def all_values
  @all_values = []
  @data_log.each do |d, e|
    e.each do |h, i|
      @all_values << @data_log[d][h]
    end
  end
  @all_values
end
context() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 115
def context
  @context ||= Cairo::Context.new(surface).tap do |c|
    c.line_width = 1
    c.set_source_rgb(1, 1, 1)
    c.rectangle(0, 0, width, height)
    c.fill

    # Set black
    c.set_source_rgb(0, 0, 0)

    # Draw X and Y axis
    c.move_to(left, top)
    c.rel_line_to(0, 8 * distance)
    c.rel_line_to(25 * distance, 0)
    c.stroke

    # Draw indicators on X and Y axis
    x, y = left, top
    8.times do
      c.move_to(x, y)
      c.rel_line_to(-indicator_length, 0)
      c.stroke
      y += distance
    end

    x += distance
    26.times do
      c.move_to(x, y)
      c.rel_line_to(0, indicator_length)
      c.stroke
      x += distance
    end

    # Select font
    c.select_font_face(FONT_FACE, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL)

    # Set and appropiate font size
    c.set_font_size(Math.sqrt( (width * height) / 3055.6))

    # Draw days on Y axis
    x, y = (left - 5), (top + distance)
    DAYS.each do |day|
      t_ext = c.text_extents(day.to_s)
      c.move_to(x - indicator_length - t_ext.width, y + t_ext.height / 2)
      c.show_text(day.to_s)
      y += distance
    end

    # Draw hours on X axis
    x, y = (left + distance), (top + (7 + 1) * distance + 5)
    HOURS.each do |hour|
      t_ext = c.text_extents(hour.to_s)
      c.move_to(x - t_ext.width / 2 - t_ext.x_bearing, y + indicator_length + t_ext.height / 2)
      c.show_text(hour.to_s)
      x += distance
    end
  end
end
distance() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 52
def distance
  distance = Math.sqrt((width * height) / 270.5).round
  if distance % 2 == 1
    distance -= 1
  end
  distance
end
draw_circle(position, weight) click to toggle source
# File lib/birdwatcher/punchcard.rb, line 174
def draw_circle(position, weight)
  x, y  = position
  alpha = (weight.to_f / max_value.to_f)
  context.set_source_rgba(0, 0, 0, alpha)
  context.move_to(x, y)
  context.arc(x, y, weight, 0, 2 * Math::PI)
  context.fill
end
get_weight(number) click to toggle source
# File lib/birdwatcher/punchcard.rb, line 82
def get_weight(number)
  return 0 if number.zero?
  (1..(distance / 2)).to_a.each do |i|
    if i * i <= number && number < (i + 1) * (i + 1)
      return i
    end
  end

  if number == max_range
    return distance/2-1
  end

  nil
end
get_x_y_from_day_and_hour(day, hour) click to toggle source
# File lib/birdwatcher/punchcard.rb, line 76
def get_x_y_from_day_and_hour(day, hour)
  y = top + (DAYS.index(day.to_s) + 1) * distance
  x = left + (hour.to_i + 1) * distance
  [x, y]
end
height() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 48
def height
  (width / 2.75).round(0)
end
indicator_length() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 68
def indicator_length
  height / 20
end
left() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 64
def left
  (width / 18) + LEFT_PADDING
end
max_range() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 60
def max_range
  (distance / 2)**2
end
max_value() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 107
def max_value
  all_values.sort.last
end
surface() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 111
def surface
  @surface ||= Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, width, height)
end
top() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 72
def top
  indicator_length + TOP_PADDING
end
width() click to toggle source
# File lib/birdwatcher/punchcard.rb, line 44
def width
  WIDTH
end