File Coverage

blib/lib/Bio/Das/Stylesheet.pm
Criterion Covered Total %
statement 40 57 70.1
branch 6 18 33.3
condition 2 4 50.0
subroutine 8 12 66.6
pod 2 7 28.5
total 58 98 59.1


line stmt bran cond sub pod time code
1             package Bio::Das::Stylesheet;
2              
3 1     1   5 use strict;
  1         2  
  1         32  
4              
5 1     1   6 use Carp 'croak';
  1         2  
  1         47  
6 1     1   1198 use Memoize;
  1         2621  
  1         48  
7              
8 1     1   8 use vars qw($VERSION);
  1         1  
  1         977  
9             $VERSION = '1.00';
10              
11             memoize('_glyph');
12              
13              
14             #
15             # Bio::Das::Stylesheet->new();
16             #
17             sub new {
18 1     1 0 4 my $class = shift;
19 1 50       4 $class = ref($class) if ref($class);
20              
21 1         8 return bless { categories => {},
22             lowzoom => 500_000,
23             highzoom => 200,
24             },$class;
25             }
26              
27             sub categories {
28 0     0 1 0 my $self = shift;
29 0         0 keys %{$self->{categories}};
  0         0  
30             }
31              
32             # in a scalar context, return name of glyph
33             # in array context, return name of glyph followed by attribute/value pairs
34             sub glyph {
35 1     1 1 639 my $self = shift;
36 1         3 my $feature = shift;
37 1   50     7 my $length = shift || 0;
38              
39 1         7 local $^W = 0;
40              
41 1 50       8 unless ($length =~ /^\d+$/) {
42 0 0       0 $length = $length eq 'low' ? $self->lowzoom : $self->highzoom;
43             }
44              
45 1 50       7 $feature = $feature->[0]
46             if ref($feature) eq 'ARRAY'; # hack to prevent common error
47              
48 1         2 my ($category,$type);
49 1 50       5 if (ref $feature) {
50 1         2 $category = eval {lc $feature->category};
  1         7  
51 1         4 $type = eval {lc $feature->type};
  1         3  
52             } else {
53 0         0 $type = $feature;
54             }
55              
56 1         43 return $self->_glyph($category,$type,$length);
57             }
58              
59             sub _glyph {
60             my $self = shift;
61             my ($category,$type,$length) = @_;
62              
63             $category = 'default' unless $self->{categories}{$category};
64             $type ||= 'default';
65              
66             (my $base = $type) =~ s/:.+$//;
67             my $zoom = $self->{categories}{$category}{$type};
68             $zoom ||= $self->{categories}{$category}{$base};
69             $zoom ||= $self->{categories}{'default'}{$type};
70             $zoom ||= $self->{categories}{'default'}{$base};
71             $zoom ||= $self->{categories}{'default'}{'default'};
72              
73             my $glyph;
74              
75             # find the best zoom level -- this is a Schwartzian Transform
76             my @zoomlevels = map {$_->[0]}
77             sort {$b->[1]<=>$a->[1]}
78             grep {!$length or $_->[1] <= $length}
79             map { $_ eq 'low' ? [$_ => $self->lowzoom]
80             : $_ eq 'high' ? [$_ => $self->highzoom]
81             : [$_ => $_ || 0] } keys %$zoom;
82              
83              
84             my ($base_glyph,@base_attributes) = _format_glyph($zoom->{$zoomlevels[-1]});
85             my ($zoom_glyph,@zoom_attributes) = _format_glyph($zoom->{$zoomlevels[0]}) if $length;
86             my %attributes = (@base_attributes,@zoom_attributes);
87             $glyph = $zoom_glyph || $base_glyph;
88              
89              
90             # MUNGES!!!
91             if ($glyph eq 'anchored_arrow') { # because the default looks ugly
92             $glyph = 'box';
93             push @base_attributes,(-stranded=>1,
94             -arrowhead=>'filled');
95             }
96            
97             if ($glyph eq 'line') {
98             my $line_type = $attributes{line_style} || $attributes{style};
99             $glyph = 'hat' if $line_type eq 'hat';
100             $glyph = 'dashed_line' if $line_type eq 'dashed';
101             }
102              
103              
104             # warn "stylesheet for $feature returning $glyph ",join ' ',%attributes;
105             # warn "category=$category, type=$type, glyph=$glyph";
106              
107             return wantarray ? ($glyph,%attributes) : $glyph;
108             }
109              
110             # turn configuration into a set of -name=>value pairs suitable for add_track()
111             sub style {
112 0     0 0 0 my $self = shift;
113 0         0 my ($glyph,%attributes) = $self->glyph(@_);
114 0         0 return ($glyph,map {("-$_" => $attributes{$_})} keys %attributes);
  0         0  
115             }
116              
117             # warning: not a method
118             sub _format_glyph {
119 1     1   1 my $glyph = shift;
120 1 50       4 return unless $glyph;
121 1         2 my $name = $glyph->{name};
122 1 50       4 return $name unless wantarray;
123 1         2 return ($name,%{$glyph->{attr}});
  1         6  
124             }
125              
126             sub add_type {
127 1292     1292 0 1508 my $self = shift;
128 1292         2069 my ($category,$type,$zoom,$glyph_name,$attributes) = @_;
129 1292   50     4148 $zoom ||= 0;
130 1292         8030 $self->{categories}{lc $category}{lc $type}{lc $zoom} = { name => $glyph_name, # a string
131             attr => $attributes, # a hashref
132             };
133 1292         5086 $self->{categories}{'default'}{lc $type}{lc $zoom} = $self->{categories}{lc $category}{lc $type}{lc $zoom};
134             # this works around the bug of gff types with no category
135 1292         5090 $self->{categories}{''}{lc $type} = $self->{categories}{lc $category}{lc $type};
136             }
137              
138             sub lowzoom {
139 0     0 0   my $self = shift;
140 0           my $d = $self->{lowzoom};
141 0 0         $self->{lowzoom} = shift if @_;
142 0           $d;
143             }
144              
145             sub highzoom {
146 0     0 0   my $self = shift;
147 0           my $d = $self->{highzoom};
148 0 0         $self->{highzoom} = shift if @_;
149 0           $d;
150             }
151              
152             1;
153              
154             __END__
155              
156             =head1 NAME
157              
158             Bio::Das::Stylesheet - Access to DAS stylesheets
159              
160             =head1 SYNOPSIS
161              
162             use Bio::Das;
163              
164             # contact the DAS server at wormbase.org (0.18 version API)
165             my $das = Bio::Das->new('http://www.wormbase.org/db/das'=>'elegans');
166              
167             # get the stylesheet
168             my $style = $das->stylesheet;
169              
170             # get features
171             my @features = $das->segment(-ref=>'Locus:unc-9')->features;
172              
173             # for each feature, ask the stylesheet what glyph to use
174             for my $f (@features) {
175             my ($glyph_name,@attributes) = $style->glyph($f);
176             }
177              
178              
179             =head1 DESCRIPTION
180              
181             The Bio::Das::Stylesheet class contains information about a remote DAS
182             server's preferred visualization style for sequence features. Each
183             server has zero or one stylesheets for each of the data sources it is
184             responsible for. Stylesheets can provide stylistic guidelines for
185             broad feature categories (such as "transcription"), or strict
186             guidelines for particular feature types (such as "Prosite motif").
187              
188             The glyph names and attributes are broadly compatible with the
189             Bio::Graphics library.
190              
191             =head2 OBJECT CREATION
192              
193             Bio::Das::Stylesheets are created by the Bio::Das object in response
194             to a call to the stylesheet() method. The Bio::Das object must
195             previously have been associated with a data source.
196              
197             =head2 METHODS
198              
199             =over 4
200              
201             =item ($glyph,@attributes) = $stylesheet->glyph($feature)
202              
203             The glyph() method takes a Bio::Das::Segment::Feature object and
204             returns the name of a suggested glyph to use, plus zero or more
205             attributes to apply to the glyph. Glyphs names are described in the
206             DAS specification, and include terms like "box" and "arrow".
207              
208             Attributes are name/value pairs, for instance:
209            
210             (-width => '10', -outlinecolor => 'black')
211              
212             The initial "-" is added to the attribute names to be consistent with
213             the Perl name/value calling style. The attribute list can be passed
214             directly to the Ace::Panel->add_track() method.
215              
216             In a scalar context, glyph() will return just the name of the glyph
217             without the attribute list.
218              
219             =item @categories = $stylesheet->categories
220              
221             Return a list of all the categories known to the stylesheet.
222              
223             =item $source = $stylesheet->source
224              
225             Return the Bio::Das object associated with the stylesheet.
226              
227             =head2 HOW GLYPH() RESOLVES FEATURES
228              
229             When a feature is passed to glyph(), the method checks the feature's
230             type ID and category against the stylesheet. If an exact match is
231             found, then the method returns the corresponding glyph name and
232             attributes. Otherwise, glyph() looks for a default style for the
233             category and returns the glyph and attributes for that. If no
234             category default is found, then glyph() returns its global default.
235              
236             =head2 USING Bio::Das::Stylesheet WITH Bio::Graphics::Panel
237              
238             The stylesheet class was designed to work hand-in-glove with
239             Bio::Graphics::Panel. You can rely entirely on the stylesheet to
240             provide the glyph name and attributes, or provide your own default
241             attributes to fill in those missing from the stylesheet.
242              
243             It is important to bear in mind that Bio::Graphics::Panel only allows
244             a single glyph type to occupy a horizontal track. This means that you
245             must sort the different features by type, determine the suggested
246             glyph for each type, and then create the tracks.
247              
248             The following code fragment illustrates the idiom. After sorting the
249             features by type, we pass the first instance of each type to glyph()
250             in order to recover a glyph name and attributes applicable to the
251             entire track.
252              
253             use Bio::Das;
254             use Bio::Graphics::Panel;
255              
256             my $das = Bio::Das->new('http://www.wormbase.org/db/das'=>'elegans');
257             my $stylesheet = $das->stylesheet;
258             my $segment = $das->segment(-ref=>'Locus:unc-9');
259             @features = $segment->features;
260              
261             my %sort;
262             for my $f (@features) {
263             my $type = $f->type;
264             # sort features by their type, and push them onto anonymous
265             # arrays in the %sort hash.
266             push @{$sort{$type}},$f;
267             }
268             my $panel = Bio::Graphics::Panel->new( -segment => $segment,
269             -width => 800 );
270             for my $type (keys %sort) {
271             my $features = $sort{$type};
272             my ($glyph,@attributes) = $stylesheet->glyph($features->[0]);
273             $panel->add_track($features=>$glyph,@attributes);
274             }
275              
276             To provide your own default attributes to be used in place of those
277             omitted by the stylesheet, just change the last line so that your
278             own attributes follow those provided by the stylesheet:
279              
280             $panel->add_track($features=>$glyph,
281             @attributes,
282             -connectgroups => 1,
283             -key => 1,
284             -labelcolor => 'chartreuse'
285             );
286              
287             =head1 AUTHOR
288              
289             Lincoln Stein <lstein@cshl.org>.
290              
291             Copyright (c) 2001 Cold Spring Harbor Laboratory
292              
293             This library is free software; you can redistribute it and/or modify
294             it under the same terms as Perl itself. See DISCLAIMER.txt for
295             disclaimers of warranty.
296              
297             =head1 SEE ALSO
298              
299             L<Bio::Das>, L<Bio::Graphics::Panel>, L<Bio::Graphics::Track>
300              
301             =cut