Class: Bio::Graphics::Panel::Track
- Inherits:
-
Object
- Object
- Bio::Graphics::Panel::Track
- Defined in:
- lib/bio/graphics/track.rb,
lib/bio/graphics/feature.rb
Overview
The Bio::Graphics::Track class describes the container for features of the same type. See Bio::Graphics documentation for explanation of interplay between different classes.
Defined Under Namespace
Classes: Feature
Instance Attribute Summary collapse
-
#feature_colour ⇒ Object
Returns the value of attribute feature_colour.
-
#feature_glyph ⇒ Object
Returns the value of attribute feature_glyph.
-
#features ⇒ Object
Returns the value of attribute features.
-
#height ⇒ Object
Returns the value of attribute height.
-
#name ⇒ Object
Returns the value of attribute name.
-
#number_of_times_bumped ⇒ Object
Returns the value of attribute number_of_times_bumped.
-
#panel ⇒ Object
Returns the value of attribute panel.
Instance Method Summary collapse
-
#add_feature(name, location_string = '1..' + @panel.length.to_s, link = nil) ⇒ Object
Adds a Bio::Graphics::Panel::Track::Feature to this track.
-
#draw(panel_drawing, vertical_offset) ⇒ Object
Adds the track to a cairo drawing.
-
#initialize(panel, name, feature_colour = [0,0,1], feature_glyph = 'generic') ⇒ Track
constructor
!!Not to be used directly.
Constructor Details
#initialize(panel, name, feature_colour = [0,0,1], feature_glyph = 'generic') ⇒ Track
!!Not to be used directly. Use Bio::Graphics::Panel.add_track instead!! A track can not exist except within the confines of a Bio::Graphics::Panel object.
– This is necessary because the track needs to know the rescale_factor and width of the picture, both of which are defined within the panel. ++
Arguments:
- panel (required)
-
Bio::Graphics::Panel object that this track
belongs to
- name (required)
-
Name of the track to be displayed (e.g. ‘genes’)
- colour
-
Colour to be used to draw the features within the track.
Default = ‘blue’
- glyph
-
Glyph to use for drawing the features. Options are:
‘generic’, ‘directed_generic’, ‘spliced, ’directed_spliced’ and ‘triangle’. Triangles can be used for features whose start and stop positions are the same (e.g. SNPs). If you try to draw a feature that is longer with triangles, an error will be shown.
- Returns
-
Bio::Graphics::Track object
38 39 40 41 42 43 44 45 |
# File 'lib/bio/graphics/track.rb', line 38 def initialize(panel, name, feature_colour = [0,0,1], feature_glyph = 'generic') @panel = panel @name = name @feature_colour = feature_colour @feature_glyph = feature_glyph @features = Array.new @number_of_times_bumped = 0 end |
Instance Attribute Details
#feature_colour ⇒ Object
Returns the value of attribute feature_colour.
46 47 48 |
# File 'lib/bio/graphics/track.rb', line 46 def feature_colour @feature_colour end |
#feature_glyph ⇒ Object
Returns the value of attribute feature_glyph.
46 47 48 |
# File 'lib/bio/graphics/track.rb', line 46 def feature_glyph @feature_glyph end |
#features ⇒ Object
Returns the value of attribute features.
46 47 48 |
# File 'lib/bio/graphics/track.rb', line 46 def features @features end |
#height ⇒ Object
Returns the value of attribute height.
46 47 48 |
# File 'lib/bio/graphics/track.rb', line 46 def height @height end |
#name ⇒ Object
Returns the value of attribute name.
46 47 48 |
# File 'lib/bio/graphics/track.rb', line 46 def name @name end |
#number_of_times_bumped ⇒ Object
Returns the value of attribute number_of_times_bumped.
46 47 48 |
# File 'lib/bio/graphics/track.rb', line 46 def number_of_times_bumped @number_of_times_bumped end |
#panel ⇒ Object
Returns the value of attribute panel.
46 47 48 |
# File 'lib/bio/graphics/track.rb', line 46 def panel @panel end |
Instance Method Details
#add_feature(name, location_string = '1..' + @panel.length.to_s, link = nil) ⇒ Object
Adds a Bio::Graphics::Panel::Track::Feature to this track. A track contains features of the same type, e.g. (for sequence annotation:) genes, polymorphisms, ESTs, etc.
est_track.add_feature('EST1','50..60')
est_track.add_feature('EST2','52..73')
est_track.add_feature('EST3','41..69')
gene_track.add_feature('gene2','39..73')
For spliced features:
est_track.add_feature('EST4','join(34..53,153..191)')
Or on the complement strand:
est_track.add_feature('EST5','complement(join(34..53,153..191))')
See the documentation in Bio::Locations for a full description of how locations can be defined.
Features are only added if they are at least partly in the displayed region. If a feature is completely outside of the region, it’s not added. If it should be only partly visible, it is added completely.
Arguments:
- name (required)
-
Name of the feature
- location
-
String. Default: whole of panel, forward strand.
- link
-
URL to link to for this glyph
- Returns
-
Bio::Graphics::Track::Feature object that was created or nil
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/bio/graphics/track.rb', line 76 def add_feature(name, location_string = '1..' + @panel.length.to_s, link = nil) if link == '' link = nil end # Calculate the ultimate start and stop of the feature: the start # of the first subfeature (e.g. exon) and the stop of the last one. # The only reason we want to know these positions, is because we want # to determine if the feature falls within the view of the image or # not (see below). location_object = Bio::Locations.new(location_string) start = location_object.collect{|l| l.from}.min.to_i stop = location_object.collect{|l| l.to}.max.to_i # If the feature wouldn't show because it's not in the region we're # looking at, don't bother storing the stuff. I think this makes huge # speed and memory differences if you've got a chromosome with # thousands of features. if stop <= panel.display_start or start >= panel.display_stop return nil else #elsif start >= panel.display_start and stop <= panel.display_stop @features.push(Bio::Graphics::Panel::Track::Feature.new(self, name, location_object, link)) return @features[-1] end return self end |
#draw(panel_drawing, vertical_offset) ⇒ Object
Adds the track to a cairo drawing. This method should not be used directly by the user, but is called by Bio::Graphics::Panel.draw
Arguments:
- paneldrawing (required)
-
the panel cairo object
- verticaloffset (required)
-
number of pixels to offset the track downwards,
based on the height of other tracks that were drawn above it
- Returns
-
FIXME: I don’t know
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
# File 'lib/bio/graphics/track.rb', line 113 def draw(panel_drawing, vertical_offset) track_drawing = Cairo::Context.new(panel_drawing) # Draw thin line above title track_drawing.set_source_rgb(0.75,0.75,0.75) track_drawing.move_to(0, vertical_offset) track_drawing.line_to(panel.width, vertical_offset) track_drawing.stroke # Draw track title track_drawing.set_source_rgb(0,0,0) track_drawing.select_font_face('Georgia',1,1) track_drawing.set_font_size(TRACK_HEADER_HEIGHT) track_drawing.move_to(0,TRACK_HEADER_HEIGHT + vertical_offset + 10) track_drawing.show_text(self.name) # Draw the features grid = Hash.new track_drawing.save do track_drawing.translate(0, vertical_offset + TRACK_HEADER_HEIGHT) track_drawing.set_source_rgb(@feature_colour) # Now draw the features # These are the basic steps: # A. find out what row to draw it on # B. see if we want to change the glyph type from directed to # undirected # C. draw the thing @features.each do |feature| # Don't even bother if the feature is not in the view if feature.stop <= self.panel.display_start or feature.start >= self.panel.display_stop next else feature_drawn = false # A. find out what row to draw it on feature_range = (feature.start.floor..feature.stop.ceil) row = 1 row_available = true until feature_drawn if ! grid[row].nil? grid[row].each do |covered| if feature_range.include?(covered.first) or covered.include?(feature_range.first) row_available = false end end end if ! row_available row += 1 row_available = true else if grid[row].nil? grid[row] = Array.new end grid[row].push(feature_range) # B. see if we want to change the glyph type from directed to # undirected # There are 2 cases where we don't want to draw arrows on # features: # (a) when the picture is really zoomed out, features are # so small that the arrow itself is too big # (b) if a directed feature on the fw strand extends beyond # the end of the picture, the arrow is out of view. This # is the same as considering the feature as undirected. # The same obviously goes for features on the reverse # strand that extend beyond the left side of the image. # # (a) Zoomed out replace_directed_with_undirected = false if (feature.stop - feature.start).to_f/panel.rescale_factor.to_f < 2 replace_directed_with_undirected = true end # (b) Extending beyond borders picture if ( feature.chopped_at_stop and feature.strand = 1 ) or ( feature.chopped_at_start and feature.strand = -1 ) replace_directed_with_undirected = true end local_feature_glyph = nil if feature_glyph == 'directed_generic' and replace_directed_with_undirected local_feature_glyph = 'generic' elsif feature_glyph == 'directed_spliced' and replace_directed_with_undirected local_feature_glyph = 'spliced' else local_feature_glyph = feature_glyph end # C. And draw the thing. top_pixel_of_feature = FEATURE_V_DISTANCE + (FEATURE_HEIGHT+FEATURE_V_DISTANCE)*row bottom_pixel_of_feature = top_pixel_of_feature + FEATURE_HEIGHT case local_feature_glyph # triangles are typical for features which have a 1 bp position (start == stop) when 'triangle' raise "Start and stop are not the same (necessary if you want triangle glyphs)" if feature.start != feature.stop # Need to get this for the imagemap left_pixel_of_feature = feature.pixel_range_collection[0].start_pixel - 3 right_pixel_of_feature = feature.pixel_range_collection[0].stop_pixel + 3 track_drawing.move_to(left_pixel_of_feature + 3, top_pixel_of_feature) track_drawing.rel_line_to(-3, FEATURE_HEIGHT) track_drawing.rel_line_to(6, 0) track_drawing.close_path.fill when 'directed_generic' # Need to get this for the imagemap left_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel right_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel if feature.strand == -1 # Reverse strand # Draw main box 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 # Draw arrow track_drawing.move_to(left_pixel_of_feature+FEATURE_ARROW_LENGTH, top_pixel_of_feature) track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) track_drawing.close_path.fill else #default is forward strand 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 track_drawing.move_to(right_pixel_of_feature - FEATURE_ARROW_LENGTH, top_pixel_of_feature) track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) track_drawing.close_path.fill end when 'spliced' gap_starts = Array.new gap_stops = Array.new # Need to get this for the imagemap left_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel right_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel # First draw the parts feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}.each do |pr| track_drawing.rectangle(pr.start_pixel, top_pixel_of_feature, (pr.stop_pixel - pr.start_pixel), FEATURE_HEIGHT).fill gap_starts.push(pr.stop_pixel) gap_stops.push(pr.start_pixel) end # And then draw the connections in the gaps # Start with removing the very first start and the very last stop. gap_starts.sort!.pop gap_stops.sort!.shift gap_starts.length.times do |gap_number| from = gap_starts[gap_number].to_f to = gap_stops[gap_number].to_f middle = from + ((to - from)/2) track_drawing.move_to(from, top_pixel_of_feature+2) track_drawing.line_to(middle, top_pixel_of_feature+7) track_drawing.line_to(to, top_pixel_of_feature+2) track_drawing.stroke end if feature.hidden_subfeatures_at_stop from = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel to = panel.width track_drawing.move_to(from, top_pixel_of_feature+5) track_drawing.line_to(to, top_pixel_of_feature+5) track_drawing.stroke end if feature.hidden_subfeatures_at_start from = 1 to = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel track_drawing.move_to(from, top_pixel_of_feature+5) track_drawing.line_to(to, top_pixel_of_feature+5) track_drawing.stroke end when 'directed_spliced' gap_starts = Array.new gap_stops = Array.new # First draw the parts locations = feature.location.sort_by{|l| l.from} # Need to get this for the imagemap left_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel right_pixel_of_feature = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel # Start with the one with the arrow pixel_ranges = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel} range_with_arrow = nil if feature.strand == -1 # reverse strand => box with arrow is first one range_with_arrow = pixel_ranges.shift 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 track_drawing.move_to(range_with_arrow.start_pixel+FEATURE_ARROW_LENGTH, top_pixel_of_feature) track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) track_drawing.close_path.fill else # forward strand => box with arrow is last one range_with_arrow = pixel_ranges.pop 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 track_drawing.move_to(range_with_arrow.stop_pixel-FEATURE_ARROW_LENGTH, top_pixel_of_feature) track_drawing.rel_line_to(FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) track_drawing.rel_line_to(-FEATURE_ARROW_LENGTH, FEATURE_HEIGHT/2) end gap_starts.push(range_with_arrow.stop_pixel) gap_stops.push(range_with_arrow.start_pixel) # And then add the others pixel_ranges.each do |range| track_drawing.rectangle(range.start_pixel, top_pixel_of_feature, range.stop_pixel - range.start_pixel, FEATURE_HEIGHT).fill gap_starts.push(range.stop_pixel) gap_stops.push(range.start_pixel) end # And then draw the connections in the gaps # Start with removing the very first start and the very last stop. gap_starts.sort!.pop gap_stops.sort!.shift gap_starts.length.times do |gap_number| from = gap_starts[gap_number].to_f to = gap_stops[gap_number].to_f middle = from + ((to - from)/2) track_drawing.move_to(from, top_pixel_of_feature+2) track_drawing.line_to(middle, top_pixel_of_feature+7) track_drawing.line_to(to, top_pixel_of_feature+2) track_drawing.stroke end if feature.hidden_subfeatures_at_stop from = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[-1].stop_pixel to = panel.width track_drawing.move_to(from, top_pixel_of_feature+5) track_drawing.line_to(to, top_pixel_of_feature+5) track_drawing.stroke end if feature.hidden_subfeatures_at_start from = 1 to = feature.pixel_range_collection.sort_by{|pr| pr.start_pixel}[0].start_pixel track_drawing.move_to(from, top_pixel_of_feature+5) track_drawing.line_to(to, top_pixel_of_feature+5) track_drawing.stroke end else #treat as 'generic' left_pixel_of_feature, right_pixel_of_feature = feature.pixel_range_collection[0].start_pixel, feature.pixel_range_collection[0].stop_pixel track_drawing.rectangle(left_pixel_of_feature, top_pixel_of_feature, (right_pixel_of_feature - left_pixel_of_feature), FEATURE_HEIGHT).fill end # And add the region to the image map if panel.clickable # Comment: we have to add the vertical_offset and TRACK_HEADER_HEIGHT! panel.image_map.elements.push(ImageMap::ImageMapElement.new(left_pixel_of_feature, top_pixel_of_feature + vertical_offset + TRACK_HEADER_HEIGHT, right_pixel_of_feature, bottom_pixel_of_feature + vertical_offset + TRACK_HEADER_HEIGHT, feature.link )) end feature_drawn = true end end end end end @number_of_times_bumped = ( grid.keys.length == 0 ) ? 1 : grid.keys.max + 1 return panel_drawing end |