bio-graphics 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/classes/Bio.html +135 -0
- data/doc/classes/Bio/Graphics.html +247 -0
- data/doc/classes/Bio/Graphics/Panel.html +344 -0
- data/doc/classes/Bio/Graphics/Panel.src/M000005.html +29 -0
- data/doc/classes/Bio/Graphics/Panel.src/M000006.html +19 -0
- data/doc/classes/Bio/Graphics/Panel.src/M000007.html +67 -0
- data/doc/classes/Bio/Graphics/Panel/Ruler.html +238 -0
- data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000008.html +20 -0
- data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000009.html +28 -0
- data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000010.html +54 -0
- data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000013.html +20 -0
- data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000014.html +28 -0
- data/doc/classes/Bio/Graphics/Panel/Ruler.src/M000015.html +59 -0
- data/doc/classes/Bio/Graphics/Panel/Track.html +342 -0
- data/doc/classes/Bio/Graphics/Panel/Track.src/M000008.html +23 -0
- data/doc/classes/Bio/Graphics/Panel/Track.src/M000009.html +42 -0
- data/doc/classes/Bio/Graphics/Panel/Track.src/M000010.html +285 -0
- data/doc/classes/Bio/Graphics/Panel/Track.src/M000011.html +23 -0
- data/doc/classes/Bio/Graphics/Panel/Track.src/M000012.html +43 -0
- data/doc/classes/Bio/Graphics/Panel/Track.src/M000013.html +259 -0
- data/doc/classes/Bio/Graphics/Panel/Track/Feature.html +292 -0
- data/doc/classes/Bio/Graphics/Panel/Track/Feature.src/M000011.html +65 -0
- data/doc/classes/Bio/Graphics/Panel/Track/Feature.src/M000014.html +65 -0
- data/doc/classes/Bio/Graphics/Panel/Track/Feature/PixelRange.html +155 -0
- data/doc/classes/Bio/Graphics/Panel/Track/Feature/PixelRange.src/M000012.html +18 -0
- data/doc/classes/Bio/Graphics/Panel/Track/Feature/PixelRange.src/M000015.html +18 -0
- data/doc/classes/ImageMap.html +185 -0
- data/doc/classes/ImageMap.src/M000001.html +18 -0
- data/doc/classes/ImageMap.src/M000002.html +24 -0
- data/doc/classes/ImageMap/ImageMapElement.html +187 -0
- data/doc/classes/ImageMap/ImageMapElement.src/M000003.html +19 -0
- data/doc/classes/ImageMap/ImageMapElement.src/M000004.html +20 -0
- data/doc/created.rid +1 -0
- data/doc/files/README_DEV.html +432 -0
- data/doc/files/TUTORIAL.html +358 -0
- data/doc/files/lib/bio-graphics_rb.html +121 -0
- data/doc/files/lib/bio/graphics/feature_rb.html +113 -0
- data/doc/files/lib/bio/graphics/image_map_rb.html +113 -0
- data/doc/files/lib/bio/graphics/panel_rb.html +113 -0
- data/doc/files/lib/bio/graphics/ruler_rb.html +113 -0
- data/doc/files/lib/bio/graphics/track_rb.html +113 -0
- data/doc/fr_class_index.html +35 -0
- data/doc/fr_file_index.html +34 -0
- data/doc/fr_method_index.html +41 -0
- data/doc/images/example.png +0 -0
- data/doc/images/glyph_showcase.png +0 -0
- data/doc/images/terms.png +0 -0
- data/doc/images/terms.svg +166 -0
- data/doc/index.html +24 -0
- data/images/example.png +0 -0
- data/images/glyph_showcase.png +0 -0
- data/images/terms.png +0 -0
- data/images/terms.svg +166 -0
- data/lib/bio-graphics.rb +18 -0
- data/lib/bio/graphics/feature.rb +136 -0
- data/lib/bio/graphics/image_map.rb +37 -0
- data/lib/bio/graphics/panel.rb +205 -0
- data/lib/bio/graphics/ruler.rb +96 -0
- data/lib/bio/graphics/track.rb +387 -0
- data/samples/arkdb_features.rb +37 -0
- data/samples/data.txt +32 -0
- data/samples/glyph_showcase.rb +29 -0
- metadata +137 -0
@@ -0,0 +1,387 @@
|
|
1
|
+
#
|
2
|
+
# = bio/graphics/track - track class
|
3
|
+
#
|
4
|
+
# Copyright:: Copyright (C) 2007
|
5
|
+
# Jan Aerts <jan.aerts@bbsrc.ac.uk>
|
6
|
+
# License:: The Ruby License
|
7
|
+
#
|
8
|
+
module Bio
|
9
|
+
module Graphics
|
10
|
+
class Panel
|
11
|
+
# The Bio::Graphics::Track class describes the container for features of
|
12
|
+
# the same type. See Bio::Graphics documentation for explanation of
|
13
|
+
# interplay between different classes.
|
14
|
+
class Track
|
15
|
+
# !!Not to be used directly. Use Bio::Graphics::Panel.add_track instead!!
|
16
|
+
# A track can not exist except within the confines of a
|
17
|
+
# Bio::Graphics::Panel object.
|
18
|
+
#
|
19
|
+
#--
|
20
|
+
# This is necessary because the track needs to know the rescale_factor
|
21
|
+
# and width of the picture, both of which are defined within the panel.
|
22
|
+
#++
|
23
|
+
#
|
24
|
+
# ---
|
25
|
+
# *Arguments*:
|
26
|
+
# * _panel_ (required) :: Bio::Graphics::Panel object that this track
|
27
|
+
# belongs to
|
28
|
+
# * _name_ (required) :: Name of the track to be displayed (e.g. 'genes')
|
29
|
+
# * _colour_ :: Colour to be used to draw the features within the track.
|
30
|
+
# Default = 'blue'
|
31
|
+
# * _glyph_ :: Glyph to use for drawing the features. Options are:
|
32
|
+
# 'generic', 'directed_generic', 'spliced, 'directed_spliced' and
|
33
|
+
# 'triangle'. Triangles can be used
|
34
|
+
# for features whose start and stop positions are the same (e.g. SNPs).
|
35
|
+
# If you try to draw a feature that is longer with triangles, an error
|
36
|
+
# will be shown.
|
37
|
+
# *Returns*:: Bio::Graphics::Track object
|
38
|
+
def initialize(panel, name, feature_colour = [0,0,1], feature_glyph = 'generic')
|
39
|
+
@panel = panel
|
40
|
+
@name = name
|
41
|
+
@feature_colour = feature_colour
|
42
|
+
@feature_glyph = feature_glyph
|
43
|
+
@features = Array.new
|
44
|
+
@number_of_times_bumped = 0
|
45
|
+
end
|
46
|
+
attr_accessor :panel, :name, :feature_colour, :feature_glyph, :features, :number_of_times_bumped, :height
|
47
|
+
|
48
|
+
# Adds a Bio::Graphics::Panel::Track::Feature to this track. A track contains
|
49
|
+
# features of the same type, e.g. (for sequence annotation:) genes,
|
50
|
+
# polymorphisms, ESTs, etc.
|
51
|
+
#
|
52
|
+
# est_track.add_feature('EST1','50..60')
|
53
|
+
# est_track.add_feature('EST2','52..73')
|
54
|
+
# est_track.add_feature('EST3','41..69')
|
55
|
+
# gene_track.add_feature('gene2','39..73')
|
56
|
+
#
|
57
|
+
# For spliced features:
|
58
|
+
# est_track.add_feature('EST4','join(34..53,153..191)')
|
59
|
+
#
|
60
|
+
# Or on the complement strand:
|
61
|
+
# est_track.add_feature('EST5','complement(join(34..53,153..191))')
|
62
|
+
#
|
63
|
+
# See the documentation in Bio::Locations for a full description of
|
64
|
+
# how locations can be defined.
|
65
|
+
#
|
66
|
+
# Features are only added if they are at least partly in the displayed
|
67
|
+
# region. If a feature is completely outside of the region, it's not
|
68
|
+
# added. If it should be only partly visible, it is added completely.
|
69
|
+
#
|
70
|
+
# ---
|
71
|
+
# *Arguments*:
|
72
|
+
# * _name_ (required) :: Name of the feature
|
73
|
+
# * _location_ :: String. Default: whole of panel, forward strand.
|
74
|
+
# * _link_ :: URL to link to for this glyph
|
75
|
+
# *Returns*:: Bio::Graphics::Track::Feature object that was created or nil
|
76
|
+
def add_feature(name, location_string = '1..' + @panel.length.to_s, link = nil)
|
77
|
+
if link == ''
|
78
|
+
link = nil
|
79
|
+
end
|
80
|
+
|
81
|
+
# Calculate the ultimate start and stop of the feature: the start
|
82
|
+
# of the first subfeature (e.g. exon) and the stop of the last one.
|
83
|
+
# The only reason we want to know these positions, is because we want
|
84
|
+
# to determine if the feature falls within the view of the image or
|
85
|
+
# not (see below).
|
86
|
+
location_object = Bio::Locations.new(location_string)
|
87
|
+
start = location_object.collect{|l| l.from}.min.to_i
|
88
|
+
stop = location_object.collect{|l| l.to}.max.to_i
|
89
|
+
|
90
|
+
# If the feature wouldn't show because it's not in the region we're
|
91
|
+
# looking at, don't bother storing the stuff. I think this makes huge
|
92
|
+
# speed and memory differences if you've got a chromosome with
|
93
|
+
# thousands of features.
|
94
|
+
if stop <= panel.display_start or start >= panel.display_stop
|
95
|
+
return nil
|
96
|
+
else #elsif start >= panel.display_start and stop <= panel.display_stop
|
97
|
+
@features.push(Bio::Graphics::Panel::Track::Feature.new(self, name, location_object, link))
|
98
|
+
return @features[-1]
|
99
|
+
end
|
100
|
+
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# Adds the track to a cairo drawing. This method should not be used
|
106
|
+
# directly by the user, but is called by Bio::Graphics::Panel.draw
|
107
|
+
# ---
|
108
|
+
# *Arguments*:
|
109
|
+
# * _paneldrawing_ (required) :: the panel cairo object
|
110
|
+
# * _verticaloffset_ (required) :: number of pixels to offset the track downwards,
|
111
|
+
# based on the height of other tracks that were drawn above it
|
112
|
+
# *Returns*:: FIXME: I don't know
|
113
|
+
def draw(panel_drawing, vertical_offset)
|
114
|
+
track_drawing = Cairo::Context.new(panel_drawing)
|
115
|
+
|
116
|
+
# Draw thin line above title
|
117
|
+
track_drawing.set_source_rgb(0.75,0.75,0.75)
|
118
|
+
track_drawing.move_to(0, vertical_offset)
|
119
|
+
track_drawing.line_to(panel.width, vertical_offset)
|
120
|
+
track_drawing.stroke
|
121
|
+
|
122
|
+
# Draw track title
|
123
|
+
track_drawing.set_source_rgb(0,0,0)
|
124
|
+
track_drawing.select_font_face('Georgia',1,1)
|
125
|
+
track_drawing.set_font_size(TRACK_HEADER_HEIGHT)
|
126
|
+
track_drawing.move_to(0,TRACK_HEADER_HEIGHT + vertical_offset + 10)
|
127
|
+
track_drawing.show_text(self.name)
|
128
|
+
|
129
|
+
# Draw the features
|
130
|
+
grid = Hash.new
|
131
|
+
|
132
|
+
track_drawing.save do
|
133
|
+
track_drawing.translate(0, vertical_offset + TRACK_HEADER_HEIGHT)
|
134
|
+
track_drawing.set_source_rgb(@feature_colour)
|
135
|
+
|
136
|
+
# Now draw the features
|
137
|
+
# These are the basic steps:
|
138
|
+
# A. find out what row to draw it on
|
139
|
+
# B. see if we want to change the glyph type from directed to
|
140
|
+
# undirected
|
141
|
+
# C. draw the thing
|
142
|
+
@features.each do |feature|
|
143
|
+
# Don't even bother if the feature is not in the view
|
144
|
+
if feature.stop <= self.panel.display_start or feature.start >= self.panel.display_stop
|
145
|
+
next
|
146
|
+
else
|
147
|
+
feature_drawn = false
|
148
|
+
|
149
|
+
# A. find out what row to draw it on
|
150
|
+
feature_range = (feature.start.floor..feature.stop.ceil)
|
151
|
+
row = 1
|
152
|
+
row_available = true
|
153
|
+
until feature_drawn
|
154
|
+
if ! grid[row].nil?
|
155
|
+
grid[row].each do |covered|
|
156
|
+
if feature_range.include?(covered.first) or covered.include?(feature_range.first)
|
157
|
+
row_available = false
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
if ! row_available
|
163
|
+
row += 1
|
164
|
+
row_available = true
|
165
|
+
else
|
166
|
+
if grid[row].nil?
|
167
|
+
grid[row] = Array.new
|
168
|
+
end
|
169
|
+
grid[row].push(feature_range)
|
170
|
+
|
171
|
+
# B. see if we want to change the glyph type from directed to
|
172
|
+
# undirected
|
173
|
+
# There are 2 cases where we don't want to draw arrows on
|
174
|
+
# features:
|
175
|
+
# (a) when the picture is really zoomed out, features are
|
176
|
+
# so small that the arrow itself is too big
|
177
|
+
# (b) if a directed feature on the fw strand extends beyond
|
178
|
+
# the end of the picture, the arrow is out of view. This
|
179
|
+
# is the same as considering the feature as undirected.
|
180
|
+
# The same obviously goes for features on the reverse
|
181
|
+
# strand that extend beyond the left side of the image.
|
182
|
+
#
|
183
|
+
# (a) Zoomed out
|
184
|
+
replace_directed_with_undirected = false
|
185
|
+
if (feature.stop - feature.start).to_f/panel.rescale_factor.to_f < 2
|
186
|
+
replace_directed_with_undirected = true
|
187
|
+
end
|
188
|
+
# (b) Extending beyond borders picture
|
189
|
+
if ( feature.chopped_at_stop and feature.strand = 1 ) or ( feature.chopped_at_start and feature.strand = -1 )
|
190
|
+
replace_directed_with_undirected = true
|
191
|
+
end
|
192
|
+
|
193
|
+
local_feature_glyph = nil
|
194
|
+
if feature_glyph == 'directed_generic' and replace_directed_with_undirected
|
195
|
+
local_feature_glyph = 'generic'
|
196
|
+
elsif feature_glyph == 'directed_spliced' and replace_directed_with_undirected
|
197
|
+
local_feature_glyph = 'spliced'
|
198
|
+
else
|
199
|
+
local_feature_glyph = feature_glyph
|
200
|
+
end
|
201
|
+
|
202
|
+
# C. And draw the thing.
|
203
|
+
top_pixel_of_feature = FEATURE_V_DISTANCE + (FEATURE_HEIGHT+FEATURE_V_DISTANCE)*row
|
204
|
+
bottom_pixel_of_feature = top_pixel_of_feature + FEATURE_HEIGHT
|
205
|
+
|
206
|
+
case local_feature_glyph
|
207
|
+
# triangles are typical for features which have a 1 bp position (start == stop)
|
208
|
+
when 'triangle'
|
209
|
+
raise "Start and stop are not the same (necessary if you want triangle glyphs)" if feature.start != feature.stop
|
210
|
+
|
211
|
+
# Need to get this for the imagemap
|
212
|
+
left_pixel_of_feature = feature.pixel_range_collection[0].start_pixel - 3
|
213
|
+
right_pixel_of_feature = feature.pixel_range_collection[0].stop_pixel + 3
|
214
|
+
track_drawing.move_to(left_pixel_of_feature + 3, top_pixel_of_feature)
|
215
|
+
track_drawing.rel_line_to(-3, FEATURE_HEIGHT)
|
216
|
+
track_drawing.rel_line_to(6, 0)
|
217
|
+
track_drawing.close_path.fill
|
218
|
+
|
219
|
+
when 'directed_generic'
|
220
|
+
# Need to get this for the imagemap
|
221
|
+
left_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel
|
222
|
+
right_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel
|
223
|
+
if feature.strand == -1 # Reverse strand
|
224
|
+
# Draw main box
|
225
|
+
track_drawing.rectangle(left_pixel_of_feature+FEATURE_ARROW_LENGTH, top_pixel_of_feature, right_pixel_of_feature - left_pixel_of_feature - FEATURE_ARROW_LENGTH, FEATURE_HEIGHT).fill
|
226
|
+
|
227
|
+
# Draw arrow
|
228
|
+
track_drawing.move_to(left_pixel_of_feature+FEATURE_ARROW_LENGTH, top_pixel_of_feature)
|
229
|
+
track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
230
|
+
track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
231
|
+
track_drawing.close_path.fill
|
232
|
+
|
233
|
+
else #default is forward strand
|
234
|
+
track_drawing.rectangle(left_pixel_of_feature, top_pixel_of_feature, right_pixel_of_feature - left_pixel_of_feature - FEATURE_ARROW_LENGTH, FEATURE_HEIGHT).fill
|
235
|
+
track_drawing.move_to(right_pixel_of_feature - FEATURE_ARROW_LENGTH, top_pixel_of_feature)
|
236
|
+
track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
237
|
+
track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
238
|
+
track_drawing.close_path.fill
|
239
|
+
end
|
240
|
+
when 'spliced'
|
241
|
+
gap_starts = Array.new
|
242
|
+
gap_stops = Array.new
|
243
|
+
|
244
|
+
# Need to get this for the imagemap
|
245
|
+
left_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel
|
246
|
+
right_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel
|
247
|
+
|
248
|
+
# First draw the parts
|
249
|
+
feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}.each do |pr|
|
250
|
+
track_drawing.rectangle(pr.start_pixel, top_pixel_of_feature, (pr.stop_pixel - pr.start_pixel), FEATURE_HEIGHT).fill
|
251
|
+
gap_starts.push(pr.stop_pixel)
|
252
|
+
gap_stops.push(pr.start_pixel)
|
253
|
+
end
|
254
|
+
|
255
|
+
# And then draw the connections in the gaps
|
256
|
+
# Start with removing the very first start and the very last stop.
|
257
|
+
gap_starts.sort!.pop
|
258
|
+
gap_stops.sort!.shift
|
259
|
+
|
260
|
+
gap_starts.length.times do |gap_number|
|
261
|
+
from = gap_starts[gap_number].to_f
|
262
|
+
to = gap_stops[gap_number].to_f
|
263
|
+
middle = from + ((to - from)/2)
|
264
|
+
track_drawing.move_to(from, top_pixel_of_feature+2)
|
265
|
+
track_drawing.line_to(middle, top_pixel_of_feature+7)
|
266
|
+
track_drawing.line_to(to, top_pixel_of_feature+2)
|
267
|
+
track_drawing.stroke
|
268
|
+
end
|
269
|
+
|
270
|
+
if feature.hidden_subfeatures_at_stop
|
271
|
+
from = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel
|
272
|
+
to = panel.width
|
273
|
+
track_drawing.move_to(from, top_pixel_of_feature+5)
|
274
|
+
track_drawing.line_to(to, top_pixel_of_feature+5)
|
275
|
+
track_drawing.stroke
|
276
|
+
end
|
277
|
+
|
278
|
+
if feature.hidden_subfeatures_at_start
|
279
|
+
from = 1
|
280
|
+
to = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel
|
281
|
+
track_drawing.move_to(from, top_pixel_of_feature+5)
|
282
|
+
track_drawing.line_to(to, top_pixel_of_feature+5)
|
283
|
+
track_drawing.stroke
|
284
|
+
end
|
285
|
+
|
286
|
+
when 'directed_spliced'
|
287
|
+
gap_starts = Array.new
|
288
|
+
gap_stops = Array.new
|
289
|
+
# First draw the parts
|
290
|
+
locations = feature.location.sort_by{|l| l.from}
|
291
|
+
|
292
|
+
# Need to get this for the imagemap
|
293
|
+
left_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel
|
294
|
+
right_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel
|
295
|
+
|
296
|
+
# Start with the one with the arrow
|
297
|
+
pixel_ranges = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}
|
298
|
+
range_with_arrow = nil
|
299
|
+
if feature.strand == -1 # reverse strand => box with arrow is first one
|
300
|
+
range_with_arrow = pixel_ranges.shift
|
301
|
+
track_drawing.rectangle((range_with_arrow.start_pixel)+FEATURE_ARROW_LENGTH, top_pixel_of_feature, range_with_arrow.stop_pixel - range_with_arrow.start_pixel, FEATURE_HEIGHT).fill
|
302
|
+
track_drawing.move_to(range_with_arrow.start_pixel+FEATURE_ARROW_LENGTH, top_pixel_of_feature)
|
303
|
+
track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
304
|
+
track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
305
|
+
track_drawing.close_path.fill
|
306
|
+
else # forward strand => box with arrow is last one
|
307
|
+
range_with_arrow = pixel_ranges.pop
|
308
|
+
track_drawing.rectangle(range_with_arrow.start_pixel-FEATURE_ARROW_LENGTH, top_pixel_of_feature, range_with_arrow.stop_pixel - range_with_arrow.start_pixel, FEATURE_HEIGHT).fill
|
309
|
+
track_drawing.move_to(range_with_arrow.stop_pixel-FEATURE_ARROW_LENGTH, top_pixel_of_feature)
|
310
|
+
track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
311
|
+
track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2)
|
312
|
+
end
|
313
|
+
gap_starts.push(range_with_arrow.stop_pixel)
|
314
|
+
gap_stops.push(range_with_arrow.start_pixel)
|
315
|
+
|
316
|
+
# And then add the others
|
317
|
+
pixel_ranges.each do |range|
|
318
|
+
track_drawing.rectangle(range.start_pixel, top_pixel_of_feature, range.stop_pixel - range.start_pixel, FEATURE_HEIGHT).fill
|
319
|
+
gap_starts.push(range.stop_pixel)
|
320
|
+
gap_stops.push(range.start_pixel)
|
321
|
+
end
|
322
|
+
|
323
|
+
# And then draw the connections in the gaps
|
324
|
+
# Start with removing the very first start and the very last stop.
|
325
|
+
gap_starts.sort!.pop
|
326
|
+
gap_stops.sort!.shift
|
327
|
+
|
328
|
+
gap_starts.length.times do |gap_number|
|
329
|
+
from = gap_starts[gap_number].to_f
|
330
|
+
to = gap_stops[gap_number].to_f
|
331
|
+
middle = from + ((to - from)/2)
|
332
|
+
track_drawing.move_to(from, top_pixel_of_feature+2)
|
333
|
+
track_drawing.line_to(middle, top_pixel_of_feature+7)
|
334
|
+
track_drawing.line_to(to, top_pixel_of_feature+2)
|
335
|
+
track_drawing.stroke
|
336
|
+
end
|
337
|
+
|
338
|
+
if feature.hidden_subfeatures_at_stop
|
339
|
+
from = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel
|
340
|
+
to = panel.width
|
341
|
+
track_drawing.move_to(from, top_pixel_of_feature+5)
|
342
|
+
track_drawing.line_to(to, top_pixel_of_feature+5)
|
343
|
+
track_drawing.stroke
|
344
|
+
end
|
345
|
+
|
346
|
+
if feature.hidden_subfeatures_at_start
|
347
|
+
from = 1
|
348
|
+
to = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel
|
349
|
+
track_drawing.move_to(from, top_pixel_of_feature+5)
|
350
|
+
track_drawing.line_to(to, top_pixel_of_feature+5)
|
351
|
+
track_drawing.stroke
|
352
|
+
end
|
353
|
+
|
354
|
+
else #treat as 'generic'
|
355
|
+
left_pixel_of_feature, right_pixel_of_feature = feature.pixel_range_collection[0].start_pixel, feature.pixel_range_collection[0].stop_pixel
|
356
|
+
track_drawing.rectangle(left_pixel_of_feature, top_pixel_of_feature, (right_pixel_of_feature - left_pixel_of_feature), FEATURE_HEIGHT).fill
|
357
|
+
end
|
358
|
+
|
359
|
+
# And add the region to the image map
|
360
|
+
if panel.clickable
|
361
|
+
# Comment: we have to add the vertical_offset and TRACK_HEADER_HEIGHT!
|
362
|
+
panel.image_map.elements.push(ImageMap::ImageMapElement.new(left_pixel_of_feature,
|
363
|
+
top_pixel_of_feature + vertical_offset + TRACK_HEADER_HEIGHT,
|
364
|
+
right_pixel_of_feature,
|
365
|
+
bottom_pixel_of_feature + vertical_offset + TRACK_HEADER_HEIGHT,
|
366
|
+
feature.link
|
367
|
+
))
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
feature_drawn = true
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
@number_of_times_bumped = ( grid.keys.length == 0 ) ? 1 : grid.keys.max + 1
|
380
|
+
|
381
|
+
return panel_drawing
|
382
|
+
end
|
383
|
+
|
384
|
+
end #Track
|
385
|
+
end #Panel
|
386
|
+
end #Graphics
|
387
|
+
end #Bio
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/bio-graphics'
|
2
|
+
|
3
|
+
#Initialize graphic for a nucleotide sequence of 4173015 bp, zooming in on the
|
4
|
+
#region 11111..3333333
|
5
|
+
my_panel = Bio::Graphics::Panel.new(4173015, 800, false, 11111, 3333333)
|
6
|
+
#my_panel = Bio::Graphics::Panel.new(4173015, 800, false, 1, 4173015)
|
7
|
+
|
8
|
+
#Create and configure tracks
|
9
|
+
scaffold_track = my_panel.add_track('scaffold')
|
10
|
+
marker_track = my_panel.add_track('marker')
|
11
|
+
clone_track = my_panel.add_track('clone')
|
12
|
+
|
13
|
+
scaffold_track.feature_colour = [1,0,0]
|
14
|
+
marker_track.feature_colour = [0,1,0]
|
15
|
+
marker_track.feature_glyph = 'triangle'
|
16
|
+
clone_track.feature_colour = [0,0,1]
|
17
|
+
|
18
|
+
# Add data to tracks
|
19
|
+
File.open('data.txt').each do |line|
|
20
|
+
line.chomp!
|
21
|
+
accession, type, start, stop = line.split(/\t/)
|
22
|
+
if type == 'scaffold'
|
23
|
+
if start.nil?
|
24
|
+
scaffold_track.add_feature(accession)
|
25
|
+
else
|
26
|
+
scaffold_track.add_feature(accession, start + '..' + stop)
|
27
|
+
end
|
28
|
+
|
29
|
+
elsif type == 'marker'
|
30
|
+
marker_track.add_feature(accession, ((start.to_i + stop.to_i)/2).to_s, 'http://www.thearkdb.org/arkdb/do/getMarkerDetail?accession=' + accession)
|
31
|
+
elsif type == 'clone'
|
32
|
+
clone_track.add_feature(accession, start + '..' + stop)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# And draw
|
37
|
+
my_panel.draw('my_panel.png')
|
data/samples/data.txt
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
test scaffold
|
2
|
+
ARKSCA00000496 scaffold 1766280 3140306
|
3
|
+
ARKSCA00000495 scaffold 1454125 1756279
|
4
|
+
ARKMKR00050962 marker 1547565 1547116
|
5
|
+
ARKMKR00049743 marker 1467043 1467548
|
6
|
+
ARKMKR00049753 marker 1293787 1294437
|
7
|
+
ARKMKR00033347 marker 976330 976530
|
8
|
+
ARKMKR00049788 marker 959182 959941
|
9
|
+
ARKMKR00049746 marker 3030496 3031106
|
10
|
+
ARKCLN00574951 clone 1454125 1743183
|
11
|
+
ARKCLN00580755 clone 1115673 1332008
|
12
|
+
ARKCLN00734502 clone 2680312 2880020
|
13
|
+
ARKCLN00637391 clone 1291903 1444124
|
14
|
+
ARKCLN00733777 clone 554351 745744
|
15
|
+
ARKCLN00516431 clone 1766280 2053569
|
16
|
+
ARKCLN00734156 clone 1973620 2155335
|
17
|
+
ARKCLN00605941 clone 1576502 1756279
|
18
|
+
ARKCLN00608810 clone 672169 860914
|
19
|
+
ARKCLN00565213 clone 348808 418529
|
20
|
+
ARKCLN00679752 clone 2321244 2563972
|
21
|
+
ARKCLN00538485 clone 2851750 3078918
|
22
|
+
ARKCLN00581628 clone 2966855 3140306
|
23
|
+
ARKCLN00511832 clone 1034205 1213124
|
24
|
+
ARKCLN00734700 clone 2566852 2709684
|
25
|
+
ARKCLN00534126 clone 1043517 1290570
|
26
|
+
ARKCLN00734007 clone 951813 1154597
|
27
|
+
ARKCLN00733988 clone 140268 332409
|
28
|
+
ARKCLN00589095 clone 800155 1015201
|
29
|
+
ARKCLN00635737 clone 265756 445381
|
30
|
+
ARKCLN00573316 clone 2226636 2363631
|
31
|
+
ARKCLN00494906 clone 2130193 2314107
|
32
|
+
ARKCLN00515356 clone 81435 254456
|