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   742 use strict;
  9         11  
  9         238  
100              
101 9     9   29 use Scalar::Util qw(looks_like_number);
  9         21  
  9         388  
102 9     9   2479 use Bio::Map::Relative;
  9         14  
  9         271  
103              
104 9     9   47 use base qw(Bio::Root::Root Bio::Map::PositionI);
  9         11  
  9         3387  
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 825 my ($class, @args) = @_;
131 226         497 my $self = $class->SUPER::new(@args);
132            
133 226         772 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     691 my $do_range = defined($start) || defined($end);
145 226 50 66     434 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       544 $map && $self->map($map);
151 226 50       335 $marker && $self->element($marker); # backwards compatibility
152 226 100       376 $element && $self->element($element);
153 226 100       343 $relative && $self->relative($relative);
154 226 100       366 defined($value) && $self->value($value);
155            
156 226 100       317 if ($do_range) {
157 145 50       337 defined($start) && $self->start($start);
158 145 100       306 defined($end) && $self->end($end);
159 145 100       197 if ($length) {
160 28 50 33     93 if (defined($start) && ! defined($end)) {
    0          
161 28         212 $self->end($start + $length - 1);
162             }
163             elsif (! defined($start)) {
164 0         0 $self->start($end - $length + 1);
165             }
166             }
167 145 50       173 defined($self->end) || $self->end($start);
168             }
169            
170 226         531 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 2469 my ($self, $relative) = @_;
189 2947 100       3622 if ($relative) {
190 91 50       149 $self->throw("Must supply an object") unless ref($relative);
191 91 50       189 $self->throw("This is [$relative], not a Bio::Map::RelativeI") unless $relative->isa('Bio::Map::RelativeI');
192 91         89 $self->{_relative_not_implicit} = 1;
193 91         84 $self->{_relative} = $relative;
194             }
195 2947   66     5570 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 2573 my $self = shift;
224 3444 100       4198 if (@_) { $self->{_absolute} = shift }
  68         81  
225 3444   100     9971 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 465 my ($self, $value) = @_;
240 537 100       733 if (defined $value) {
241 189         193 $self->{'_value'} = $value;
242 189 100       251 $self->start($self->numeric) unless defined($self->start);
243 189 50       280 $self->end($self->numeric) unless defined($self->end);
244             }
245 537         1139 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 642 my ($self, $value) = @_;
263 811         706 my $num = $self->{'_value'};
264 811 50       1028 $self->throw("The value has not been set, can't convert to numeric") unless defined($num);
265 811 50       1290 $self->throw("This value [$num] is not numeric") unless looks_like_number($num);
266            
267 811 100 66     2582 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
268             # get the value after co-ordinate conversion
269 569         483 my $raw = $num;
270 569         778 my ($abs_start, $rel_start) = $self->_relative_handler($value);
271 569         883 return $abs_start + $raw - $rel_start;
272             }
273            
274             # get the value as per absolute
275 242 50 66     394 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         331 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 3376 my ($self, $value) = @_;
299 3888 100       4913 if (defined $value) {
300 1295 100 66     3810 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
301             # get the value after co-ordinate conversion
302 970         945 my $raw = $self->{start};
303 970 50       1314 defined $raw || return;
304 970         1428 my ($abs_start, $rel_start) = $self->_relative_handler($value);
305 970         1762 return $abs_start + $raw - $rel_start;
306             }
307             else {
308             # set the value
309 325 50       703 $self->throw("This is [$value], not a number") unless looks_like_number($value);
310 325         369 $self->{start} = $value;
311 325 100       510 $self->value($value) unless defined($self->value);
312             }
313             }
314            
315             # get the value as per absolute
316 2918 100 100     4846 if ($self->{_relative_not_implicit} && $self->absolute) {
317 32         54 return $self->relative->absolute_conversion($self);
318             }
319            
320 2886 100       5061 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 1275 my ($self, $value) = @_;
339 1649 100       2036 if (defined $value) {
340 986 100 66     2427 if (ref($value) && $value->isa('Bio::Map::RelativeI')) {
341             # get the value after co-ordinate conversion
342 523         499 my $raw = $self->{end};
343 523 50       696 defined $raw || return;
344 523         629 my ($abs_start, $rel_start) = $self->_relative_handler($value);
345 523         949 return $abs_start + $raw - $rel_start;
346             }
347             else {
348             # set the value
349 463 50       753 $self->throw("This value [$value] is not numeric!") unless looks_like_number($value);
350 463         739 $self->{end} = $value;
351             }
352             }
353            
354             # get the value as per absolute
355 1126 100 100     1729 if ($self->{_relative_not_implicit} && $self->absolute) {
356 32   50     69 my $raw = $self->{end} || return;
357 32   50     56 my $abs_start = $self->relative->absolute_conversion($self) || return;
358 32         93 return $abs_start + ($raw - $self->{start});
359             }
360            
361 1094 100       2257 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 33 my ($self, $length) = @_;
378 43 100       60 if ($length) {
379 14 50       23 $length > 0 || return;
380 14   50     19 my $existing_length = $self->length || return;
381 14 100       25 return $length if $existing_length == $length;
382 3         11 $self->end($self->{start} + $length - 1);
383             }
384            
385 32 50 33     40 if (defined($self->start) && defined($self->end)) {
386 32         41 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 446 my ($self, $given_map) = @_;
406 569         729 my $answer = $self->numeric($self->absolute_relative);
407 569         679 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 880 my ($self, $rel) = @_;
423 83 50 33     104 if (defined($self->start) && defined($self->end)) {
424 83         110 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 5783 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   1464 my ($self, $value) = @_;
449            
450 2062         2366 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         3182 my ($own_type, $req_type) = ($own_relative->type, $value->type);
455 2062 100 33     10026 if ($own_type && $req_type && $own_type eq $req_type && $own_relative->$own_type eq $value->$req_type) {
      66        
      100        
456 1302         2293 return (0, 0);
457             }
458            
459 760         1204 my $abs_start = $own_relative->absolute_conversion($self);
460 760         1079 my $rel_start = $value->absolute_conversion($self);
461 760 50 33     2086 $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         1115 return ($abs_start, $rel_start);
465             }
466              
467             1;