bug 2549; fixed small bug in Bio::Taxon which doesn't catch -common_name
[bioperl-live.git] / Bio / Map / Relative.pm
blob35663179f5e5b4582d096e47e0c64a49a5b0f8b7
1 # $Id$
3 # BioPerl module for Bio::Map::Relative
5 # Cared for by Sendu Bala <bix@sendu.me.uk>
7 # Copyright Sendu Bala
9 # You may distribute this module under the same terms as perl itself
11 # POD documentation - main docs before the code
13 =head1 NAME
15 Bio::Map::Relative - Represents what a Position's coordiantes are relative to.
17 =head1 SYNOPSIS
19 # Get a Bio::Map::PositionI somehow
20 my $pos = Bio::Map::Position->new(-value => 100);
22 # its co-ordinates are implicitly relative to the start of its map
23 my $implicit_relative = $pos->relative;
24 my $type = $implicit_relative->type; # $type eq 'map'
25 my $value = $implicit_relative->$type(); # $value == 0
27 # make its co-ordinates relative to another Position
28 my $pos_we_are_relative_to = Bio::Map::Position->new(-value => 200);
29 my $relative = Bio::Map::Relative->new(-position => $pos_we_are_relative_to);
30 $pos->relative($relative);
32 # Get the start co-ordinate of $pos relative to $pos_we_are_relative_to
33 my $start = $pos->start; # $start == 100
35 # Get the start co-ordinate of $pos relative to the start of the map
36 my $abs_start = $relative->absolute_conversion($pos); # $abs_start == 300
37 # - or -
38 $pos->absolute(1);
39 my $abs_start = $pos->start; # $abs_start == 300
40 $pos->absolute(0);
42 # Get the start co-ordinate of $pos relative to a third Position
43 my $pos_frame_of_reference = Bio::Map::Position->new(-value => 10);
44 my $relative2 = Bio::Map::Relative->new(-position => $pos_frame_of_reference);
45 my $start = $pos->start($relative2); # $start == 290
47 =head1 DESCRIPTION
49 A Relative object is used to describe what the co-ordinates (numerical(),
50 start(), end()) of a Position are relative to. By default they are
51 implicitly assumed to be relative to the start of the map the Position is on.
52 But setting the relative() of a Position to one of these objects lets us
53 define otherwise.
55 =head1 FEEDBACK
57 =head2 Mailing Lists
59 User feedback is an integral part of the evolution of this and other
60 Bioperl modules. Send your comments and suggestions preferably to
61 the Bioperl mailing list. Your participation is much appreciated.
63 bioperl-l@bioperl.org - General discussion
64 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
66 =head2 Reporting Bugs
68 Report bugs to the Bioperl bug tracking system to help us keep track
69 of the bugs and their resolution. Bug reports can be submitted via the
70 web:
72 http://bugzilla.open-bio.org/
74 =head1 AUTHOR - Sendu Bala
76 Email bix@sendu.me.uk
78 =head1 APPENDIX
80 The rest of the documentation details each of the object methods.
81 Internal methods are usually preceded with a _
83 =cut
85 # Let the code begin...
87 package Bio::Map::Relative;
88 use strict;
89 use Scalar::Util qw(looks_like_number);
91 use base qw(Bio::Root::Root Bio::Map::RelativeI);
93 =head2 new
95 Title : new
96 Usage : my $relative = Bio::Map::Relative->new();
97 Function: Build a new Bio::Map::Relative object.
98 Returns : Bio::Map::Relative object
99 Args : -map => int : coordinates are relative to this point on the
100 Position's map [default is map => 0, ie.
101 relative to the start of the map],
102 -element => Mappable : or relative to this element's (a
103 Bio::Map::MappableI) position in the map
104 (only works if the given element has only one
105 position in the map the Position belongs to),
106 -position => Position : or relative to this other Position (a
107 Bio::Map::PositionI, fails if the other
108 Position is on a different map to this map)
110 -description => string: Free text description of what this relative
111 describes
113 (To say a Position is relative to something and upstream of it,
114 the Position's start() co-ordinate should be set negative)
116 =cut
118 sub new {
119 my ($class, @args) = @_;
120 my $self = $class->SUPER::new(@args);
122 my ($map, $element, $position, $desc) =
123 $self->_rearrange([qw( MAP ELEMENT POSITION DESCRIPTION )], @args);
125 if (defined($map) + defined($element) + defined($position) > 1) {
126 $self->throw("-map, -element and -position are mutually exclusive");
129 defined($map) && $self->map($map);
130 $element && $self->element($element);
131 $position && $self->position($position);
132 $desc && $self->description($desc);
134 return $self;
137 =head2 absolute_conversion
139 Title : absolute_conversion
140 Usage : my $absolute_coord = $relative->absolute_conversion($pos);
141 Function: Convert the start co-ordinate of the supplied position into a number
142 relative to the start of its map.
143 Returns : scalar number
144 Args : Bio::Map::PositionI object
146 =cut
148 sub absolute_conversion {
149 my ($self, $pos) = @_;
150 $self->throw("Must supply an object") unless ref($pos);
151 $self->throw("This is [$pos], not a Bio::Map::PositionI") unless $pos->isa('Bio::Map::PositionI');
153 # get the raw start position of our position
154 my $prior_abs = $pos->absolute;
155 $pos->absolute(0) if $prior_abs;
156 my $raw = $pos->start;
157 $pos->absolute($prior_abs) if $prior_abs;
158 $self->throw("Can't convert co-ordinates when start isn't set") unless defined($raw); #*** needed? return undef?
160 # what are we relative to?
161 my $type = $self->type;
162 my $value = $self->$type;
163 $self->throw("Details not yet set for this Relative, cannot convert") unless $type && defined($value);
165 # get the absolute start of the thing we're relative to
166 my $map = $pos->map;
167 if ($type eq 'element') {
168 $self->throw("Relative to a Mappable, but the Position has no map") unless $map;
169 my @positions = $value->get_positions($map);
170 $value = shift(@positions);
171 $self->throw("Relative to a Mappable, but this Mappable has no positions on the supplied Position's map") unless $value;
173 if (ref($value)) {
174 # psuedo-recurse
175 my $rel = $value->relative;
176 $value = $rel->absolute_conversion($value);
179 if (defined($value)) {
180 return $value + $raw;
182 return;
185 =head2 type
187 Title : type
188 Usage : my $type = $relative->type();
189 Function: Get the type of thing we are relative to. The types correspond
190 to a method name, so the value of what we are relative to can
191 subsequently be found by $value = $relative->$type;
193 Note that type is set by the last method that was set, or during
194 new().
196 Returns : the string 'map', 'element' or 'position', or undef
197 Args : none
199 =cut
201 sub type {
202 my $self = shift;
203 return $self->{_use} || return;
206 =head2 map
208 Title : map
209 Usage : my $int = $relative->map();
210 $relative->map($int);
211 Function: Get/set the distance from the start of the map that the Position's
212 co-ordiantes are relative to.
213 Returns : int
214 Args : none to get, OR
215 int to set; a value of 0 means relative to the start of the map.
217 =cut
219 sub map {
220 my ($self, $num) = @_;
221 if (defined($num)) {
222 $self->throw("This is [$num], not a number") unless looks_like_number($num);
223 $self->{_use} = 'map';
224 $self->{_map} = $num;
226 return defined($self->{_map}) ? $self->{_map} : return;
229 =head2 element
231 Title : element
232 Usage : my $element = $relative->element();
233 $relative->element($element);
234 Function: Get/set the map element (Mappable) the Position is relative to. If
235 the Mappable has more than one Position on the Position's map, we
236 will be relative to the Mappable's first Position on the map.
237 Returns : Bio::Map::MappableI
238 Args : none to get, OR
239 Bio::Map::MappableI to set
241 =cut
243 sub element {
244 my ($self, $element) = @_;
245 if ($element) {
246 $self->throw("Must supply an object") unless ref($element);
247 $self->throw("This is [$element], not a Bio::Map::MappableI") unless $element->isa('Bio::Map::MappableI');
248 $self->{_use} = 'element';
249 $self->{_element} = $element;
251 return $self->{_element} || return;
254 =head2 position
256 Title : position
257 Usage : my $position = $relative->position();
258 $relative->position($position);
259 Function: Get/set the Position your Position is relative to. Your Position
260 will be made relative to the start of this supplied Position. It
261 makes no difference what maps the Positions are on.
262 Returns : Bio::Map::PositionI
263 Args : none to get, OR
264 Bio::Map::PositionI to set
266 =cut
268 sub position {
269 my ($self, $pos) = @_;
270 if ($pos) {
271 $self->throw("Must supply an object") unless ref($pos);
272 $self->throw("This is [$pos], not a Bio::Map::PositionI") unless $pos->isa('Bio::Map::PositionI');
273 $self->{_use} = 'position';
274 $self->{_position} = $pos;
276 return $self->{_position} || return;
279 =head2 description
281 Title : description
282 Usage : my $description = $relative->description();
283 $relative->description($description);
284 Function: Get/set a textual description of what this relative describes.
285 Returns : string
286 Args : none to get, OR
287 string to set
289 =cut
291 sub description {
292 my $self = shift;
293 if (@_) { $self->{desc} = shift }
294 return $self->{desc} || '';