ensembl-hive  2.7.0
Interval.pm
Go to the documentation of this file.
1 =head1 LICENSE
2 
3 See the NOTICE file distributed with this work for additional information
4 regarding copyright ownership.
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9 
10  http://www.apache.org/licenses/LICENSE-2.0
11 
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 
18 =cut
19 
20 
21 =head1 CONTACT
22 
23  Please email comments or questions to the public Ensembl
24  developers list at <http://lists.ensembl.org/mailman/listinfo/dev>.
25 
26  Questions may also be sent to the Ensembl help desk at
27  <http://www.ensembl.org/Help/Contact>.
28 
29 =cut
30 
31 =head1 NAME
32 
34 
35 =head1 SYNOPSIS
36 
37  # let's get an interval spanning 9e5 bp and associated it with some data
38  my $i1 = Bio::EnsEMBL::Utils::Interval->new(1e5, 1e6, { 'key1' => 'value1', 'key2' => 'value2' });
39 
40  # and another one which overlaps with the previous,
41  # but with scalar associated data
42  my $i2 = Bio::EnsEMBL::Utils::Interval->new(2e5, 3e5, 'a string' );
43 
44  warn "Empty interval(s)\n" if $i1->is_empty or $i2->is_empty;
45  warn "Point interval(s)\n" if $i1->is_point or $i2->is_point;
46 
47  if ($i1->intersects($i2)) {
48  print "I1 and I2 overlap\n";
49  } else {
50  print "I1 and I2 do not overlap\n";
51  }
52 
53  # If an interval is defined with a start > end, then it is assumed
54  # to be spanning the origin on a circular chromosome
55  my $i3 = Bio::EnsEMBL::Utilities::Interval->new(1e5, 1e2);
56  warn "Interval spans the origin" if $i3->spans_origin;
57 
58  etc.
59 
60 =head1 DESCRIPTION
61 
62 A class representing an interval defined on a genomic region. Instances of this
63 class can store arbitrarily defined data. If created with start > end, then it
64 is assumed that this interval is on a circular chromosome spanning the origin.
65 
66 =head1 METHODS
67 
68 =cut
69 
70 package Bio::EnsEMBL::Utils::Interval;
71 
72 use strict;
73 
74 use Scalar::Util qw(looks_like_number);
75 use Bio::EnsEMBL::Utils::Scalar qw(assert_ref);
76 use Bio::EnsEMBL::Utils::Exception qw(throw);
77 
78 =head2 new
79 
80  Arg [1] : scalar $start
81  The start coordinate of the region
82  Arg [2] : scalar $end
83  The end coordinate of the region
84  Arg [3] : (optional) $data
85  The data associated with the interval, can be anything
86  Example : my $i = Bio::EnsEMBL::Utils::Interval(1e2, 2e2, { 'key' => 'value' });
87  my $i2 = Bio::EnsEMBL::Utilities::Interval(1e5, 1e2);
88  $i->spans_origin # returns 0
89  $i2->spans_origin # returns 1
90  Description : Constructor. Creates a new instance
92  Exceptions : Throws an exception if start and end are not defined.
93  Caller : general
94 
95 =cut
96 
97 sub new {
98  my $caller = shift;
99  my $class = ref($caller) || $caller;
100 
101  my ($start, $end, $data) = @_;
102  throw 'Must specify interval boundaries [start, end]'
103  unless defined $start and defined $end;
104 
105  my $spans_origin = 0;
106  if ($start > $end) {
107  $spans_origin = 1;
108  }
109 
110  my $self = bless({ start => $start,
111  end => $end,
112  data => $data ,
113  spans_origin => $spans_origin},
114  $class);
115  return $self;
116 }
117 
118 =head2 start
119 
120  Arg [] : none
121  Description : Returns the start coordinate of the region
122  Returntype : scalar
123  Exceptions : none
124  Caller : general
125 
126 =cut
127 
128 sub start {
129  my $self = shift;
130 
131  return $self->{start};
132 }
133 
134 =head2 end
135 
136  Arg [] : none
137  Description : Returns the end coordinate of the region
138  Returntype : scalar
139  Exceptions : none
140  Caller : general
141 
142 =cut
143 
144 sub end {
145  my $self = shift;
146 
147  return $self->{end};
148 }
149 
150 =head2 data
151 
152  Arg [] : none
153  Description : Returns the data associated with the region
154  Returntype : Any
155  Exceptions : none
156  Caller : general
157 
158 =cut
159 
160 sub data {
161  my $self = shift;
162 
163  return $self->{data};
164 }
165 
166 =head2 spans_origin
167 
168  Arg [] : none
169  Description : Returns whether this interval was created spanning zero
170  (more particularly: if the interval was instantiated with start > end)
171  Returntype : boolean
172  Exceptions : none
173  Caller : general
174 
175 =cut
176 
177 sub spans_origin {
178  my $self = shift;
179 
180  return $self->{spans_origin};
181 }
182 
183 =head2 is_empty
184 
185  Arg [] : none
186  Description : Returns whether or not the interval is empty
187  Returntype : boolean
188  Exceptions : none
189  Caller : general
190 
191 =cut
192 
193 sub is_empty {
194  my $self = shift;
195 
196  if ($self->spans_origin) {
197  return ($self->end >= $self->start);
198  } else {
199  return ($self->start >= $self->end);
200  }
201 }
202 
203 =head2 is_point
204 
205  Arg [] : none
206  Description : Determines if the current interval is a single point
207  Returntype : boolean
208  Exceptions : none
209  Caller : general
210 
211 =cut
212 
213 sub is_point {
214  my $self = shift;
215 
216  return $self->start == $self->end;
217 }
218 
219 =head2 contains
220 
221  Arg [1] : scalar, the point coordinate
222  Description : Determines if the current instance contains the query point
223  Returntype : boolean
224  Exceptions : none
225  Caller : general
226 
227 =cut
228 
229 sub contains {
230  my ($self, $point) = @_;
231 
232  return 0 if $self->is_empty or not defined $point;
233  throw 'point must be a number' unless looks_like_number($point);
234 
235  if ($self->spans_origin) {
236  return ($point >= $self->start or $point <= $self->end);
237  } else {
238  return ($point >= $self->start and $point <= $self->end);
239  }
240 }
241 
242 =head2 intersects
243 
244  Arg [1] : An instance of Bio::EnsEMBL::Utils::Interval
245  Description : Determines if the the instance intersects the given interval
246  Returntype : boolean
247  Exceptions : none
248  Caller : general
249 
250 =cut
251 
252 sub intersects {
253  my ($self, $interval) = @_;
254  assert_ref($interval, 'Bio::EnsEMBL::Utils::Interval');
255 
256  if ($self->spans_origin and $interval->spans_origin) {
257  return 1;
258  } elsif ($self->spans_origin or $interval->spans_origin) {
259  return ($interval->end >= $self->start or $interval->start <= $self->end);
260  } else {
261  return ($self->start <= $interval->end and $interval->start <= $self->end);
262  }
263 }
264 
265 =head2 is_right_of
266 
267  Arg [1] : An instance of Bio::EnsEMBL::Utils::Interval or a scalar
268  Description : Checks if this current interval is entirely to the right of a point
269  or Interval.
270  More formally, the method will return true, if for every point x from
271  the current interval the inequality x > point holds, where point
272  is either a single scalar, or point is the end of another Interval.
273  If spans_origin is true for either this Interval or an Interval
274  passed in, then this method returns false.
275  Returntype : boolean
276  Exceptions : none
277  Caller : general
278 
279 =cut
280 
281 sub is_right_of {
282  my ($self, $other) = @_;
283 
284  return 0 unless defined $other;
285 
286  if ( looks_like_number($other) ) {
287  return $self->spans_origin ?
288  throw "is_right_of not defined for an interval that spans the origin" :
289  $self->start > $other;
290  } elsif ($self->spans_origin or $other->spans_origin) {
291  throw "is_right_of not defined for an interval that spans the origin";
292  } else {
293  return $self->start > $other->end;
294  }
295 }
296 
297 =head2 is_left_of
298 
299  Arg [1] : An instance of Bio::EnsEMBL::Utils::Interval or a scalar
300  Description : Checks if this current interval is entirely to the left of a point
301  or Interval.
302  More formally, the method will return true, if for every point x from
303  the current interval the inequality x < point holds, where point
304  is either a single scalar, or point is the start of another Interval.
305  If spans_origin is true for either this Interval or an Interval
306  passed in, then this method returns false
307  Returntype : boolean
308  Exceptions : none
309  Caller : general
310 
311 =cut
312 
313 sub is_left_of {
314  my ($self, $other) = @_;
315 
316  return 0 unless defined $other;
317 
318  if ( looks_like_number($other) ) {
319  return $self->spans_origin ?
320  throw "is_left_of not defined for an interval that spans the origin" :
321  $self->end < $other;
322  } elsif ($self->spans_origin or $other->spans_origin) {
323  throw "is_left_of not defined for an interval that spans the origin";
324  } else {
325  return $self->end < $other->start;
326  }
327 }
328 
329 1;
330 
Bio::EnsEMBL::Utils::Interval
Definition: Interval.pm:41
Bio::EnsEMBL::Utils::Interval::is_empty
public Boolean is_empty()
Bio::EnsEMBL::Utils::Scalar
Definition: Scalar.pm:66
Bio::EnsEMBL::Utils::Interval::start
public Scalar start()
Bio::EnsEMBL::Utils::Interval::new
public Bio::EnsEMBL::Utils::Interval new()
Bio::EnsEMBL::Utils::Interval::spans_origin
public Boolean spans_origin()
Bio::EnsEMBL::Utils::Exception
Definition: Exception.pm:68