Archive for April, 2007

APP Interop Pictures

Tuesday, April 17th, 2007

I took a couple of quick shots of the group:

The assembled crowd at the APP interop

and the grid:

The interop grid at the APP interop

More details on my xml.com blog post.

Tim Bray has better photos here.

JRuby + JFreeChart = Sparklines

Friday, April 13th, 2007

Inspired by how easy it was to get JFreeChart working and some code from former colleague Andrew Bruno, I thought it’d be nice to write some JRuby to generate Edward Tufte’s Sparklines.

Here’s some simple example code on a semi-random dataset:

# Mostly inspired by
# http://left.subtree.org/2007/01/15/creating-sparklines-with-jfreechart/
# have JFreeChart in your classpath, obviously, as well as jcommon.jar
require 'java'

module Graph
  class Sparkline
    include_class 'java.io.File'
    include_class 'org.jfree.chart.ChartUtilities'
    include_class 'org.jfree.chart.JFreeChart'
    include_class 'org.jfree.chart.axis.NumberAxis'
    include_class 'org.jfree.chart.plot.XYPlot'
    include_class 'org.jfree.chart.renderer.xy.StandardXYItemRenderer'
    include_class 'org.jfree.data.xy.XYSeries'
    include_class 'org.jfree.data.xy.XYSeriesCollection'
    include_class 'org.jfree.chart.plot.PlotOrientation'

    def initialize(width=200, height=80, data=[])
      @width = width
      @height = height
      dataset = create_sample_data() if data.empty?
      @chart = create_chart(dataset)
    end

    def render_to_file(filename, format="png")
      javafile = java.io.File.new(filename)
      ChartUtilities.saveChartAsPNG(javafile, @chart, @width, @height)
    end

    private
    def create_sample_data
       series = XYSeries.new("Sparkline")
      data = [20]
      (1..99).each {|x|
        y = (data.last + (rand(x) + 1)) / 2
        data << y
        series.add(x, y)
      }

      dataset = XYSeriesCollection.new
      dataset.addSeries(series)
      return dataset
    end

    def create_chart(dataset)
      x = NumberAxis.new
      x.setTickLabelsVisible(false)
      x.setTickMarksVisible(false)
      x.setAxisLineVisible(false)
      x.setNegativeArrowVisible(false)
      x.setPositiveArrowVisible(false)
      x.setVisible(false)

      y = NumberAxis.new
      y.setTickLabelsVisible(false)
      y.setTickMarksVisible(false)
      y.setAxisLineVisible(false)
      y.setNegativeArrowVisible(false)
      y.setPositiveArrowVisible(false)
      y.setVisible(false)

      plot = XYPlot.new
      plot.setDataset(dataset)
      plot.setDomainAxis(x)
      plot.setDomainGridlinesVisible(false)
      plot.setDomainCrosshairVisible(false)
      plot.setRangeGridlinesVisible(false)
      plot.setRangeCrosshairVisible(false)
      plot.setRangeAxis(y)
      plot.setOutlinePaint(nil)
      plot.setRenderer(StandardXYItemRenderer.new(StandardXYItemRenderer::LINES))

      chart = JFreeChart.new(nil, JFreeChart::DEFAULT_TITLE_FONT, plot, false)
      chart.setBorderVisible(false);
      return chart
    end

  end # class Sparkline  
end # class Graph

sp = Graph::Sparkline.new
puts "Rendering sparkline"
sp.render_to_file("sparkline.png")

And the resulting sparkline chart:
An Example Sparkline Chart

Code: http://kfahlgren.com/code/sparkline.jrb

UPDATE: Removed some of the useless sample generation code

More JRuby Play: JFreeChart

Thursday, April 12th, 2007

I’ve been messing around at work trying to make some automated scheduling charts (basically Gantt-like) in Ruby. I’ve implemented it a couple of times using SVG::Graph, which is close to what I need, but I end up having to rewrite a lot of methods whenever I really start using it. It occurred to me today that I might be able to co-opt a sexy Java library to do my dirty work. JFreeChart to the rescue!

As before, I’m generally amazed at how little work goes into integrating Java and JRuby these days. It’s a testament to the JRuby team and to the wealth of well-written, well-documented Java libraries out there.

Here’s some toy code that makes a simple Gantt chart and saves it as a PNG to a file:

# have jfreechart.jar in your classpath, obviously, as well as jcommon.jar
# and use a recent jruby
require 'java'
module Gantt
  class Simple
    include_class 'org.jfree.chart.ChartFactory'
    include_class 'org.jfree.chart.ChartUtilities'
    include_class 'org.jfree.chart.JFreeChart'
    include_class 'org.jfree.data.gantt.Task'
    include_class 'org.jfree.data.gantt.TaskSeries'
    include_class 'org.jfree.data.gantt.TaskSeriesCollection'
    include_class 'org.jfree.data.time.SimpleTimePeriod'
    include_class 'java.lang.System'
    include_class 'java.io.File'

    MILLIS_IN_A_DAY = 86400000

    def initialize(title="Chunky Bacon", width=700, height=400, data=[])
      @width = width
      @height = height
      @title = title
      dataset = create_sample_data() if data.empty?
      @chart = create_chart(dataset)
    end

    def render_to_file(filename, format="png")
      javafile = java.io.File.new(filename)
      ChartUtilities.saveChartAsPNG(javafile, @chart, @width, @height)
    end

    private
    def create_sample_data
      # dates as milliseconds seems the easiet
      now = System.currentTimeMillis
      tomorrow = now + (MILLIS_IN_A_DAY * 1)
      day_after_tomorrow = now + (MILLIS_IN_A_DAY * 2)
      week_from_today = now + (MILLIS_IN_A_DAY * 7)

      s1 = TaskSeries.new("JRuby")
      s1.add(Task.new("Download JRuby",
                      SimpleTimePeriod.new(now, tomorrow)))
      s1.add(Task.new("Write Code",
                      SimpleTimePeriod.new(tomorrow, day_after_tomorrow)))
      s1.add(Task.new("Setup CLASSPATH",
                      SimpleTimePeriod.new(day_after_tomorrow, week_from_today)))

      s2 = TaskSeries.new("Java")
      s2.add(Task.new("Read Comics",
                      SimpleTimePeriod.new(now, tomorrow)))
      s2.add(Task.new("Write Code",
                      SimpleTimePeriod.new(tomorrow, day_after_tomorrow)))
      s2.add(Task.new("Setup CLASSPATH",
                      SimpleTimePeriod.new(day_after_tomorrow, week_from_today)))

      collection = TaskSeriesCollection.new
      collection.add(s1)
      collection.add(s2)
      return collection
    end

    def create_chart(dataset)
      opts = {
              :title => @title,
              :domain_axis_label => "Task",
              :range_axis_label => "Date",
              :data => dataset,
              :include_legend => true,
              :tooltips => false,
              :urls => false
             }
      chart = ChartFactory.createGanttChart(
                                              opts[:title],
                                              opts[:domain_axis_label],
                                              opts[:range_axis_label],
                                              opts[:data],
                                              opts[:include_legend],
                                              opts[:tooltips],
                                              opts[:urls]
                                            )
      return chart
    end

  end # class Simple
end # module Gantt

chart = Gantt::Simple.new("Gantt Chart Demo")
puts "Rendering chart"
chart.render_to_file("simplegantt.png")

Example PNG:

A Simple Gantt Chart Example

Code: http://kfahlgren.com/code/simple_gantt.jrb

Hiding Complexity

Saturday, April 7th, 2007

I just started reading the second edition of The Ruby Way by Hal Fulton and came across this gem:

We can’t avoid complexity, but we can push it around. We can bury it out of sight. This is the old “black box” principle at work; a black box performs a complex task, but it possesses simplicity on the outside.

This idea of managing complexity is one of the classic commandments of programming, of course, and a core theme of Structure and Interpretation of Computer Programs, but this was a nice restatement.

It looks like this edition (in all it’s 800+ page glory) will be quite a treat.