mtah-ruby-treemap 0.0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,72 @@
1
+ # -*- coding: undecided -*-
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+ require 'rake/packagetask'
8
+ require 'rake/gempackagetask'
9
+
10
+ $:.unshift(File.dirname(__FILE__) + "/lib")
11
+ require 'treemap'
12
+
13
+ CLEAN.include FileList['test/*.html', 'test/*.png', 'test/*.svg']
14
+ CLEAN.include 'docs'
15
+
16
+ PKG_NAME = 'mtah-ruby-treemap'
17
+ PKG_VERSION = Treemap::VERSION
18
+
19
+ desc 'list available tasks'
20
+ task :default do
21
+ puts "Run 'rake --tasks' for the list of available tasks"
22
+ end
23
+
24
+ Rake::RDocTask.new do |rdoc|
25
+ rdoc.rdoc_dir = 'docs'
26
+ rdoc.title = "RubyTreemap"
27
+ rdoc.options << "--all"
28
+ rdoc.options << "-S"
29
+ rdoc.options << "-N"
30
+ rdoc.options << "--main=README"
31
+ rdoc.rdoc_files.include("README", "EXAMPLES", "TODO", "ChangeLog", "lib/*.rb", "lib/**/*.rb")
32
+ end
33
+
34
+ desc "Run unit tests"
35
+ Rake::TestTask.new do |t|
36
+ t.libs << "test"
37
+ begin
38
+ require 'RMagick'
39
+ t.pattern = 'test/tc_*.rb'
40
+ rescue LoadError
41
+ t.test_files = ['tc_color.rb','tc_html.rb']
42
+ end
43
+ t.verbose = true
44
+ end
45
+
46
+ spec = Gem::Specification.new do |s|
47
+ s.platform = Gem::Platform::RUBY
48
+
49
+ s.name = PKG_NAME
50
+ s.summary = "Treemap visualization in ruby"
51
+ s.description = %q{Treemap visualization in ruby}
52
+ s.version = PKG_VERSION
53
+ s.author = "Martin Häger"
54
+ s.email = "martin.haeger@gmail.com"
55
+ s.homepage = "http://github.com/mtah/ruby-treemap"
56
+
57
+ s.has_rdoc = true
58
+ s.extra_rdoc_files = [ "README", "EXAMPLES" ]
59
+ s.rdoc_options = [ "--main", "README" ]
60
+ s.requirements << 'none'
61
+
62
+ s.require_path = 'lib'
63
+ s.autorequire = 'treemap'
64
+
65
+ s.files = [ "Rakefile", "TODO", "EXAMPLES", "README", "ChangeLog", "COPYING"]
66
+ s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
67
+ s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
68
+ end
69
+
70
+ Rake::GemPackageTask.new(spec) do |p|
71
+ p.need_tar = true
72
+ end
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ = RubyTreemap - TODO
2
+
3
+ * Lots of testing. Need to create solid unit tests.
4
+ * HTML output seems to be a few pixels off when drawing treemap squares.
5
+ * Better image output support. Need to add the ability to draw labels.
6
+ * Add option to pad squares in treemap.
data/lib/treemap.rb ADDED
@@ -0,0 +1,66 @@
1
+ #
2
+ # treemap.rb - RubyTreemap
3
+ #
4
+ # Copyright (c) 2006 by Andrew Bruno <aeb@qnot.org>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ #
12
+
13
+ require File.dirname(__FILE__) + '/treemap/node'
14
+ require File.dirname(__FILE__) + '/treemap/layout_base'
15
+ require File.dirname(__FILE__) + '/treemap/output_base'
16
+ require File.dirname(__FILE__) + '/treemap/color_base'
17
+ require File.dirname(__FILE__) + '/treemap/slice_layout'
18
+ require File.dirname(__FILE__) + '/treemap/squarified_layout'
19
+ require File.dirname(__FILE__) + '/treemap/html_output'
20
+ require File.dirname(__FILE__) + '/treemap/rectangle'
21
+ require File.dirname(__FILE__) + '/treemap/gradient_color'
22
+ require File.dirname(__FILE__) + '/treemap/svg_output'
23
+
24
+ # XXX these are still expirmental. Requires RMagick
25
+ # require File.dirname(__FILE__) + '/treemap/image_output'
26
+
27
+ module Treemap
28
+ VERSION = "0.0.3.1"
29
+
30
+ def Treemap::dump_tree(node)
31
+ puts "#{node.label}: #{node.bounds.to_s}"
32
+ node.children.each do |c|
33
+ dump_tree(c)
34
+ end
35
+ end
36
+
37
+ def Treemap::tree_from_xml(file)
38
+ doc = REXML::Document.new(file)
39
+ node_from_xml(doc.root)
40
+ end
41
+
42
+ def Treemap::node_from_xml(xmlnode)
43
+ node = Treemap::Node.new
44
+
45
+ node.label = xmlnode.attributes["label"]
46
+ id = xmlnode.attributes["id"]
47
+ if(!id.nil?)
48
+ node.id = id.to_s
49
+ end
50
+
51
+ node.size = xmlnode.attributes["size"]
52
+ node.size = node.size.to_f unless node.size.nil?
53
+
54
+ node.color = xmlnode.attributes["change"]
55
+ node.color = node.color.to_f unless node.color.nil?
56
+
57
+
58
+ xmlnode.elements.each do |c|
59
+ child = node_from_xml(c)
60
+ node.add_child(child) if !child.nil?
61
+ end
62
+
63
+ return nil if node.size < 5
64
+ node
65
+ end
66
+ end
@@ -0,0 +1,61 @@
1
+ #
2
+ # color_base.rb - RubyTreemap
3
+ #
4
+ # Copyright (c) 2006 by Andrew Bruno <aeb@qnot.org>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ #
12
+
13
+ module Treemap
14
+ class ColorBase
15
+ def get_hex_color(value)
16
+ # base classes override
17
+ end
18
+
19
+ def get_rgb_color(value)
20
+ # base classes override
21
+ end
22
+
23
+ def red(color)
24
+ color.sub!(/^#/, "")
25
+ color[0,2].hex
26
+ end
27
+
28
+ def green(color)
29
+ color.sub!(/^#/, "")
30
+ color[2,2].hex
31
+ end
32
+
33
+ def blue(color)
34
+ color.sub!(/^#/, "")
35
+ color[4,2].hex
36
+ end
37
+
38
+ def to_rgb(color)
39
+ [red(color), green(color), blue(color)]
40
+ end
41
+
42
+ def to_html(width=1, height=20)
43
+ html = "<div>"
44
+ index = @min
45
+
46
+ while(index <= @max)
47
+ html += "<span style=\"display: block; float: left;"
48
+ html += "width: " + width.to_s + "px;"
49
+ html += "height: " + height.to_s + "px;"
50
+ html += "background-color: #" + get_hex_color(index) + ";"
51
+ html += "\">"
52
+ html += "<img width=\"1\" height=\"1\" />"
53
+ html += "</span>"
54
+ index += @increment
55
+ end
56
+
57
+ html += "</div>"
58
+ html
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,71 @@
1
+ #
2
+ # gradient_color.rb - RubyTreemap
3
+ #
4
+ # Copyright (c) 2006 by Andrew Bruno <aeb@qnot.org>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ #
12
+
13
+ require File.dirname(__FILE__) + "/color_base"
14
+
15
+ class Treemap::GradientColor < Treemap::ColorBase
16
+ attr_accessor(:min, :max, :min_color, :mean_color, :max_color, :increment)
17
+
18
+ def initialize
19
+ @min = -100
20
+ @max = 100
21
+ @min_color = "FF0000" # red
22
+ @mean_color = "000000" # black
23
+ @max_color = "00FF00" # green
24
+ @increment = 1
25
+
26
+ yield self if block_given?
27
+
28
+ # XXX add in error checking. if min >= max, if colors aren't hex, etc.
29
+ @min = @min.to_f
30
+ @max = @max.to_f
31
+ @mean = (@min + @max) / 2.to_f
32
+ @slices = (1.to_f / @increment.to_f) * (@max - @mean).to_f
33
+ end
34
+
35
+ def get_hex_color(value)
36
+ value = @max if(value > @max)
37
+ vaue = @min if(value < @min)
38
+
39
+
40
+ r1, g1, b1 = to_rgb(@mean_color)
41
+ r2, g2, b2 = to_rgb(@min_color)
42
+ if(value >= @mean)
43
+ r2, g2, b2 = to_rgb(@max_color)
44
+ end
45
+
46
+ rfactor = ((r1 -r2).abs.to_f / @slices) * value.abs
47
+ gfactor = ((g1 -g2).abs.to_f / @slices) * value.abs
48
+ bfactor = ((b1 -b2).abs.to_f / @slices) * value.abs
49
+
50
+ newr = r1 + rfactor
51
+ if(r1 > r2)
52
+ newr = r1 - rfactor
53
+ end
54
+
55
+ newg = g1 + gfactor
56
+ if(g1 > g2)
57
+ newg = g1 - gfactor
58
+ end
59
+
60
+ newb = b1 + bfactor
61
+ if(b1 > b2)
62
+ newb = b1 - bfactor
63
+ end
64
+
65
+ sprintf("%02X", newr.round) + sprintf("%02X", newg.round) + sprintf("%02X", newb.round)
66
+ end
67
+
68
+ def get_rgb_color(value)
69
+ to_rgb(get_hex_color(value))
70
+ end
71
+ end
@@ -0,0 +1,173 @@
1
+ #
2
+ # html_output.rb - RubyTreemap
3
+ #
4
+ # Copyright (c) 2006 by Andrew Bruno <aeb@qnot.org>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ #
12
+
13
+ require 'cgi'
14
+ require File.dirname(__FILE__) + "/output_base"
15
+ require File.dirname(__FILE__) + "/slice_layout"
16
+
17
+ class Treemap::HtmlOutput < Treemap::OutputBase
18
+ attr_accessor(:full_html, :base_font_size, :center_labels_at_depth, :center_labels_at_z, :stylesheets, :javascripts, :metadata)
19
+
20
+ def initialize
21
+ super
22
+
23
+ # default options for HtmlOutput
24
+ @full_html = true
25
+ @center_labels_at_depth = nil
26
+ @center_labels_at_z = 100
27
+ @stylesheets = ""
28
+ @javascripts = ""
29
+ @base_font_size = 14
30
+
31
+ yield self if block_given?
32
+
33
+ @layout.position = :absolute
34
+ end
35
+
36
+ def default_css
37
+ css = <<CSS
38
+ .node {
39
+ border: 1px solid black;
40
+ }
41
+ .label {
42
+ color: #FFFFFF;
43
+ font-size: 11px;
44
+ }
45
+ .label-heading {
46
+ color: #FFFFFF;
47
+ font-size: 14pt;
48
+ font-weight: bold;
49
+ text-decoration: underline;
50
+ }
51
+ CSS
52
+ end
53
+
54
+ def node_label(node)
55
+ CGI.escapeHTML(node.label)
56
+ end
57
+
58
+ def node_color(node)
59
+ color = "#CCCCCC"
60
+
61
+ if(!node.color.nil?)
62
+ if(not Numeric === node.color)
63
+ color = node.color
64
+ else
65
+ color = "#" + @color.get_hex_color(node.color)
66
+ end
67
+ end
68
+
69
+ color
70
+ end
71
+
72
+ def to_html(node)
73
+ @bounds = self.bounds
74
+
75
+ @layout.process(node, @bounds)
76
+
77
+ draw_map(node)
78
+ end
79
+
80
+ def draw_map(node)
81
+ html = ""
82
+
83
+ if(@full_html)
84
+ html += "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
85
+ html += "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
86
+ html += "<html><head>"
87
+ html += "<title>Treemap - #{node_label(node)}</title>"
88
+ html += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
89
+ html += "<style type=\"text/css\">" + default_css + "</style>"
90
+ html += self.stylesheets
91
+ html += self.javascripts
92
+ html += "</head><body>"
93
+ end
94
+
95
+ html += draw_node(node)
96
+
97
+ if(@full_html)
98
+ html += "</body></html>"
99
+ end
100
+
101
+ html
102
+ end
103
+
104
+ def draw_label(node)
105
+ label= "<span"
106
+ if(!@center_labels_at_depth.nil? and @center_labels_at_depth == node.depth)
107
+ px_per_point = 20
108
+ label_size = node.label.length * px_per_point
109
+
110
+ label += " style=\""
111
+ label += "overflow: hidden; position: absolute;"
112
+ label += "margin-top: " + (node.bounds.height/2 - node.font_size(@base_font_size)/2).to_s + "px;"
113
+
114
+ label += "left: 0px; top: 0px; width: 100%; height: 100%; line-height: 100%; text-align: center;"
115
+ label += "z-index: #{@center_labels_at_z};"
116
+ label += "font-size:#{node.font_size(@base_font_size)}px;"
117
+ label += "\""
118
+ label += " class=\"label-heading\""
119
+ else
120
+ label += " class=\"label\""
121
+ label += " style=\"font-size:#{node.font_size(@base_font_size)}px\""
122
+ end
123
+ label += ">"
124
+ label += node_label(node)
125
+ label += "</span>"
126
+
127
+ label
128
+ end
129
+
130
+ # Subclass can override to add more html inside <div/> of node
131
+ def draw_node_body(node)
132
+ draw_label(node)
133
+ end
134
+
135
+ def draw_node(node)
136
+ return "" if node.bounds.nil?
137
+
138
+ root_class = node.depth == 0 ? " treemap-root" : ""
139
+
140
+ html = "<div id=\"node-#{node.id}\""
141
+ html += " style=\""
142
+ html += "overflow: hidden; position: absolute; display: inline;"
143
+ html += "left: #{node.bounds.x1}px; top: #{node.bounds.y1}px;"
144
+ html += "width: #{node.bounds.width}px; height: #{node.bounds.height}px;"
145
+ html += "background-color: " + node_color(node) + ";"
146
+ if(!@center_labels_at_depth.nil? and @center_labels_at_depth == node.depth)
147
+ html += "border: 1px solid black;"
148
+ end
149
+ html += "\" class=\"#{metadata(node)}node#{root_class}\""
150
+ html += ">"
151
+
152
+ html += draw_node_body(node)
153
+
154
+ if(!node.children.nil? and node.children.size > 0)
155
+ node.children.each do |c|
156
+ html += draw_node(c)
157
+ end
158
+ end
159
+
160
+ html += "</div>"
161
+ end
162
+
163
+ private
164
+ def metadata(node)
165
+ if node.object && @metadata
166
+ metadata = @metadata.map { |c| "'#{c}': '#{node.object.send(c).to_s}'" } || []
167
+
168
+ return "" if metadata.empty?
169
+
170
+ return "{#{metadata.join(',')}} "
171
+ end
172
+ end
173
+ end