Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-9 of 9 total  RSS 

Generate a graph using Gruff

This Ruby code produced a graph using gruff. The output shows a line graph [twitxr.com] for the different fruits. Source code origin: Gruff Update With Bar Graphs | Ruby on Rails for Newbies [rubyonrails.com]

require 'gruff'

g = Gruff::Line.new
g.title = "My Graph" 

g.data("Apples", [1, 2, 3, 4, 4, 3])
g.data("Oranges", [4, 8, 7, 9, 8, 9])
g.data("Watermelon", [2, 3, 1, 5, 6, 8])
g.data("Peaches", [9, 9, 10, 8, 7, 9])

g.labels = {0 => '2003', 2 => '2004', 4 => '2005'}

g.write('my_fruity_graph.png')


Note: I executed the code within an irb session on my Gentoo box. With Gentoo, Gruff was installed [gentoo-portage.com] using the command emerge -va gruff. I tried installing it on Ubuntu but ran into some difficulty, even with help from the article install rmagick ubuntu [dzone.com].

*udpate 21:48 24-Feb*

The following code does exactly as the same code above, however it uses XML to separate the data from the process, making it easier and more efficient to build graphs.
#!/usr/bin/ruby
# file: xml2gruff.rb

require 'rexml/document'
require 'gruff'
include REXML

class Xml2Gruff
  
  def initialize(filename)
    file = File.new(filename, 'r')
    doc = Document.new(file)
    # get the title
    @title = doc.root.elements['summary/title'].text
    
    @record = Hash.new
    # get each record
    doc.root.elements.each('records/item') {|item|
      avalues = Array.new
      item.elements.each('values/value') { |value| avalues << value.text.to_i }
      @record[item.elements['label'].text] = avalues
    }
    
    # get the summary labels
    @labels = Hash.new  
    doc.root.elements.each('summary/scale/label') {|l| @labels[l.elements['value'].text.to_i] = l.elements['title'].text} 
    
  end
  
  def save_line_graph(filename)

    g = Gruff::Line.new
    g.title = @title 
    @record.each {|label, data| g.data(label, data) }
    g.labels = @labels
    g.write(filename)

  end
end

if __FILE__ == $0
 x2g = Xml2Gruff.new('my_fruit.xml')
 x2g.save_line_graph('my_fruit2.png')
end


file: my_fruit.xml
<graph>
  <summary>
    <title>My Graph</title>
    <scale>
      <label><title>2003</title><value>0</value></label>
      <label><title>2004</title><value>2</value></label>
      <label><title>2005</title><value>4</value></label>
    </scale>
  </summary>
  <records>
    <item>
      <label>Apples</label>
      <values><value>1</value><value>2</value><value>3</value><value>4</value><value>4</value><value>3</value></values>
    </item>
    <item>
      <label>Oranges</label>
      <values><value>4</value><value>8</value><value>7</value><value>9</value><value>8</value><value>9</value></values>
    </item>
    <item>
      <label>Watermelon</label>
      <values><value>2</value><value>3</value><value>1</value><value>5</value><value>6</value><value>8</value></values>
    </item>
    <item>
      <label>Peaches</label>
      <values><value>9</value><value>9</value><value>10</value><value>8</value><value>7</value><value>9</value></values>
    </item>
  </records>
</graph>

Reference: gruff's gruff-0.2.9 Documentation [rubyforge.org]
to ruby rmagick image ubuntu gentoo magick graph chart gruff by jrobertson on Feb 24, 2008

install rmagick on leopard

#!/bin/sh
curl -O http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz
tar xzvf freetype-2.3.5.tar.gz
cd freetype-2.3.5
./configure --prefix=/usr/local
make
sudo make install
cd ..

curl -O http://superb-west.dl.sourceforge.net/sourceforge/libpng/libpng-1.2.22.tar.bz2
tar jxvf libpng-1.2.22.tar.bz2
cd libpng-1.2.22
./configure --prefix=/usr/local
make
sudo make install
cd ..

curl -O ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
tar xzvf jpegsrc.v6b.tar.gz
cd jpeg-6b
ln -s `which glibtool` ./libtool
export MACOSX_DEPLOYMENT_TARGET=10.5
./configure --enable-shared --prefix=/usr/local
make
sudo make install
cd ..

curl -O ftp://ftp.remotesensing.org/libtiff/tiff-3.8.2.tar.gz
tar xzvf tiff-3.8.2.tar.gz
cd tiff-3.8.2
./configure --prefix=/usr/local
make
sudo make install
cd ..

curl -O http://jaist.dl.sourceforge.net/sourceforge/wvware/libwmf-0.2.8.4.tar.gz
tar xzvf libwmf-0.2.8.4.tar.gz
cd libwmf-0.2.8.4
make clean
./configure
make
sudo make install
cd ..

curl -O http://www.littlecms.com/lcms-1.17.tar.gz
tar xzvf lcms-1.17.tar.gz
cd lcms-1.17
make clean
./configure
make
sudo make install
cd ..

curl -O ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/gs860/ghostscript-8.60.tar.gz
tar zxvf ghostscript-8.60.tar.gz
cd ghostscript-8.60/
./configure  --prefix=/usr/local
make
sudo make install
cd ..

curl -O ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/current/ghostscript-fonts-std-8.11.tar.gz
tar zxvf ghostscript-fonts-std-8.11.tar.gz
sudo mv fonts /usr/local/share/ghostscript

curl -O http://imagemagick.site2nd.org/imagemagick/ImageMagick-6.3.5-9.tar.gz
tar xzvf ImageMagick-6.3.5-9.tar.gz
cd ImageMagick-6.3.5
export CPPFLAGS=-I/usr/local/include
export LDFLAGS=-L/usr/local/lib
./configure --prefix=/usr/local --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8 --with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install
cd ..

sudo gem install RMagick

Produce GIF or PNG barcode images from a Ruby on Rails application using RMagick and Gbarcode

This is a Ruby on Rails controller that produces PNG barcode images using RMagick (http://rmagick.rubyforge.org/) and Gbarcode (http://gbarcode.rubyforge.org/), the Ruby Gnu Barcode ( http://www.gnu.org/software/barcode/barcode.html ) wrapper. You will need to install RMagick and Gbarcode.

On Mac OS X, you can use the Locomotive RMagick bundle (http://locomotive.raaum.org/bundles/index.html) if you install the gbarcode gem into it first, for instance:

% export GEM_HOME=/Applications/Locomotive2/Bundles/rmagickRailsMar2007_i386.locobundle/framework/lib/ruby/gems/1.8/
% gem install gbarcode


To use it, save this code as barcode_controller.rb in the apps/controllers directory of your Rails application. Generate the barcodes using a URL like this:

http://localhost:3000/barcode/get_barcode_image?string=foo

This example uses the BARCODE_128 encoding; you can use different barcode encodings by adjusting the code. To produce PNG images, make sure you have have the PNG libraries installed on your system, and that your ImageMagick is compiled with PNG support, and then change 'image/gif' to 'image/png' and im.format = "GIF" to im.format = "PNG".

# $RAILS_ROOT/apps/controllers/barcode_controller.rb
class BarcodeController < ApplicationController

  def get_barcode_image
     string_to_encode = params[:string]
     barcode_image = BarcodeGenerator.get_barcode_image(string_to_encode)
     send_data(barcode_image, :type     => 'image/gif',
                                  :disposition => 'inline')
  end
end


# $RAILS_ROOT/app/helpers/barcode_generator.rb
#
# note: this will not work without rmagick (Ruby ImageMagick interface) and gbarcode (GNU barcode) 
# gems installed. rmagick needs ImageMagick plus dependencies.

class BarcodeGenerator

  # Uses subprocesses because 
  # 1. ImageMagick/RMagick leaks memory,
  #    and doesn't work in a long-running process. The fork makes it safe.
  # 2. The output from the Gbarcode and ImageMagick is often longer than the pipe buffer,
  #    so we have to empty the buffer from another subprocess
  def BarcodeGenerator.get_barcode_image(barcode_string)
    return BarcodeGenerator.get_subprocess_output do
                   barcode_generator = BarcodeGenerator.new
                   $stdout.write(barcode_generator.get_barcode_image(barcode_string))
    end
  end

  def initialize
    # we do the imports here to protect long-running processes (like mongrel) from ImageMagick's memory leaks
    require 'RMagick'
    require 'gbarcode'
  end

  def get_barcode_image(string_to_encode)
    if string_to_encode.nil?
      string_to_encode = "No string specified"
    end
    string_to_encode = remove_rails_file_extension(string_to_encode)
    eps_barcode = get_barcode_eps(string_to_encode)
    gif_barcode = convert_eps_to_gif(eps_barcode)
    return gif_barcode
  end

  def remove_rails_file_extension(string_to_encode)
    if string_to_encode[-4..-1] == ".png"
      string_to_encode = string_to_encode[0..-5]
    end
    return string_to_encode
  end

  def get_barcode_eps(string_to_encode)
    barcode_object = Gbarcode.barcode_create(string_to_encode)
    Gbarcode.barcode_encode(barcode_object, Gbarcode::BARCODE_128)
    return BarcodeGenerator.get_subprocess_output do
        Gbarcode.barcode_print(barcode_object, $stdout, Gbarcode::BARCODE_OUT_EPS)    
    end
  end
  
  def convert_eps_to_gif(eps_image)
    base64_eps_image = Base64.encode64(eps_image)
    im = Magick::Image::read_inline(base64_eps_image).first
    im.format = "GIF"
    return BarcodeGenerator.get_subprocess_output do
       im.write($stdout) 
    end
  end

  # execute a block's code in a subprocess, returning any output
  def BarcodeGenerator.get_subprocess_output()
    data = ""
    IO.popen('-', 'r+') do |child_filehandle|
      if child_filehandle
        begin
          data = child_filehandle.read
        ensure
          child_filehandle.close_write
        end
      else
        yield
      end
    end
    return data
  end

end    

install rmagick ubuntu

let's undo some of the damage (just in case):
sudo apt-get remove --purge librmagick-ruby-doc librmagick-ruby1.8


Next let's get the version we need:
sudo apt-get install libmagick9-dev ruby1.8-dev


Lastly, go grab rmagick, the gem
sudo gem install rmagick

Avatar Resizer

I go on a lot of Bulletin Board, every has its own limits of size for the avatars, instead of the resize manually I created a script which does it for me with RMagick

#!/usr/bin/ruby
require "RMagick"
$SIZES = [80 , 100 , 110 , 128]

if !ARGV[0]
  puts "Usage: mk_avatars.rb SourceAvatarPath"
  exit
end

image = Magick::Image.read(ARGV[0]).first
$SIZES.each do |sz|
  puts "Generating Avatar : #{sz}"
  out = image.thumbnail(sz,sz)
  file = "out_#{sz}.#{image.format}"
  out.write(file)
end

Garmin Forerunner TCX file processing

This is a small ruby file I wrote to process the TCX file that I can download from MotionBased website that is processed from the data off my Garmin Forerunner 305. It processes the XML file and generates a little badge/infographic that I can put on a website.

// insert code here..
require "date"
require "rexml/document"
require 'rubygems'
require 'RMagick'

class ForeRunner
  
  M2MI = 1609.344 # meters to miles
  MINIMUM_LAP_TIME = 300 # minimum seconds to count as a full lap
  
  attr_accessor :laps, :total_time, :distance, :calories
  
  def initialize(file)
    @source_doc = REXML::Document.new file
    
    @laps = 0
    @lap_times = Array.new
    @lap_bpm = Array.new
    @full_laps = 0

    @total_time = 0    
    @distance = 0
    @calories = 0
    @map_data = []
    
    self.process_file  
  end
  
  def generate_infographic(output_filename)
    canvas = Magick::Image.new(250, 80)
    map_size = 50
    map_color = 'green'

    max_lap_height = 25
    
    gc = Magick::Draw.new

    # Draw ellipse
    gc.stroke('grey50')
    gc.stroke_width(2)
    gc.fill_opacity(0)

    # draw the relative lap times
    lap = 0
    max_time = @lap_times.max
    @lap_times.each do |s|
      lap += 1
      x = 10 + (lap * 5)
      y = 60 - (s / (max_time / max_lap_height))
      gc.line(x, 60, x, y)
    end

    #draw the heartbeat avg
    gc.stroke('#c9a')
    
    lap = 0
    lx = nil
    ly = nil
    max_bpm = @lap_bpm.max
    min_bpm = @lap_bpm.min
    
    max_bpm_height = 18
    
    @lap_bpm.each do |s|
      lap += 1
      x = 10 + (lap * 5)
      y = 21 - ( (s - min_bpm) / ((max_bpm - min_bpm) / max_bpm_height))
      if !lx
        lx = x
        ly = y
      end

      gc.line(lx, ly, x, y)

      lx = x
      ly = y
    end

    # draw the map
    lat_diff = (@map_data['max_lat'] - @map_data['min_lat']).abs
    lon_diff = (@map_data['max_lon'] - @map_data['min_lon']).abs

    lat_off = lat_diff / map_size
    lon_off = lon_diff / map_size
    
    gc.fill(map_color)
    @map_data['map_data'].each do |i|
      lt = (map_size - ((i[0] - @map_data['min_lat']) / lat_off)).round
      lg = ((i[1] - @map_data['min_lon']) / lon_off).round
      gc.point((240 - map_size) + lg, (65 - map_size) + lt)
    end

    # Annotate
    gc.stroke('transparent')
    gc.fill('black')
    gc.text(120, 15, @distance.to_s[0, 5] + ' mi')
    gc.text(120, 30, (@total_time / 60 / 60).to_s[0, 4] + ' hr')
    gc.text(120, 45, @calories.to_s + ' cal')

    gc.fill('#555')
    total_hr = 0 
    @lap_bpm.each { |hr| total_hr += hr }
    gc.text(15, 32, 'avg bpm: ' + (total_hr / @laps).round.to_s)
    gc.text(15, 75, 'avg pace: ' + ((@total_time / @full_laps) / 60).to_s[0, 4] + ' min/lap')

    gc.draw(canvas)
    canvas.write(output_filename)
  end
  
  protected 
  
  def process_file
    @source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/*') do |element| 
      if element.name == "TotalTimeSeconds"
        @lap_times << element.text.to_f
        if element.text.to_f > 300  # for removing warmup and warmdown laps
          @total_time += element.text.to_f
          @full_laps += 1
        end
        @laps += 1
      end

      if element.name == "DistanceMeters" 
        @distance += (element.text.to_f / M2MI)
      end

      if element.name == "Calories" 
        @calories += element.text.to_f
      end

      if element.name == "AverageHeartRateBpm" 
        element.elements.each('Value') { |v| @lap_bpm << v.text.to_f }
      end
    end
    @map_data = self.generate_map_points
  end
  
  def generate_map_points  
    map = []

    max_lat = -300
    max_lon = -300
    min_lat = 300
    min_lon = 300

    @source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/Track/Trackpoint/*') do |element|
      if element.elements['LatitudeDegrees']
        lat = element.elements['LatitudeDegrees'].text.to_f
        lon = element.elements['LongitudeDegrees'].text.to_f

        map << [lat, lon]
        max_lat = lat if (lat > max_lat) 
        max_lon = lon if (lon > max_lon) 
        min_lat = lat if (lat < min_lat) 
        min_lon = lon if (lon < min_lon) 
      end
    end
    {'map_data' => map, 'max_lat' => max_lat, 'min_lat' => min_lat, 'max_lon' => max_lon, 'min_lon' => min_lon}
  end
  

end


if ARGV[0].nil? 
  puts "This program takes one argument, a Garmin Forerunner TCX File"
  puts "Like this: run.rb filename.tcx"
  exit
end

output_filename = ARGV[0].gsub(/\.tcx$/, "-" + Time.now.strftime("%Y%m%d") + ".png")
puts "Generating Graph #{output_filename}"

fr = ForeRunner.new(File.new(ARGV[0]))
fr.generate_infographic(output_filename)

generic file and image models for uploaded files

These are basic models that store a file in a dedicated files table. Use has_one or has_many to associate this with your actual models. RMagick is required for images.

This is my first code dealing with uploads and rmagick, so please comment if you have suggestions.

class DbFile < ActiveRecord::Base
  IMAGE_TYPES = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png']
  before_validation     :sanitize_filename
  validates_presence_of :size, :filename, :content_type

  class << self
    def new_file(file_data)
      content_type = file_data.content_type.strip
      (IMAGE_TYPES.include?(content_type) ? DbImage : DbFile).new \
        :data         => file_data.read,
        :filename     => file_data.original_filename,
        :size         => file_data.size,
        :content_type => content_type
    end
  end

  protected
  def sanitize_filename
      # NOTE: File.basename doesn't work right with Windows paths on Unix
      # get only the filename, not the whole path
      filename.gsub! /^.*(\\|\/)/, ''

      # Finally, replace all non alphanumeric, underscore or periods with underscore
      filename.gsub! /[^\w\.\-]/, '_'
  end
end

require 'rmagick'
require 'base64'
class DbImage < DbFile
  def data=(file_data)
    with_image(file_data, true) do |img|
      self.width  = img.columns
      self.height = img.rows
    end
  end

  def with_image(file_data = nil, save_image = false, &block)
    img = Magick::Image::read_inline(Base64.b64encode(file_data || self.data)).first
    block.call(img)
    write_attribute('data', img.to_blob) if save_image
    img = nil
    GC.start
  end
end


Controller Usage:

# returns DbImage if content_type matches
db_file = DbFile.new_file(params[:file][:data])
db_file.save


Model Usage:

# raw binary image data
File.open('my_file', 'w') { |f| f.write(db_file.data) }

# Image resizing with rmagick
# automatically creates RMagick::Image and 
# invokes GC.start
db_file.with_image do |img|
  img.scale(.25)
  img.write('thumb.jpg')
end

On-the-fly thumbnailer method for a Rails 'Photo' controller

require 'RMagick'

class PhotoController < ApplicationController

[...snip...]

    def render_resized_image
                @photo=Photo.find(@params["id"])

                maxw = @params["width"] != nil ? @params["width"].to_i : 90
                maxh = @params["height"] != nil ? @params["height"].to_i : 90
                aspectratio = maxw.to_f / maxh.to_f


                pic = Magick::Image.from_blob(@photo.image)[0]


                picw = pic.columns
                pich = pic.rows
                picratio = picw.to_f / pich.to_f

                if picratio > aspectratio then
                        scaleratio = maxw.to_f / picw
                else
                        scaleratio = maxh.to_f / pich
                end

                #breakpoint

                thumb = pic.resize(scaleratio)

                @response.headers["Content-type"]=@photo.mime
    end
end

Requires RMagick

Based on Thumbnailer in Ruby and RMagick

Thumbnailer in Ruby and RMagick

require 'RMagick'

maxwidth = 120
maxheight = 160
aspectratio = maxwidth.to_f / maxheight.to_f
imgfile = 'world'

pic = Magick::Image.read(imgfile + '.jpg').first
imgwidth = pic.columns
imgheight = pic.rows
imgratio = imgwidth.to_f / imgheight.to_f
imgratio > aspectratio ? scaleratio = maxwidth.to_f / imgwidth : scaleratio = maxheight.to_f / imgheight
thumb = pic.resize(scaleratio)

white_bg = Magick::Image.new(maxwidth, thumb.height)
pic = white_bg.composite(thumb, Magick::CenterGravity, Magick::OverCompositeOp)
pic.write(imgfile + '.thumb.jpg')
« Newer Snippets
Older Snippets »
Showing 1-9 of 9 total  RSS