bio-ensembl 1.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.
Files changed (81) hide show
  1. data/.document +5 -0
  2. data/Gemfile +20 -0
  3. data/Gemfile.lock +40 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +19 -0
  6. data/Rakefile +71 -0
  7. data/VERSION +1 -0
  8. data/bin/ensembl +40 -0
  9. data/bin/variation_effect_predictor +106 -0
  10. data/bio-ensembl.gemspec +190 -0
  11. data/lib/bio-ensembl.rb +65 -0
  12. data/lib/bio-ensembl/core/activerecord.rb +1812 -0
  13. data/lib/bio-ensembl/core/collection.rb +64 -0
  14. data/lib/bio-ensembl/core/project.rb +262 -0
  15. data/lib/bio-ensembl/core/slice.rb +657 -0
  16. data/lib/bio-ensembl/core/transcript.rb +409 -0
  17. data/lib/bio-ensembl/core/transform.rb +95 -0
  18. data/lib/bio-ensembl/db_connection.rb +205 -0
  19. data/lib/bio-ensembl/variation/activerecord.rb +536 -0
  20. data/lib/bio-ensembl/variation/variation_feature.rb +376 -0
  21. data/lib/bio-ensembl/variation/variation_feature62.rb +444 -0
  22. data/samples/ensembl_genomes_example.rb +60 -0
  23. data/samples/examples_perl_tutorial.rb +125 -0
  24. data/samples/small_example_ruby_api.rb +34 -0
  25. data/samples/variation_effect_predictor_data.txt +4 -0
  26. data/samples/variation_example.rb +67 -0
  27. data/test/data/seq_c6qbl.fa +10 -0
  28. data/test/data/seq_cso19_coding.fa +16 -0
  29. data/test/data/seq_cso19_transcript.fa +28 -0
  30. data/test/data/seq_drd3_gene.fa +838 -0
  31. data/test/data/seq_drd3_transcript.fa +22 -0
  32. data/test/data/seq_drd4_transcript.fa +24 -0
  33. data/test/data/seq_forward_composite.fa +1669 -0
  34. data/test/data/seq_par_boundary.fa +169 -0
  35. data/test/data/seq_rnd3_transcript.fa +47 -0
  36. data/test/data/seq_ub2r1_coding.fa +13 -0
  37. data/test/data/seq_ub2r1_gene.fa +174 -0
  38. data/test/data/seq_ub2r1_transcript.fa +26 -0
  39. data/test/data/seq_y.fa +2 -0
  40. data/test/default/test_connection.rb +60 -0
  41. data/test/default/test_releases.rb +130 -0
  42. data/test/ensembl_genomes/test_collection.rb +122 -0
  43. data/test/ensembl_genomes/test_gene.rb +46 -0
  44. data/test/ensembl_genomes/test_slice.rb +65 -0
  45. data/test/ensembl_genomes/test_variation.rb +38 -0
  46. data/test/helper.rb +18 -0
  47. data/test/release_50/core/test_project.rb +210 -0
  48. data/test/release_50/core/test_project_human.rb +52 -0
  49. data/test/release_50/core/test_relationships.rb +72 -0
  50. data/test/release_50/core/test_sequence.rb +170 -0
  51. data/test/release_50/core/test_slice.rb +116 -0
  52. data/test/release_50/core/test_transcript.rb +125 -0
  53. data/test/release_50/core/test_transform.rb +217 -0
  54. data/test/release_50/variation/test_activerecord.rb +138 -0
  55. data/test/release_50/variation/test_variation.rb +79 -0
  56. data/test/release_53/core/test_gene.rb +61 -0
  57. data/test/release_53/core/test_project.rb +91 -0
  58. data/test/release_53/core/test_project_human.rb +61 -0
  59. data/test/release_53/core/test_slice.rb +42 -0
  60. data/test/release_53/core/test_transform.rb +57 -0
  61. data/test/release_53/variation/test_activerecord.rb +137 -0
  62. data/test/release_53/variation/test_variation.rb +66 -0
  63. data/test/release_56/core/test_gene.rb +61 -0
  64. data/test/release_56/core/test_project.rb +91 -0
  65. data/test/release_56/core/test_slice.rb +49 -0
  66. data/test/release_56/core/test_transform.rb +57 -0
  67. data/test/release_56/variation/test_activerecord.rb +141 -0
  68. data/test/release_56/variation/test_consequence.rb +131 -0
  69. data/test/release_56/variation/test_variation.rb +63 -0
  70. data/test/release_60/core/test_gene.rb +61 -0
  71. data/test/release_60/core/test_project_human.rb +34 -0
  72. data/test/release_60/core/test_slice.rb +42 -0
  73. data/test/release_60/core/test_transcript.rb +120 -0
  74. data/test/release_60/core/test_transform.rb +57 -0
  75. data/test/release_60/variation/test_activerecord.rb +216 -0
  76. data/test/release_60/variation/test_consequence.rb +153 -0
  77. data/test/release_60/variation/test_variation.rb +64 -0
  78. data/test/release_62/core/test_gene.rb +42 -0
  79. data/test/release_62/variation/test_activerecord.rb +86 -0
  80. data/test/release_62/variation/test_consequence.rb +191 -0
  81. metadata +287 -0
@@ -0,0 +1,409 @@
1
+ #
2
+ # = ensembl/core/transcript.rb - ActiveRecord mapping to Ensembl core for transcript
3
+ #
4
+ # Copyright:: Copyright (C) 2007 Jan Aerts <http://jandot.myopenid.com>
5
+ # License:: The Ruby License
6
+ #
7
+ # @author Jan Aerts
8
+ nil
9
+ module Ensembl
10
+ nil
11
+ module Core
12
+ # The Intron class describes an intron.
13
+ #
14
+ # This class does _not_ use ActiveRecord and is only defined within the API.
15
+ # There is no _introns_ table in the Ensembl database.
16
+ #
17
+ # This class includes the mixin Sliceable, which means that it is mapped
18
+ # to a SeqRegion object and a Slice can be created for objects o this
19
+ # class. See Sliceable and Slice for more information.
20
+ #
21
+ # @example
22
+ # exon1 = Ensembl::Core::Exon.find(292811)
23
+ # exon2 = Ensembl::Core::Exon.find(292894)
24
+ # intron = Ensembl::Core::Intron.new(exon1,exon2)
25
+ # puts intron.to_yaml
26
+ #
27
+ # transcript = Ensembl::Core::Transcript.find(58972)
28
+ # puts transcript.introns.to_yaml
29
+ class Intron
30
+ include Sliceable
31
+ attr_accessor :seq_region, :seq_region_start, :seq_region_end, :seq_region_strand
32
+ attr_accessor :previous_exon, :next_exon, :transcript
33
+
34
+ def initialize(exon_1, exon_2)
35
+ # Check if these are actually two adjacent exons from the same transcript
36
+ ok = true
37
+
38
+ transcript = nil
39
+ exon_1.transcripts.each do |t|
40
+ transcript = t if exon_2.transcripts.include?(t)
41
+ end
42
+ raise ArgumentError, "Arguments should be adjacent exons of same transcript" if transcript.nil?
43
+
44
+ rank_1 = ExonTranscript.find_by_transcript_id_and_exon_id(transcript.id, exon_1.id).rank
45
+ rank_2 = ExonTranscript.find_by_transcript_id_and_exon_id(transcript.id, exon_2.id).rank
46
+ raise ArgumentError, "Arguments should be adjacent exons of same transcript" if (rank_2 - rank_1).abs > 1
47
+
48
+ @previous_exon, @next_exon = [exon_1, exon_2].sort_by{|e| e.seq_region_start}
49
+ @transcript = transcript
50
+ @seq_region = @previous_exon.seq_region
51
+ @seq_region_start = @previous_exon.seq_region_end + 1
52
+ @seq_region_end = @next_exon.seq_region_start - 1
53
+ @seq_region_strand = @previous_exon.seq_region_strand
54
+ end
55
+
56
+ end
57
+
58
+ # The Transcript class provides an interface to the transcript
59
+ # table. This table contains mappings of transcripts for a Gene to a
60
+ # SeqRegion.
61
+ #
62
+ # This class uses ActiveRecord to access data in the Ensembl database.
63
+ # See the general documentation of the Ensembl module for
64
+ # more information on what this means and what methods are available.
65
+ #
66
+ # This class includes the mixin Sliceable, which means that it is mapped
67
+ # to a SeqRegion object and a Slice can be created for objects of this
68
+ # class. See Sliceable and Slice for more information.
69
+ #
70
+ # @example
71
+ # #TODO
72
+ class Transcript < DBConnection
73
+ include Sliceable
74
+
75
+ set_table_name 'transcript'
76
+ set_primary_key 'transcript_id'
77
+
78
+ belongs_to :gene
79
+ belongs_to :seq_region
80
+ has_one :transcript_stable_id
81
+ has_many :transcript_attribs
82
+
83
+ has_many :exon_transcripts
84
+ has_many :exons, :through => :exon_transcripts, :order => "exon_transcript.rank"
85
+
86
+ has_one :translation
87
+
88
+ has_many :object_xrefs, :foreign_key => 'ensembl_id', :conditions => "ensembl_object_type = 'Transcript'"
89
+ has_many :xrefs, :through => :object_xrefs
90
+
91
+ has_many :transcript_supporting_features
92
+ has_many :dna_align_features, :through => :transcript_supporting_features, :conditions => ["feature_type = 'dna_align_feature'"]
93
+ has_many :protein_align_features, :through => :transcript_supporting_features, :conditions => ["feature_type = 'protein_align_feature'"]
94
+
95
+ alias attribs transcript_attribs
96
+
97
+ # The Transcript#exons method returns the exons for this transcript in
98
+ # the order of their ranks in the exon_transcript table.
99
+ #
100
+ # @return [Array<Exon>] Sorted array of Exon objects
101
+ #def exons
102
+ # if @exons.nil?
103
+ # @exons = self.exon_transcripts(:include => [:exons]).sort_by{|et| et.rank.to_i}.collect{|et| et.exon}
104
+ # end
105
+ # return @exons
106
+ #end
107
+
108
+ # The Transcript#introns methods returns the introns for this transcript
109
+ #
110
+ # @return [Array<Intron>] Sorted array of Intron objects
111
+ def introns
112
+ if @introns.nil?
113
+ @introns = Array.new
114
+ if self.exons.length > 1
115
+ self.exons.each_with_index do |exon, index|
116
+ next if index == 0
117
+ @introns.push(Intron.new(self.exons[index - 1], exon))
118
+ end
119
+ end
120
+ end
121
+ return @introns
122
+ end
123
+
124
+ # The Transcript#stable_id method returns the stable ID of the transcript.
125
+ #
126
+ # @return [String] Ensembl stable ID of the transcript.
127
+ def stable_id
128
+ return self.transcript_stable_id.stable_id
129
+ end
130
+
131
+ # The Transcript#display_label method returns the default name of the transcript.
132
+ def display_label
133
+ return Xref.find(self.display_xref_id).display_label
134
+ end
135
+ alias :display_name :display_label
136
+ alias :label :display_label
137
+ alias :name :display_label
138
+
139
+ # The Transcript#find_all_by_stable_id class method returns an array of
140
+ # transcripts with the given stable_id. If none were found, an empty
141
+ # array is returned.
142
+ def self.find_all_by_stable_id(stable_id)
143
+ answer = Array.new
144
+ transcript_stable_id_objects = Ensembl::Core::TranscriptStableId.find_all_by_stable_id(stable_id)
145
+ transcript_stable_id_objects.each do |transcript_stable_id_object|
146
+ answer.push(Ensembl::Core::Transcript.find(transcript_stable_id_object.transcript_id))
147
+ end
148
+
149
+ return answer
150
+ end
151
+
152
+ # The Transcript#find_all_by_stable_id class method returns a
153
+ # transcripts with the given stable_id. If none was found, nil is returned.
154
+ def self.find_by_stable_id(stable_id)
155
+ all = self.find_all_by_stable_id(stable_id)
156
+ if all.length == 0
157
+ return nil
158
+ else
159
+ return all[0]
160
+ end
161
+ end
162
+
163
+ # The Transcript#find_by_stable_id class method fetches a Transcript object based on
164
+ # its stable ID (i.e. the "ENST" accession number). If the name is
165
+ # not found, it returns nil.
166
+ def self.find_by_stable_id(stable_id)
167
+ transcript_stable_id = TranscriptStableId.find_by_stable_id(stable_id)
168
+ if transcript_stable_id.nil?
169
+ return nil
170
+ else
171
+ return transcript_stable_id.transcript
172
+ end
173
+ end
174
+
175
+ # The Transcript#seq method returns the full sequence of all concatenated
176
+ # exons.
177
+ def seq
178
+ if @seq.nil?
179
+ @seq = ''
180
+ self.exons.each do |exon|
181
+ @seq += exon.seq
182
+ end
183
+ end
184
+ return @seq
185
+ end
186
+
187
+ # The Transcript#cds_seq method returns the coding sequence of the transcript,
188
+ # i.e. the concatenated sequence of all exons minus the UTRs.
189
+ def cds_seq
190
+ cds_length = self.coding_region_cdna_end - self.coding_region_cdna_start + 1
191
+
192
+ return self.seq[(self.coding_region_cdna_start - 1), cds_length]
193
+ end
194
+
195
+ # The Transcript#five_prime_utr_seq method returns the sequence of the
196
+ # 5'UTR of the transcript.
197
+ def five_prime_utr_seq
198
+ return self.seq[0, self.coding_region_cdna_start - 1]
199
+ end
200
+
201
+ # The Transcript#three_prime_utr_seq method returns the sequence of the
202
+ # 3'UTR of the transcript.
203
+ def three_prime_utr_seq
204
+ return self.seq[self.coding_region_cdna_end..-1]
205
+ end
206
+
207
+ # The Transcript#protein_seq method returns the sequence of the
208
+ # protein of the transcript.
209
+ def protein_seq
210
+ return Bio::Sequence::NA.new(self.cds_seq).translate.seq
211
+ end
212
+
213
+
214
+ # The Transcript#coding_region_genomic_start returns the start position
215
+ # of the CDS in genomic coordinates. Note that, in contrast to
216
+ # Transcript#coding_region_cdna_start, the CDS start position is _always_
217
+ # ''left'' of the end position. So for transcripts on the reverse strand,
218
+ # the CDS start position is at the border of the 3'UTR instead of the
219
+ # 5'UTR.
220
+ def coding_region_genomic_start
221
+ strand = self.translation.start_exon.seq_region_strand
222
+ if strand == 1
223
+ return self.translation.start_exon.seq_region_start + ( self.translation.seq_start - 1 )
224
+ else
225
+ return self.translation.end_exon.seq_region_end - ( self.translation.seq_end - 1 )
226
+ end
227
+ end
228
+
229
+ # The Transcript#coding_region_genomic_end returns the stop position
230
+ # of the CDS in genomic coordinates. Note that, in contrast to
231
+ # Transcript#coding_region_cdna_end, the CDS stop position is _always_
232
+ # ''right'' of the start position. So for transcripts on the reverse strand,
233
+ # the CDS stop position is at the border of the 5'UTR instead of the
234
+ # 3'UTR.
235
+ def coding_region_genomic_end
236
+ strand = self.translation.start_exon.seq_region_strand
237
+ if strand == 1
238
+ return self.translation.end_exon.seq_region_start + ( self.translation.seq_end - 1 )
239
+ else
240
+ return self.translation.start_exon.seq_region_end - ( self.translation.seq_start - 1 )
241
+ end
242
+ end
243
+
244
+ # The Transcript#coding_region_cdna_start returns the start position
245
+ # of the CDS in cDNA coordinates. Note that, in contrast to the
246
+ # Transcript#coding_region_genomic_start, the CDS start position is
247
+ # _always_ at the border of the 5'UTR. So for genes on the reverse
248
+ # strand, the CDS start position in cDNA coordinates will be ''right''
249
+ # of the CDS stop position.
250
+ def coding_region_cdna_start
251
+ answer = 0
252
+
253
+ self.exons.each do |exon|
254
+ if exon == self.translation.start_exon
255
+ answer += self.translation.seq_start
256
+ return answer
257
+ else
258
+ answer += exon.length
259
+ end
260
+ end
261
+
262
+ end
263
+
264
+ # The Transcript#coding_region_cdna_end returns the stop position
265
+ # of the CDS in cDNA coordinates. Note that, in contrast to the
266
+ # Transcript#coding_region_genomic_end, the CDS start position is
267
+ # _always_ at the border of the 3'UTR. So for genes on the reverse
268
+ # strand, the CDS start position in cDNA coordinates will be ''right''
269
+ # of the CDS stop position.
270
+ def coding_region_cdna_end
271
+ answer = 0
272
+
273
+ self.exons.each do |exon|
274
+ if exon == self.translation.end_exon
275
+ answer += self.translation.seq_end
276
+ return answer
277
+ else
278
+ answer += exon.length
279
+ end
280
+ end
281
+ end
282
+
283
+
284
+ # The Transcript#exon_for_position identifies the exon that covers a given
285
+ # genomic position. Returns the exon object, or nil if in intron.
286
+ def exon_for_genomic_position(pos)
287
+ if pos < self.seq_region_start or pos > self.seq_region_end
288
+ raise RuntimeError, "Position has to be within transcript"
289
+ end
290
+ self.exons.each do |exon|
291
+ if exon.start <= pos and exon.stop >= pos
292
+ return exon
293
+ end
294
+ end
295
+ return nil
296
+ end
297
+
298
+ # The Transcript#exon_for_position identifies the exon that covers a given
299
+ # position of the cDNA.
300
+ def exon_for_cdna_position(pos)
301
+ # FIXME: Still have to check for when pos is outside of scope of cDNA.
302
+ accumulated_exon_length = 0
303
+
304
+ self.exons.each do |exon|
305
+ accumulated_exon_length += exon.length
306
+ if accumulated_exon_length > pos
307
+ return exon
308
+ end
309
+ end
310
+ raise RuntimeError, "Position outside of cDNA scope"
311
+ end
312
+
313
+ # The Transcript#cdna2genomic method converts cDNA coordinates to
314
+ # genomic coordinates for this transcript.
315
+ #
316
+ # @param [Integer] pos Position on the cDNA
317
+ # @return [Integer] Position on the genomic DNA
318
+ def cdna2genomic(pos)
319
+ #FIXME: Still have to check for when pos is outside of scope of cDNA.
320
+ # Identify the exon we're looking at.
321
+ exon_with_target = self.exon_for_cdna_position(pos)
322
+
323
+ accumulated_position = 0
324
+ ex = self.exons.sort_by {|e| e.seq_region_start}
325
+ ex.reverse! if self.strand == -1
326
+ ex.each do |exon|
327
+ if exon == exon_with_target
328
+ length_to_be_taken_from_exon = pos - (accumulated_position + 1)
329
+ if self.strand == -1
330
+ return exon.seq_region_end - length_to_be_taken_from_exon
331
+ else
332
+ return exon.seq_region_start + length_to_be_taken_from_exon
333
+ end
334
+ else
335
+ accumulated_position += exon.length
336
+ end
337
+ end
338
+ end
339
+
340
+ # The Transcript#cds2genomic method converts CDS coordinates to
341
+ # genomic coordinates for this transcript.
342
+ #
343
+ # @param [Integer] pos Position on the CDS
344
+ # @return [Integer] Position on the genomic DNA
345
+ def cds2genomic(pos)
346
+ return self.cdna2genomic(pos + self.coding_region_cdna_start)
347
+ end
348
+
349
+ # The Transcript#pep2genomic method converts peptide coordinates to
350
+ # genomic coordinates for this transcript.
351
+ #
352
+ # @param [Integer] pos Aminoacid position on the protein
353
+ # @return [Integer] Position on the genomic DNA
354
+ def pep2genomic(pos)
355
+ raise NotImplementedError
356
+ end
357
+
358
+ # The Transcript#genomic2cdna method converts genomic coordinates to
359
+ # cDNA coordinates for this transcript.
360
+ #
361
+ # @param [Integer] pos Position on the genomic DNA
362
+ # @return [Integer] Position on the cDNA
363
+ def genomic2cdna(pos)
364
+ #FIXME: Still have to check for when pos is outside of scope of cDNA.
365
+ # Identify the exon we're looking at.
366
+ exon_with_target = self.exon_for_genomic_position(pos)
367
+
368
+ accumulated_position = 0
369
+ ex = self.exons.sort_by {|e| e.seq_region_start}
370
+ ex.reverse! if self.strand == -1
371
+ ex.each do |exon|
372
+ if exon.stable_id == exon_with_target.stable_id
373
+ if self.strand == 1
374
+ accumulated_position += ( pos - exon.start) +1
375
+ else
376
+ accumulated_position += ( exon.stop - pos ) +1
377
+ end
378
+ return accumulated_position
379
+ else
380
+ accumulated_position += exon.length
381
+ end
382
+ end
383
+ return RuntimeError, "Position outside of cDNA scope"
384
+ end
385
+
386
+ # The Transcript#genomic2cds method converts genomic coordinates to
387
+ # CDS coordinates for this transcript.
388
+ #
389
+ # @param [Integer] pos Position on the genomic DNA
390
+ # @return [Integer] Position on the CDS
391
+ def genomic2cds(pos)
392
+ return self.genomic2cdna(pos) - self.coding_region_cdna_start
393
+ end
394
+
395
+ # The Transcript#genomic2pep method converts genomic coordinates to
396
+ # peptide coordinates for this transcript.
397
+ #
398
+ # @param [Integer] pos Base position on the genomic DNA
399
+ # @return [Integer] Aminoacid position in the protein
400
+ # *Arguments*:
401
+ # * pos:: position on the chromosome (required)
402
+ # *Returns*::
403
+ def genomic2pep(pos)
404
+ raise NotImplementedError
405
+ end
406
+
407
+ end
408
+ end
409
+ end
@@ -0,0 +1,95 @@
1
+ #
2
+ # = bio/api/ensembl/core/transform.rb - transform positions for Ensembl Slice
3
+ #
4
+ # Copyright:: Copyright (C) 2007 Jan Aerts <http://jandot.myopenid.com>
5
+ # License:: The Ruby License
6
+ #
7
+ # @author Jan Aerts
8
+ nil
9
+ module Ensembl
10
+ nil
11
+ module Core
12
+ nil
13
+ module Sliceable
14
+ # The #transform method is used to transfer coordinates for a feature
15
+ # from one coordinate system to another. It basically creates a clone of
16
+ # the original feature and changes the seq_region, start position, stop
17
+ # position and strand.
18
+ #
19
+ # Suppose you have a feature on a
20
+ # contig in human (let's say on contig AC000031.6.1.38703) and you
21
+ # want to know the coordinates on the chromosome. This is a
22
+ # transformation of coordinates from a higher ranked coordinate system to
23
+ # a lower ranked coordinate system. Transformations can also be done
24
+ # from a chromosome to the contig level.
25
+ #
26
+ # In contrast to the #project method of Sliceables, the
27
+ # coordinates of a feature can only transformed to the target
28
+ # coordinate system if there is no ambiguity to which SeqRegion.
29
+ #
30
+ # For example, gene A can be transferred from the chromosome system to
31
+ # the clone coordinate system, whereas gene B can not.
32
+ #
33
+ # gene A gene B
34
+ # |---<=====>--------------------<=====>----------------| chromosome
35
+ #
36
+ # |-----------| |-------| |---------| clones
37
+ # |-----------| |-------| |--------|
38
+ #
39
+ # gene_a.transform('clone') --> gene
40
+ # gene_b.transform('clone') --> nil
41
+ #
42
+ # At the moment, transformations can only be done if the two coordinate
43
+ # systems are linked directly in the 'assembly' table.
44
+ #
45
+ # @example
46
+ # # Get a gene in cow and transform to scaffold level
47
+ # # (i.e. going from a high rank coord system to a lower rank coord
48
+ # # system)
49
+ # # Cow scaffold Chr4.10 lies on Chr4 from 8030345 to 10087277 on the
50
+ # # reverse strand
51
+ # source_gene = Gene.find(2408)
52
+ # target_gene = source_gene.transform('scaffold')
53
+ # puts source_gene.seq_region.name #--> 4
54
+ # puts source_gene.seq_region_start #--> 8104409
55
+ # puts source_gene.seq_region_end #--> 8496477
56
+ # puts source_gene.seq_region_strand #--> -1
57
+ # puts target_gene.seq_region.name #--> Chr4.003.10
58
+ # puts target_gene.seq_region_start #--> 1590800
59
+ # puts target_gene.seq_region_end #--> 1982868
60
+ # puts target_gene.seq_region_strand #--> 1
61
+ #
62
+ # @param [String] coord_system_name Name of the coordinate system to
63
+ # transform the coordinates to
64
+ # @return Nil or an object of the same class
65
+ # as self
66
+ def transform(coord_system_name)
67
+ #-
68
+ # There are two things I can do:
69
+ # (1) just use project
70
+ # (2) avoid doing all the calculations in project if the source slice
71
+ # covers multiple target slices, and _then_ go for project.
72
+ # Let's go for nr 1 for the moment and optimize later.
73
+ #+
74
+
75
+ if self.slice.seq_region.coord_system.name == coord_system_name
76
+ return self
77
+ end
78
+
79
+ target_slices = self.slice.project(coord_system_name)
80
+ if target_slices.length > 1
81
+ return nil
82
+ else
83
+ clone = self.clone
84
+ clone.seq_region_id = target_slices[0].seq_region.id
85
+ clone.seq_region_start = target_slices[0].start
86
+ clone.seq_region_end = target_slices[0].stop
87
+
88
+ clone.seq_region_strand = target_slices[0].strand * self.strand
89
+
90
+ return clone
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end