File Coverage

Bio/Map/Position.pm
Criterion Covered Total %
statement 108 114 94.7
branch 67 88 76.1
condition 35 56 62.5
subroutine 16 16 100.0
pod 11 11 100.0
total 237 285 83.1


line stmt bran cond sub pod time code
1             #
2             # BioPerl module for Bio::Map::Position
3             #
4             # Please direct questions and support issues to
5             #
6             # Cared for by Sendu Bala
7             #
8             # Copyright Jason Stajich
9             #
10             # You may distribute this module under the same terms as perl itself
11              
12             # POD documentation - main docs before the code
13              
14             =head1 NAME
15              
16             Bio::Map::Position - A single position of a Marker, or the range over which
17             that marker lies, in a Map
18              
19             =head1 SYNOPSIS
20              
21             use Bio::Map::Position;
22             my $position = Bio::Map::Position->new(-map => $map,
23             -element => $marker,
24             -value => 100
25             );
26              
27             my $position_with_range = Bio::Map::Position->new(-map => $map,
28             -element => $marker,
29             -start => 100,
30             -length => 10
31             );
32              
33             =head1 DESCRIPTION
34              
35             This object is an implementation of the PositionI interface that
36             handles the specific values of a position. This allows a map element
37             (e.g. Marker) to have multiple positions within a map and still be
38             treated as a single entity.
39              
40             This handles the concept of a relative map in which the order of
41             elements and the distance between them is known, but does not
42             directly handle the case when distances are unknown - in that case
43             arbitrary values must be assigned for position values.
44              
45             No units are assumed here - units are handled by context of which Map
46             a position is placed in or the subclass of this Position.
47              
48             =head1 FEEDBACK
49              
50             =head2 Mailing Lists
51              
52             User feedback is an integral part of the evolution of this and other
53             Bioperl modules. Send your comments and suggestions preferably to
54             the Bioperl mailing list. Your participation is much appreciated.
55              
56             bioperl-l@bioperl.org - General discussion
57             http://bioperl.org/wiki/Mailing_lists - About the mailing lists
58              
59             =head2 Support
60              
61             Please direct usage questions or support issues to the mailing list:
62              
63             I
64              
65             rather than to the module maintainer directly. Many experienced and
66             reponsive experts will be able look at the problem and quickly
67             address it. Please include a thorough description of the problem
68             with code and data examples if at all possible.
69              
70             =head2 Reporting Bugs
71              
72             Report bugs to the Bioperl bug tracking system to help us keep track
73             of the bugs and their resolution. Bug reports can be submitted via the
74             web:
75              
76             https://github.com/bioperl/bioperl-live/issues
77              
78             =head1 AUTHOR - Jason Stajich
79              
80             Email jason@bioperl.org
81              
82             =head1 CONTRIBUTORS
83              
84             Lincoln Stein, lstein@cshl.org
85             Heikki Lehvaslaiho, heikki-at-bioperl-dot-org
86             Chad Matsalla, bioinformatics1@dieselwurks.com
87             Sendu Bala, bix@sendu.me.uk
88              
89             =head1 APPENDIX
90              
91             The rest of the documentation details each of the object methods.
92             Internal methods are usually preceded with a _
93              
94             =cut
95              
96             # Let the code begin...
97              
98             package Bio::Map::Position;
99 9     9   900 use strict;
  9         18  
  9         273  
100              
101 9     9   47 use Scalar::Util qw(looks_like_number);
  9         38  
  9         453  
102 9     9   2347 use Bio::Map::Relative;
  9         20  
  9         237  
103              
104 9     9   49 use base qw(Bio::Root::Root Bio::Map::PositionI);
  9         13  
  9         3003  
105              
106             =head2 new
107              
108             Title : new
109             Usage : my $obj = Bio::Map::Position->new();
110             Function: Builds a new Bio::Map::Position object
111             Returns : Bio::Map::Position
112             Args : -map => Bio::Map::MapI object
113             -element => Bio::Map::MappableI object
114             -relative => Bio::Map::RelativeI object
115              
116             * If this position has no range, or if a single value can describe
117             the range *
118             -value => scalar : something that describes the single
119             point position or range of this
120             Position, most likely an int
121              
122             * Or if this position has a range, at least two of *
123             -start => int : value of the start co-ordinate
124             -end => int : value of the end co-ordinate
125             -length => int : length of the range
126              
127             =cut
128              
129             sub new {
130 226     226 1 989 my ($class, @args) = @_;
131 226         555 my $self = $class->SUPER::new(@args);
132            
133 226         922 my ($map, $marker, $element, $value, $start, $end, $length, $relative) =
134             $self->_rearrange([qw( MAP
135             MARKER
136             ELEMENT
137             VALUE
138             START
139             END
140             LENGTH
141             RELATIVE
142             )], @args);
143            
144 226   66     700 my $do_range = defined($start) || defined($end);
145 226 50 66     447 if ($value && $do_range) {
146 0         0 $self->warn("-value and (-start|-end|-length) are mutually exclusive, ignoring value");
147 0         0 $value = undef;
148             }
149            
150 226 100       628 $map && $self->map($map);
151 226 50       360 $marker && $self->element($marker); # backwards compatibility
152 226 100       383 $element && $self->element($element);
153 226 100       368 $relative && $self->relative($relative);
154 226 100       428 defined($value) && $self->value($value);
155            
156 226 100       326 if ($do_range) {
157 145 50       369 defined($start) && $self->start($start);
158 145 100       275 defined($end) && $self->end($end);
159 145 100       188 if ($length) {
160 28 50 33     109 if (defined($start) && ! defined($end)) {
    0          
161 28         50 $self->end($start + $length - 1);
162             }
163             elsif (! defined($start)) {
164 0         0 $self->start($end - $length + 1);
165             }
166             }
167 145 50       183 defined($self->end) || $self->end($start);
168             }
169            
170 226         634 return $self;
171             }
172              
173             =head2 relative
174              
175             Title : relative
176             Usage : my $relative = $position->relative();
177             $position->relative($relative);
178             Function: Get/set the thing this Position's coordinates (numerical(), start(),
179             end()) are relative to, as described by a Relative object.
180             Returns : Bio::Map::RelativeI (default is one describing "relative to the
181             start of the Position's map")
182             Args : none to get, OR
183             Bio::Map::RelativeI to set
184              
185             =cut
186              
187             sub relative {
188 2935     2935 1 3814 my ($self, $relative) = @_;
189 2935 100       3922 if ($relative) {
190 91 50       158 $self->throw("Must supply an object") unless ref($relative);
191 91 50       200 $self->throw("This is [$relative], not a Bio::Map::RelativeI") unless $relative->isa('Bio::Map::RelativeI');
192 91         132 $self->{_relative_not_implicit} = 1;
193 91         154 $self->{_relative} = $relative;
194             }
195 2935   66     5571 return $self->{_relative} || $self->absolute_relative;
196             }
197              
198             =head2 absolute
199              
200             Title : absolute
201             Usage : my $absolute = $position->absolute();
202             $position->absolute($absolute);
203             Function: Get/set how this Position's co-ordinates (numerical(), start(),
204             end()) are reported. When absolute is off, co-ordinates are
205             relative to the thing described by relative(). Ie. the value
206             returned by start() will be the same as the value you set start()
207             to. When absolute is on, co-ordinates are converted to be relative
208             to the start of the map.
209              
210             So if relative() currently points to a Relative object describing
211             "relative to another position which is 100 bp from the start of
212             the map", this Position's start() had been set to 50 and absolute()
213             returns 1, $position->start() will return 150. If absolute() returns
214             0 in the same situation, $position->start() would return 50.
215              
216             Returns : boolean (default 0)
217             Args : none to get, OR
218             boolean to set
219              
220             =cut
221              
222             sub absolute {
223 3438     3438 1 3608 my $self = shift;
224 3438 100       4282 if (@_) { $self->{_absolute} = shift }
  68         85  
225 3438   100     8153 return $self->{_absolute} || 0;
226             }
227              
228             =head2 value
229              
230             Title : value
231             Usage : my $pos = $position->value;
232             Function: Get/Set the value for this position
233             Returns : scalar, value
234             Args : [optional] new value to set
235              
236             =cut
237              
238             sub value {
239 537     537 1 720 my ($self, $value) = @_;
240 537 100       755 if (defined $value) {
241 189         253 $self->{'_value'} = $value;
242 189 100       315 $self->start($self->numeric) unless defined($self->start);
243 189 50       341 $self->end($self->numeric) unless defined($self->end);
244             }
245 537         1085 return $self->{'_value'};
246             }
247              
248             =head2 numeric
249              
250             Title : numeric
251             Usage : my $num = $position->numeric;
252             Function: Read-only method that is guaranteed to return a numeric
253             representation of the start of this position.
254             Returns : scalar numeric
255             Args : none to get the co-ordinate normally (see absolute() method), OR
256             Bio::Map::RelativeI to get the co-ordinate converted to be
257             relative to what this Relative describes.
258              
259             =cut
260              
261             sub numeric {
262 811     811 1 1031 my ($self, $value) = @_;
263 811         984 my $num = $self->{'_value'};
264 811 50       1048 $self->throw("The value has not been set, can't convert to numeric") unless defined($num);
265 811 50       1349 $self->throw("This value [$num] is not numeric") unless looks_like_number($num);
266            
267 811 100 66     2305 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
268             # get the value after co-ordinate conversion
269 569         646 my $raw = $num;
270 569         790 my ($abs_start, $rel_start) = $self->_relative_handler($value);
271 569         1009 return $abs_start + $raw - $rel_start;
272             }
273            
274             # get the value as per absolute
275 242 50 66     445 if ($self->{_relative_not_implicit} && $self->absolute) {
276             # this actually returns the start, but should be the same thing...
277 0         0 return $self->relative->absolute_conversion($self);
278             }
279            
280 242         398 return $num;
281             }
282              
283             =head2 start
284              
285             Title : start
286             Usage : my $start = $position->start();
287             $position->start($start);
288             Function: Get/set the start co-ordinate of this position.
289             Returns : the start of this position
290             Args : scalar numeric to set, OR
291             none to get the co-ordinate normally (see absolute() method), OR
292             Bio::Map::RelativeI to get the co-ordinate converted to be
293             relative to what this Relative describes.
294              
295             =cut
296              
297             sub start {
298 3875     3875 1 5133 my ($self, $value) = @_;
299 3875 100       4802 if (defined $value) {
300 1285 100 66     3538 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
301             # get the value after co-ordinate conversion
302 960         1244 my $raw = $self->{start};
303 960 50       1219 defined $raw || return;
304 960         1478 my ($abs_start, $rel_start) = $self->_relative_handler($value);
305 960         1963 return $abs_start + $raw - $rel_start;
306             }
307             else {
308             # set the value
309 325 50       713 $self->throw("This is [$value], not a number") unless looks_like_number($value);
310 325         431 $self->{start} = $value;
311 325 100       554 $self->value($value) unless defined($self->value);
312             }
313             }
314            
315             # get the value as per absolute
316 2915 100 100     4803 if ($self->{_relative_not_implicit} && $self->absolute) {
317 32         69 return $self->relative->absolute_conversion($self);
318             }
319            
320 2883 100       5629 return defined($self->{start}) ? $self->{start} : return;
321             }
322              
323             =head2 end
324              
325             Title : end
326             Usage : my $end = $position->end();
327             $position->end($end);
328             Function: Get/set the end co-ordinate of this position.
329             Returns : the end of this position
330             Args : scalar numeric to set, OR
331             none to get the co-ordinate normally (see absolute() method), OR
332             Bio::Map::RelativeI to get the co-ordinate converted to be
333             relative to what this Relative describes.
334              
335             =cut
336              
337             sub end {
338 1646     1646 1 1966 my ($self, $value) = @_;
339 1646 100       2056 if (defined $value) {
340 986 100 66     2240 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
341             # get the value after co-ordinate conversion
342 523         677 my $raw = $self->{end};
343 523 50       756 defined $raw || return;
344 523         712 my ($abs_start, $rel_start) = $self->_relative_handler($value);
345 523         1000 return $abs_start + $raw - $rel_start;
346             }
347             else {
348             # set the value
349 463 50       776 $self->throw("This value [$value] is not numeric!") unless looks_like_number($value);
350 463         608 $self->{end} = $value;
351             }
352             }
353            
354             # get the value as per absolute
355 1123 100 100     1780 if ($self->{_relative_not_implicit} && $self->absolute) {
356 29   50     60 my $raw = $self->{end} || return;
357 29   50     58 my $abs_start = $self->relative->absolute_conversion($self) || return;
358 29         114 return $abs_start + ($raw - $self->{start});
359             }
360            
361 1094 100       2295 return defined($self->{end}) ? $self->{end} : return;
362             }
363              
364             =head2 length
365              
366             Title : length
367             Usage : $length = $position->length();
368             Function: Get/set the length of this position's range, changing the end() if
369             necessary. Getting and even setting the length will fail if both
370             start() and end() are not already defined.
371             Returns : the length of this range
372             Args : none to get, OR scalar numeric (>0) to set.
373              
374             =cut
375              
376             sub length {
377 43     43 1 63 my ($self, $length) = @_;
378 43 100       68 if ($length) {
379 14 50       21 $length > 0 || return;
380 14   50     23 my $existing_length = $self->length || return;
381 14 100       31 return $length if $existing_length == $length;
382 3         10 $self->end($self->{start} + $length - 1);
383             }
384            
385 32 50 33     43 if (defined($self->start) && defined($self->end)) {
386 32         43 return $self->end - $self->start + 1;
387             }
388 0         0 return;
389             }
390              
391             =head2 sortable
392              
393             Title : sortable
394             Usage : my $num = $position->sortable();
395             Function: Read-only method that is guaranteed to return a value suitable
396             for correctly sorting this kind of position amongst other positions
397             of the same kind on the same map. Note that sorting different kinds
398             of position together is unlikely to give sane results.
399             Returns : numeric
400             Args : none
401              
402             =cut
403              
404             sub sortable {
405 569     569 1 703 my ($self, $given_map) = @_;
406 569         827 my $answer = $self->numeric($self->absolute_relative);
407 569         1046 return $answer;
408             }
409              
410             =head2 toString
411              
412             Title : toString
413             Usage : print $position->toString(), "\n";
414             Function: stringifies this range
415             Returns : a string representation of the range of this Position
416             Args : optional Bio::Map::RelativeI to have the co-ordinates reported
417             relative to the thing described by that Relative
418              
419             =cut
420              
421             sub toString {
422 83     83 1 1207 my ($self, $rel) = @_;
423 83 50 33     139 if (defined($self->start) && defined($self->end)) {
424 83         133 return $self->start($rel).'..'.$self->end($rel);
425             }
426 0         0 return '';
427             }
428              
429             =head2 absolute_relative
430              
431             Title : absolute_relative
432             Usage : my $rel = $position->absolute_relative();
433             Function: Get a relative describing the start of the map. This is useful for
434             supplying to the coordinate methods (start(), end() etc.) to get
435             the temporary effect of having set absolute(1).
436             Returns : Bio::Map::Relative
437             Args : none
438              
439             =cut
440              
441             sub absolute_relative {
442 2513     2513 1 5530 return Bio::Map::Relative->new(-map => 0, -description => 'start of map');
443             }
444              
445             # get our own absolute start and that of the thing we want as a frame of
446             # reference
447             sub _relative_handler {
448 2052     2052   2319 my ($self, $value) = @_;
449            
450 2052         2776 my $own_relative = $self->relative;
451            
452             # if the requested relative position is the same as the actual
453             # relative, the current co-ordinate values are correct so shortcut
454 2052         3170 my ($own_type, $req_type) = ($own_relative->type, $value->type);
455 2052 100 33     8597 if ($own_type && $req_type && $own_type eq $req_type && $own_relative->$own_type eq $value->$req_type) {
      66        
      100        
456 1292         3128 return (0, 0);
457             }
458            
459 760         1203 my $abs_start = $own_relative->absolute_conversion($self);
460 760         1177 my $rel_start = $value->absolute_conversion($self);
461 760 50 33     1905 $self->throw("Unable to resolve co-ordinate because relative to something that ultimately isn't relative to the map start")
462             unless defined($abs_start) && defined($rel_start);
463            
464 760         1357 return ($abs_start, $rel_start);
465             }
466              
467             1;