Class: Bio::Graphics::Feature

Inherits:
Object
  • Object
show all
Defined in:
lib/bio/graphics/feature.rb

Overview

The Bio::Graphics::Feature class describes features to be placed on the graph. See Bio::Graphics documentation for explanation of interplay between different classes.

The position of the Feature is a Bio::Locations object to make it possible to transparently work with simple and spliced features.

The Bio::Graphics::Feature class inherits from Bio::Feature.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(track, feature_object, opts = {}) ⇒ Feature

!!Not to be used directly. Use Bio::Graphics::Track.add_feature instead!! A feature can not exist except within the confines of a Bio::Graphics::Track object.

– This is necessary because the feature needs to know the colour and glyph, both of which are defined within the panel. ++


Arguments:

  • track (required)

    Bio::Graphics::Track object that this

    feature belongs to

  • feature object (required)

    A Bio::Feature object (see bioruby)

  • :label

    Label of the feature. Default = ‘anonymous’

  • :link

    URL for clickable images. Default = nil

  • :glyph

    Glyph to use. Default = glyph of the track

  • :colour

    Colour. Default = colour of the track

Returns

Bio::Graphics::Feature object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/bio/graphics/feature.rb', line 39

def initialize(track, feature_object, opts = {})
  @track = track
  @feature_object = feature_object
  opts = {
    :label => 'anonymous',
    :link => nil,
    :glyph => @track.glyph,
    :colour => @track.colour
  }.merge(opts)
  
  @label = opts[:label]
  @link = opts[:link]
  @glyph = opts[:glyph]
  @colour = opts[:colour]
  
  @locations = @feature_object.locations

  @start = @locations.collect{|l| l.from}.min.to_i
  @stop = @locations.collect{|l| l.to}.max.to_i

  # Create Bio::Graphics SubFeatures
  # The drawing is handled by subfeatures. If there are no defined, the
  # subfeatures array will just hold one element: the @feature_object of
  # self.
  @subfeatures = Array.new
  if ! @feature_object.subfeatures.empty?
    @feature_object.subfeatures.each do |subfeature|
      @subfeatures.push(Bio::Graphics::SubFeature.new(self, subfeature, :glyph => @glyph, :colour => @colour))
    end
  else
    @subfeatures.push(Bio::Graphics::SubFeature.new(self, @feature_object, :glyph => @glyph, :colour => @colour))
  end

  @left_pixel_of_subfeatures = Array.new
  @right_pixel_of_subfeatures = Array.new
end

Instance Attribute Details

#colourObject

The colour to use to draw this (sub)feature



98
99
100
# File 'lib/bio/graphics/feature.rb', line 98

def colour
  @colour
end

#feature_objectObject

The bioruby Bio::Feature object



77
78
79
# File 'lib/bio/graphics/feature.rb', line 77

def feature_object
  @feature_object
end

#glyphObject

The glyph to use to draw this (sub)feature



95
96
97
# File 'lib/bio/graphics/feature.rb', line 95

def glyph
  @glyph
end

#labelObject Also known as: name

The label of the feature



88
89
90
# File 'lib/bio/graphics/feature.rb', line 88

def label
  @label
end

#left_pixel_of_featureObject

Returns the value of attribute left_pixel_of_feature.



101
102
103
# File 'lib/bio/graphics/feature.rb', line 101

def left_pixel_of_feature
  @left_pixel_of_feature
end

#left_pixel_of_subfeaturesObject

Returns the value of attribute left_pixel_of_subfeatures.



102
103
104
# File 'lib/bio/graphics/feature.rb', line 102

def left_pixel_of_subfeatures
  @left_pixel_of_subfeatures
end

The URL to be followed when the glyph for this feature is clicked



92
93
94
# File 'lib/bio/graphics/feature.rb', line 92

def link
  @link
end

#locationsObject

Returns the value of attribute locations.



79
80
81
# File 'lib/bio/graphics/feature.rb', line 79

def locations
  @locations
end

#right_pixel_of_subfeaturesObject

Returns the value of attribute right_pixel_of_subfeatures.



102
103
104
# File 'lib/bio/graphics/feature.rb', line 102

def right_pixel_of_subfeatures
  @right_pixel_of_subfeatures
end

#startObject

Returns the value of attribute start.



100
101
102
# File 'lib/bio/graphics/feature.rb', line 100

def start
  @start
end

#stopObject

Returns the value of attribute stop.



100
101
102
# File 'lib/bio/graphics/feature.rb', line 100

def stop
  @stop
end

#subfeaturesObject

The Bio::Graphics SubFeatures



82
83
84
# File 'lib/bio/graphics/feature.rb', line 82

def subfeatures
  @subfeatures
end

#top_pixel_of_featureObject

Returns the value of attribute top_pixel_of_feature.



101
102
103
# File 'lib/bio/graphics/feature.rb', line 101

def top_pixel_of_feature
  @top_pixel_of_feature
end

#trackObject

The track that this feature belongs to



85
86
87
# File 'lib/bio/graphics/feature.rb', line 85

def track
  @track
end

#vertical_offsetObject

Returns the value of attribute vertical_offset.



104
105
106
# File 'lib/bio/graphics/feature.rb', line 104

def vertical_offset
  @vertical_offset
end

Instance Method Details

#draw(panel_destination) ⇒ Object

Adds the feature to the track cairo context. This method should not be used directly by the user, but is called by Bio::Graphics::Track.draw


Arguments:

  • track_drawing (required)

    the track cairo object

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
# File 'lib/bio/graphics/feature.rb', line 113

def draw(panel_destination)
  feature_context = Cairo::Context.new(panel_destination)

  # Move the feature drawing down based on track it's in and the number
  # of times is has to be bumped
  row = self.find_row

  @vertical_offset = self.track.vertical_offset + Bio::Graphics::TRACK_HEADER_HEIGHT + Bio::Graphics::FEATURE_V_DISTANCE
  @vertical_offset += (Bio::Graphics::FEATURE_HEIGHT+Bio::Graphics::FEATURE_V_DISTANCE)*row
  
  feature_context.translate(0, @vertical_offset)

  # Let the subfeatures do the drawing.
  @subfeatures.each do |subfeature|
    subfeature.draw(feature_context)
  end

  @left_pixel_of_feature = @left_pixel_of_subfeatures.min
  @right_pixel_of_feature = @right_pixel_of_subfeatures.max
  
  # Add the label for the feature
  if @track.show_label
    pango_layout = feature_context.create_pango_layout
    pango_layout.text = @label
    fdesc = Pango::FontDescription.new('Sans Serif')
    fdesc.set_size(8 * Pango::SCALE)
    pango_layout.font_description = fdesc

    text_range = @start.floor..(@start.floor + pango_layout.pixel_size[0]*@track.panel.rescale_factor)
    if @track.grid[row+1].nil?
      @track.grid[row+1] = Array.new
    end
    @track.grid[row].push(text_range)
    @track.grid[row+1].push(text_range)
    feature_context.move_to(@left_pixel_of_feature, Bio::Graphics::TRACK_HEADER_HEIGHT)
    feature_context.set_source_rgb(0,0,0)
    feature_context.show_pango_layout(pango_layout)
    feature_context.set_source_rgb(@colour)
  end


  # And add the region to the image map
  # Comment: we have to add the vertical_offset and TRACK_HEADER_HEIGHT!
  @track.panel.image_map.add_element(@left_pixel_of_feature,
                                     @vertical_offset,
                                     @right_pixel_of_feature,
                                     @vertical_offset + Bio::Graphics::FEATURE_HEIGHT,
                                     @link
                                     )
end

#find_rowObject

Calculates the row within the track where this feature should be drawn. This method should not be used directly by the user, but is called by Bio::Graphics::Feature.draw


Arguments

none

Returns

row number



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
# File 'lib/bio/graphics/feature.rb', line 171

def find_row
  row_found = false

  # We've got to find out what row to draw the feature on. If two 
  # features overlap, one of them has to be 'bumped' down. So we'll
  # first try to draw a new feature at the top of the track. If
  # it however would overlap with another one, we'll bump it down
  # to the next row.
  feature_range = (@start.floor - 1..@stop.ceil + 1)
  row = 1
  row_available = true
  until row_found
    if ! @track.grid[row].nil?
      @track.grid[row].each do |covered|
        if feature_range.include?(covered.first) or covered.include?(feature_range.first)
          row_available = false
        end
      end
      if ! @track.grid[row+1].nil? #Still have to check if there is no label there.
        @track.grid[row+1].each do |covered|
          if feature_range.include?(covered.first) or covered.include?(feature_range.first)
            row_available = false
          end
        end
      end
    end

    if ! row_available
      row += 1
      row_available = true
    else # We've found the place where to draw the feature.
      if @track.grid[row].nil?
        @track.grid[row] = Array.new
      end
      @track.grid[row].push(feature_range)
      row_found = true
    end
  end
  return row
end