File Coverage

blib/lib/Bio/DB/Das/Chado/Segment/Feature.pm
Criterion Covered Total %
statement 30 405 7.4
branch 0 182 0.0
condition 0 63 0.0
subroutine 10 49 20.4
pod 36 36 100.0
total 76 735 10.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Bio::DB::Das::Chado::Segment::Feature
4              
5             =head1 SYNOPSIS
6              
7             See L<Bio::DB::Das::Chado>.
8              
9             =head1 DESCRIPTION
10              
11             Not yet written
12              
13             =head1 API
14              
15             =cut
16              
17             package Bio::DB::Das::Chado::Segment::Feature;
18              
19 1     1   6 use strict;
  1         3  
  1         36  
20              
21 1     1   5 use Bio::DB::Das::Chado::Segment;
  1         2  
  1         61  
22 1     1   953 use Bio::SeqFeatureI;
  1         25902  
  1         41  
23 1     1   12 use Bio::Root::Root;
  1         2  
  1         22  
24 1     1   993 use Bio::LocationI;
  1         812  
  1         27  
25 1     1   2948 use Data::Dumper;
  1         9563  
  1         78  
26 1     1   781 use URI::Escape;
  1         1519  
  1         65  
27              
28 1     1   8 use constant DEBUG => 0;
  1         2  
  1         66  
29              
30 1     1   6 use vars qw(@ISA $AUTOLOAD %CONSTANT_TAGS $VERSION);
  1         2  
  1         110  
31             @ISA = qw(Bio::DB::Das::Chado::Segment Bio::SeqFeatureI
32             Bio::Root::Root);
33             $VERSION = 0.34;
34              
35             %CONSTANT_TAGS = ();
36              
37 1     1   5 use overload '""' => 'asString';
  1         2  
  1         10  
38              
39             =head2 new
40              
41             Title : new
42             Usage : $f = Bio::DB::Das::Chado::Segment::Feature->new(@args);
43             Function: create a new feature object
44             Returns : new Bio::DB::Das::Chado::Segment::Feature object
45             Args : see below
46             Status : Internal
47              
48             This method is called by Bio::DB::Das::Chado::Segment to create a new
49             feature using information obtained from the chado database.
50              
51             The 11 arguments are positional:
52              
53             $factory a Bio::DB::Das::Chado adaptor object (or descendent)
54             $parent the parent feature object (if it exists)
55             $srcseq the source sequence
56             $start start of this feature
57             $stop stop of this feature
58             $type a Bio::DB::GFF::Typename (containing a method and source)
59             $score the feature's score
60             $strand this feature's strand (relative to the source
61             sequence, which has its own strandedness!)
62             $phase this feature's phase (often with respect to the
63             previous feature in a group of related features)
64             $group this feature's featureloc.locgroup (NOT a GFF holdover)
65             $uniquename this feature's internal unique database
66             name (feature.uniquename)
67             $feature_id the feature's feature_id
68              
69             This is called when creating a feature from scratch. It does not have
70             an inherited coordinate system.
71              
72             =cut
73              
74             sub new {
75 0     0 1   my $package = shift;
76 0           my ($factory,
77             $parent,
78             $srcseq,
79             $start,$end,
80             $type,
81             $score,
82             $strand,
83             $phase,
84             $group,
85             $uniquename,
86             $feature_id) = @_;
87              
88 0           my $self = bless { },$package;
89              
90             #check that this is what you want!
91             #($start,$end) = ($end,$start) if defined($strand) and $strand == -1;
92              
93 0           $self->factory($factory);
94 0 0         $self->parent($parent) if $parent;
95 0           $self->seq_id($srcseq);
96 0           $self->start($start);
97 0           $self->end($end);
98 0           $self->score($score);
99 0           $self->strand($strand);
100 0           $self->phase($phase);
101 0           $self->type($type);
102 0           $self->group($group);
103 0           $self->uniquename($uniquename);
104             # $self->absolute($factory->absolute);
105 0           $self->absolute(1);
106              
107 0           $self->feature_id($feature_id);
108              
109 0 0 0       if ($srcseq && !$parent) {
110 0           $parent = $factory->segment( -name => $srcseq,
111             -start=> $start,
112             -stop => $end,
113             );
114             }
115              
116 0 0 0       $self->srcfeature_id($parent->srcfeature_id() )
117             if (ref $parent && $parent->can('srcfeature_id'));
118              
119 0           return $self;
120             }
121              
122             ######################################################################
123             # feature and featureloc db value slots
124             ######################################################################
125              
126             =head1 feature and featureloc accessors
127              
128             Methods below are accessors for data that is drawn directly from the
129             Chado database and can be considered "primary" accessors for this
130             class.
131              
132             =cut
133              
134             =head2 feature_id()
135              
136             Title : feature_id
137             Usage : $obj->feature_id($newval)
138             Function: holds feature.feature_id
139             Returns : value of feature_id (a scalar)
140             Args : on set, new value (a scalar or undef, optional)
141              
142             Implemented in Bio::DB::Das::Chado::Segment
143              
144             =cut
145              
146             =head2 organism
147              
148             =over
149              
150             =item Usage
151              
152             $obj->organism() #get existing value
153             $obj->organism($newval) #set new value
154              
155             =item Function
156              
157             =item Returns
158              
159             value of organism (a scalar)
160              
161             =item Arguments
162              
163             new value of organism (to set)
164              
165             =back
166              
167             =cut
168              
169             sub organism {
170 0     0 1   my $self = shift;
171 0 0         my $organism = shift if defined(@_);
172 0 0         return $self->{'organism'} = $organism if defined($organism);
173              
174             #if it isn't passed in, we need to try to go get it
175              
176 0           my $dbh = $self->factory->dbh;
177              
178 0           my $organism_query = $dbh->prepare("
179             SELECT genus, species FROM organism WHERE organism_id IN
180             (SELECT organism_id FROM feature WHERE feature_id = ?)
181             ");
182 0           $organism_query->execute($self->feature_id);
183              
184 0           my ($genus, $species) = $organism_query->fetchrow_array;
185              
186 0           $organism_query->finish;
187 0           $self->{'organism'} = "$genus $species";
188 0           return $self->{'organism'};
189             }
190              
191              
192             =head2 group()
193              
194             Title : group
195             Usage : $group = $f->group([$new_group]);
196             Function: Returns a feature name--this is here to maintain backward
197             compatibility with GFF and gbrowse.
198             Returns : value of group (a scalar)
199             Args : on set, new value (a scalar or undef, optional)
200              
201              
202             =cut
203              
204             sub group {
205 0     0 1   my $self = shift;
206              
207 0 0         return $self->{'group'} = shift if @_;
208 0           return $self->{'group'};
209             }
210              
211             =head2 srcfeature_id()
212              
213             Title : srcfeature_id
214             Usage : $obj->srcfeature_id($newval)
215             Function: ???
216             Returns : value of srcfeature_id (a scalar)
217             Args : on set, new value (a scalar or undef, optional)
218              
219              
220             =cut
221              
222             sub srcfeature_id {
223 0     0 1   my $self = shift;
224              
225 0 0         return $self->{'srcfeature_id'} = shift if @_;
226              
227 0           my $feature_id = $self->feature_id;
228 0           my $sf_query = $self->factory->dbh->prepare("select srcfeature_id from featureloc where feature_id = ? and rank=0");
229 0           $sf_query->execute($feature_id);
230 0           my ($sf) = $sf_query->fetchrow_array;
231              
232 0           return $self->{'srcfeature_id'} = $sf;
233             }
234              
235             =head2 strand()
236              
237             Title : strand
238             Usage : $obj->strand()
239             Function: Returns the strand of the feature. Unlike the other
240             methods, the strand cannot be changed once the object is
241             created (due to coordinate considerations).
242             corresponds to featureloc.strand
243             Returns : -1, 0, or 1
244             Args : on set, new value (a scalar or undef, optional)
245              
246              
247             =cut
248              
249             sub strand {
250 0     0 1   my $self = shift;
251              
252 0 0         return $self->{'strand'} = shift if @_;
253 0   0       return $self->{'strand'} || 0;
254             }
255              
256             =head2 phase
257              
258             =over
259              
260             =item Usage
261              
262             $obj->phase() #get existing value
263             $obj->phase($newval) #set new value
264              
265             =item Function
266              
267             =item Returns
268              
269             value of phase (a scalar)
270              
271             =item Arguments
272              
273             new value of phase (to set)
274              
275             =back
276              
277             =cut
278              
279             sub phase {
280 0     0 1   my $self = shift;
281 0 0         return $self->{'phase'} = shift if defined($_[0]);
282 0           return $self->{'phase'};
283             }
284              
285              
286             =head2 type()
287              
288             Title : type
289             Usage : $obj->type($newval)
290             Function: holds a Bio::DB::GFF::Typename object
291             Returns : returns a Bio::DB::GFF::Typename object
292             Args : on set, new value
293              
294             =cut
295              
296             sub type {
297 0     0 1   my $self = shift;
298              
299 0 0         return $self->{'type'} = shift if @_;
300 0           return $self->{'type'};
301             }
302              
303             =head2 uniquename()
304              
305             Title : uniquename
306             Usage : $obj->uniquename($newval)
307             Function: holds feature.uniquename
308             Returns : value of uniquename (a scalar)
309             Args : on set, new value (a scalar or undef, optional)
310              
311             =cut
312              
313             sub uniquename {
314 0     0 1   my $self = shift;
315              
316 0 0         return $self->{'uniquename'} = shift if @_;
317 0           return $self->{'uniquename'};
318             }
319              
320             =head2 is_analysis()
321              
322             Title : is_analysis
323             Usage : $obj->is_analysis($newval)
324             Function: holds feature.is_analysis
325             Returns : value of is_analysis (a scalar)
326             Args : on set, new value (a scalar or undef, optional)
327              
328             =cut
329              
330             sub is_analysis {
331 0     0 1   my $self = shift;
332 0 0         return $self->{'is_analysis'} = shift if defined($_[0]);
333              
334 0           my $dbh = $self->factory->dbh;
335 0           my $fid = $self->feature_id;
336 0           my $sth = $dbh->prepare("SELECT is_analysis FROM feature WHERE feature_id =?");
337 0           $sth->execute($fid);
338              
339 0           my ($is_analysis) = $sth->fetchrow_array;
340              
341 0           $sth->finish;
342 0           return $self->{'is_analysis'} = $is_analysis;
343             }
344              
345              
346             ######################################################################
347             # ISA Bio::SeqFeatureI
348             ######################################################################
349              
350             =head1 SeqFeatureI methods
351              
352             Bio::DB::Das::Chado::Segment::Feature implements the Bio::SeqFeatureI
353             interface. Methods described below, see Bio:SeqFeatureI for more
354             details.
355              
356             =cut
357              
358             =head2 attach_seq()
359              
360             Title : attach_seq
361             Usage : $sf->attach_seq($seq)
362             Function: Attaches a Bio::Seq object to this feature. This
363             Bio::Seq object is for the *entire* sequence: ie
364             from 1 to 10000
365             Example :
366             Returns : TRUE on success
367             Args : a Bio::PrimarySeqI compliant object
368              
369             =cut
370              
371             sub attach_seq {
372 0     0 1   my ($self) = @_;
373              
374 0           $self->throw_not_implemented();
375             }
376              
377             =head2 display_name()
378              
379             Title : display_name
380             Function: aliased to uniquename() for Bio::SeqFeatureI compatibility
381              
382             =cut
383              
384             *display_name = \&group;
385              
386             =head2 entire_seq()
387              
388             Title : entire_seq
389             Usage : $whole_seq = $sf->entire_seq()
390             Function: gives the entire sequence that this seqfeature is attached to
391             Example :
392             Returns : a Bio::PrimarySeqI compliant object, or undef if there is no
393             sequence attached
394             Args : none
395              
396              
397             =cut
398              
399             sub entire_seq {
400 0     0 1   my $self = shift;
401 0           $self->SUPER::seq();
402             }
403              
404             =head2 get_all_tags()
405              
406             Title : get_all_tags
407             Function: aliased to all_tags() for Bio::SeqFeatureI compatibility
408              
409             =cut
410              
411             *get_all_tags = \&all_tags;
412              
413             =head2 get_SeqFeatures()
414              
415             Title : get_SeqFeatures
416             Function: aliased to sub_SeqFeature() for Bio::SeqFeatureI compatibility
417              
418             =cut
419              
420             *get_SeqFeatures = \&sub_SeqFeature;
421              
422             =head2 get_tag_values()
423              
424             Title : get_tag_values
425             Usage : $feature->get_tag_values
426             Function: Returns values associated with a particular tag
427             Returns : A list of values
428             Args : A string (the name of the tag)
429              
430              
431             =cut
432              
433             sub get_tag_values {
434 0     0 1   my $self = shift;
435 0           my $tag = shift;
436              
437 0           my @return = $self->attributes($tag);
438 0           return @return;
439             }
440              
441             =head2 get_tagset_values()
442              
443             Title : get_tagset_values
444             Usage :
445             Function: ???
446             Returns :
447             Args :
448              
449              
450             =cut
451              
452             sub get_tagset_values {
453 0     0 1   my ($self,%arg) = @_;
454              
455 0           $self->throw_not_implemented();
456             }
457              
458             =head2 gff_string()
459              
460             Title : gff_string
461             Usage :
462             Function: ???
463             Returns :
464             Args :
465              
466              
467             =cut
468              
469             sub gff_string {
470 0     0 1   my $self = shift;
471 0           my $recurse = shift;
472 0           my $feature_id=$self->feature_id;
473              
474 0           my $gff_init_query = "SELECT ref,source,type,fstart,fend,score,strand,phase FROM gff3view WHERE feature_id=$feature_id";
475 0           my @row_ary = $self->factory->dbh->selectrow_array($gff_init_query);
476 0           my $string = join("\t",@row_ary)."\t";
477              
478 0           my $gff_atts_query = "SELECT type,attribute from gff3atts where feature_id=?";
479 0           my $sth = $self->factory->dbh->prepare($gff_atts_query);
480 0           $sth->execute($feature_id);
481              
482 0           while (my $hashref = $sth->fetchrow_hashref()) {
483 0           my $attribute = uri_escape($$hashref{'attribute'});
484 0 0         next unless $attribute;
485 0           $string .= "$$hashref{'type'}=$attribute;";
486             }
487              
488 0 0         if ($recurse) {
489 0           foreach($self->sub_SeqFeature) {
490 0           $string .= "\n";
491 0           $string .= $_->gff_string(1);
492             }
493             }
494              
495 0           return $string;
496             }
497              
498             =head2 has_tag()
499              
500             Title : has_tag
501             Usage :
502             Function: ???
503             Returns :
504             Args :
505              
506              
507             =cut
508              
509             sub has_tag {
510 0     0 1   my $self = shift;
511 0           my $tag = shift;
512 0           my %tags = map {$_=>1} $self->all_tags;
  0            
513 0           return $tags{$tag};
514             }
515              
516             =head2 primary_tag()
517              
518             Title : primary_tag
519             Function: aliased to type() for Bio::SeqFeatureI compatibility
520              
521             =cut
522              
523             *primary_tag = \&method;
524              
525             =head2 seq()
526              
527             Title : seq
528             Usage :
529             Function: ???
530             Returns :
531             Args :
532              
533             =cut
534              
535             #sub seq {
536             # my ($self,%arg) = @_;
537             #
538             # $self->throw_not_implemented();
539             #}
540              
541             =head2 seq_id()
542              
543             Title : seq_id
544             Usage : $obj->seq_id($newval)
545             Function: Set or get the name of the reference sequence that the feature
546             resides on.
547             Returns : value of seq_id (a scalar)
548             Args : on set, new value (a scalar or undef, optional)
549              
550              
551             =cut
552              
553             sub seq_id {
554 0     0 1   my $self = shift;
555              
556 0 0         return $self->{'seq_id'} = shift if @_;
557 0 0         return $self->{'seq_id'} if defined $self->{'seq_id'};
558              
559             #OK, no seq_id found, we'll try to find one.
560              
561 0           my $feature_id = $self->feature_id;
562 0 0         return unless $feature_id;
563              
564 0           my $query =<<END
565             SELECT COALESCE(f.name,f.uniquename) AS seq_id
566             FROM feature f join featureloc fl ON (f.feature_id = fl.srcfeature_id)
567             WHERE fl.feature_id = $feature_id AND fl.rank = 0
568             END
569             ;
570              
571 0           my ($seq_id) = $self->factory->dbh->selectrow_array($query);
572              
573 0           return $self->{'seq_id'} = $seq_id;
574             }
575              
576             ######################################################################
577             # ISA Bio::SeqFeatureI
578             ######################################################################
579              
580             =head1 Bio::RangeI methods
581              
582             Bio::SeqFeatureI in turn ISA Bio::RangeI. Bio::RangeI interface
583             methods described below, L<Bio::RangeI> for details.
584              
585             =cut
586              
587             =head2 end()
588              
589             Title : end
590             Function: inherited, L<Bio::DB::Das::Chado::Segment>
591              
592             =cut
593              
594             =head2 start()
595              
596             Title : start
597             Function: inherited, L<Bio::DB::Das::Chado::Segment>
598              
599             =cut
600              
601             =head2 strand()
602              
603             Title : strand
604             Function: inherited, L<Bio::DB::Das::Chado::Segment>
605              
606             =cut
607              
608              
609              
610             ###############################################################
611             # get/setters and their composites, alphabetical
612             ###############################################################
613              
614             =head1 other get/setters
615              
616             =cut
617              
618             =head2 abs_strand()
619              
620             Title : abs_strand
621             Usage : $obj->abs_strand($newval)
622             Function: aliased to strand() for backward compatibility
623              
624             =cut
625              
626             *abs_strand = \&strand;
627              
628             =head2 db_id()
629              
630             Title : db_id
631             Function: aliased to uniquename() for backward compatibility
632              
633             =cut
634              
635             *db_id = \&uniquename;
636              
637             =head2 factory()
638              
639             Title : factory
640             Usage : $obj->factory($newval)
641             Function: ???
642             Returns : value of factory (a scalar)
643             Args : on set, new value (a scalar or undef, optional)
644              
645              
646             =cut
647              
648             sub factory {
649 0     0 1   my $self = shift;
650              
651 0 0         return $self->{'factory'} = shift if @_;
652 0           return $self->{'factory'};
653             }
654              
655             =head2 id()
656              
657             Title : id
658             Function: aliased to uniquename() for backward compatibility
659              
660             =cut
661              
662             *id = \&uniquename;
663              
664             =head2 info()
665              
666             Title : info
667             Function: aliased to uniquename() for backward compatibility
668             with broken generic glyphs primarily
669              
670             =cut
671              
672             *info = \&uniquename;
673              
674             =head2 length()
675              
676             Title : length
677             Usage : $obj->length()
678             Function: convenience for end - start + 1
679             Returns : length of feature in basepairs
680             Args : none
681              
682             =cut
683              
684             sub length {
685 0     0 1   my ($self) = @_;
686 0           my $len = $self->end() - $self->start() +1;
687 0           return $len;
688             }
689              
690             =head2 method()
691              
692             Title : method
693             Usage : $obj->method
694             Function: returns a Feature's method (SOFA type)
695             Returns : the Features SOFA type
696             Args : none
697              
698             =cut
699              
700             sub method {
701 0     0 1   my $self = shift;
702 0           return $self->type->method();
703             }
704              
705             =head2 name()
706              
707             Title : name
708             Function: aliased to group for backward compatibility
709              
710             =cut
711              
712             *name = \&group;
713              
714             =head2 parent()
715              
716             Title : parent
717             Usage : $obj->parent($newval)
718             Function: ???
719             Returns : value of parent (a scalar)
720             Args : on set, new value (a scalar or undef, optional)
721              
722              
723             =cut
724              
725             sub parent {
726 0     0 1   my $self = shift;
727              
728 0 0         return $self->{'parent'} = shift if @_;
729 0           return $self->{'parent'};
730             }
731              
732             =head2 score()
733              
734             Title : score
735             Usage : $obj->score($newval)
736             Function: holds the (alignment?) feature's score
737             Returns : value of score (a scalar)
738             Args : on set, new value (a scalar or undef, optional)
739              
740              
741             =cut
742              
743             sub score {
744 0     0 1   my $self = shift;
745              
746 0 0         return $self->{'score'} = shift if @_;
747 0           return $self->{'score'};
748             }
749              
750             =head2 target()
751              
752             Title : target
753             Usage : $feature->target
754             Function: returns a Bio::DB::Das::Chado::Segment that corresponds
755             to the target of a similarity pair
756             Returns : a Bio::DB::Das::Chado::Segment object
757             Args : none
758              
759             =cut
760              
761             sub target {
762 0     0 1   my ($self) = shift;
763              
764 0           my $self_id = $self->feature_id;
765              
766             #so, we need to construct a segment that corresponds to to the
767             #target sequence. So, what do I need from chado:
768             #
769             # - the feature_id of the target (from featureloc.srcfeature_id
770             # where featureloc.rank > 0 ; when rank = 0, it corresponds
771             # to the feature's coords on the 'main' sequence)
772             # - featureloc.fmin and fmax for the target
773             # - feature.name
774              
775 0           my $query = "SELECT fl.srcfeature_id,fl.fmin,fl.fmax,f.name,f.uniquename
776             FROM featureloc fl JOIN feature f
777             ON (fl.feature_id = ? AND fl.srcfeature_id=f.feature_id)
778             WHERE fl.rank > 0";
779              
780 0           my $sth = $self->factory->dbh->prepare($query);
781 0           $sth->execute($self_id);
782              
783             # While it is theoretically possible for there to be more than
784             # on target per feature, Bio::Graphics::Browser doesn't support it
785              
786 0           my $hashref = $sth->fetchrow_hashref;
787 0           $sth->finish;
788              
789 0 0         if ($$hashref{'name'}) {
790 0           my $segment = Bio::DB::Das::Chado::Segment->new(
791             $$hashref{'name'},
792             $self->factory,
793             $$hashref{'fmin'}+1,
794             $$hashref{'fmax'},
795             $$hashref{'uniquename'},
796             1, #new arg to tell Segment this is a Target
797             $$hashref{'srcfeature_id'},
798             );
799 0           return $segment;
800             }
801 0           return; #didn't get anything
802             }
803              
804             *hit = \&target;
805              
806             #####################################################################
807             # other methods
808             ######################################################################
809              
810             =head1 Other methods
811              
812             =cut
813              
814             =head2 all_tags()
815              
816             Title : all_tags
817             Usage :
818             Function: ???
819             Returns :
820             Args :
821              
822              
823             =cut
824              
825             sub all_tags {
826 0     0 1   my $self = shift;
827 0           my $feature_id = $self->feature_id;
828              
829 0           my $tag_query = $self->factory->dbh->prepare(
830             "SELECT distinct type FROM gffatts WHERE feature_id=?"
831             );
832              
833 0           $tag_query->execute($feature_id);
834              
835 0           my @tags;
836 0           while (my @tag = $tag_query->fetchrow_array) {
837 0           push @tags, $tag[0];
838             }
839              
840 0           return @tags;
841             }
842              
843             =head2 source()
844              
845             Title : source
846             Usage : $f->source();
847             Function: caches and returns the source from a GFF file, this is stored
848             in dbxref with a db of 'GFF_Source'
849             Returns : See above
850             Args : none
851              
852             =cut
853              
854             sub source {
855 0     0 1   my $self = shift;
856              
857 0           return $self->type->source();
858             }
859              
860             =head2 segments()
861              
862             Title : segments
863             Function: aliased to sub_SeqFeature() for compatibility
864              
865              
866             =cut
867              
868             *segments = \&sub_SeqFeature;
869              
870             =head2 subfeatures
871              
872             Title : subfeatures
873             Usage : $obj->subfeatures($newval)
874             Function: returns a list of subfeatures
875             Returns : value of subfeatures (a scalar)
876             FIXME THIS SHOULD RETURN A LIST OR AN ARRAY AND BE DOCUMENTED AS SUCH
877             NOT RETURN AN ARRAYREF OR HASHREF. FOR ADDING/SETTING ELEMENTS WE
878             NEED ADD_ AND SET_ METHODS
879             Args : on set, new value (a scalar or undef, optional)
880              
881              
882             =cut
883              
884             sub subfeatures {
885 0     0 1   my $self = shift;
886              
887 0 0         return $self->{'subfeatures'} = shift if @_;
888 0           return $self->{'subfeatures'};
889             }
890              
891              
892             =head2 sub_SeqFeature()
893              
894             Title : sub_SeqFeature
895             Usage : @feat = $feature->sub_SeqFeature([$type])
896             Function: This method returns a list of any subfeatures
897             that belong to the main feature. For those
898             features that contain heterogeneous subfeatures,
899             you can retrieve a subset of the subfeatures by
900             providing an array of types to filter on.
901              
902             For AcePerl compatibility, this method may also
903             be called as segments().
904             Returns : a list of Bio::DB::Das::Chado::Segment::Feature objects
905             Args : a feature method (optional)
906             Status : Public
907              
908             =cut
909              
910             sub sub_SeqFeature {
911 0     0 1   my($self,@type) = @_;
912              
913 0           my @features;
914              
915             #warn "starting subfeatures";
916              
917             #first call, cache subfeatures
918             #Bio::SeqFeature::CollectionI?
919             #like SeqFeature::Generic?
920              
921             # if(!$self->subfeatures ){
922              
923 0           my $parent_id = $self->feature_id();
924 0           my $inferCDS = $self->factory->inferCDS;
925              
926             #warn "inferCDS:$inferCDS";
927              
928             ##URGI - We get the reference_class feature_id to filter out the sub_features results
929 0   0       my $refclass_feature_id = $self->factory->refclass_feature_id() || undef;
930 0           my($join_part, $where_part);
931 0 0         if(defined($refclass_feature_id)){
932 0           $join_part = " inner join featureloc as parentloc on (parent.feature_id = parentloc.feature_id) ";
933 0           $where_part = "and childloc.srcfeature_id = $refclass_feature_id and parentloc.srcfeature_id = $refclass_feature_id ";
934             }
935              
936 0           my $typewhere = '';
937              
938 0 0         if (@type > 0) {
939 0           my @id_list = map { $self->factory->name2term($_) } @type;
  0            
940              
941              
942             # if CDS features were requested, and inferCDS is set, add
943             # polypeptide and exon features to the list so they can be fetched too
944 0 0 0       if ($inferCDS && grep {'CDS|UTR'} @type ) {
  0            
945             #warn "adding exon and polypeptide to type list\n";
946 0           push @id_list,
947             ( $self->factory->name2term('exon'),
948             $self->factory->name2term('polypeptide') );
949             }
950              
951 0           $typewhere = " and child.type_id in (". join(',',@id_list) .")" ;
952              
953             #warn $typewhere;
954              
955 0           warn "type:@type, type_id:@id_list" if DEBUG;
956             }
957              
958 0           my $handle = $self->factory->dbh();
959              
960             #$self->factory->dbh->trace(2) if DEBUG;
961              
962 0           my $partof = $self->factory->name2term('part_of');
963 0           my $derivesfrom = $self->factory->name2term('derives_from');
964 0 0         $self->throw("part_of cvterm wasn't found. is DB sane?") unless $partof;
965 0 0         $partof = join ',', @$partof if ref($partof) eq 'ARRAY';
966 0 0         $derivesfrom = join ',', @$derivesfrom if ref($derivesfrom) eq 'ARRAY';
967 0           $partof .= ",$derivesfrom";
968              
969             #deal with Tripal oddness
970 0           my $analsysfeature_part ='';
971 0           my $score_part = 0;
972 0 0         unless ($self->factory->tripal()) {
973 0           $analsysfeature_part = <<END
974             left join
975             analysisfeature as af on
976             (child.feature_id = af.feature_id)
977             END
978             ;
979 0           $score_part = 'COALESCE(af.significance,af.identity,af.normscore,af.rawscore)';
980             }
981              
982 0           warn "partof = $partof" if DEBUG;
983              
984             #silencing unit warnings
985 0   0       $typewhere ||='';
986 0   0       $where_part ||='';
987 0   0       $join_part ||='';
988              
989 0           my $sql = "
990             select child.feature_id, child.name, child.type_id, child.uniquename, parent.name as pname, child.is_obsolete,
991             childloc.fmin, childloc.fmax, childloc.strand, childloc.locgroup, childloc.phase, $score_part as score,
992             childloc.srcfeature_id
993             from feature as parent
994             inner join
995             feature_relationship as fr0 on
996             (parent.feature_id = fr0.object_id)
997             inner join
998             feature as child on
999             (child.feature_id = fr0.subject_id)
1000             inner join
1001             featureloc as childloc on
1002             (child.feature_id = childloc.feature_id)
1003             $analsysfeature_part
1004             $join_part
1005             where parent.feature_id = $parent_id
1006             and childloc.rank = 0
1007             and fr0.type_id in ($partof)
1008             $where_part
1009             $typewhere
1010             ";
1011              
1012             #Recursive Mapping
1013             #Construct a query that recursively maps clone's features on the underlying chromosome
1014 0 0         if ($self->factory->recursivMapping){
1015              
1016             #Notes on the interbase computation :
1017             #$self->start is already converted to base coordinates, so we need to substract the unit which has been added by this conversion
1018 0           $sql="
1019             select child.feature_id, child.name, child.type_id, child.uniquename, parent.name as pname,child.is_obsolete,
1020             (childloc.fmin + ".$self->start." - parentloc.fmin -1) AS fmin,
1021             (childloc.fmax + ".$self->start." - parentloc.fmin -1) AS fmax,
1022             (childloc.strand * ".$self->strand." * parentloc.strand) AS strand,
1023             childloc.locgroup, childloc.phase, af.significance as score,
1024             CASE WHEN (
1025             parentloc.srcfeature_id=
1026             (select distinct srcfeature_id from featureloc where feature_id=".$self->feature_id." and rank=0)
1027             )
1028             THEN ".$self->srcfeature_id."
1029             ELSE childloc.srcfeature_id END as srcfeature_id
1030             from feature as parent
1031             inner join
1032             feature_relationship as fr0 on
1033             (parent.feature_id = fr0.object_id)
1034             inner join
1035             feature as child on
1036             (child.feature_id = fr0.subject_id)
1037             inner join
1038             featureloc as childloc on
1039             (child.feature_id = childloc.feature_id)
1040             inner join
1041             featureloc as parentloc on
1042             (parent.feature_id = parentloc.feature_id)
1043             $analsysfeature_part
1044             where parent.feature_id = $parent_id
1045             and childloc.rank = 0
1046             and fr0.type_id in ($partof)
1047             $where_part
1048             $typewhere";
1049             }
1050              
1051             #END Recursive Mapping
1052              
1053 0           $sql =~ s/\s+/ /gs if DEBUG;
1054 0           warn $sql if DEBUG;
1055              
1056 0           my $subfeature_query = $self->factory->dbh->prepare($sql);
1057 0 0         $subfeature_query->execute
1058             or $self->throw("subfeature query failed; here's the sql:$sql");
1059              
1060             #$self->factory->dbh->trace(0) if DEBUG;
1061              
1062 0           my $rows = $subfeature_query->rows;
1063 0 0 0       ($subfeature_query->finish && return)
1064             if ($rows<1); #nothing retrieve during query
1065              
1066 0           my @p_e_cache;
1067 0           while (my $hashref = $subfeature_query->fetchrow_hashref) {
1068              
1069 0 0 0       next if ($$hashref{is_obsolete} and !$self->factory->allow_obsolete);
1070 0 0         next unless $$hashref{srcfeature_id} == $self->srcfeature_id;
1071              
1072             # this problem can't be solved this way--group really needs to return 'name'
1073             # in order for the adaptor to work with gbrowse
1074             # next unless $$hashref{locgroup} eq $self->group; #look out, subfeatures may reside on other segments
1075              
1076 0           my $stop = $$hashref{fmax};
1077 0           my $interbase_start = $$hashref{fmin};
1078 0           my $base_start = $interbase_start +1;
1079              
1080 0           my $source_query = $self->factory->dbh->prepare("
1081             select d.accession from dbxref d,feature_dbxref fd
1082             where fd.feature_id = ? and
1083             fd.dbxref_id = d.dbxref_id and
1084             d.db_id = ?");
1085 0           $source_query->execute($$hashref{feature_id}, $self->factory->gff_source_db_id);
1086              
1087 0           my ($source) = $source_query->fetchrow_array;
1088 0           my $type_obj = Bio::DB::GFF::Typename->new(
1089             $self->factory->term2name($$hashref{type_id}),
1090             $source
1091             );
1092 0           $source_query->finish;
1093              
1094 0           warn "creating new subfeat, $$hashref{name}, $base_start, $stop, $$hashref{phase}" if DEBUG;
1095              
1096 0           my $feat = Bio::DB::Das::Chado::Segment::Feature->new (
1097             $self->factory,
1098             $self,
1099             $self->ref,
1100             $base_start,$stop,
1101             $type_obj,
1102             $$hashref{score},
1103             $$hashref{strand},
1104             $$hashref{phase},
1105             $$hashref{name},
1106             $$hashref{uniquename},
1107             $$hashref{feature_id}
1108             );
1109 0           push @features, $feat;
1110              
1111 0 0 0       if ($inferCDS && ($feat->type =~ /exon/ or $feat->type =~ /polypeptide/ )) {
      0        
1112             #saving an object to an array saves a reference to the object--
1113             #we don't want that, so we have to use the clone method to make a copy
1114 0           push @p_e_cache, $feat->clone;
1115             }
1116             }
1117              
1118 0           $subfeature_query->finish;
1119             #now deal with converting polypeptide and exons to CDS
1120              
1121             my @cds_utr_features
1122 0 0         = $self->_do_the_inferring(@p_e_cache) if (@p_e_cache > 0);
1123 0           push @features, @cds_utr_features;
1124            
1125             #this shouldn't be necessary, as filtering took place via the query
1126             #except that is now that infering of CDS features is a possibility
1127              
1128 0 0 0       if(@type && $inferCDS){
1129 0           my @ok_feats;
1130            
1131 0           my $type_str = join("|", @type);
1132 0           for my $feat (@features) {
1133 0 0         if ($feat->method =~ /$type_str/) {
1134 0           push @ok_feats, $feat;
1135             }
1136             }
1137 0           warn @ok_feats if DEBUG;
1138 0           return @ok_feats;
1139             }
1140            
1141             =item Argh...! DONT DROP THE PROTEIN FEATURE
1142              
1143             dgg: polypeptide or protein is a most important feature, don't drop it!
1144            
1145             This is the part of a gene that has lots of attached critical info:
1146             protein ID, translation, GO terms, Dbxrefs to other proteins)
1147             While this exclusion fixes a display bug, e.g. Glyph/processed_transcript
1148             it is much less problematic to patch the glyph displayers.
1149            
1150             elsif ( 0 && $inferCDS) {
1151             #just remove polypeptide features
1152             my @ok_feats = grep {$_->type->method ne 'polypeptide'} @features;
1153             warn @ok_feats if DEBUG;
1154             return @ok_feats;
1155             }
1156            
1157             =cut
1158              
1159 0           return @features;
1160             }
1161              
1162             =head2 _do_the_inferring
1163              
1164             =over
1165              
1166             =item Usage
1167              
1168             $obj->_do_the_inferring(@features)
1169              
1170             =item Function
1171              
1172             Takes a list of polypeptide and exon features and infers CDS and UTR
1173             features from them.
1174              
1175             =item Returns
1176              
1177             A list of CDS and UTR features
1178              
1179             =item Arguments
1180              
1181             A list of polypeptide and exon features
1182              
1183             =item Caveats
1184              
1185             This function will break with polycistronic genes, as there
1186             will be more than one polypeptide per set of exons, and this
1187             function assumes that there is only one.
1188              
1189             =back
1190              
1191             =cut
1192              
1193             sub _do_the_inferring {
1194 0     0     my ($self, @p_e_feats) = @_;
1195              
1196             #get the polypeptide at the top of the list
1197             #and get the exons in translation order
1198 0 0         my @sorted = sort {
1199 0           $b->type cmp $a->type
1200             || $a->start * $a->strand <=> $b->start * $b->strand
1201             } @p_e_feats;
1202              
1203 0           my ($start,$stop);
1204 0           my $poly = shift @sorted;
1205              
1206 0 0         if ($poly->type->method =~ /poly/) {
1207 0           $start = $poly->start;
1208 0           $stop = $poly->end;
1209             }
1210             else {
1211             #if there's no polypeptide feature, there's no point in continuing
1212 0           return;
1213             }
1214              
1215 0           warn "poly:$poly,start:$start, stop:$stop" if DEBUG;
1216 0           warn $poly->start if DEBUG;
1217 0           warn $poly->end if DEBUG;
1218              
1219              
1220             #keep two arrays: one with exons that are coding, one noncoding
1221 0           my @coding_array;
1222             my @noncoding_array;
1223 0           for (my $i=0; $i < scalar @sorted; $i++) {
1224 0           my $feat = $sorted[$i];
1225              
1226 0 0 0       if ($feat->start < $start and $feat->end < $start) {
    0 0        
    0 0        
1227             #this is a 'left' utr
1228 0 0         if ( $feat->strand ) {
1229 0 0         if ( $feat->strand > 0 ) {
    0          
1230 0           $feat->type->method('five_prime_UTR');
1231             }
1232             elsif ( $feat->strand < 0 ) {
1233 0           $feat->type->method('three_prime_UTR');
1234             }
1235             }
1236             else {
1237 0           $feat->type->method('UTR');
1238             }
1239 0           push @noncoding_array, $feat;
1240             }
1241             elsif ($feat->start > $stop and $feat->end > $stop) {
1242             #this is a 'right' utr
1243 0 0         if ( $feat->strand ) {
1244 0 0         if ( $feat->strand > 0 ) {
    0          
1245 0           $feat->type->method('three_prime_UTR');
1246             }
1247             elsif ( $feat->strand < 0 ) {
1248 0           $feat->type->method('five_prime_UTR');
1249             }
1250             }
1251             else {
1252 0           $feat->type->method('UTR');
1253             }
1254 0           push @noncoding_array, $feat;
1255             }
1256             elsif ($feat->start >= $start and $feat->end <= $stop) {
1257             #this is an 'internal' cds
1258 0           $feat->type->method('CDS');
1259 0           push @coding_array, $feat;
1260             }
1261             else {
1262             #this exon needs to be split into two features (CDS & UTR)
1263 0           my $utr = $feat->clone;
1264             #check for left utr/CDS split
1265 0 0 0       if ( $feat->start < $start and $feat->end >= $start ) {
    0 0        
1266             #this on stradles the left end
1267 0 0         if ( $utr->strand ) {
1268 0 0         if ( $utr->strand > 0 ) {
    0          
1269 0           $utr->type->method('five_prime_UTR');
1270             }
1271             elsif ( $utr->strand < 0 ) {
1272 0           $utr->type->method('three_prime_UTR');
1273             }
1274             }
1275             else {
1276 0           $utr->type->method('UTR');
1277             }
1278 0           $utr->end($start -1);
1279              
1280 0           $feat->type->method('CDS');
1281 0           $feat->start($start);
1282             }
1283             elsif ( $feat->start >= $start and $feat->end > $stop ) {
1284             #this one stradles the right end
1285 0 0         if ( $utr->strand ) {
1286 0 0         if ( $feat->strand > 0 ) {
    0          
1287 0           $utr->type->method('three_prime_UTR');
1288             }
1289             elsif ( $feat->strand < 0 ) {
1290 0           $utr->type->method('five_prime_UTR');
1291             }
1292             }
1293             else {
1294 0           $utr->type->method('UTR');
1295             }
1296 0           $utr->start($stop+1);
1297            
1298 0           $feat->type->method('CDS');
1299 0           $feat->end($stop);
1300             }
1301             else {
1302 0           warn "this should never happen";
1303             }
1304 0           push @noncoding_array, $utr;
1305 0           push @coding_array, $feat;
1306             }
1307             }
1308              
1309 0 0 0       return unless (@coding_array > 0 or @noncoding_array > 0);
1310              
1311 0           my @features;
1312 0 0         if (defined $coding_array[0]->phase) {
1313 0           push @features, @coding_array;
1314             }
1315             else {
1316 0           push @features, $self->_calc_phases(@coding_array);
1317             }
1318              
1319 0           push @features, @noncoding_array;
1320              
1321 0           return @features;
1322             }
1323              
1324              
1325             =head2 _calc_phases
1326              
1327             Title : _calc_phases
1328             Usage : $feature->_calc_phases(@exons)
1329             Function: calculstes phases for exons without phases
1330             Returns : a list of exon feature objects with phases
1331             Args : a list of sorted (by transcription order) exons
1332             Status : private
1333              
1334             =cut
1335              
1336             sub _calc_phases {
1337 0     0     my $self = shift;
1338 0           my @exons = @_;
1339              
1340             # L0 is length of the first segment measured from the start site
1341             # Li is length of the current segment measured from its splice start
1342             # P0 is the phase of the first segment, always 0
1343             # Pi is the phase of the current segment
1344             # P(i+1) = 3 - (Li - Pi) mod 3
1345              
1346 0           $exons[0]->phase(0);
1347              
1348 0           for (my $i = 0; $i < (scalar @exons) -1; $i++) {
1349 0 0         next unless defined $exons[$i];
1350 0           my $phase = (3 - ($exons[$i]->length - $exons[$i]->phase) % 3) % 3;
1351 0           $exons[$i+1]->phase($phase);
1352              
1353 0           warn $exons[$i]->parent." ".$exons[$i]." ".$exons[$i]->start." ".$exons[$i]->phase." ".$exons[$i+1]->phase() if DEBUG;
1354             }
1355              
1356 0           return @exons;
1357             }
1358              
1359              
1360             =head2 notes
1361              
1362             Title : notes
1363             Usage : @notes = $feature->notes
1364             Function: get the "notes" on a particular feature
1365             Returns : an array of string
1366             Args : feature ID
1367             Status : public
1368              
1369             =cut
1370              
1371             sub notes {
1372 0     0 1   my $self = shift;
1373 0           $self->attributes('Note');
1374             }
1375              
1376              
1377             =head2 add_subfeature()
1378              
1379             Title : add_subfeature
1380             Usage : $feature->add_subfeature($feature)
1381             Function: This method adds a new subfeature to the object.
1382             It is used internally by aggregators, but is
1383             available for public use as well.
1384             Returns : nothing
1385             Args : a Bio::DB::Das::Chado::Segment::Feature object
1386             Status : Public
1387              
1388              
1389             =cut
1390              
1391             sub add_subfeature {
1392 0     0 1   my $self = shift;
1393 0           my $subfeature = shift;
1394              
1395             # warn "in add_subfeat:$subfeature";
1396              
1397 0 0         return undef unless ref($subfeature);
1398 0 0         return undef unless $subfeature->isa('Bio::DB::Das::Chado::Segment::Feature');
1399              
1400 0           push @{$self->{subfeatures}}, $subfeature;
  0            
1401 0           return $subfeature;
1402             }
1403              
1404             =head2 location()
1405              
1406             Title : location
1407             Usage : my $location = $seqfeature->location()
1408             Function: returns a location object suitable for identifying location
1409             of feature on sequence or parent feature
1410             Returns : Bio::LocationI object
1411             Args : none
1412              
1413             =cut
1414              
1415             sub location {
1416 0     0 1   my $self = shift;
1417 0 0         require Bio::Location::Split unless Bio::Location::Split->can('new');
1418 0 0         require Bio::Location::Simple unless Bio::Location::Simple->can('new');
1419              
1420 0           my $location;
1421 0 0         if (my @segments = $self->sub_SeqFeature) {
1422 0           $location = Bio::Location::Split->new(-seq_id => $self->seq_id);
1423 0           foreach (@segments) {
1424 0           $location->add_sub_Location($_->location);
1425             }
1426             } else {
1427 0           $location = Bio::Location::Simple->new(-start => $self->start,
1428             -end => $self->stop,
1429             -strand => $self->strand,
1430             -seq_id => $self->seq_id);
1431             }
1432 0           $location;
1433             }
1434              
1435             *merged_segments = \&sub_SeqFeature;
1436              
1437             =head2 clone()
1438              
1439             Title : clone
1440             Usage : $feature = $f->clone
1441             Function: make a copy of the feature
1442             Returns : a new Bio::DB::Das::Chado::Segment::Feature object
1443             Args : none
1444             Status : Public
1445              
1446             This method returns a copy of the feature.
1447              
1448             =cut
1449              
1450             sub clone {
1451 0     0 1   my $self = shift;
1452 0           my $clone = $self->SUPER::clone;
1453              
1454 0 0         if (ref(my $t = $clone->type)) {
1455 0 0         my $type = $t->can('clone') ? $t->clone : bless {%$t},ref $t;
1456 0           $clone->type($type);
1457             }
1458              
1459 0 0         if (ref(my $g = $clone->group)) {
1460 0 0         my $group = $g->can('clone') ? $g->clone : bless {%$g},ref $g;
1461 0           $clone->group($group);
1462             }
1463              
1464 0 0         if (my $merged = $self->{merged_segs}) {
1465 0           $clone->{merged_segs} = { %$merged };
1466             }
1467              
1468 0           $clone;
1469             }
1470              
1471              
1472             =head2 sub_types()
1473              
1474             Title : sub_types
1475             Usage : @methods = $feature->sub_types
1476             Function: get methods of all sub-seqfeatures
1477             Returns : a list of method names
1478             Args : none
1479             Status : Public
1480              
1481             For those features that contain subfeatures, this method will return a
1482             unique list of method names of those subfeatures, suitable for use
1483             with sub_SeqFeature().
1484              
1485             =cut
1486              
1487             sub sub_types {
1488 0     0 1   my $self = shift;
1489 0           $self->warn("this method appears to be broken, check subfeatures() return value");
1490 0 0         my $subfeat = $self->subfeatures or return;
1491 0           return keys %$subfeat;
1492             }
1493              
1494             =head2 Autogenerated Methods
1495              
1496             Title : AUTOLOAD
1497             Usage : @subfeat = $feature->Method
1498             Function: Return subfeatures using autogenerated methods
1499             Returns : a list of Bio::DB::Das::Chado::Segment::Feature objects
1500             Args : none
1501             Status : Public
1502              
1503             Any method that begins with an initial capital letter will be passed
1504             to AUTOLOAD and treated as a call to sub_SeqFeature with the method
1505             name used as the method argument. For instance, this call:
1506              
1507             @exons = $feature->Exon;
1508              
1509             is equivalent to this call:
1510              
1511             @exons = $feature->sub_SeqFeature('exon');
1512              
1513             =cut
1514              
1515             sub AUTOLOAD {
1516 0     0     my($pack,$func_name) = $AUTOLOAD=~/(.+)::([^:]+)$/;
1517 0           my $sub = $AUTOLOAD;
1518 0           my $self = $_[0];
1519              
1520             # ignore DESTROY calls
1521 0 0         return if $func_name eq 'DESTROY';
1522              
1523             # fetch subfeatures if func_name has an initial cap
1524             #return sort {$a->start <=> $b->start} $self->sub_SeqFeature($func_name) if $func_name =~ /^[A-Z]/;
1525 0 0         return $self->sub_SeqFeature($func_name) if $func_name =~ /^[A-Z]/;
1526              
1527             # error message of last resort
1528             #$self->throw(qq(Can't locate object method "$func_name" via package "$pack"));
1529             }
1530              
1531             =head2 adjust_bounds()
1532              
1533             Title : adjust_bounds
1534             Usage : $feature->adjust_bounds
1535             Function: adjust the bounds of a feature
1536             Returns : ($start,$stop,$strand)
1537             Args : none
1538             Status : Public
1539              
1540             This method adjusts the boundaries of the feature to enclose all its
1541             subfeatures. It returns the new start, stop and strand of the
1542             enclosing feature.
1543              
1544             =cut
1545              
1546             # adjust a feature so that its boundaries are synched with its subparts' boundaries.
1547             # this works recursively, so subfeatures can contain other features
1548             sub adjust_bounds {
1549 0     0 1   my $self = shift;
1550 0           my $g = $self->{group};
1551              
1552 0           $self->warn("this method appears to be broken, check subfeatures() return value");
1553              
1554 0 0         if (my $subfeat = $self->subfeatures) {
1555 0           for my $list (values %$subfeat) {
1556 0           for my $feat (@$list) {
1557              
1558             # fix up our bounds to hold largest subfeature
1559 0           my($start,$stop,$strand) = $feat->adjust_bounds;
1560 0 0         $self->{strand} = $strand unless defined $self->{strand};
1561 0 0         if ($start <= $stop) {
1562 0 0 0       $self->{start} = $start if !defined($self->{start}) || $start < $self->{start};
1563 0 0 0       $self->{stop} = $stop if !defined($self->{stop}) || $stop > $self->{stop};
1564             } else {
1565 0 0 0       $self->{start} = $start if !defined($self->{start}) || $start > $self->{start};
1566 0 0 0       $self->{stop} = $stop if !defined($self->{stop}) || $stop < $self->{stop};
1567             }
1568              
1569             # fix up endpoints of targets too (for homologies only)
1570             # my $h = $feat->group;
1571             # next unless $h && $h->isa('Bio::DB::GFF::Homol'); # always false (for now)
1572             # next unless $g && $g->isa('Bio::DB::GFF::Homol');
1573             # ($start,$stop) = ($h->{start},$h->{stop});
1574             # if ($h->strand >= 0) {
1575             # $g->{start} = $start if !defined($g->{start}) || $start < $g->{start};
1576             # $g->{stop} = $stop if !defined($g->{stop}) || $stop > $g->{stop};
1577             # } else {
1578             # $g->{start} = $start if !defined($g->{start}) || $start > $g->{start};
1579             # $g->{stop} = $stop if !defined($g->{stop}) || $stop < $g->{stop};
1580             # }
1581             }
1582             }
1583             }
1584              
1585 0           ( $self->start(),$self->stop(),$self->strand() );
1586             }
1587              
1588             =head2 sort_features()
1589              
1590             Title : sort_features
1591             Usage : $feature->sort_features
1592             Function: sort features
1593             Returns : nothing
1594             Args : none
1595             Status : Public
1596              
1597             This method sorts subfeatures in ascending order by their start
1598             position. For reverse strand features, it sorts subfeatures in
1599             descending order. After this is called sub_SeqFeature will return the
1600             features in order.
1601              
1602             This method is called internally by merged_segments().
1603              
1604             =cut
1605              
1606             # sort features
1607             sub sort_features {
1608 0     0 1   my $self = shift;
1609 0 0         return if $self->{sorted}++;
1610 0 0         my $strand = $self->strand or return;
1611 0 0         my $subfeat = $self->subfeatures or return;
1612 0           for my $type (keys %$subfeat) {
1613 0           $subfeat->{$type} = [map { $_->[0] }
  0            
1614 0           sort {$a->[1] <=> $b->[1] }
1615 0           map { [$_,$_->start] }
1616 0 0         @{$subfeat->{$type}}] if $strand > 0;
1617 0           $subfeat->{$type} = [map { $_->[0] }
  0            
1618 0           sort {$b->[1] <=> $a->[1]}
1619 0           map { [$_,$_->start] }
1620 0 0         @{$subfeat->{$type}}] if $strand < 0;
1621             }
1622             }
1623              
1624             =head2 asString()
1625              
1626             Title : asString
1627             Usage : $string = $feature->asString
1628             Function: return human-readabled representation of feature
1629             Returns : a string
1630             Args : none
1631             Status : Public
1632              
1633             This method returns a human-readable representation of the feature and
1634             is called by the overloaded "" operator.
1635              
1636             =cut
1637              
1638             sub asString {
1639 0     0 1   my $self = shift;
1640 0           my $type = $self->type;
1641 0           my $name = $self->uniquename;
1642              
1643 0 0         return "$type($name)" if $name;
1644 0           return $type;
1645             # my $type = $self->method;
1646             # my $id = $self->group || 'unidentified';
1647             # return join '/',$id,$type,$self->SUPER::asString;
1648             }
1649              
1650             =head2 synonyms()
1651              
1652             Title : synonyms
1653             Usage : @synonyms = $feature->synonyms
1654             Function: return a list of synonyms for a feature
1655             Returns : a list of strings
1656             Args : none
1657             Status : Public
1658              
1659             Looks in the synonym table to collect all synonyms of a feature.
1660              
1661             =cut
1662              
1663              
1664             sub synonyms {
1665             #returns an array with synonyms
1666 0     0 1   my $self = shift;
1667 0           my $dbh = $self->factory->dbh();
1668            
1669 0           my $sth;
1670 0 0         if ($self->factory->use_all_feature_names()) {
1671 0           $sth = $dbh->prepare("
1672             select name from all_feature_names where ? = feature_id
1673             ");
1674             }
1675             else {
1676 0           $sth = $dbh->prepare("
1677             select s.name from synonym s, feature_synonym fs
1678             where ? = fs.feature_id and
1679             fs.synonym_id = s.synonym_id
1680             ");
1681             }
1682 0 0         $sth->execute($self->feature_id()) or $self->throw("synonym query failed");
1683            
1684 0           my $name = $self->display_name;
1685 0           my @synonyms;
1686 0           while (my $hashref = $sth->fetchrow_hashref) {
1687 0 0         push @synonyms, $$hashref{name} if ($$hashref{name} ne $name);
1688             }
1689              
1690 0           $sth->finish;
1691 0           return @synonyms;
1692             }
1693              
1694             =head2 cmap_link()
1695              
1696             Title : cmap_link
1697             Usage : $link = $feature->cmap_link
1698             Function: returns a URL link to the corresponding feature in cmap
1699             Returns : a string
1700             Args : none
1701             Status : Public
1702              
1703             Returns a link to a cmap installation (which is assumed to be on the
1704             same host as gbrowse). In addition to the cmap tables being present
1705             in chado, this method also assumes the presence of a link table called
1706             feature_to_cmap. See the cmap documentation for more information.
1707              
1708             This function is intended primarily to be used in gbrowse conf files.
1709             For example:
1710              
1711             link = sub {my $self = shift; return $self->cmap_link();}
1712              
1713             =cut
1714              
1715              
1716             sub cmap_link {
1717             # Use ONLY if CMap is installed in chado and
1718             # the feature_to_cmap table is also installed
1719             # This table is provided with CMap.
1720 0     0 1   my $self = shift;
1721 0           my $data_source = shift;
1722            
1723 0           my $dbh = $self->factory->dbh();
1724              
1725 0           my $sth = $dbh->prepare("
1726             select cm_f.feature_name,
1727             cm_m.accession_id as map_aid
1728             from cmap_feature cm_f,
1729             cmap_map cm_m,
1730             feature_to_cmap ftc
1731             where ? = ftc.feature_id
1732             and cm_f.accession_id=ftc.cmap_feature_aid
1733             and cm_f.map_id=cm_m.map_id
1734             ");
1735 0 0         $sth->execute($self->feature_id()) or $self->throw("cmap link query
1736             failed");
1737 0           my $link_str='';
1738 0 0         if (my $hashref = $sth->fetchrow_hashref) {
1739            
1740 0           $link_str='/cgi-bin/cmap/viewer?ref_map_aids='.$$hashref{map_aid}.'&data_source='.$data_source.'&highlight='.$$hashref{'feature_name'};
1741             }
1742              
1743 0           $sth->finish;
1744 0           return $link_str;
1745             }
1746              
1747             1;