bio-graphics 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|