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   911 use strict;
  9         11  
  9         225  
100              
101 9     9   29 use Scalar::Util qw(looks_like_number);
  9         19  
  9         1132  
102 9     9   2448 use Bio::Map::Relative;
  9         13  
  9         208  
103              
104 9     9   34 use base qw(Bio::Root::Root Bio::Map::PositionI);
  9         11  
  9         3191  
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 934 my ($class, @args) = @_;
131 226         487 my $self = $class->SUPER::new(@args);
132            
133 226         834 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     740 my $do_range = defined($start) || defined($end);
145 226 50 66     454 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       553 $map && $self->map($map);
151 226 50       318 $marker && $self->element($marker); # backwards compatibility
152 226 100       347 $element && $self->element($element);
153 226 100       382 $relative && $self->relative($relative);
154 226 100       372 defined($value) && $self->value($value);
155            
156 226 100       348 if ($do_range) {
157 145 50       417 defined($start) && $self->start($start);
158 145 100       283 defined($end) && $self->end($end);
159 145 100       199 if ($length) {
160 28 50 33     86 if (defined($start) && ! defined($end)) {
    0          
161 28         154 $self->end($start + $length - 1);
162             }
163             elsif (! defined($start)) {
164 0         0 $self->start($end - $length + 1);
165             }
166             }
167 145 50       167 defined($self->end) || $self->end($start);
168             }
169            
170 226         583 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 2947     2947 1 2557 my ($self, $relative) = @_;
189 2947 100       3749 if ($relative) {
190 91 50       205 $self->throw("Must supply an object") unless ref($relative);
191 91 50       215 $self->throw("This is [$relative], not a Bio::Map::RelativeI") unless $relative->isa('Bio::Map::RelativeI');
192 91         104 $self->{_relative_not_implicit} = 1;
193 91         98 $self->{_relative} = $relative;
194             }
195 2947   66     5592 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 3444     3444 1 2472 my $self = shift;
224 3444 100       4259 if (@_) { $self->{_absolute} = shift }
  68         73  
225 3444   100     9843 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 postion
233             Returns : scalar, value
234             Args : [optional] new value to set
235              
236             =cut
237              
238             sub value {
239 537     537 1 441 my ($self, $value) = @_;
240 537 100       696 if (defined $value) {
241 189         193 $self->{'_value'} = $value;
242 189 100       269 $self->start($self->numeric) unless defined($self->start);
243 189 50       282 $self->end($self->numeric) unless defined($self->end);
244             }
245 537         941 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 693 my ($self, $value) = @_;
263 811         765 my $num = $self->{'_value'};
264 811 50       1018 $self->throw("The value has not been set, can't convert to numeric") unless defined($num);
265 811 50       1325 $self->throw("This value [$num] is not numeric") unless looks_like_number($num);
266            
267 811 100 66     2504 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
268             # get the value after co-ordinate conversion
269 569         469 my $raw = $num;
270 569         753 my ($abs_start, $rel_start) = $self->_relative_handler($value);
271 569         847 return $abs_start + $raw - $rel_start;
272             }
273            
274             # get the value as per absolute
275 242 50 66     420 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         334 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 3888     3888 1 3497 my ($self, $value) = @_;
299 3888 100       5182 if (defined $value) {
300 1295 100 66     4138 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
301             # get the value after co-ordinate conversion
302 970         953 my $raw = $self->{start};
303 970 50       1203 defined $raw || return;
304 970         1175 my ($abs_start, $rel_start) = $self->_relative_handler($value);
305 970         2121 return $abs_start + $raw - $rel_start;
306             }
307             else {
308             # set the value
309 325 50       774 $self->throw("This is [$value], not a number") unless looks_like_number($value);
310 325         414 $self->{start} = $value;
311 325 100       541 $self->value($value) unless defined($self->value);
312             }
313             }
314            
315             # get the value as per absolute
316 2918 100 100     5080 if ($self->{_relative_not_implicit} && $self->absolute) {
317 32         53 return $self->relative->absolute_conversion($self);
318             }
319            
320 2886 100       5658 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 1649     1649 1 1369 my ($self, $value) = @_;
339 1649 100       2104 if (defined $value) {
340 986 100 66     2619 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
341             # get the value after co-ordinate conversion
342 523         523 my $raw = $self->{end};
343 523 50       786 defined $raw || return;
344 523         617 my ($abs_start, $rel_start) = $self->_relative_handler($value);
345 523         925 return $abs_start + $raw - $rel_start;
346             }
347             else {
348             # set the value
349 463 50       795 $self->throw("This value [$value] is not numeric!") unless looks_like_number($value);
350 463         582 $self->{end} = $value;
351             }
352             }
353            
354             # get the value as per absolute
355 1126 100 100     1890 if ($self->{_relative_not_implicit} && $self->absolute) {
356 32   50     57 my $raw = $self->{end} || return;
357 32   50     58 my $abs_start = $self->relative->absolute_conversion($self) || return;
358 32         90 return $abs_start + ($raw - $self->{start});
359             }
360            
361 1094 100       2360 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 35 my ($self, $length) = @_;
378 43 100       57 if ($length) {
379 14 50       24 $length > 0 || return;
380 14   50     17 my $existing_length = $self->length || return;
381 14 100       26 return $length if $existing_length == $length;
382 3         6 $self->end($self->{start} + $length - 1);
383             }
384            
385 32 50 33     62 if (defined($self->start) && defined($self->end)) {
386 32         37 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 471 my ($self, $given_map) = @_;
406 569         723 my $answer = $self->numeric($self->absolute_relative);
407 569         806 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 1249 my ($self, $rel) = @_;
423 83 50 33     119 if (defined($self->start) && defined($self->end)) {
424 83         156 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 2516     2516 1 6461 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 2062     2062   1525 my ($self, $value) = @_;
449            
450 2062         2472 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 2062         3107 my ($own_type, $req_type) = ($own_relative->type, $value->type);
455 2062 100 33     10343 if ($own_type && $req_type && $own_type eq $req_type && $own_relative->$own_type eq $value->$req_type) {
      66        
      100        
456 1302         2544 return (0, 0);
457             }
458            
459 760         1012 my $abs_start = $own_relative->absolute_conversion($self);
460 760         1059 my $rel_start = $value->absolute_conversion($self);
461 760 50 33     2167 $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         1212 return ($abs_start, $rel_start);
465             }
466              
467             1;