bio-svgenes 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -7,8 +7,9 @@ source "http://rubygems.org"
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
9
  gem "shoulda", ">= 0"
10
- gem "bundler", "~> 1.0.0"
10
+ gem "bundler", ">= 1.0.0"
11
11
  gem "jeweler", "~> 1.6.4"
12
12
  gem "rcov", ">= 0"
13
13
  gem "bio", ">= 1.4.2"
14
+ gem "json", ">=1.7"
14
15
  end
@@ -7,6 +7,7 @@ GEM
7
7
  bundler (~> 1.0)
8
8
  git (>= 1.2.5)
9
9
  rake
10
+ json (1.7.7)
10
11
  rake (0.9.2.2)
11
12
  rcov (0.9.11)
12
13
  shoulda (2.11.3)
@@ -16,7 +17,8 @@ PLATFORMS
16
17
 
17
18
  DEPENDENCIES
18
19
  bio (>= 1.4.2)
19
- bundler (~> 1.0.0)
20
+ bundler (>= 1.0.0)
20
21
  jeweler (~> 1.6.4)
22
+ json (>= 1.7)
21
23
  rcov
22
24
  shoulda
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "bio-svgenes"
8
- s.version = "0.3.0"
8
+ s.version = "0.3.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Dan MacLean"]
12
- s.date = "2013-03-04"
12
+ s.date = "2013-03-05"
13
13
  s.description = "This bio-gem facilitates the creation of pretty, publication quality SVG images from feature data."
14
14
  s.email = "maclean.daniel@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -17,7 +17,6 @@ Gem::Specification.new do |s|
17
17
  "README.rdoc"
18
18
  ]
19
19
  s.files = [
20
- ".DS_Store",
21
20
  ".document",
22
21
  "Gemfile",
23
22
  "Gemfile.lock",
@@ -26,9 +25,7 @@ Gem::Specification.new do |s|
26
25
  "Rakefile",
27
26
  "VERSION",
28
27
  "bio-svgenes.gemspec",
29
- "doc/.DS_Store",
30
28
  "doc/Bio.html",
31
- "doc/Bio/.DS_Store",
32
29
  "doc/Bio/Graphics.html",
33
30
  "doc/Bio/Graphics/Glyph.html",
34
31
  "doc/Bio/Graphics/MiniFeature.html",
@@ -72,12 +69,9 @@ Gem::Specification.new do |s|
72
69
  "doc/js/searcher.js",
73
70
  "doc/rdoc.css",
74
71
  "doc/table_of_contents.html",
75
- "examples/.DS_Store",
76
72
  "examples/annotate_snps.rb",
77
73
  "examples/data.txt",
78
74
  "examples/draw_from_json.rb",
79
- "examples/drawn_from_json.svg",
80
- "examples/drawn_from_json2.svg",
81
75
  "examples/eg2.rb",
82
76
  "examples/example.rb",
83
77
  "examples/example.svg",
@@ -86,19 +80,15 @@ Gem::Specification.new do |s|
86
80
  "examples/get_coverage_in_windows.rb",
87
81
  "examples/make_example.rb",
88
82
  "examples/transcripts.gff",
89
- "lib/.DS_Store",
90
83
  "lib/bio-svgenes.rb",
91
- "lib/bio/.DS_Store",
92
84
  "lib/bio/graphics/glyph.rb",
93
85
  "lib/bio/graphics/mini_feature.rb",
94
86
  "lib/bio/graphics/page.rb",
95
87
  "lib/bio/graphics/primitive.rb",
96
88
  "lib/bio/graphics/svgee.rb",
97
89
  "lib/bio/graphics/track.rb",
98
- "test/gene.gff",
99
90
  "test/helper.rb",
100
91
  "test/json_config.json",
101
- "test/test_bio-svgenes.rb",
102
92
  "test/test_glyph.rb",
103
93
  "test/test_mini_feature.rb",
104
94
  "test/test_page.rb",
@@ -118,23 +108,26 @@ Gem::Specification.new do |s|
118
108
 
119
109
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
120
110
  s.add_development_dependency(%q<shoulda>, [">= 0"])
121
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
111
+ s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
122
112
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
123
113
  s.add_development_dependency(%q<rcov>, [">= 0"])
124
114
  s.add_development_dependency(%q<bio>, [">= 1.4.2"])
115
+ s.add_development_dependency(%q<json>, [">= 1.7"])
125
116
  else
126
117
  s.add_dependency(%q<shoulda>, [">= 0"])
127
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
118
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
128
119
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
129
120
  s.add_dependency(%q<rcov>, [">= 0"])
130
121
  s.add_dependency(%q<bio>, [">= 1.4.2"])
122
+ s.add_dependency(%q<json>, [">= 1.7"])
131
123
  end
132
124
  else
133
125
  s.add_dependency(%q<shoulda>, [">= 0"])
134
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
126
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
135
127
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
136
128
  s.add_dependency(%q<rcov>, [">= 0"])
137
129
  s.add_dependency(%q<bio>, [">= 1.4.2"])
130
+ s.add_dependency(%q<json>, [">= 1.7"])
138
131
  end
139
132
  end
140
133
 
@@ -1,10 +1,39 @@
1
1
  module Bio
2
2
  class Graphics
3
+ #A Glyph is an array of Primitive objects, holding information about the type of Glyph being created.
4
+ #Each different type of Glyph has different arguments, pertaining to how the Glyph will be drawn and the parameters provided to SVGEE.
5
+ #
6
+
3
7
  class Glyph
4
- attr_reader :glyphs
8
+
9
+ #The different type of Glyphs are:
10
+ #* generic
11
+ #* circle
12
+ #* directed
13
+ #* \down_triangle
14
+ #* \up_triangle
15
+ #* span
16
+ #* transcript
17
+ #* scale
18
+ #* label
19
+ #* histogram
20
+
21
+ attr_reader :glyphs
5
22
  #holds a load of definitions for glyphs .. a glyph is an array of primitives...
6
23
  @glyphs = [:generic, :directed, :transcript, :scale, :label, :histogram, :circle, :down_triangle, :up_triangle, :span]
7
-
24
+ #Creates a generic glyph, which is a rectangle
25
+ #
26
+ #+args+
27
+ #* height = the height of the Glyph (10)
28
+ #* fill_color = the fill colour of the Glyph ('red')
29
+ #* stroke = the outline colour of the Glyph ("black")
30
+ #* stroke_width = The width of the outline stroke (1)
31
+ #* x_round = x-axis radius of the ellipse used to round off the corners of the rectangle (1)
32
+ #* y_round = y-axis radius of the ellipse used to round off the corners of the rectangle (1)
33
+ #* style = the opacity of the fill color ("fill-opacity:0.4;")
34
+ #* x = the co-ordinates of the Glyph for the x-axis
35
+ #* y = the co-ordinates of the Glyph for the y-axis
36
+
8
37
  def self.generic(args) #:x, :y, :width :fill, :stroke :stroke_width, :style, :height,
9
38
  args = {
10
39
  :height => 10,
@@ -15,8 +44,19 @@ class Glyph
15
44
  :y_round => 1,
16
45
  :style => "fill-opacity:0.4;"}.merge!(args)
17
46
  [Bio::Graphics::Primitive.new(:rectangle, args)]
18
- end
19
-
47
+ end
48
+
49
+ #Creates a circular Glyph
50
+ #
51
+ #+args+
52
+ #* radius = the radius of the circle (10)
53
+ #* fill_color = the fill colour of the Glyph ('red')
54
+ #* stroke = the outline colour of the Glyph ("black")
55
+ #* stroke_width = The width of the outline stroke (1)
56
+ #* style = the opacity of the fill color
57
+ #* x = x-axis centre of the circle
58
+ #* y = y-axis centre of the circle
59
+
20
60
  def self.circle(args)
21
61
  args = {
22
62
  :radius => 10,
@@ -30,7 +70,18 @@ class Glyph
30
70
  [Bio::Graphics::Primitive.new(:circle, args)]
31
71
  end
32
72
 
33
-
73
+ #Creates a polygon Glyph to indicate the direction in which the Glyph is pointing
74
+ #+args+
75
+ #* width = the width of the feature
76
+ #* fill_color = the fill colour of the Glyph ('red')
77
+ #* stroke = the outline colour of the Glyph ("black")
78
+ #* stroke_width = The width of the outline stroke (1)
79
+ #* style = the opacity of the fill color
80
+ #* strand = the strand on which the Glyph is located. May be '+' or '-'
81
+ #* points = the x and y axis points used to calculate the shape of the polygon
82
+ #* x = the co-ordinates of the Glyph for the x-axis
83
+ #* y = the co-ordinates of the Glyph for the y-axis
84
+ #The points of the polygon are calculated form the +x+ and +y+ co-ordinates
34
85
  def self.directed(args) #:x, :y, :width :fill, :stroke :stroke_width, :style, :height
35
86
  args = {
36
87
 
@@ -43,11 +94,22 @@ class Glyph
43
94
  if args[:strand] == '-'
44
95
  args[:points] = "#{args[:x]},#{args[:y]} #{args[:x] + args[:width]},#{args[:y]} #{args[:x] + args[:width]},#{args[:y] + args[:height] } #{args[:x]},#{args[:y] + (args[:height])} #{args[:x] - (args[:height] * 0.2)},#{args[:y] + (args[:height]/2)}"
45
96
  else
46
- args[:points] = "#{args[:x]},#{args[:y]} #{args[:x] + args[:width] - (args[:height] * 0.2)},#{args[:y]} #{args[:x] + args[:width]},#{args[:y] + (args[:height]/2) } #{args[:x] + args[:width] - (args[:height] * 0.2)},#{args[:y] + args[:height]} #{args[:x]},#{args[:y] + args[:height]}"
97
+ args[:points] = "#{args[:x]},#{args[:y]} #{args[:x] + args[:width] - (args[:height] * 0.2)},#{args[:y]} #{args[:x] + args[:width]},#{args[:y] + (args[:height]/2) } #{args[:x] + args[:width] - (args[:height] * 0.2)},#{args[:y] + args[:height]} #{args[:x]},#{args[:y] + args[:height]}"
98
+
47
99
  end
48
100
  [Bio::Graphics::Primitive.new(:polygon, args)]
49
101
  end
50
-
102
+ #Creates a polygon Glyph for a downward-pointing triangle
103
+ #+args+
104
+ #* height = the height of the Glyph (10)
105
+ #* fill_color = the fill colour of the Glyph ('red')
106
+ #* stroke = the outline colour of the Glyph ("black")
107
+ #* stroke_width = The width of the outline stroke (1)
108
+ #* style = the opacity of the fill color ("fill-opacity:0.4;")
109
+ #* x = the co-ordinates of the Glyph for the x-axis
110
+ #* y = the co-ordinates of the Glyph for the y-axis
111
+ #The points of the triangle are calculated form the +x+ and +y+ co-ordinates
112
+
51
113
  def self.down_triangle(args) #:x, :y, :width :fill, :stroke :stroke_width, :style, :height
52
114
  args = {
53
115
 
@@ -56,10 +118,21 @@ class Glyph
56
118
  :stroke => "black",
57
119
  :stroke_width => 1,
58
120
  :style => "fill-opacity:0.4;"}.merge!(args)
121
+
59
122
  args[:points] = "#{args[:x]},#{args[:y]} #{args[:x] + args[:width]},#{args[:y]} #{ args[:x] + (args[:width]/2) },#{(args[:y] + args[:height]) }"
60
123
  [Bio::Graphics::Primitive.new(:polygon, args)]
61
124
  end
62
-
125
+ #Creates a polygon Glyph for an upward-pointing triangle
126
+ #+args+
127
+ #* height = the height of the Glyph (10)
128
+ #* fill_color = the fill colour of the Glyph ('red')
129
+ #* stroke = the outline colour of the Glyph ("black")
130
+ #* stroke_width = The width of the outline stroke (1)
131
+ #* style = the opacity of the fill color ("fill-opacity:0.4;")
132
+ #* x = the co-ordinates of the Glyph for the x-axis
133
+ #* y = the co-ordinates of the Glyph for the y-axis
134
+ #The points of the triangle are calculated form the +x+ and +y+ co-ordinates
135
+
63
136
  def self.up_triangle(args) #:x, :y, :width :fill, :stroke :stroke_width, :style, :height
64
137
  args = {
65
138
  :height => 10,
@@ -70,7 +143,16 @@ class Glyph
70
143
  args[:points] = "#{args[:x]},#{args[:y] + args[:height]} #{args[:x] + args[:width]},#{args[:y] + args[:height]} #{ args[:x] + (args[:width]/2) },#{args[:y] }"
71
144
  [Bio::Graphics::Primitive.new(:polygon, args)]
72
145
  end
73
-
146
+ #Creates a span glyph, which is a line
147
+ #
148
+ #+args+
149
+ #* height = the height of the Glyph (10)
150
+ #* fill_color = the fill colour of the Glyph ('red')
151
+ #* stroke = the outline colour of the Glyph ("black")
152
+ #* stroke_width = The width of the outline stroke (1)
153
+ #* style = the opacity of the fill color ("fill-opacity:0.4;")
154
+ #* x = the co-ordinates of the Glyph for the x-axis
155
+ #* y = the co-ordinates of the Glyph for the y-axis
74
156
  def self.span(args)
75
157
  args = {
76
158
  :height => 10,
@@ -85,7 +167,26 @@ class Glyph
85
167
  args[:y2] = args[:y]
86
168
  [Bio::Graphics::Primitive.new(:line, args)]
87
169
  end
88
-
170
+ #Creates a transcript glyph, which is a number of different types of Glyph, depending on the features
171
+ #within the transcript
172
+ #
173
+ #+args+
174
+ #* height = the height of the Glyph (10)
175
+ #* utr_fill_color = the fill colour of the Glyph ('black')
176
+ #* utr_stroke = the outline colour of the Glyph ("black")
177
+ #* utr_stroke_width = The width of the outline stroke (1)
178
+ #* exon_fill_color = the fill colour of the Glyph ('red')
179
+ #* exon_stroke = the outline colour of the Glyph ("black")
180
+ #* exon_stroke_width = The width of the outline stroke (1)
181
+ #* line_color = the colour for any line Glyphs
182
+ #* line_width = the width for any line Glyphs
183
+ #* exon_style = the opacity of the fill color for exons ("fill-opacity:0.4;")
184
+ #* utr_style = the opacity of the fill color for utrs
185
+ #* line_style = the opacity of the fill color for lines
186
+ #* block_gaps = ****I'm not sure what these are****
187
+ #* gap_marker = ****I'm not sure what these are****
188
+ #* x = the co-ordinates of the Glyph for the x-axis
189
+ #* y = the co-ordinates of the Glyph for the y-axis
89
190
  def self.transcript(args)
90
191
  args = {
91
192
  :height => 10,
@@ -178,10 +279,19 @@ class Glyph
178
279
  end
179
280
  composite
180
281
  end
181
-
282
+ #Creates the scale across the top of the SVG page
283
+ #
284
+ #+args+
285
+ #* start = the start of the scale
286
+ #* stop = the end of the scale
287
+ #* \number_of_intervals = the number of tick-marks on the scale to show the current position
288
+ #* page_width = the width of the page
289
+ #
290
+ #+returns+
291
+ #
292
+ #* An Array[http://www.ruby-doc.org/core-2.0/Array.html] of Primitive objects (of type 'line', 'rectangle' and 'text')
293
+
182
294
  def self.scale(args)
183
-
184
-
185
295
  first_mark = args[:start]
186
296
  last_mark = args[:stop]
187
297
  #(num.to_f / @nt_per_px_x.to_f)
@@ -217,7 +327,15 @@ class Glyph
217
327
  end
218
328
  return a
219
329
  end
220
-
330
+ #Creates a label Glyph to write text
331
+ #
332
+ #+args+
333
+ #* text = the text to write
334
+ #* fill = the colour of the text ("black")
335
+ #* style = the style of writing ("font-family:monospace;")
336
+ #* x = the co-ordinates of the Glyph for the x-axis
337
+ #* y = the co-ordinates of the Glyph for the y-axis
338
+
221
339
  def self.label(args)
222
340
  [Bio::Graphics::Primitive.new(:text,
223
341
  :text => args[:text],
@@ -226,11 +344,26 @@ class Glyph
226
344
  :fill => "black",
227
345
  :style => "font-family:monospace;")]
228
346
  end
229
-
347
+ #The list of pre-defined gradients
230
348
  def self.gradients #needs to know which of its gradients are predefined
231
349
  [:red_white_h, :green_white_h, :blue_white_h, :yellow_white_h, :red_white_radial, :green_white_radial, :blue_white_radial, :yellow_white_radial ]
232
350
  end
233
-
351
+
352
+ #Sets the the type (linear or radial) and colour of gradient for a pre-defined gradient
353
+ #along with the pertinent parameters for that type
354
+ #
355
+ #+args+
356
+ #* gradient = a pre-defined gradient
357
+ #The types of gradient are:
358
+ #* red_white_h
359
+ #* green_white_h
360
+ #* blue_white_h
361
+ #* yellow_white_h
362
+ #* red_white_radial
363
+ #* green_white_radial
364
+ #* blue_white_radial
365
+ #* yellow_white_radial
366
+
234
367
  def self.gradient(gradient)
235
368
  type, color = case gradient
236
369
  when :red_white_h
@@ -1,7 +1,30 @@
1
1
  module Bio
2
2
  class Graphics
3
+ ##
4
+ #The MiniFeature class represents any feature (e.g. a gene, transcript, exon, start codon, etc) on a track.
5
+ #
3
6
  class MiniFeature
4
7
  attr_accessor :start, :end, :strand, :exons, :utrs, :block_gaps, :segment_height, :id
8
+ #Creates a new MiniFeature
9
+ #
10
+ #_Arguments_
11
+ #* start = the start position of the feature
12
+ #* end = the end position of the feature
13
+ #* strand = the strand of the feature
14
+ #* exons = an array of exon positions
15
+ #* utrs = an array of utrs positions
16
+ #* \block_gaps = an array of regions with nothing to be drawn, e.g. introns
17
+ #* id = the name of the feature such as the gene name or transcript ID
18
+ #* \segment_height = the height of the current feature
19
+ #
20
+ #==Example usage
21
+ #
22
+ ##@mini1 = Bio::Graphics::MiniFeature.new(:start=>3631, :end=>5899, :strand=>'+',
23
+ # :exons=>[4000, 4500, 4700, 5000], :utrs=>[3631, 3650], :segment_height=>5, :id=>"AT1G01010")
24
+ #
25
+ #===MiniFeatures and Tracks
26
+ #MiniFeatures are added to Track objects, with overlapping MiniFeatures being placed onto separate rows
27
+ #
5
28
  def initialize(args)
6
29
  @start = args[:start]
7
30
  @end = args[:end]
@@ -1,290 +1,343 @@
1
1
  module Bio
2
2
  class Graphics
3
- class Page
4
-
5
- def initialize(args)
6
- @height = args[:height]
7
- @width = args[:width]
8
- args[:style] = "background-color:#{args[:background_color]};" if args[:background_color]
9
- @svg = SVGEE.new(args)
10
- @scale_start = 1.0/0.0
11
- @scale_stop = -1.0/0.0
12
- @tracks = [] #array of track objects with loads of features in it...
13
- @nt_per_percent = 1;
14
- @num_intervals = args[:number_of_intervals]
15
- @track_top = 30
16
-
17
- def @svg.update_height(height)
18
- @height = height
19
- end
20
-
21
- #def @svg.update_width(width)
22
- # @width = width
23
- #end
24
- end
25
-
26
- def self.from_json(args)
27
- require 'rubygems'
28
- require 'json'
29
- data = JSON.parse(File.open(args[:json], 'r').read)
30
- p = Page.new(:width => data["Page"]["width"],
31
- :height => data["Page"]["height"],
32
- :number_of_intervals => data["Page"]["intervals"])
33
- data["Tracks"].each do |track|
34
- #prep the track args
35
- track_args = track.dup
36
- track_args.delete("file")
37
- track_args.delete("file_type")
38
- track_args = track_args.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
39
- ##convert any of the pre-made gradient strings in the keys to symbol
40
- track_args.each_key do |k|
41
- next unless track_args[k].respond_to?(:to_sym)
42
- track_args[k] = track_args[k].to_sym if Glyph.gradients.include?(track_args[k].to_sym)
3
+
4
+ ##
5
+ #The Page class represents the top level container on which svg objects are written. It will contain a scale
6
+ #and all the tracks along with their features. The scale will start at the genomic co-ordinates of the start
7
+ #of the first feature and stop at the end of the last feature)
8
+ #
9
+
10
+
11
+ class Page
12
+ #Creates a new Page object.
13
+ #
14
+ #A new Page contains the following arguments:
15
+ #* height = the height of the page
16
+ #* width = the width of the page
17
+ #* style = includes the a background colour
18
+ #* svg = a new SVGEE object
19
+ #* scale_start = the scale-start of the page (1.0/0.0)
20
+ #* scale_stop = the scale-stop of the page (-1.0/0.0)
21
+ #* tracks = an array of track objects with loads of features in it...
22
+ #* nt_per_percent = the number of nucleotides that are represented by 1% of the page (1);
23
+ #* num_intervals = the number of intervals
24
+ #* track_top = the position of the top of the first track (30)
25
+ #
26
+ #A new page maybe set up as follows:
27
+ # @page = Bio::Graphics::Page.new(:width => 800,
28
+ # :height => 110, :number_of_intervals => 10)
29
+ #
30
+ def initialize(args)
31
+ @height = args[:height]
32
+ @width = args[:width]
33
+ args[:style] = "background-color:#{args[:background_color]};" if args[:background_color]
34
+ @svg = SVGEE.new(args)
35
+ @scale_start = 1.0/0.0
36
+ @scale_stop = -1.0/0.0
37
+ @tracks = [] #array of track objects with loads of features in it...
38
+ @nt_per_percent = 1;
39
+ @num_intervals = args[:number_of_intervals]
40
+ @track_top = 30
41
+
42
+ def @svg.update_height(height)
43
+ @height = height
44
+ end
45
+
46
+ #def @svg.update_width(width)
47
+ # @width = width
48
+ #end
43
49
  end
50
+ #Takes a custom-written json file and writes an svg page to file which will contain the given features described within the json file.
51
+ #* +args+ - a custom-written json file describing the parameters and resources needed for a new page
44
52
 
45
- svg_track = p.add_track(track_args)
46
- features = [] ##set up the features...
47
-
48
- case track["file_type"] ##deal with the gff and data files
49
- when "gff"
50
- groups = {}
51
- parentless_features = []
52
- Page.parse_gff(track["file"]).each do |gff| #gets features in a list and links their children to them as members of the array at the key
53
- parent_id = gff.attributes.select { |a| a.first == 'Parent'}
54
- if parent_id.empty?
55
- parentless_features << gff
56
- else
57
- if groups[parent_id.first.last].nil?
58
- groups[parent_id.first.last] = []
59
- groups[parent_id.first.last] << gff
60
- else
61
- groups[parent_id.first.last] << gff
62
- end
53
+ def self.from_json(args)
54
+ require 'rubygems'
55
+ require 'json'
56
+ data = JSON.parse(File.open(args[:json], 'r').read)
57
+ p = Page.new(:width => data["Page"]["width"],
58
+ :height => data["Page"]["height"],
59
+ :number_of_intervals => data["Page"]["intervals"])
60
+ data["Tracks"].each do |track|
61
+ #prep the track args
62
+ track_args = track.dup
63
+ track_args.delete("file")
64
+ track_args.delete("file_type")
65
+ track_args = track_args.inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo }
66
+ ##convert any of the pre-made gradient strings in the keys to symbol
67
+ track_args.each_key do |k|
68
+ next unless track_args[k].respond_to?(:to_sym)
69
+ track_args[k] = track_args[k].to_sym if Glyph.gradients.include?(track_args[k].to_sym)
63
70
  end
64
- end
65
- #now flick through the parentless features and add any exons / UTRs
66
- parentless_features.each do |plf|
67
- require 'pp'
68
- pp parentless_features
69
- gff_id = plf.attributes.select {|a| a.first == 'ID'}
70
- gff_id = gff_id.first.last
71
- exons = []
72
- utrs = []
73
- children = groups[gff_id] || children = []
74
- children.each do |child|
75
- if child.feature == 'exon' or child.feature == 'CDS'
76
- exons += [child.start,child.end]
77
- elsif child.feature =~ /utr/i
78
- utrs += [child.start,child.end]
71
+
72
+ svg_track = p.add_track(track_args)
73
+ features = [] ##set up the features...
74
+
75
+ case track["file_type"] ##deal with the gff and data files
76
+ when "gff"
77
+ groups = {}
78
+ parentless_features = []
79
+ Page.parse_gff(track["file"]).each do |gff| #gets features in a list and links their children to them as members of the array at the key
80
+ parent_id = gff.attributes.select { |a| a.first == 'Parent' }
81
+ if parent_id.empty?
82
+ parentless_features << gff
83
+ else
84
+ if groups[parent_id.first.last].nil?
85
+ groups[parent_id.first.last] = []
86
+ groups[parent_id.first.last] << gff
87
+ else
88
+ groups[parent_id.first.last] << gff
89
+ end
90
+ end
79
91
  end
80
- end
81
- features << MiniFeature.new(:start => plf.start, :end => plf.end, :exons => exons, :utrs => utrs, :strand => plf.strand, :id => gff_id)
82
- end #parentless features end
83
- when "data"
84
- ##each line is a data feature
85
- File.open(track["file"], "r").each do |line|
86
- start,stop,value = line.split(/\t/)
87
- features << MiniFeature.new(:start => start.to_i, :end => stop.to_i, :segment_height => value.to_f)
92
+ #now flick through the parentless features and add any exons / UTRs
93
+ parentless_features.each do |plf|
94
+ require 'pp'
95
+ #pp parentless_features
96
+ gff_id = plf.attributes.select { |a| a.first == 'ID' }
97
+ gff_id = gff_id.first.last
98
+ exons = []
99
+ utrs = []
100
+ children = groups[gff_id] || children = []
101
+ children.each do |child|
102
+ if child.feature == 'exon' or child.feature == 'CDS'
103
+ exons += [child.start, child.end]
104
+ elsif child.feature =~ /utr/i
105
+ utrs += [child.start, child.end]
106
+ end
107
+ end
108
+ features << MiniFeature.new(:start => plf.start, :end => plf.end, :exons => exons, :utrs => utrs, :strand => plf.strand, :id => gff_id)
109
+ end #parentless features end
110
+ when "data"
111
+ ##each line is a data feature
112
+ File.open(track["file"], "r").each do |line|
113
+ start, stop, value = line.split(/\t/)
114
+ features << MiniFeature.new(:start => start.to_i, :end => stop.to_i, :segment_height => value.to_f)
115
+ end
116
+ end #data end
117
+ features.each { |f| svg_track.add(f) }
118
+ end #track end
119
+ p.write(args[:outfile])
120
+ end
121
+
122
+ #Parses a GFF file into an Array of Bio::GFF::GFF3::Record[http://bioruby.org/rdoc/Bio/GFF/GFF3/Record.html] objects
123
+ #* +gff_file+ - a GFF-formatted file
124
+ #* +returns+ - an Array of Bio::GFF::GFF3::Record[http://bioruby.org/rdoc/Bio/GFF/GFF3/Record.html] objects
125
+ def self.parse_gff(gff_file)
126
+ require 'bio'
127
+ a = []
128
+ File.open(gff_file).each do |line|
129
+ next if line =~ /^#/
130
+ a << Bio::GFF::GFF3::Record.new(line)
88
131
  end
89
- end #data end
90
- features.each {|f| svg_track.add(f) }
91
- end #track end
92
- p.write(args[:outfile])
93
- end
94
-
95
- def self.parse_gff(gff_file)
96
- require 'bio'
97
- a = []
98
- File.open( gff_file ).each do |line|
99
- next if line =~ /^#/
100
- a << Bio::GFF::GFF3::Record.new(line)
101
- end
102
- a
103
- end
104
-
105
- def add_track(args)
106
- #sort out the colour/gradient options
107
- [:fill_color, :exon_fill_color, :utr_fill_color].each do |colour_tag|
108
- if Glyph.gradients.include?(args[colour_tag])
109
- @svg.gradient(Glyph.gradient(args[colour_tag]) )
110
- args[colour_tag] = "url(##{args[colour_tag]})"
111
- elsif
112
- args[colour_tag].instance_of?(Hash)
113
- @svg.gradient(args[colour_tag])
114
- args[colour_tag] = "url(##{args[colour_tag][:id]})"
132
+ a
115
133
  end
116
- end
117
- @tracks << Track.new(args)
118
- return @tracks.last
119
- end
120
-
121
- def get_limits
122
- @tracks.each do |track|
123
- lowest = track.features.sort {|x,y| x.start <=> y.start}.first.start
124
- highest = track.features.sort {|x,y| y.end <=> x.end}.first.end
125
- @scale_start = lowest if lowest < @scale_start
126
- @scale_stop = highest if highest > @scale_stop
127
- @nt_per_px_x = (@scale_stop - @scale_start).to_f / @width.to_f
128
- end
129
- end
130
-
131
- def draw_scale
132
- Glyph.scale(:start => @scale_start,
133
- :stop => @scale_stop,
134
- :number_of_intervals => @num_intervals, :page_width => @width).each {|g| @svg.add_primitive(g)}
135
- end
136
-
137
- def draw_label(args)
138
- Glyph.label(:text => args[:text],
139
- :x => args[:x],
140
- :y => args[:y] ).each {|g| @svg.add_primitive(g)}
141
- end
142
-
143
- def draw_generic(args) #remember presentation info comes from track@args when the track is defined
144
- Glyph.generic(args).each {|g| @svg.add_primitive(g) }
145
- end
146
-
147
- def draw_directed(args)
148
- Glyph.directed(args).each {|g| @svg.add_primitive(g) }
149
- end
150
-
151
- def draw_circle(args)
152
- Glyph.circle(args).each {|g| @svg.add_primitive(g) }
153
- end
154
-
155
- def draw_transcript(args)
156
- Glyph.transcript(args).each {|g| @svg.add_primitive(g) }
157
- end
158
-
159
- def draw_histogram(args)
160
- Glyph.generic(args).each {|g| @svg.add_primitive(g) }
161
- end
162
-
163
- def draw_up_triangle(args)
164
- Glyph.up_triangle(args).each {|g| @svg.add_primitive(g) }
165
- end
166
134
 
167
- def draw_down_triangle(args)
168
- Glyph.down_triangle(args).each {|g| @svg.add_primitive(g) }
169
- end
135
+ #Calculates the colour or gradient for the supplied Bio::Graphics::Track objects and adds them to the Track array for the current Page
136
+ #* +args+ - an Array of Bio::Graphics::Track objects
137
+ #* +returns+ - the last element of the Track array
138
+ def add_track(args)
139
+ #sort out the colour/gradient options
140
+ [:fill_color, :exon_fill_color, :utr_fill_color].each do |colour_tag|
141
+ if Glyph.gradients.include?(args[colour_tag])
142
+ @svg.gradient(Glyph.gradient(args[colour_tag]))
143
+ args[colour_tag] = "url(##{args[colour_tag]})"
144
+ elsif args[colour_tag].instance_of?(Hash)
145
+ @svg.gradient(args[colour_tag])
146
+ args[colour_tag] = "url(##{args[colour_tag][:id]})"
147
+ end
148
+ end
149
+ @tracks << Track.new(args)
150
+ return @tracks.last
151
+ end
170
152
 
171
- def draw_span(args)
172
- Glyph.span(args).each {|g| @svg.add_primitive(g) }
173
- end
174
-
175
- def draw_features(track) #sort out the input information into a user friendly format..
176
- if [:histogram, "histogram"].include?(track.glyph) #do different stuff for data tracks...
153
+ #Calculates the Page scale-start and scale-stop position and the nucleotides per pixel for the current Page
154
+ def get_limits
155
+ @tracks.each do |track|
156
+ lowest = track.features.sort { |x, y| x.start <=> y.start }.first.start
157
+ highest = track.features.sort { |x, y| y.end <=> x.end }.first.end
158
+ @scale_start = lowest if lowest < @scale_start
159
+ @scale_stop = highest if highest > @scale_stop
160
+ @nt_per_px_x = (@scale_stop - @scale_start).to_f / @width.to_f
161
+ end
162
+ end
177
163
 
178
- y = @track_top + (track.track_height)
179
- max = track.max_y ? track.max_y : track.features.sort {|a,b| a.segment_height <=> b.segment_height}.last.segment_height
180
- min = 0
181
- if track.label
182
- draw_label(:text => track.name, :y => @track_top += 30, :x => 3 )
164
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
165
+ def draw_scale
166
+ Glyph.scale(:start => @scale_start,
167
+ :stop => @scale_stop,
168
+ :number_of_intervals => @num_intervals, :page_width => @width).each { |g| @svg.add_primitive(g) }
169
+ end
170
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
171
+ #* +args+ - an Array of Bio::Graphics::Primitive object
172
+ def draw_label(args)
173
+ Glyph.label(:text => args[:text],
174
+ :x => args[:x],
175
+ :y => args[:y]).each { |g| @svg.add_primitive(g) }
176
+ end
177
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
178
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
179
+ def draw_generic(args) #remember presentation info comes from track@args when the track is defined
180
+ Glyph.generic(args).each { |g| @svg.add_primitive(g) }
181
+ end
182
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
183
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
184
+ def draw_directed(args)
185
+ Glyph.directed(args).each { |g| @svg.add_primitive(g) }
186
+ end
187
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
188
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
189
+ def draw_circle(args)
190
+ Glyph.circle(args).each { |g| @svg.add_primitive(g) }
183
191
  end
184
- draw_label(:text => max, :x => to_px(@scale_stop - @scale_start ) + 5, :y => @track_top + 5)
185
- draw_label(:text => min, :x => to_px(@scale_stop - @scale_start ) + 5, :y => @track_top + track.track_height + 5)
186
- data_interval = max - min
187
- data_per_px = track.track_height.to_f / data_interval.to_f
188
- track.features.each do |f|
189
- x = to_px(f.start - @scale_start)
190
- width = to_px( (f.end - @scale_start) - (f.start - @scale_start) )
191
- height = f.segment_height.to_f * data_per_px
192
- y = @track_top + track.track_height - height + 5
193
- #$stderr.puts f.segment_height, data_per_px, data_interval, max, min, "------"
194
- self.send("draw_#{track.glyph}", {:x => x,
195
- :y => y,
196
- :width => width,
197
- :height => height }.merge!(track.args) )
192
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
193
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
194
+ def draw_transcript(args)
195
+ Glyph.transcript(args).each { |g| @svg.add_primitive(g) }
198
196
  end
199
- @track_top += (track.track_height) + 20
200
- else ##following stuff for the features
201
- if track.label
202
- draw_label(:text => track.name, :y => @track_top += 30, :x => 3 )
197
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
198
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
199
+ def draw_histogram(args)
200
+ Glyph.generic(args).each { |g| @svg.add_primitive(g) }
201
+ end
202
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
203
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
204
+ def draw_up_triangle(args)
205
+ Glyph.up_triangle(args).each { |g| @svg.add_primitive(g) }
206
+ end
207
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
208
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
209
+ def draw_down_triangle(args)
210
+ Glyph.down_triangle(args).each { |g| @svg.add_primitive(g) }
211
+ end
212
+ #Adds the Bio::Graphics::Primitive objects to the SVGEE object
213
+ #* +args+ - an Array of Bio::Graphics::Primitive objects of the stated type
214
+ def draw_span(args)
215
+ Glyph.span(args).each { |g| @svg.add_primitive(g) }
203
216
  end
204
- track.get_rows ##work out how many rows and which features belong in each row...
205
- track.features.each_with_index do |f,index|
206
- x = to_px(f.start - @scale_start) #bottom left of feature
207
- all_sub_blocks = []
208
217
 
209
- #convert the exon and utr start stops to px start stops and px widths
210
- exons = []
211
- if f.exons
212
- f.exons.each_slice(2).each do |exon|
213
- all_sub_blocks << exon
214
- next if exon.nil?
215
- exons << [to_px(exon[0] - @scale_start), to_px( (exon[1] - @scale_start) - (exon[0] - @scale_start) ) ]
216
- end
217
- end
218
- f.exons = exons
218
+ #Takes a Bio::Graphics::Track object and sorts out the input information into a user friendly format.
219
+ #
220
+ #It examines the the type of track and calculates the required parameters
221
+ #* +args+ - an Array of Bio::Graphics::Track object
222
+ def draw_features(track)
223
+ if [:histogram, "histogram"].include?(track.glyph) #do different stuff for data tracks...
219
224
 
220
- utrs = []
221
- if f.utrs
222
- f.utrs.each_slice(2).each do |utr|
223
- all_sub_blocks << utr
224
- next if utr.nil?
225
- utrs << [to_px(utr[0] - @scale_start), to_px( (utr[1] - @scale_start) - (utr[0] - @scale_start) ) ]
225
+ y = @track_top + (track.track_height)
226
+ max = track.max_y ? track.max_y : track.features.sort { |a, b| a.segment_height <=> b.segment_height }.last.segment_height
227
+ min = 0
228
+ if track.label
229
+ draw_label(:text => track.name, :y => @track_top += 30, :x => 3)
226
230
  end
227
- end
228
- f.utrs = utrs
231
+ draw_label(:text => max, :x => to_px(@scale_stop - @scale_start) + 5, :y => @track_top + 5)
232
+ draw_label(:text => min, :x => to_px(@scale_stop - @scale_start) + 5, :y => @track_top + track.track_height + 5)
233
+ data_interval = max - min
234
+ data_per_px = track.track_height.to_f / data_interval.to_f
235
+ track.features.each do |f|
236
+ x = to_px(f.start - @scale_start)
237
+ width = to_px((f.end - @scale_start) - (f.start - @scale_start))
238
+ height = f.segment_height.to_f * data_per_px
239
+ y = @track_top + track.track_height - height + 5
240
+ #$stderr.puts f.segment_height, data_per_px, data_interval, max, min, "------"
241
+ self.send("draw_#{track.glyph}", {:x => x,
242
+ :y => y,
243
+ :width => width,
244
+ :height => height}.merge!(track.args))
245
+ end
246
+ @track_top += (track.track_height) + 20
247
+ else ##following stuff for the features
248
+ if track.label
249
+ draw_label(:text => track.name, :y => @track_top += 30, :x => 3)
250
+ end
251
+ track.get_rows ##work out how many rows and which features belong in each row...
252
+ track.features.each_with_index do |f, index|
253
+ x = to_px(f.start - @scale_start) #bottom left of feature
254
+ all_sub_blocks = []
255
+
256
+ #convert the exon and utr start stops to px start stops and px widths
257
+ exons = []
258
+ if f.exons
259
+ f.exons.each_slice(2).each do |exon|
260
+ all_sub_blocks << exon
261
+ next if exon.nil?
262
+ exons << [to_px(exon[0] - @scale_start), to_px((exon[1] - @scale_start) - (exon[0] - @scale_start))]
263
+ end
264
+ end
265
+ f.exons = exons
266
+
267
+ utrs = []
268
+ if f.utrs
269
+ f.utrs.each_slice(2).each do |utr|
270
+ all_sub_blocks << utr
271
+ next if utr.nil?
272
+ utrs << [to_px(utr[0] - @scale_start), to_px((utr[1] - @scale_start) - (utr[0] - @scale_start))]
273
+ end
274
+ end
275
+ f.utrs = utrs
276
+
277
+ #if there are any intron like gaps.. get where they would be
278
+ if not all_sub_blocks.empty?
279
+ all_sub_blocks = all_sub_blocks.sort { |a, b| a.first <=> b.first }
280
+ all_sub_blocks.each_index do |i|
281
+ next if i + 1 == all_sub_blocks.length or all_sub_blocks[i].last >= all_sub_blocks[i + 1].first #skip if there is no gap
282
+ f.block_gaps << [to_px(all_sub_blocks[i].last - @scale_start), to_px((all_sub_blocks[i + 1].first - @scale_start) - (all_sub_blocks[i].last - @scale_start))]
283
+ end
284
+ end
229
285
 
230
- #if there are any intron like gaps.. get where they would be
231
- if not all_sub_blocks.empty?
232
- all_sub_blocks = all_sub_blocks.sort {|a,b| a.first <=> b.first}
233
- all_sub_blocks.each_index do |i|
234
- next if i + 1 == all_sub_blocks.length or all_sub_blocks[i].last >= all_sub_blocks[i + 1].first #skip if there is no gap
235
- f.block_gaps << [to_px(all_sub_blocks[i].last - @scale_start) , to_px( (all_sub_blocks[i + 1].first - @scale_start) - (all_sub_blocks[i].last - @scale_start) ) ]
286
+ width = to_px((f.end - @scale_start) - (f.start - @scale_start))
287
+ if track.min_width and width < track.min_width
288
+ width = track.min_width
289
+ end
290
+ y = @track_top + (track.feature_rows[index] * 2 * track.feature_height)
291
+
292
+ self.send("draw_#{track.glyph}", {:x => x,
293
+ :y => y,
294
+ :width => width,
295
+ :strand => f.strand,
296
+ :exons => f.exons,
297
+ :utrs => f.utrs,
298
+ :block_gaps => f.block_gaps,
299
+ :height => track.feature_height
300
+ }.merge!(track.args))
301
+
302
+ if f.id
303
+ draw_label(:y => y, :x => x + width +2, :text => f.id)
304
+ end
236
305
  end
306
+ @track_top += (track.feature_height * track.number_rows * 2) + 20
237
307
  end
238
308
 
239
- width = to_px( (f.end - @scale_start) - (f.start - @scale_start) )
240
- if track.min_width and width < track.min_width
241
- width = track.min_width
242
- end
243
- y = @track_top + (track.feature_rows[index] * 2 * track.feature_height)
309
+ @height = @track_top + 100 #fudge...
310
+ @svg.update_height(@height)
311
+ #@svg.update_width(@width + 200)
244
312
 
245
- self.send("draw_#{track.glyph}", {:x => x,
246
- :y => y,
247
- :width => width,
248
- :strand => f.strand,
249
- :exons => f.exons,
250
- :utrs => f.utrs,
251
- :block_gaps => f.block_gaps,
252
- :height => track.feature_height
253
- }.merge!(track.args) )
254
-
255
- if f.id
256
- draw_label(:y => y, :x => x + width +2, :text => f.id)
313
+ end
314
+ #Provides the number of pixels required for a given number of nucleotides, e.g the length of a certain exon
315
+ #* +num+ - the number of nucleotides
316
+ #* +returns+ - the required number of pixels to draw the object
317
+ def to_px(num)
318
+ (num.to_f / @nt_per_px_x.to_f)
319
+ end
320
+
321
+ #Pulls together all track information to create svg text which will draw all the features on the page to scale
322
+ #Uses the methods +get_limits+ and +draw_scale+ and then +draw_feature+ for each track on the page
323
+ def get_markup
324
+ get_limits
325
+ draw_scale
326
+ @tracks.each do |track|
327
+ draw_features(track)
257
328
  end
329
+ @svg.draw
330
+ end
331
+ #Prints out the svg text
332
+ def draw
333
+ puts get_markup
258
334
  end
259
- @track_top += (track.feature_height * track.number_rows * 2) + 20
260
- end
261
-
262
- @height = @track_top + 100 #fudge...
263
- @svg.update_height(@height)
264
- #@svg.update_width(@width + 200)
265
335
 
266
- end
267
-
268
- def to_px(num)
269
- (num.to_f / @nt_per_px_x.to_f)
270
- end
271
-
272
- def get_markup
273
- get_limits
274
- draw_scale
275
- @tracks.each do |track|
276
- draw_features(track)
336
+ #Writes the svg text to a file
337
+ #* +file+ - the file to which the svg text should be written
338
+ def write(file)
339
+ File.open(file, 'w').write(get_markup)
340
+ end
277
341
  end
278
- @svg.draw
279
- end
280
-
281
- def draw
282
- puts get_markup
283
- end
284
-
285
- def write(file)
286
- File.open(file, 'w').write(get_markup)
287
342
  end
288
- end
289
- end
290
343
  end