Class: Bioroebe::SVG::Page

Inherits:
Object
  • Object
show all
Defined in:
lib/bioroebe/svg/page.rb

Overview

Bioroebe::SVG::Page

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(i) ⇒ Page

#

initialize

Creates a new Page object.

Arguments:

height: this is the minimum height of the rendered page.
width:  this is the minimum width of the rendered page.
background_color: this is the background color of the page
                  (default is none), can be any SVG colour
                  eg rgb(256,0,0) or #FF0000.
#


47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/bioroebe/svg/page.rb', line 47

def initialize(i)
  @height = i[:height]
  @width  = i[:width]
  i[:style] = "background-color:#{i[:background_color]};" if i[:background_color]
  # ======================================================================= #
  # === @svg
  #
  # Instantiate a new SVGEE object next.
  # ======================================================================= #
  @svg = SVGEE.new(i)
  @num_intervals = i[:number_of_intervals]
  @svg.update_height(@height)
  reset
end

Class Method Details

.from_json(args) ⇒ Object

#

Bioroebe::SVG::Page.from_json

Takes a custom-written json file and uses it to generate an SVG document based on the information in that page.

  • :json a JSON file describing the parameters and files needed to build

an SVG document

#


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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
# File 'lib/bioroebe/svg/page.rb', line 93

def self.from_json(args)
  require 'json'
  data = JSON.parse(File.open(args[:json], 'r').read)
  p = Page.new(:width => data['Page']['width'],
               :height => data['Page']['height'],
               :number_of_intervals => data['Page']['intervals'])
  data['Tracks'].each { |track|
    # Prepare the tracks-arguments.
    track_args = track.dup
    track_args.delete('file')
    track_args.delete('file_type')
    track_args = track_args.inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo }
    # ===================================================================== #
    # Convert any of the pre-made gradient strings in the keys
    # to symbol.
    # ===================================================================== #
    track_args.each_key { |k|
      next unless track_args[k].respond_to?(:to_sym)
      track_args[k] = track_args[k].to_sym if Glyph.gradients.include?(track_args[k].to_sym)
    }
    svg_track = p.add_track(track_args)
    features = [] ##set up the features...
    case track['file_type'] # Deal with the gff and data files.
    # ===================================================================== #
    # === gff
    # ===================================================================== #
    when 'gff'
      groups = {}
      parentless_features = []
      Page.parse_gff(track['file']).each { |gff| #gets features in a list and links their children to them as members of the array at the key
        parent_id = gff.attributes.select { |a| a.first == 'Parent' }
        if parent_id.empty?
          parentless_features << gff
        else
          if groups[parent_id.first.last].nil?
            groups[parent_id.first.last] = []
            groups[parent_id.first.last] << gff
          else
            groups[parent_id.first.last] << gff
          end
        end
      }
      # =================================================================== #
      # Now flick through the parentless features and add any
      # exons or UTRs.
      # =================================================================== #
      parentless_features.each { |plf|
        gff_id = plf.attributes.select { |a| a.first == 'ID' }
        gff_id = gff_id.first.last
        exons = []
        utrs = []
        children = groups[gff_id] || children = []
        children.each { |child|
          case child.feature
          when 'exon','CDS'
            exons += [child.start, child.end]
          when /utr/i # Handle UTRs here.
            utrs += [child.start, child.end]
          end
        }
        features << MiniFeature.new(
          :start  => plf.start,
          :end    => plf.end,
          :exons  => exons,
          :utrs   => utrs,
          :strand => plf.strand,
          :id     => gff_id
        )
      } # parentless features end
    # ===================================================================== #
    # === data
    # ===================================================================== #
    when 'data'
      # =================================================================== #
      # Each line is a data feature.
      # =================================================================== #
      File.open(track['file'], 'r').each { |line|
        start, stop, value = line.split(/\t/)
        features << MiniFeature.new(
          :start          => start.to_i,
          :end            => stop.to_i,
          :segment_height => value.to_f
        )
      }
    end
    features.each { |f| svg_track.add(f) }
  } # track end
  p.write(args[:outfile])
end

.parse_gff(gff_file = HOME_DIRECTORY_OF_USER_X+'DATA/PROGRAMMING_LANGUAGES/RUBY/src/bioroebe/test/test_gene.gff') ⇒ Object

#

Page.parse_gff

This method will parse a GFF file into an Array of Bio::GFF::GFF3::Record objects

#


364
365
366
367
368
369
370
371
372
373
374
# File 'lib/bioroebe/svg/page.rb', line 364

def self.parse_gff(
    gff_file = HOME_DIRECTORY_OF_USER_X+'DATA/PROGRAMMING_LANGUAGES/RUBY/src/bioroebe/test/test_gene.gff'
  )
  require 'bio' # This depends on 'bio' gem for now, unortunately.
  a = []
  File.open(gff_file).each { |line|
    next if line =~ /^#/
    a << Bio::GFF::GFF3::Record.new(line)
  }
  a
end

Instance Method Details

#add_track(args) ⇒ Object

#

add_track

Adds a new Bioroebe::SVG::Track object to the current Bioroebe::SVG::Page object.

Arguments:

  • :glyph = one of Bioroebe::SVG::Glyphs#glyphs currently

:generic, :directed, :transcript, :scale, :label, :histogram, :circle, :down_triangle, :up_triangle, :span
  • :stroke_color = the outline colour of the glyphs in the track (default = “black”), can be any SVG colour eg rgb(256,0,0) or #FF0000

  • :fill_color = the fill colour of the glyphs in the track (default = ‘red’), can be any SVG colour eg rgb(256,0,0) or #FF0000, or one of the built in gradient types Bioroebe::SVG::Glyph#gradients

:red_white_h, :green_white_h, :blue_white_h, :yellow_white_h, :red_white_radial, :green_white_radial, :blue_white_radial, :yellow_white_radial

or a custom definition of a gradient.

{:type => :radial, :id => :custom, :cx => 5, :cy => 5, :r => 50, :fx => 50, :fy => 50,

:stops => [ {
     :offset => 0, 
     :color => 'rgb(255,255,255)', 
     :opacity => 0
     },  {
     :offset => 100, 
     :color => 'rgb(0,127,200)', 
     :opacity => 1
     }, ]

}

  • :track_height = minimum height for the track, will be modified automatically if more space is needed e.g for overlapping features (default = auto),

  • :name = a displayed name for the track (default = ‘feature_track’)

  • :label = display the name given to the track (default = true),

  • :stroke_width = width in pixels of the outline of the glyphs (default=1)

  • :x_round = x radius of the ellipse used to round off the corners of rectangles (default = 1)

  • :y_round = y radius of the ellipse used to round off the corners of rectangles (default = 1)

:utr_fill_color = the fill colour of the utr part of the glyph (default = ‘black’), can be any SVG colour eg rgb(256,0,0) or #FF0000, or one of the built in gradient types Bioroebe::SVG::Glyph#gradients

[:red_white_h, :green_white_h, :blue_white_h, :yellow_white_h, :red_white_radial, :green_white_radial, :blue_white_radial, :yellow_white_radial ]

or a custom definition of a gradient

{:type => :radial, :id => :custom, :cx => 5, :cy => 5, :r => 50, :fx => 50, :fy => 50,

:stops => [ {
     :offset => 0, 
     :color => 'rgb(255,255,255)', 
     :opacity => 0
     },  {
     :offset => 100, 
     :color => 'rgb(0,127,200)', 
     :opacity => 1
     }, ]

}

  • :utr_stroke = the outline colour of the utr part of

    the glyph (default = "black"), can be
    any SVG colour eg rgb(256,0,0) or #FF0000
    
  • :utr_stroke_width = The width of the outline stroke for the utr part of the glyph (default = 1)

  • :exon_fill_color = the fill colour of the utr part of the glyph (default = ‘red’), can be any SVG colour eg rgb(256,0,0) or #FF0000, or one of the built in gradient types Bioroebe::SVG::Glyph#gradients or a custom definition of a gradient

:red_white_h, :green_white_h, :blue_white_h, :yellow_white_h, :red_white_radial, :green_white_radial, :blue_white_radial, :yellow_white_radial

or a custom definition of a gradient {:type => :radial, :id => :custom, :cx => 5, :cy => 5, :r => 50, :fx => 50, :fy => 50,

:stops => [ {
     :offset => 0, 
     :color => 'rgb(255,255,255)', 
     :opacity => 0
     },  {
     :offset => 100, 
     :color => 'rgb(0,127,200)', 
     :opacity => 1
     }, ]

:exon_stroke = the outline colour of the exon part of the glyph (default = “black”) can be any SVG colour eg rgb(256,0,0) or #FF0000 :exon_stroke_width = The width of the outline stroke for the exon part

of the glyph (default = 1)

:line_color = the colour for the line part that joins the blocks (default = ‘black’) can be any SVG colour eg rgb(256,0,0) or #FF0000 :line_width = the width ffor the line part that joins the blocks (default = 1) :exon_style = an arbitrary SVG compliant style string eg “fill-opacity:0.4;” :utr_style = an arbitrary SVG compliant style string eg “fill-opacity:0.4;” :line_style = an arbitrary SVG compliant style string eg “fill-opacity:0.4;” :gap_marker = style of the line between blocks - either angled or straight

Will return:

a new Bioroebe::SVG::Track object
#


284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/bioroebe/svg/page.rb', line 284

def add_track(args)
  # ======================================================================= #
  # Sort out the colour/gradient options
  # ======================================================================= #
  [
    :fill_color,
    :exon_fill_color,
    :utr_fill_color
  ].each { |colour_tag|
    if Glyph.gradients.include?(args[colour_tag])
      @svg.gradient(Glyph.gradient(args[colour_tag]))
      args[colour_tag] = "url(##{args[colour_tag]})"
    elsif args[colour_tag].instance_of?(Hash)
      @svg.gradient(args[colour_tag])
      args[colour_tag] = "url(##{args[colour_tag][:id]})"
    end
  }
  @tracks << Track.new(args)
  return @tracks.last
end

#compute_boundaries(feature) ⇒ Object

#

compute_boundaries

#


338
339
340
341
342
# File 'lib/bioroebe/svg/page.rb', line 338

def compute_boundaries(feature)
  feat_end = feature.end
  feat_end += (8 * @nt_per_px_x * feature.id.to_s.length).to_i if feature.id and @nt_per_px_x
  [feature.start, feat_end]
end

#drawObject

#

draw

Prints the svg text to STDOUT. You can capture this and redirect it into a file.

#


568
569
570
# File 'lib/bioroebe/svg/page.rb', line 568

def draw
  puts get_markup
end

#draw_circle(args) ⇒ Object

#

draw_circle

#


405
406
407
# File 'lib/bioroebe/svg/page.rb', line 405

def draw_circle(args)
  Glyph.circle(args).each { |g| @svg.add_primitive(g) }
end

#draw_directed(args) ⇒ Object

#

draw_directed

#


398
399
400
# File 'lib/bioroebe/svg/page.rb', line 398

def draw_directed(args)
  Glyph.directed(args).each { |g| @svg.add_primitive(g) }
end

#draw_down_triangle(args) ⇒ Object

#

draw_down_triangle

#


433
434
435
# File 'lib/bioroebe/svg/page.rb', line 433

def draw_down_triangle(args)
  Glyph.down_triangle(args).each { |g| @svg.add_primitive(g) }
end

#draw_features(track) ⇒ Object

#

draw_features

Takes a Bioroebe::SVG::Track object and does the work to draw features.

It examines the the type of track and calculates the required parameters * args - an Array of Bioroebe::SVG::Track object

#


454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/bioroebe/svg/page.rb', line 454

def draw_features(track)
  if [:histogram, 'histogram'].include?(track.glyph) #do different stuff for data tracks...
     y = @track_top + (track.track_height)
    max = track.max_y ? track.max_y : track.features.sort { |a, b| a.segment_height <=> b.segment_height }.last.segment_height
    min = 0
    if track.label
      draw_label(:text => track.name, :y => @track_top += 30, :x => 3)
    end
    draw_label(:text => max, :x => to_px(@scale_stop - @scale_start) + 5, :y => @track_top + 5)
    draw_label(:text => min, :x => to_px(@scale_stop - @scale_start) + 5, :y => @track_top + track.track_height + 5)
    data_interval = max - min
    data_per_px = track.track_height.to_f / data_interval.to_f
    track.features.each do |f|
      x = to_px(f.start - @scale_start)
      width = to_px((f.end - @scale_start) - (f.start - @scale_start))
      height = f.segment_height.to_f * data_per_px
      y = @track_top + track.track_height - height + 5
      #$stderr.puts f.segment_height, data_per_px, data_interval, max, min, "------"
      self.send("draw_#{track.glyph}", {:x => x,
                                        :y => y,
                                        :width => width,
                                        :height => height}.merge!(track.args))
    end
    @track_top += (track.track_height) + 20
  else ##following stuff for the features
    if track.label
      draw_label(:text => track.name, :y => @track_top += 30, :x => 3)
    end
    track.get_rows(self) ##work out how many rows and which features belong in each row...
    track.features.each_with_index do |f, index|
      x = to_px(f.start - @scale_start) #bottom left of feature
      all_sub_blocks = []
      #convert the exon and utr start stops to px start stops and px widths
      exons = []
      if f.exons
        f.exons.each_slice(2).each { |exon|
          all_sub_blocks << exon
          next if exon.nil?
          exons << [to_px(exon[0] - @scale_start), to_px((exon[1] - @scale_start) - (exon[0] - @scale_start))]
        }
      end
      f.exons = exons
      utrs = []
      if f.utrs
        f.utrs.each_slice(2).each { |utr|
          all_sub_blocks << utr
          next if utr.nil?
          utrs << [to_px(utr[0] - @scale_start), to_px((utr[1] - @scale_start) - (utr[0] - @scale_start))]
        }
      end
      f.utrs = utrs
      # If there are any intron like gaps.. get where they would be.
      if not all_sub_blocks.empty?
        all_sub_blocks = all_sub_blocks.sort { |a, b| a.first <=> b.first }
        all_sub_blocks.each_index do |i|
          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
          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))]
        end
      end
      width = to_px((f.end - @scale_start) - (f.start - @scale_start))
      if track.min_width and width < track.min_width
        width = track.min_width
      end
      y = @track_top + (track.feature_rows[index] * 2 * track.feature_height)
      self.send("draw_#{track.glyph}", {:x => x,
                                        :y => y,
                                        :width => width,
                                        :strand => f.strand,
                                        :exons => f.exons,
                                        :utrs => f.utrs,
                                        :block_gaps => f.block_gaps,
                                        :height => track.feature_height,
                                        :params => f.params
      }.merge!(track.args))
      if f.id
        draw_label(:y => y, :x => x+width+2, :text => f.id)
      end
    end
    @track_top += (track.feature_height * track.number_rows * 2) + 20
  end
  @height = @track_top + 100 # Fudge.
  @svg.update_height(@height)
  # @svg.update_width(@width + 200)
end

#draw_generic(args) ⇒ Object

#

draw_generic

#


391
392
393
# File 'lib/bioroebe/svg/page.rb', line 391

def draw_generic(args) #remember presentation info comes from track@args when the track is defined
  Glyph.generic(args).each { |g| @svg.add_primitive(g) }
end

#draw_histogram(args) ⇒ Object

#

draw_histogram

#


419
420
421
# File 'lib/bioroebe/svg/page.rb', line 419

def draw_histogram(args)
  Glyph.generic(args).each { |g| @svg.add_primitive(g) }
end

#draw_label(args) ⇒ Object

#

draw_label

Adds the Bioroebe::SVG::Primitive objects to the SVGEE object

  • args - an Array of Bioroebe::SVG::Primitive object

#


382
383
384
385
386
# File 'lib/bioroebe/svg/page.rb', line 382

def draw_label(args)
  Glyph.label(:text => args[:text],
              :x => args[:x],
              :y => args[:y]).each { |g| @svg.add_primitive(g) }
end

#draw_scaleObject

#

draw_scale

Adds scale bar to the list of objects to be rendered in the final.

#


349
350
351
352
353
354
# File 'lib/bioroebe/svg/page.rb', line 349

def draw_scale
  Glyph.scale(:start => @scale_start,
              :stop => @scale_stop,
              :number_of_intervals => @num_intervals,
              :page_width => @width).each { |entry| @svg.add_primitive(entry) }
end

#draw_span(args) ⇒ Object

#

draw_span

#


440
441
442
443
444
# File 'lib/bioroebe/svg/page.rb', line 440

def draw_span(args)
  Glyph.span(args).each { |g|
    @svg.add_primitive(g)
  }
end

#draw_transcript(args) ⇒ Object

#

draw_transcript

#


412
413
414
# File 'lib/bioroebe/svg/page.rb', line 412

def draw_transcript(args)
  Glyph.transcript(args).each { |g| @svg.add_primitive(g) }
end

#draw_up_triangle(args) ⇒ Object

#

draw_up_triangle

#


426
427
428
# File 'lib/bioroebe/svg/page.rb', line 426

def draw_up_triangle(args)
  Glyph.up_triangle(args).each { |g| @svg.add_primitive(g) }
end

#get_limitsObject

#

get_limits

Calculates the Page scale-start and scale-stop position and the nucleotides per pixel for the current Page.

#


311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/bioroebe/svg/page.rb', line 311

def get_limits
  @tracks.each { |track|
    lowest = track.features.sort { |x, y|
      x.start <=> y.start
    }.first.start
    highest = track.features.sort { |x, y| y.end <=> x.end }.first.end
    @scale_start = lowest if lowest < @scale_start
    @scale_stop = highest if highest > @scale_stop
    @nt_per_px_x = (@scale_stop - @scale_start).to_f / @width.to_f
  }
  begin
    old_nt_per_px_x = @nt_per_px_x
    @tracks.each { |track|
      highest = track.features.map { |feat|
        compute_boundaries(feat)[1]
      }.max
      if highest > @scale_stop
        @scale_stop = highest
        @nt_per_px_x = (@scale_stop - @scale_start).to_f / @width.to_f
      end
    }
  end while (@nt_per_px_x - old_nt_per_px_x).abs > 1
end

#get_markupObject

#

get_markup

Generates the SVG text.

#


553
554
555
556
557
558
559
560
# File 'lib/bioroebe/svg/page.rb', line 553

def get_markup
  get_limits
  draw_scale
  @tracks.each { |track|
    draw_features(track)
  }
  @svg.draw
end

#resetObject

#

reset (reset tag)

#


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/bioroebe/svg/page.rb', line 65

def reset
  # ======================================================================= #
  # === @track_top
  # ======================================================================= #
  @track_top = 30
  # ======================================================================= #
  # === @tracks
  #
  # Array of track objects with loads of features in it.
  # ======================================================================= #
  @tracks = []
  # ======================================================================= #
  # === @scale_start
  # ======================================================================= #
  @scale_start = 1.0 / 0.0
  @scale_stop  = -1.0/0.0
  @nt_per_percent = 1
end

#to_px(num) ⇒ Object

#

to_px

Calculates the pixel value for a given number.

#


544
545
546
# File 'lib/bioroebe/svg/page.rb', line 544

def to_px(num)
  (num.to_f / @nt_per_px_x.to_f)
end

#write(file) ⇒ Object

#

write (write tag)

Writes the SVG text to a file

#


577
578
579
# File 'lib/bioroebe/svg/page.rb', line 577

def write(file)
  File.open(file, 'w').write(get_markup)
end