| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Bio::Graphics::FeatureFile; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# This package parses and renders a simple tab-delimited format for features. |
|
4
|
|
|
|
|
|
|
# It is simpler than GFF, but still has a lot of expressive power. |
|
5
|
|
|
|
|
|
|
# See __END__ for the file format |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Bio::Graphics::FeatureFile -- A set of Bio::Graphics features, stored in a file |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use Bio::Graphics::FeatureFile; |
|
14
|
|
|
|
|
|
|
my $data = Bio::Graphics::FeatureFile->new(-file => 'features.txt'); |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# create a new panel and render contents of the file onto it |
|
18
|
|
|
|
|
|
|
my $panel = $data->new_panel; |
|
19
|
|
|
|
|
|
|
my $tracks_rendered = $data->render($panel); |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# or do it all in one step |
|
22
|
|
|
|
|
|
|
my ($tracks_rendered,$panel) = $data->render; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# for more control, render tracks individually |
|
25
|
|
|
|
|
|
|
my @feature_types = $data->types; |
|
26
|
|
|
|
|
|
|
for my $type (@feature_types) { |
|
27
|
|
|
|
|
|
|
my $features = $data->features($type); |
|
28
|
|
|
|
|
|
|
my %options = $data->style($type); |
|
29
|
|
|
|
|
|
|
$panel->add_track($features,%options); # assuming we have a Bio::Graphics::Panel |
|
30
|
|
|
|
|
|
|
} |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# get individual settings |
|
33
|
|
|
|
|
|
|
my $est_fg_color = $data->setting(EST => 'fgcolor'); |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# or create the FeatureFile by hand |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# add a type |
|
38
|
|
|
|
|
|
|
$data->add_type(EST => {fgcolor=>'blue',height=>12}); |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# add a feature |
|
41
|
|
|
|
|
|
|
my $feature = Bio::Graphics::Feature->new( |
|
42
|
|
|
|
|
|
|
# params |
|
43
|
|
|
|
|
|
|
); # or some other SeqI |
|
44
|
|
|
|
|
|
|
$data->add_feature($feature=>'EST'); |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
The Bio::Graphics::FeatureFile module reads and parses files that |
|
49
|
|
|
|
|
|
|
describe sequence features and their renderings. It accepts both GFF |
|
50
|
|
|
|
|
|
|
format and a more human-friendly file format described below. Once a |
|
51
|
|
|
|
|
|
|
FeatureFile object has been initialized, you can interrogate it for |
|
52
|
|
|
|
|
|
|
its consistuent features and their settings, or render the entire file |
|
53
|
|
|
|
|
|
|
onto a Bio::Graphics::Panel. |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
This module is a precursor of Jason Stajich's |
|
56
|
|
|
|
|
|
|
Bio::Annotation::Collection class, and fulfills a similar function of |
|
57
|
|
|
|
|
|
|
storing a collection of sequence features. However, it also stores |
|
58
|
|
|
|
|
|
|
rendering information about the features, and does not currently |
|
59
|
|
|
|
|
|
|
follow the CollectionI interface. |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 The File Format |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
There are two types of entry in the file format: feature entries, and |
|
64
|
|
|
|
|
|
|
formatting entries. They can occur in any order. See the Appendix |
|
65
|
|
|
|
|
|
|
for a full example. |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head2 Formatting Entries |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Formatting entries are in the form: |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
[Stanza Name] |
|
72
|
|
|
|
|
|
|
option1 = value1 |
|
73
|
|
|
|
|
|
|
option2 = value2 |
|
74
|
|
|
|
|
|
|
option3 = value3 |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
[Stanza Name 2] |
|
77
|
|
|
|
|
|
|
option1 = value1 |
|
78
|
|
|
|
|
|
|
option2 = value2 |
|
79
|
|
|
|
|
|
|
... |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
There can be zero or more stanzas, each with a unique name. The names |
|
82
|
|
|
|
|
|
|
can contain any character except the [] characters. Each stanza |
|
83
|
|
|
|
|
|
|
consists of one or more option = value pairs, where the option and the |
|
84
|
|
|
|
|
|
|
value are separated by an "=" sign and optional whitespace. Values can |
|
85
|
|
|
|
|
|
|
be continued across multiple lines by indenting the continuation lines |
|
86
|
|
|
|
|
|
|
by one or more spaces, as in: |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
[Named Genes] |
|
89
|
|
|
|
|
|
|
feature = gene |
|
90
|
|
|
|
|
|
|
glyph = transcript2 |
|
91
|
|
|
|
|
|
|
description = These are genes that have been named |
|
92
|
|
|
|
|
|
|
by the international commission on gene naming |
|
93
|
|
|
|
|
|
|
(The Hague). |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Typically configuration stanzas will consist of several Bio::Graphics |
|
96
|
|
|
|
|
|
|
formatting options. A -option=>$value pair passed to |
|
97
|
|
|
|
|
|
|
Bio::Graphics::Panel->add_track() becomes a "option=value" pair in the |
|
98
|
|
|
|
|
|
|
feature file. |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=head2 Feature Entries |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Feature entries can take several forms. At their simplest, they look |
|
103
|
|
|
|
|
|
|
like this: |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Gene B0511.1 Chr1:516..11208 |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
This means that a feature of type "Gene" and name "B0511.1" occupies |
|
108
|
|
|
|
|
|
|
the range between bases 516 and 11208 on a sequence entry named |
|
109
|
|
|
|
|
|
|
Chr1. Columns are separated using whitespace (tabs or spaces). |
|
110
|
|
|
|
|
|
|
Embedded whitespace can be escaped using quote marks or backslashes: |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Gene "My Favorite Gene" Chr1:516..11208 |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 Specifying Positions and Ranges |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
A feature position is specified using a sequence ID (a genbank |
|
117
|
|
|
|
|
|
|
accession number, a chromosome name, a contig, or any other meaningful |
|
118
|
|
|
|
|
|
|
reference system, followed by a colon and a position range. Ranges are |
|
119
|
|
|
|
|
|
|
two integers separated by double dots or the hyphen. Examples: |
|
120
|
|
|
|
|
|
|
"Chr1:516..11208", "ctgA:1-5000". Negative coordinates are allowed, as |
|
121
|
|
|
|
|
|
|
in "Chr1:-187..1000". |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
A discontinuous range ("split location") uses commas to separate the |
|
124
|
|
|
|
|
|
|
ranges. For example: |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
Gene B0511.1 Chr1:516..619,3185..3294,10946..11208 |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
In the case of a split location, the sequence id only has to appear in |
|
129
|
|
|
|
|
|
|
front of the first range. |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Alternatively, a split location can be indicated by repeating the |
|
132
|
|
|
|
|
|
|
features type and name on multiple adjacent lines: |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Gene B0511.1 Chr1:516..619 |
|
135
|
|
|
|
|
|
|
Gene B0511.1 Chr1:3185..3294 |
|
136
|
|
|
|
|
|
|
Gene B0511.1 Chr1:10946..11208 |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
If all the locations are on the same reference sequence, you can |
|
139
|
|
|
|
|
|
|
specify a default chromosome using a "reference=": |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
reference=Chr1 |
|
142
|
|
|
|
|
|
|
Gene B0511.1 516..619 |
|
143
|
|
|
|
|
|
|
Gene B0511.1 3185..3294 |
|
144
|
|
|
|
|
|
|
Gene B0511.1 10946..11208 |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
The default seqid is in effect until the next "reference" line |
|
147
|
|
|
|
|
|
|
appears. |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head2 Feature Tags |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Tags can be added to features by adding a fourth column consisting of |
|
152
|
|
|
|
|
|
|
"tag=value" pairs: |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Gene B0511.1 Chr1:516..619,3185..3294 Note="Putative primase" |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Tags and their values take any form you want, and multiple tags can be |
|
157
|
|
|
|
|
|
|
separated by semicolons. You can also repeat tags multiple times: |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Gene B0511.1 Chr1:516..619,3185..3294 GO_Term=GO:100;GO_Term=GO:2087 |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
Several tags have special meanings: |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
Tag Meaning |
|
164
|
|
|
|
|
|
|
--- ------- |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Type The primary tag for a subfeature. |
|
167
|
|
|
|
|
|
|
Score The score of a feature or subfeature. |
|
168
|
|
|
|
|
|
|
Phase The phase of a feature or subfeature. |
|
169
|
|
|
|
|
|
|
URL A URL to link to (via the Bio::Graphics library). |
|
170
|
|
|
|
|
|
|
Note A note to attach to the feature for display by the Bio::Graphics library. |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
For example, in the common case of an mRNA, you can use the "Type" tag |
|
173
|
|
|
|
|
|
|
to distinguish the parts of the mRNA into UTR and CDS: |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
mRNA B0511.1 Chr1:1..100 Type=UTR |
|
176
|
|
|
|
|
|
|
mRNA B0511.1 Chr1:101..200,300..400,500..800 Type=CDS |
|
177
|
|
|
|
|
|
|
mRNA B0511.1 Chr1:801..1000 Type=UTR |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
The top level feature's primary tag will be "mRNA", and its subparts |
|
180
|
|
|
|
|
|
|
will have types UTR and CDS as indicated. Additional tags that are |
|
181
|
|
|
|
|
|
|
placed in the first line of the feature will be applied to the top |
|
182
|
|
|
|
|
|
|
level. In this example, the note "Putative primase" will be applied to |
|
183
|
|
|
|
|
|
|
the mRNA at the top level of the feature: |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
mRNA B0511.1 Chr1:1..100 Type=UTR;Note="Putative primase" |
|
186
|
|
|
|
|
|
|
mRNA B0511.1 Chr1:101..200,300..400,500..800 Type=CDS |
|
187
|
|
|
|
|
|
|
mRNA B0511.1 Chr1:801..1000 Type=UTR |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head2 Feature Groups |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Features can be grouped so that they are rendered by the "group" |
|
192
|
|
|
|
|
|
|
glyph. To start a group, create a two-column feature entry showing |
|
193
|
|
|
|
|
|
|
the group type and a name for the group. Follow this with a list of |
|
194
|
|
|
|
|
|
|
feature entries with a blank type. For example: |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
EST yk53c10 |
|
197
|
|
|
|
|
|
|
yk53c10.3 15000-15500,15700-15800 |
|
198
|
|
|
|
|
|
|
yk53c10.5 18892-19154 |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
This example is declaring that the ESTs named yk53c10.3 and yk53c10.5 |
|
201
|
|
|
|
|
|
|
belong to the same group named yk53c10. |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head2 Comments |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Lines that begin with the # sign are treated as comments and |
|
206
|
|
|
|
|
|
|
ignored. When a # sign appears within a line, everything to the right |
|
207
|
|
|
|
|
|
|
of the symbol is also ignored, unless it looks like an HTML fragment or |
|
208
|
|
|
|
|
|
|
an HTML color, e.g.: |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# this is ignored |
|
211
|
|
|
|
|
|
|
[Example] |
|
212
|
|
|
|
|
|
|
glyph = generic # this comment is ignored |
|
213
|
|
|
|
|
|
|
bgcolor = #FF0000 |
|
214
|
|
|
|
|
|
|
link = http://www.google.com/search?q=$name#results |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Be careful, because the processing of # signs uses a regexp heuristic. To be safe, |
|
217
|
|
|
|
|
|
|
always put a space after the # sign to make sure it is treated as a comment. |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head2 The #include and #exec Directives |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
The special comment "#include 'filename'" acts like the C preprocessor |
|
222
|
|
|
|
|
|
|
directive and will insert the comments of a named file into the |
|
223
|
|
|
|
|
|
|
position at which it occurs. Relative paths will be treated relative |
|
224
|
|
|
|
|
|
|
to the file in which the #include occurs. Nested #include directives |
|
225
|
|
|
|
|
|
|
(a #include located in a file that is itself an include file) are |
|
226
|
|
|
|
|
|
|
#allowed. You may also use one of the shell wildcard characters * and |
|
227
|
|
|
|
|
|
|
#? to include all matching files in a directory. |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
The following are examples of valid #include directives: |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
#include "/usr/local/share/my_directives.txt" |
|
232
|
|
|
|
|
|
|
#include 'my_directives.txt' |
|
233
|
|
|
|
|
|
|
#include chromosome3_features.gff3 |
|
234
|
|
|
|
|
|
|
#include gff.d/*.conf |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
You can enclose the file path in single or double quotes as shown |
|
237
|
|
|
|
|
|
|
above. If there are no spaces in the filename the quotes are optional. |
|
238
|
|
|
|
|
|
|
The #include directive is case insensitive, allowing you to use |
|
239
|
|
|
|
|
|
|
#INCLUDE or #Include if you prefer. |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Include file processing is not very smart and will not catch all |
|
242
|
|
|
|
|
|
|
circular #include references. You have been warned! |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
The special comment "#exec 'command'" will spawn a shell and |
|
245
|
|
|
|
|
|
|
incorporate the output of the command into the configuration |
|
246
|
|
|
|
|
|
|
file. This command will be executed quite frequently, so it is |
|
247
|
|
|
|
|
|
|
suggested that any time-consuming processing that does not need to be |
|
248
|
|
|
|
|
|
|
performed on the fly each time should be cached in a local file. |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=cut |
|
251
|
|
|
|
|
|
|
|
|
252
|
2
|
|
|
2
|
|
62701
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
85
|
|
|
253
|
2
|
|
|
2
|
|
1288
|
use Bio::Graphics::Feature; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
81
|
|
|
254
|
2
|
|
|
2
|
|
1258
|
use Bio::DB::GFF::Util::Rearrange; |
|
|
2
|
|
|
|
|
1007
|
|
|
|
2
|
|
|
|
|
119
|
|
|
255
|
2
|
|
|
2
|
|
11
|
use Carp 'cluck','carp','croak'; |
|
|
2
|
|
|
|
|
2
|
|
|
|
2
|
|
|
|
|
101
|
|
|
256
|
2
|
|
|
2
|
|
539
|
use IO::File; |
|
|
2
|
|
|
|
|
840
|
|
|
|
2
|
|
|
|
|
277
|
|
|
257
|
2
|
|
|
2
|
|
10
|
use File::Glob ':glob'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
438
|
|
|
258
|
2
|
|
|
2
|
|
1035
|
use Text::ParseWords 'shellwords'; |
|
|
2
|
|
|
|
|
2261
|
|
|
|
2
|
|
|
|
|
116
|
|
|
259
|
2
|
|
|
2
|
|
1776
|
use Bio::DB::SeqFeature::Store; |
|
|
2
|
|
|
|
|
35404
|
|
|
|
2
|
|
|
|
|
95
|
|
|
260
|
2
|
|
|
2
|
|
23
|
use File::Basename 'dirname'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
184
|
|
|
261
|
2
|
|
|
2
|
|
14
|
use File::Spec; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
52
|
|
|
262
|
2
|
|
|
2
|
|
12
|
use Cwd 'getcwd'; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
159
|
|
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# default colors for unconfigured features |
|
265
|
|
|
|
|
|
|
my @COLORS = qw(cyan blue red yellow green wheat turquoise orange); |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# package variable which holds the limited set of libraries accessible |
|
268
|
|
|
|
|
|
|
# from within the Safe::World container (please see the description of |
|
269
|
|
|
|
|
|
|
# the -safe_world option). |
|
270
|
|
|
|
|
|
|
# my $SAFE_LIB; |
|
271
|
|
|
|
|
|
|
|
|
272
|
2
|
|
|
2
|
|
38
|
use constant WIDTH => 600; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
200
|
|
|
273
|
2
|
|
|
2
|
|
10
|
use constant MAX_REMAP => 100; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
544
|
|
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head2 METHODS |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=over 4 |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=item $version = Bio::Graphics::FeatureFile-Eversion |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Return the version number -- needed for API checking by GBrowse |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=cut |
|
284
|
|
|
|
|
|
|
|
|
285
|
0
|
|
|
0
|
1
|
0
|
sub version { return 2 } |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=item $features = Bio::Graphics::FeatureFile-Enew(@args) |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
Create a new Bio::Graphics::FeatureFile using @args to initialize the |
|
290
|
|
|
|
|
|
|
object. Arguments are -name=Evalue pairs: |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Argument Value |
|
293
|
|
|
|
|
|
|
-------- ----- |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
-file Read data from a file path or filehandle. Use |
|
296
|
|
|
|
|
|
|
"-" to read from standard input. |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
-text Read data from a text scalar. |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
-allow_whitespace If true, relax GFF2 and GFF3 parsing rules to allow |
|
301
|
|
|
|
|
|
|
columns to be delimited by whitespace rather than |
|
302
|
|
|
|
|
|
|
tabs. |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
-map_coords Coderef containing a subroutine to use for remapping |
|
305
|
|
|
|
|
|
|
all coordinates. |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
-smart_features Flag indicating that the features created by this |
|
308
|
|
|
|
|
|
|
module should be made aware of the FeatureFile |
|
309
|
|
|
|
|
|
|
object by calling their configurator() method. |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
-safe Indicates that the contents of this file is trusted. |
|
312
|
|
|
|
|
|
|
Any option value that begins with the string "sub {" |
|
313
|
|
|
|
|
|
|
or \&subname will be evaluated as a code reference. |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
-safe_world If the -safe option is not set, and -safe_world |
|
316
|
|
|
|
|
|
|
is set to a true value, then Bio::Graphics::FeatureFile |
|
317
|
|
|
|
|
|
|
will evalute "sub {}" options in a L |
|
318
|
|
|
|
|
|
|
environment with minimum permissions. Subroutines |
|
319
|
|
|
|
|
|
|
will be able to access and interrogate |
|
320
|
|
|
|
|
|
|
Bio::DB::SeqFeature objects and perform basic Perl |
|
321
|
|
|
|
|
|
|
operations, but will have no ability to load or |
|
322
|
|
|
|
|
|
|
access other modules, to access the file system, |
|
323
|
|
|
|
|
|
|
or to make system calls. This feature depends on |
|
324
|
|
|
|
|
|
|
availability of the CPAN-installable L |
|
325
|
|
|
|
|
|
|
module. |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
The -file and -text arguments are mutually exclusive, and -file will |
|
328
|
|
|
|
|
|
|
supersede the other if both are present. |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
-map_coords points to a coderef with the following signature: |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
($newref,[$start1,$end1],[$start2,$end2]....) |
|
333
|
|
|
|
|
|
|
= coderef($ref,[$start1,$end1],[$start2,$end2]...) |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
See the Bio::Graphics::Browser (part of the generic genome browser |
|
336
|
|
|
|
|
|
|
package) for an illustration of how to use this to do wonderful stuff. |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
The -smart_features flag is used by the generic genome browser to |
|
339
|
|
|
|
|
|
|
provide features with a way to access the link-generation code. See |
|
340
|
|
|
|
|
|
|
gbrowse for how this works. |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
If the file is trusted, and there is an option named "init_code" in |
|
343
|
|
|
|
|
|
|
the [GENERAL] section of the file, it will be evaluated as perl code |
|
344
|
|
|
|
|
|
|
immediately after parsing. You can use this to declare global |
|
345
|
|
|
|
|
|
|
variables and subroutines for use in option values. |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=cut |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
# args array: |
|
350
|
|
|
|
|
|
|
# -file => parse from a file (- allowed for ARGV) |
|
351
|
|
|
|
|
|
|
# -text => parse from a text scalar |
|
352
|
|
|
|
|
|
|
# -map_coords => code ref to do coordinate mapping |
|
353
|
|
|
|
|
|
|
# called with ($ref,[$start1,$stop1],[$start2,$stop2]...) |
|
354
|
|
|
|
|
|
|
# returns ($newref,$new_coord1,$new_coord2...) |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub new { |
|
357
|
1
|
|
|
1
|
1
|
134
|
shift->_new(@_); |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub _new { |
|
361
|
1
|
|
|
1
|
|
2
|
my $class = shift; |
|
362
|
1
|
|
|
|
|
4
|
my %args = @_; |
|
363
|
1
|
|
|
|
|
11
|
my $self = bless { |
|
364
|
|
|
|
|
|
|
config => {}, |
|
365
|
|
|
|
|
|
|
features => {}, |
|
366
|
|
|
|
|
|
|
seenit => {}, |
|
367
|
|
|
|
|
|
|
types => [], |
|
368
|
|
|
|
|
|
|
max => undef, |
|
369
|
|
|
|
|
|
|
min => undef, |
|
370
|
|
|
|
|
|
|
stat => [], |
|
371
|
|
|
|
|
|
|
refs => {}, |
|
372
|
|
|
|
|
|
|
safe => undef, |
|
373
|
|
|
|
|
|
|
safe_world => undef, |
|
374
|
|
|
|
|
|
|
},$class; |
|
375
|
|
|
|
|
|
|
$self->{coordinate_mapper} = $args{-map_coords} |
|
376
|
1
|
50
|
33
|
|
|
5
|
if exists $args{-map_coords} && ref($args{-map_coords}) eq 'CODE'; |
|
377
|
|
|
|
|
|
|
|
|
378
|
1
|
50
|
|
|
|
5
|
$self->smart_features($args{-smart_features}) if exists $args{-smart_features}; |
|
379
|
1
|
50
|
|
|
|
9
|
$self->{safe} = $args{-safe} if exists $args{-safe}; |
|
380
|
1
|
50
|
|
|
|
5
|
$self->safe_world(1) if $args{-safe_world}; |
|
381
|
1
|
50
|
|
|
|
3
|
$self->allow_whitespace(1) if $args{-allow_whitespace}; |
|
382
|
|
|
|
|
|
|
|
|
383
|
1
|
|
|
|
|
5
|
$self->init_parse(); |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# call with |
|
386
|
|
|
|
|
|
|
# -file |
|
387
|
|
|
|
|
|
|
# -text |
|
388
|
1
|
50
|
|
|
|
4
|
if (my $file = $args{-file}) { |
|
|
|
0
|
|
|
|
|
|
|
389
|
2
|
|
|
2
|
|
13
|
no strict 'refs'; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
22035
|
|
|
390
|
1
|
50
|
|
|
|
9
|
if (defined fileno($file)) { # a filehandle |
|
|
|
50
|
|
|
|
|
|
|
391
|
0
|
|
|
|
|
0
|
$self->parse_fh($file); |
|
392
|
|
|
|
|
|
|
} elsif ($file eq '-') { |
|
393
|
0
|
|
|
|
|
0
|
$self->parse_argv(); |
|
394
|
|
|
|
|
|
|
} else { |
|
395
|
1
|
|
|
|
|
5
|
$self->parse_file($file); |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
} elsif (my $text = $args{-text}) { |
|
398
|
0
|
|
|
|
|
0
|
$self->parse_text($text); |
|
399
|
|
|
|
|
|
|
} |
|
400
|
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
0
|
$self->finish_parse(); |
|
402
|
0
|
|
|
|
|
0
|
return $self; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=item $features = Bio::Graphics::FeatureFile-Enew_from_cache(@args) |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
Like new() but caches the parsed file in /tmp/bio_graphics_ff_cache_* |
|
408
|
|
|
|
|
|
|
(where * is the UID of the current user). This can speed up parsing |
|
409
|
|
|
|
|
|
|
tremendously for files that have many includes. |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
Note that the presence of an #exec statement always invalidates the |
|
412
|
|
|
|
|
|
|
cache and causes a full parse. |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=cut |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
sub new_from_cache { |
|
417
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
418
|
0
|
|
|
|
|
0
|
my %args = @_; |
|
419
|
0
|
|
|
|
|
0
|
my $has_libs; |
|
420
|
|
|
|
|
|
|
|
|
421
|
0
|
0
|
|
|
|
0
|
unless ($has_libs = defined &nfreeze) { |
|
422
|
0
|
|
|
|
|
0
|
$has_libs = eval <
|
|
423
|
|
|
|
|
|
|
use Storable 'lock_store','lock_retrieve'; |
|
424
|
|
|
|
|
|
|
use File::Path 'mkpath'; |
|
425
|
|
|
|
|
|
|
1; |
|
426
|
|
|
|
|
|
|
END |
|
427
|
0
|
0
|
|
|
|
0
|
warn "You need Storable to use new_from_cache(); returning uncached data" unless $has_libs; |
|
428
|
|
|
|
|
|
|
} |
|
429
|
|
|
|
|
|
|
|
|
430
|
0
|
|
|
|
|
0
|
$Storable::Deparse = 1; |
|
431
|
0
|
|
|
|
|
0
|
$Storable::Eval = 1; |
|
432
|
|
|
|
|
|
|
|
|
433
|
0
|
0
|
0
|
|
|
0
|
my $file = $has_libs && $args{-file} or return $self->_new(@_); |
|
434
|
0
|
|
|
|
|
0
|
(my $name = $args{-file}) =~ s!/!_!g; |
|
435
|
0
|
|
|
|
|
0
|
my $cachefile = $self->cachefile($name); |
|
436
|
0
|
0
|
0
|
|
|
0
|
if (-e $cachefile && (stat(_))[9] >= $self->file_mtime($args{-file})) { # cache is valid |
|
437
|
|
|
|
|
|
|
# if (-e $cachefile && -M $cachefile < 0) { # cache is valid |
|
438
|
0
|
|
|
|
|
0
|
my $parsed_file = lock_retrieve($cachefile); |
|
439
|
0
|
0
|
|
|
|
0
|
$parsed_file->initialize_code if $parsed_file->safe; |
|
440
|
0
|
|
|
|
|
0
|
return $parsed_file; |
|
441
|
|
|
|
|
|
|
} else { |
|
442
|
0
|
|
|
|
|
0
|
mkpath(dirname($cachefile)); |
|
443
|
0
|
|
|
|
|
0
|
my $parsed = $self->_new(@_); |
|
444
|
0
|
|
|
|
|
0
|
$parsed->initialize_code(); |
|
445
|
0
|
|
|
|
|
0
|
eval {lock_store($parsed,$cachefile)}; |
|
|
0
|
|
|
|
|
0
|
|
|
446
|
0
|
0
|
|
|
|
0
|
warn $@ if $@; |
|
447
|
0
|
|
|
|
|
0
|
return $parsed; |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub cachedir { |
|
453
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
454
|
0
|
|
|
|
|
0
|
my $uid = $<; |
|
455
|
0
|
|
|
|
|
0
|
return File::Spec->catfile(File::Spec->tmpdir,"bio_graphics_ff_cache_${uid}"); |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
sub cachefile { |
|
459
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
460
|
0
|
|
|
|
|
0
|
my $name = shift; |
|
461
|
0
|
|
|
|
|
0
|
return File::Spec->catfile($self->cachedir,$name); |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=item $mtime = Bio::Graphics::FeatureFile->file_mtime($path) |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Return the modification time of the indicated feature file without performing a full parse. This |
|
467
|
|
|
|
|
|
|
takes into account the various #include and #exec directives and returns the maximum mtime of |
|
468
|
|
|
|
|
|
|
any of the included files. Any #exec directive will return the current time. This is |
|
469
|
|
|
|
|
|
|
useful for caching the parsed data structure. |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=back |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=cut |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
sub file_mtime { |
|
476
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
477
|
|
|
|
|
|
|
|
|
478
|
0
|
|
|
|
|
0
|
my $file = shift; |
|
479
|
0
|
|
|
|
|
0
|
my $mtime = 0; |
|
480
|
|
|
|
|
|
|
|
|
481
|
0
|
|
|
|
|
0
|
for my $f (glob($file)) { |
|
482
|
0
|
0
|
|
|
|
0
|
my $m = (stat($f))[9] or next; |
|
483
|
0
|
0
|
|
|
|
0
|
$mtime = $m if $mtime < $m; |
|
484
|
0
|
0
|
|
|
|
0
|
open my $fh,'<',$file or next; |
|
485
|
0
|
|
|
|
|
0
|
my $cwd = getcwd(); |
|
486
|
0
|
|
|
|
|
0
|
chdir(dirname($file)); |
|
487
|
|
|
|
|
|
|
|
|
488
|
0
|
|
|
|
|
0
|
local $_; |
|
489
|
0
|
|
|
|
|
0
|
while (<$fh>) { |
|
490
|
0
|
0
|
|
|
|
0
|
if (/^\#exec/) { |
|
491
|
0
|
|
|
|
|
0
|
return time(); # now! |
|
492
|
|
|
|
|
|
|
} |
|
493
|
0
|
0
|
|
|
|
0
|
if (/^\#include\s+(.+)/i) { # #include directive |
|
494
|
0
|
|
|
|
|
0
|
my ($include_file) = shellwords($1); |
|
495
|
0
|
|
|
|
|
0
|
my $m = $self->file_mtime($include_file); |
|
496
|
0
|
0
|
|
|
|
0
|
$mtime = $m if $mtime < $m; |
|
497
|
|
|
|
|
|
|
} |
|
498
|
|
|
|
|
|
|
} |
|
499
|
0
|
|
|
|
|
0
|
chdir($cwd); |
|
500
|
|
|
|
|
|
|
} |
|
501
|
|
|
|
|
|
|
|
|
502
|
0
|
|
|
|
|
0
|
return $mtime; |
|
503
|
|
|
|
|
|
|
} |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
sub file_list { |
|
506
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
507
|
0
|
|
|
|
|
0
|
my @list = (); |
|
508
|
0
|
|
|
|
|
0
|
my $file = shift; |
|
509
|
|
|
|
|
|
|
|
|
510
|
0
|
|
|
|
|
0
|
for my $f (glob($file)) { |
|
511
|
0
|
0
|
|
|
|
0
|
open my $fh,'<',$file or next; |
|
512
|
0
|
|
|
|
|
0
|
my $cwd = getcwd(); |
|
513
|
0
|
|
|
|
|
0
|
chdir(dirname($file)); |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
|
|
516
|
0
|
|
|
|
|
0
|
while (<$fh>) { |
|
517
|
0
|
0
|
|
|
|
0
|
if (/^\#include\s+(.+)/i) { # #include directive |
|
518
|
0
|
|
|
|
|
0
|
my ($include_file) = shellwords($1); |
|
519
|
0
|
|
|
|
|
0
|
my @files = glob($include_file); |
|
520
|
0
|
0
|
|
|
|
0
|
@files ? @list = (@list,@files) : push(@list,$include_file); |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
} |
|
523
|
0
|
|
|
|
|
0
|
chdir($cwd); |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
|
|
526
|
0
|
|
|
|
|
0
|
return \@list; |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# render our features onto a panel using configuration data |
|
530
|
|
|
|
|
|
|
# return the number of tracks inserted |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=over 4 |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=item ($rendered,$panel,$tracks) = $features-Erender([$panel, $position_to_insert, $options, $max_bump, $max_label, $selector]) |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
Render features in the data set onto the indicated |
|
537
|
|
|
|
|
|
|
Bio::Graphics::Panel. If no panel is specified, creates one. |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
All arguments are optional. |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
$panel is a Bio::Graphics::Panel that has previously been created and |
|
542
|
|
|
|
|
|
|
configured. |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
$position_to_insert indicates the position at which to start inserting |
|
545
|
|
|
|
|
|
|
new tracks. The last current track on the panel is assumed. |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
$options is a scalar used to control automatic expansion of the |
|
548
|
|
|
|
|
|
|
tracks. 0=auto, 1=compact, 2=expanded, 3=expand and label, |
|
549
|
|
|
|
|
|
|
4=hyperexpand, 5=hyperexpand and label. |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
$max_bump and $max_label indicate the maximum number of features |
|
552
|
|
|
|
|
|
|
before bumping and labeling are turned off. |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
$selector is a code ref that can be used to filter which features to |
|
555
|
|
|
|
|
|
|
render. It receives a feature and should return true to include the |
|
556
|
|
|
|
|
|
|
feature and false to exclude it. |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
In a scalar context returns the number of tracks rendered. In a list |
|
559
|
|
|
|
|
|
|
context, returns a three-element list containing the number of |
|
560
|
|
|
|
|
|
|
features rendered, the created panel, and an array ref of all the |
|
561
|
|
|
|
|
|
|
track objects created. |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
Instead of a Bio::Graphics::Panel object, you can provide a hash |
|
564
|
|
|
|
|
|
|
reference containing the arguments that you would pass to |
|
565
|
|
|
|
|
|
|
Bio::Graphics::Panel->new(). For example, to render an SVG image, you |
|
566
|
|
|
|
|
|
|
could do this: |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
my ($tracks_rendered,$panel) = $data->render({-image_class=>'GD::SVG'}); |
|
569
|
|
|
|
|
|
|
print $panel->svg; |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=back |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=cut |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
#" |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
sub render { |
|
578
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
579
|
0
|
|
|
|
|
0
|
my $panel = shift; # 8 arguments |
|
580
|
0
|
|
|
|
|
0
|
my ($position_to_insert, |
|
581
|
|
|
|
|
|
|
$options, |
|
582
|
|
|
|
|
|
|
$max_bump, |
|
583
|
|
|
|
|
|
|
$max_label, |
|
584
|
|
|
|
|
|
|
$selector, |
|
585
|
|
|
|
|
|
|
$range, |
|
586
|
|
|
|
|
|
|
$override_options |
|
587
|
|
|
|
|
|
|
) = @_; |
|
588
|
0
|
|
|
|
|
0
|
my %seenit; |
|
589
|
|
|
|
|
|
|
|
|
590
|
0
|
0
|
0
|
|
|
0
|
unless ($panel && UNIVERSAL::isa($panel,'Bio::Graphics::Panel')) { |
|
591
|
0
|
|
|
|
|
0
|
$panel = $self->new_panel($panel); |
|
592
|
|
|
|
|
|
|
} |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
# count up number of tracks inserted |
|
595
|
0
|
|
|
|
|
0
|
my @tracks; |
|
596
|
|
|
|
|
|
|
my $color; |
|
597
|
0
|
|
|
|
|
0
|
my @labels = $self->labels; |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# we need to add a dummy section for each type that isn't |
|
600
|
|
|
|
|
|
|
# specifically configured |
|
601
|
0
|
|
|
|
|
0
|
my %types = map {$_=>1 |
|
602
|
|
|
|
|
|
|
} map { |
|
603
|
0
|
|
0
|
|
|
0
|
shellwords ($self->setting($_=>'feature')||$_) } @labels; |
|
|
0
|
|
|
|
|
0
|
|
|
604
|
0
|
|
|
|
|
0
|
my %lc_types = map {lc($_)}%types; |
|
|
0
|
|
|
|
|
0
|
|
|
605
|
|
|
|
|
|
|
|
|
606
|
0
|
|
|
|
|
0
|
my @unconfigured_types = sort grep {!exists $lc_types{lc $_} && |
|
607
|
0
|
|
0
|
|
|
0
|
!exists $lc_types{lc $_->method} |
|
608
|
|
|
|
|
|
|
} $self->types; |
|
609
|
|
|
|
|
|
|
|
|
610
|
0
|
|
|
|
|
0
|
my @configured_types = keys %types; |
|
611
|
|
|
|
|
|
|
|
|
612
|
0
|
|
|
|
|
0
|
my @labels_to_render = (@labels,@unconfigured_types); |
|
613
|
|
|
|
|
|
|
|
|
614
|
0
|
|
|
|
|
0
|
my @base_config = $self->style('general'); |
|
615
|
|
|
|
|
|
|
|
|
616
|
0
|
|
|
|
|
0
|
my @pack_options = (); |
|
617
|
0
|
0
|
0
|
|
|
0
|
if ($options && ref $options eq 'HASH') { |
|
618
|
0
|
|
|
|
|
0
|
@pack_options = %$options; |
|
619
|
|
|
|
|
|
|
} else { |
|
620
|
0
|
|
0
|
|
|
0
|
$options ||= 0; |
|
621
|
0
|
0
|
|
|
|
0
|
if ($options == 1) { # compact |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
622
|
0
|
|
|
|
|
0
|
push @pack_options,(-bump => 0,-label=>0); |
|
623
|
|
|
|
|
|
|
} elsif ($options == 2) { #expanded |
|
624
|
0
|
|
|
|
|
0
|
push @pack_options,(-bump=>1); |
|
625
|
|
|
|
|
|
|
} elsif ($options == 3) { #expand and label |
|
626
|
0
|
|
|
|
|
0
|
push @pack_options,(-bump=>1,-label=>1); |
|
627
|
|
|
|
|
|
|
} elsif ($options == 4) { #hyperexpand |
|
628
|
0
|
|
|
|
|
0
|
push @pack_options,(-bump => 2); |
|
629
|
|
|
|
|
|
|
} elsif ($options == 5) { #hyperexpand and label |
|
630
|
0
|
|
|
|
|
0
|
push @pack_options,(-bump => 2,-label=>1); |
|
631
|
|
|
|
|
|
|
} |
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
|
|
634
|
0
|
|
|
|
|
0
|
for my $label (@labels_to_render) { |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
|
|
637
|
0
|
|
0
|
|
|
0
|
my @types = shellwords($self->setting($label=>'feature')||''); |
|
638
|
0
|
0
|
|
|
|
0
|
@types = $label unless @types; |
|
639
|
|
|
|
|
|
|
|
|
640
|
0
|
0
|
0
|
|
|
0
|
next if defined $selector and !$selector->($self,$label); |
|
641
|
|
|
|
|
|
|
|
|
642
|
0
|
0
|
|
|
|
0
|
my @features = !$range ? grep {$self->_visible($_)} $self->features(\@types) |
|
|
0
|
|
|
|
|
0
|
|
|
643
|
|
|
|
|
|
|
: $self->features(-types => \@types, |
|
644
|
|
|
|
|
|
|
-seq_id => $range->seq_id, |
|
645
|
|
|
|
|
|
|
-start => $range->start, |
|
646
|
|
|
|
|
|
|
-end => $range->end |
|
647
|
|
|
|
|
|
|
); |
|
648
|
0
|
0
|
|
|
|
0
|
next unless @features; # suppress tracks for features that don't appear |
|
649
|
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# fix up funky group hack |
|
651
|
0
|
0
|
|
|
|
0
|
foreach (@features) {$_->primary_tag('group') if $_->has_tag('_ff_group')}; |
|
|
0
|
|
|
|
|
0
|
|
|
652
|
0
|
|
|
|
|
0
|
my $features = \@features; |
|
653
|
|
|
|
|
|
|
|
|
654
|
0
|
|
|
|
|
0
|
my @auto_bump; |
|
655
|
0
|
0
|
|
|
|
0
|
push @auto_bump,(-bump => @$features < $max_bump) if defined $max_bump; |
|
656
|
0
|
0
|
|
|
|
0
|
push @auto_bump,(-label => @$features < $max_label) if defined $max_label; |
|
657
|
|
|
|
|
|
|
|
|
658
|
0
|
0
|
|
|
|
0
|
my @more_arguments = $override_options ? @$override_options : (); |
|
659
|
|
|
|
|
|
|
|
|
660
|
0
|
|
0
|
|
|
0
|
my @config = ( -glyph => 'segments', # really generic |
|
661
|
|
|
|
|
|
|
-bgcolor => $COLORS[$color++ % @COLORS], |
|
662
|
|
|
|
|
|
|
-label => 1, |
|
663
|
|
|
|
|
|
|
-description => 1, |
|
664
|
|
|
|
|
|
|
-key => $features[0]->type || $label, |
|
665
|
|
|
|
|
|
|
@auto_bump, |
|
666
|
|
|
|
|
|
|
@base_config, # global |
|
667
|
|
|
|
|
|
|
$self->style($label), # feature-specific |
|
668
|
|
|
|
|
|
|
@pack_options, |
|
669
|
|
|
|
|
|
|
@more_arguments, |
|
670
|
|
|
|
|
|
|
); |
|
671
|
|
|
|
|
|
|
|
|
672
|
0
|
0
|
|
|
|
0
|
if (defined($position_to_insert)) { |
|
673
|
0
|
|
|
|
|
0
|
push @tracks,$panel->insert_track($position_to_insert++,$features,@config); |
|
674
|
|
|
|
|
|
|
} else { |
|
675
|
0
|
|
|
|
|
0
|
push @tracks,$panel->add_track($features,@config); |
|
676
|
|
|
|
|
|
|
} |
|
677
|
|
|
|
|
|
|
} |
|
678
|
0
|
0
|
|
|
|
0
|
return wantarray ? (scalar(@tracks),$panel,\@tracks) : scalar @tracks; |
|
679
|
|
|
|
|
|
|
} |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
sub _stat { |
|
682
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
|
683
|
1
|
|
|
|
|
1
|
my $file = shift; |
|
684
|
1
|
50
|
|
|
|
7
|
defined fileno($file) or return; |
|
685
|
1
|
50
|
|
|
|
11
|
my @stat = stat($file) or return; |
|
686
|
1
|
50
|
33
|
|
|
5
|
if ($self->{stat} && @{$self->{stat}}) { # merge #includes so that mtime etc are max age |
|
|
1
|
|
|
|
|
7
|
|
|
687
|
0
|
|
|
|
|
0
|
for (8,9,10) { |
|
688
|
0
|
0
|
|
|
|
0
|
$self->{stat}[$_] = $stat[$_] if $stat[$_] > $self->{stat}[$_]; |
|
689
|
|
|
|
|
|
|
} |
|
690
|
0
|
|
|
|
|
0
|
$self->{stat}[7] += $stat[7]; |
|
691
|
|
|
|
|
|
|
} else { |
|
692
|
1
|
|
|
|
|
2
|
$self->{stat} = \@stat; |
|
693
|
|
|
|
|
|
|
} |
|
694
|
|
|
|
|
|
|
} |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
sub _visible { |
|
697
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
698
|
0
|
|
|
|
|
0
|
my $feat = shift; |
|
699
|
0
|
|
|
|
|
0
|
my $min = $self->min; |
|
700
|
0
|
|
|
|
|
0
|
my $max = $self->max; |
|
701
|
0
|
|
0
|
|
|
0
|
return $feat->start<=$max && $feat->end>=$min; |
|
702
|
|
|
|
|
|
|
} |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=over 4 |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=item $error = $features-Eerror([$error]) |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
Get/set the current error message. |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
=back |
|
711
|
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=cut |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
sub error { |
|
715
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
716
|
0
|
|
|
|
|
0
|
my $d = $self->{error}; |
|
717
|
0
|
0
|
|
|
|
0
|
$self->{error} = shift if @_; |
|
718
|
0
|
|
|
|
|
0
|
$d; |
|
719
|
|
|
|
|
|
|
} |
|
720
|
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=over 4 |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=item $smart_features = $features-Esmart_features([$flag] |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
Get/set the "smart_features" flag. If this is set, then any features |
|
726
|
|
|
|
|
|
|
added to the featurefile object will have their configurator() method |
|
727
|
|
|
|
|
|
|
called using the featurefile object as the argument. |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=back |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=cut |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
sub smart_features { |
|
734
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
735
|
0
|
|
|
|
|
0
|
my $d = $self->{smart_features}; |
|
736
|
0
|
0
|
|
|
|
0
|
$self->{smart_features} = shift if @_; |
|
737
|
0
|
|
|
|
|
0
|
$d; |
|
738
|
|
|
|
|
|
|
} |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
sub parse_argv { |
|
741
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
742
|
0
|
|
|
|
|
0
|
local $/ = "\n"; |
|
743
|
0
|
|
|
|
|
0
|
local $_; |
|
744
|
0
|
|
|
|
|
0
|
while (<>) { |
|
745
|
0
|
|
|
|
|
0
|
chomp; |
|
746
|
0
|
|
|
|
|
0
|
$self->parse_line($_); |
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
} |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
sub parse_file { |
|
751
|
1
|
|
|
1
|
0
|
1
|
my $self = shift; |
|
752
|
1
|
|
|
|
|
2
|
my $file = shift; |
|
753
|
|
|
|
|
|
|
|
|
754
|
1
|
|
|
|
|
3
|
$file =~ s/(\s)/\\$1/g; # escape whitespace from glob expansion |
|
755
|
|
|
|
|
|
|
|
|
756
|
1
|
|
|
|
|
50
|
for my $f (glob($file)) { |
|
757
|
1
|
50
|
|
|
|
10
|
my $fh = IO::File->new($f) or return; |
|
758
|
1
|
|
|
|
|
134
|
my $cwd = getcwd(); |
|
759
|
1
|
|
|
|
|
79
|
chdir(dirname($f)); |
|
760
|
1
|
|
|
|
|
5
|
$self->parse_fh($fh); |
|
761
|
0
|
|
|
|
|
0
|
chdir($cwd); |
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
} |
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
sub parse_fh { |
|
766
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
|
767
|
1
|
|
|
|
|
2
|
my $fh = shift; |
|
768
|
1
|
|
|
|
|
4
|
$self->_stat($fh); |
|
769
|
1
|
|
|
|
|
4
|
local $/ = "\n"; |
|
770
|
1
|
|
|
|
|
2
|
local $_; |
|
771
|
1
|
|
|
|
|
13
|
while (<$fh>) { |
|
772
|
5
|
|
|
|
|
7
|
chomp; |
|
773
|
5
|
50
|
|
|
|
10
|
$self->parse_line($_) || last; |
|
774
|
|
|
|
|
|
|
} |
|
775
|
|
|
|
|
|
|
} |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
sub parse_text { |
|
778
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
779
|
0
|
|
|
|
|
0
|
my $text = shift; |
|
780
|
|
|
|
|
|
|
|
|
781
|
0
|
|
|
|
|
0
|
foreach (split m/\015?\012|\015\012?/,$text) { |
|
782
|
0
|
|
|
|
|
0
|
$self->parse_line($_); |
|
783
|
|
|
|
|
|
|
} |
|
784
|
|
|
|
|
|
|
} |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
sub parse_line { |
|
787
|
5
|
|
|
5
|
0
|
5
|
my $self = shift; |
|
788
|
5
|
|
|
|
|
6
|
my $line = shift; |
|
789
|
|
|
|
|
|
|
|
|
790
|
5
|
|
|
|
|
6
|
$line =~ s/\015//g; # get rid of carriage returns left over by MS-DOS/Windows systems |
|
791
|
5
|
|
|
|
|
10
|
$line =~ s/\s+$//; # get rid of trailing whitespace |
|
792
|
|
|
|
|
|
|
|
|
793
|
5
|
50
|
|
|
|
12
|
if (/^#include\s+(.+)/i) { # #include directive |
|
794
|
0
|
|
|
|
|
0
|
my ($include_file) = shellwords($1); |
|
795
|
|
|
|
|
|
|
# detect some loops |
|
796
|
|
|
|
|
|
|
croak "#include loop detected at $include_file" |
|
797
|
0
|
0
|
|
|
|
0
|
if $self->{includes}{$include_file}++; |
|
798
|
0
|
|
|
|
|
0
|
$self->parse_file($include_file); |
|
799
|
0
|
|
|
|
|
0
|
return 1; |
|
800
|
|
|
|
|
|
|
} |
|
801
|
|
|
|
|
|
|
|
|
802
|
5
|
50
|
|
|
|
9
|
if (/^#exec\s+(.+)/i) { # #exec directive |
|
803
|
0
|
|
|
|
|
0
|
my ($command,@args) = shellwords($1); |
|
804
|
0
|
0
|
|
|
|
0
|
open (my $fh,'-|') || exec $command,@args; |
|
805
|
0
|
|
|
|
|
0
|
$self->parse_fh($fh); |
|
806
|
0
|
|
|
|
|
0
|
return 1; |
|
807
|
|
|
|
|
|
|
} |
|
808
|
|
|
|
|
|
|
|
|
809
|
5
|
50
|
|
|
|
10
|
return 1 if $line =~ /^\s*\#[^\#]?$/; # comment line |
|
810
|
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
# Are we in a configuration section or a data section? |
|
812
|
|
|
|
|
|
|
# We start out in 'config' state, and are triggered to |
|
813
|
|
|
|
|
|
|
# reenter config state whenever we see a /^\[ pattern (config section) |
|
814
|
5
|
|
|
|
|
7
|
my $old_state = $self->{state}; |
|
815
|
5
|
|
|
|
|
9
|
my $new_state = $self->_state_transition($line); |
|
816
|
|
|
|
|
|
|
|
|
817
|
5
|
100
|
|
|
|
10
|
if ($new_state ne $old_state) { |
|
818
|
1
|
|
|
|
|
2
|
delete $self->{current_config}; |
|
819
|
1
|
|
|
|
|
1
|
delete $self->{current_tag}; |
|
820
|
|
|
|
|
|
|
} |
|
821
|
|
|
|
|
|
|
|
|
822
|
5
|
100
|
|
|
|
9
|
if ($new_state eq 'config') { |
|
|
|
50
|
|
|
|
|
|
|
823
|
4
|
|
|
|
|
8
|
$self->parse_config_line($line); |
|
824
|
|
|
|
|
|
|
} elsif ($new_state eq 'data') { |
|
825
|
1
|
|
|
|
|
4
|
$self->parse_data_line($line); |
|
826
|
|
|
|
|
|
|
} |
|
827
|
4
|
|
|
|
|
7
|
$self->{state} = $new_state; |
|
828
|
4
|
|
|
|
|
14
|
1; |
|
829
|
|
|
|
|
|
|
} |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
sub _state_transition { |
|
832
|
5
|
|
|
5
|
|
4
|
my $self = shift; |
|
833
|
5
|
|
|
|
|
5
|
my $line = shift; |
|
834
|
5
|
|
|
|
|
4
|
my $current_state = $self->{state}; |
|
835
|
|
|
|
|
|
|
|
|
836
|
5
|
50
|
|
|
|
15
|
if ($current_state eq 'data') { |
|
|
|
50
|
|
|
|
|
|
|
837
|
0
|
0
|
|
|
|
0
|
return 'config' if $line =~ m/^\s*\[([^\]]+)\]/; # start of a configuration section |
|
838
|
|
|
|
|
|
|
} |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
elsif ($current_state eq 'config') { |
|
841
|
5
|
50
|
|
|
|
10
|
return 'data' if $line =~ /^\#\#(\w+)/; # GFF3 meta instruction |
|
842
|
5
|
100
|
|
|
|
10
|
return 'data' if $line =~ /^reference\s*=/; # feature-file reference sequence directive |
|
843
|
|
|
|
|
|
|
|
|
844
|
4
|
50
|
|
|
|
10
|
return 'config' if $line =~ /^\s*$/; #empty line |
|
845
|
4
|
100
|
|
|
|
11
|
return 'config' if $line =~ m/^\[(.+)\]/; # section beginning |
|
846
|
|
|
|
|
|
|
return 'config' if $line =~ m/^[\w:\s]+=/ |
|
847
|
3
|
50
|
33
|
|
|
18
|
&& $self->{current_config}; # configuration line |
|
848
|
|
|
|
|
|
|
return 'config' if $line =~ m/^\s+(.+)/ |
|
849
|
0
|
0
|
0
|
|
|
0
|
&& $self->{current_tag}; # continuation section |
|
850
|
0
|
0
|
|
|
|
0
|
return 'config' if $line =~ /^\#/; # comment -not a meta |
|
851
|
0
|
|
|
|
|
0
|
return 'data'; |
|
852
|
|
|
|
|
|
|
} |
|
853
|
0
|
|
|
|
|
0
|
return $current_state; |
|
854
|
|
|
|
|
|
|
} |
|
855
|
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
sub parse_config_line { |
|
857
|
4
|
|
|
4
|
0
|
2
|
my $self = shift; |
|
858
|
4
|
|
|
|
|
5
|
local $_ = shift; |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# strip right-column comments unless they look like colors or html fragments |
|
861
|
4
|
50
|
33
|
|
|
28
|
s/\s*\#.*$// unless /\#[0-9a-f]{6,8}\s*$/i || /\w+\#\w+/ || /\w+\"*\s*\#\d+$/; |
|
|
|
|
33
|
|
|
|
|
|
862
|
|
|
|
|
|
|
|
|
863
|
4
|
50
|
33
|
|
|
27
|
if (/^\s+(.+)/ && $self->{current_tag}) { # configuration continuation line |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
864
|
0
|
|
|
|
|
0
|
my $value = $1; |
|
865
|
0
|
|
0
|
|
|
0
|
my $cc = $self->{current_config} ||= 'general'; # in case no configuration named |
|
866
|
0
|
|
|
|
|
0
|
$self->{config}{$cc}{$self->{current_tag}} .= ' ' . $value; |
|
867
|
|
|
|
|
|
|
# respect newlines in code subs |
|
868
|
|
|
|
|
|
|
$self->{config}{$cc}{$self->{current_tag}} .= "\n" |
|
869
|
0
|
0
|
|
|
|
0
|
if $self->{config}{$cc}{$self->{current_tag}}=~ /^sub\s*\{/; |
|
870
|
0
|
|
|
|
|
0
|
return 1; |
|
871
|
|
|
|
|
|
|
} |
|
872
|
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
elsif (/^\[(.+)\]/) { # beginning of a configuration section |
|
874
|
1
|
|
|
|
|
3
|
my $label = $1; |
|
875
|
1
|
50
|
|
|
|
6
|
my $cc = $label =~ /^(general|default)$/i ? 'general' : $label; # normalize |
|
876
|
1
|
50
|
|
|
|
3
|
push @{$self->{types}},$cc unless $cc eq 'general'; |
|
|
0
|
|
|
|
|
0
|
|
|
877
|
1
|
|
|
|
|
3
|
$self->{current_config} = $cc; |
|
878
|
1
|
|
|
|
|
2
|
return 1; |
|
879
|
|
|
|
|
|
|
} |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
elsif (/^([\w: -]+?)\s*=\s*(.*)/) { # key value pair within a configuration section |
|
882
|
3
|
|
|
|
|
8
|
my $tag = lc $1; |
|
883
|
3
|
|
50
|
|
|
7
|
my $cc = $self->{current_config} ||= 'general'; # in case no configuration named |
|
884
|
3
|
50
|
|
|
|
6
|
my $value = defined $2 ? $2 : ''; |
|
885
|
3
|
|
|
|
|
8
|
$self->{config}{$cc}{$tag} = $value; |
|
886
|
3
|
|
|
|
|
3
|
$self->{current_tag} = $tag; |
|
887
|
3
|
|
|
|
|
5
|
return 1; |
|
888
|
|
|
|
|
|
|
} |
|
889
|
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
elsif (/^$/) { # empty line |
|
892
|
|
|
|
|
|
|
# no longer required -- new sections are indicated by the start of a [stanza] |
|
893
|
|
|
|
|
|
|
# line and not by termination with a blank line |
|
894
|
|
|
|
|
|
|
# undef $self->{current_tag}; |
|
895
|
0
|
|
|
|
|
0
|
return 1; |
|
896
|
|
|
|
|
|
|
} |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
} |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
sub parse_data_line { |
|
901
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
|
902
|
1
|
|
|
|
|
2
|
my $line = shift; |
|
903
|
1
|
0
|
33
|
|
|
8
|
$self->{loader} ||= $self->_make_loader($line) or return; |
|
904
|
0
|
|
|
|
|
0
|
$self->{loader}->load_line($line); |
|
905
|
|
|
|
|
|
|
} |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
sub _make_loader { |
|
908
|
1
|
|
|
1
|
|
1
|
my $self = shift; |
|
909
|
1
|
|
|
|
|
1
|
local $_ = shift; |
|
910
|
1
|
|
|
|
|
4
|
my $db = $self->db; |
|
911
|
|
|
|
|
|
|
|
|
912
|
0
|
|
|
|
|
0
|
my $type; |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
# we support gff2, gff3 and featurefile formats |
|
915
|
0
|
0
|
|
|
|
0
|
if (/^\#\#gff-version\s+([23])/) { |
|
|
|
0
|
|
|
|
|
|
|
916
|
0
|
|
|
|
|
0
|
$type = "Bio::DB::SeqFeature::Store::GFF$1Loader"; |
|
917
|
|
|
|
|
|
|
} |
|
918
|
|
|
|
|
|
|
elsif (/^reference\s*=.+/) { |
|
919
|
0
|
|
|
|
|
0
|
$type = "Bio::DB::SeqFeature::Store::FeatureFileLoader"; |
|
920
|
|
|
|
|
|
|
} |
|
921
|
|
|
|
|
|
|
else { |
|
922
|
0
|
|
|
|
|
0
|
my @tokens = shellwords($_); |
|
923
|
0
|
0
|
0
|
|
|
0
|
unshift @tokens,'' if /^\s+/ and length $tokens[0]; |
|
924
|
|
|
|
|
|
|
|
|
925
|
0
|
0
|
0
|
|
|
0
|
if (@tokens >=8 && $tokens[3]=~ /^-?\d+$/ && $tokens[4]=~ /^-?\d+$/) { |
|
|
|
|
0
|
|
|
|
|
|
926
|
0
|
|
|
|
|
0
|
$type = 'Bio::DB::SeqFeature::Store::GFF3Loader'; |
|
927
|
|
|
|
|
|
|
} |
|
928
|
|
|
|
|
|
|
else { |
|
929
|
0
|
|
|
|
|
0
|
$type = 'Bio::DB::SeqFeature::Store::FeatureFileLoader'; |
|
930
|
|
|
|
|
|
|
} |
|
931
|
|
|
|
|
|
|
} |
|
932
|
0
|
0
|
|
|
|
0
|
eval "require $type" |
|
933
|
|
|
|
|
|
|
unless $type->can('new'); |
|
934
|
|
|
|
|
|
|
my $loader = $type->new(-store => $db, |
|
935
|
|
|
|
|
|
|
-map_coords => $self->{coordinate_mapper}, |
|
936
|
0
|
|
|
|
|
0
|
-index_subfeatures => 0, |
|
937
|
|
|
|
|
|
|
); |
|
938
|
0
|
0
|
|
|
|
0
|
eval {$loader->allow_whitespace(1)} |
|
|
0
|
|
|
|
|
0
|
|
|
939
|
|
|
|
|
|
|
if $self->allow_whitespace; # gff2 and gff3 loaders allow this |
|
940
|
|
|
|
|
|
|
|
|
941
|
0
|
0
|
|
|
|
0
|
$loader->start_load() if $loader; |
|
942
|
0
|
|
|
|
|
0
|
return $loader; |
|
943
|
|
|
|
|
|
|
} |
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
sub db { |
|
946
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
|
947
|
1
|
|
33
|
|
|
18
|
return $self->{db} ||= Bio::DB::SeqFeature::Store->new(-adaptor=>'memory', |
|
948
|
|
|
|
|
|
|
-write => 1); |
|
949
|
|
|
|
|
|
|
} |
|
950
|
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
=over 4 |
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
=item $flat = $features-Eallow_whitespace([$new_flag]) |
|
954
|
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
If true, then GFF3 and GFF2 parsing is relaxed to allow whitespace to |
|
956
|
|
|
|
|
|
|
delimit the columns. Default is false. |
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=back |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
=cut |
|
961
|
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
sub allow_whitespace { |
|
963
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
964
|
0
|
|
|
|
|
0
|
my $d = $self->{allow_whitespace}; |
|
965
|
0
|
0
|
|
|
|
0
|
$self->{allow_whitespace} = shift if @_; |
|
966
|
0
|
|
|
|
|
0
|
$d; |
|
967
|
|
|
|
|
|
|
} |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
=over 4 |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=item $features-Eadd_feature($feature [=E$type]) |
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
Add a new Bio::FeatureI object to the set. If $type is specified, the |
|
974
|
|
|
|
|
|
|
object's primary_tag() will be set to that type. Otherwise, the method |
|
975
|
|
|
|
|
|
|
will use the feature's existing primary_tag() to index and store the |
|
976
|
|
|
|
|
|
|
feature. |
|
977
|
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
=back |
|
979
|
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
=cut |
|
981
|
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
# add a feature of given type to our list |
|
983
|
|
|
|
|
|
|
# we use the primary_tag() method |
|
984
|
|
|
|
|
|
|
sub add_feature { |
|
985
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
986
|
0
|
|
|
|
|
0
|
my ($feature,$type) = @_; |
|
987
|
0
|
0
|
|
|
|
0
|
$feature->configurator($self) if $self->smart_features; |
|
988
|
0
|
0
|
|
|
|
0
|
$feature->primary_tag($type) if defined $type; |
|
989
|
0
|
|
|
|
|
0
|
$self->db->store($feature); |
|
990
|
|
|
|
|
|
|
} |
|
991
|
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
=over 4 |
|
994
|
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
=item $features-Eadd_type($type=E$hashref) |
|
996
|
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
Add a new feature type to the set. The type is a string, such as |
|
998
|
|
|
|
|
|
|
"EST". The hashref is a set of key=Evalue pairs indicating options to |
|
999
|
|
|
|
|
|
|
set on the type. Example: |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
$features->add_type(EST => { glyph => 'generic', fgcolor => 'blue'}) |
|
1002
|
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
When a feature of type "EST" is rendered, it will use the generic |
|
1004
|
|
|
|
|
|
|
glyph and have a foreground color of blue. |
|
1005
|
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
=back |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
=cut |
|
1009
|
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# Add a type to the list. Hash values are used for key/value pairs |
|
1011
|
|
|
|
|
|
|
# in the configuration. Call as add_type($type,$configuration) where |
|
1012
|
|
|
|
|
|
|
# $configuration is a hashref. |
|
1013
|
|
|
|
|
|
|
sub add_type { |
|
1014
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1015
|
0
|
|
|
|
|
0
|
my ($type,$type_configuration) = @_; |
|
1016
|
0
|
0
|
|
|
|
0
|
my $cc = $type =~ /^(general|default)$/i ? 'general' : $type; # normalize |
|
1017
|
0
|
0
|
0
|
|
|
0
|
push @{$self->{types}},$cc unless $cc eq 'general' or $self->{config}{$cc}; |
|
|
0
|
|
|
|
|
0
|
|
|
1018
|
0
|
0
|
|
|
|
0
|
if (defined $type_configuration) { |
|
1019
|
0
|
|
|
|
|
0
|
for my $tag (keys %$type_configuration) { |
|
1020
|
0
|
|
|
|
|
0
|
$self->{config}{$cc}{lc $tag} = $type_configuration->{$tag}; |
|
1021
|
|
|
|
|
|
|
} |
|
1022
|
|
|
|
|
|
|
} |
|
1023
|
|
|
|
|
|
|
} |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
=over 4 |
|
1028
|
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
=item $features-Eset($type,$tag,$value) |
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
Change an individual option for a particular type. For example, this |
|
1032
|
|
|
|
|
|
|
will change the foreground color of EST features to my favorite color: |
|
1033
|
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
$features->set('EST',fgcolor=>'chartreuse') |
|
1035
|
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
=back |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
=cut |
|
1039
|
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
# change configuration of a type. Call as set($type,$tag,$value) |
|
1041
|
|
|
|
|
|
|
# $type will be added if not already there. |
|
1042
|
|
|
|
|
|
|
sub set { |
|
1043
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1044
|
0
|
0
|
|
|
|
0
|
croak("Usage: \$featurefile->set(\$type,\$tag,\$value\n") |
|
1045
|
|
|
|
|
|
|
unless @_ == 3; |
|
1046
|
0
|
|
|
|
|
0
|
my ($type,$tag,$value) = @_; |
|
1047
|
0
|
0
|
|
|
|
0
|
unless ($self->{config}{$type}) { |
|
1048
|
0
|
|
|
|
|
0
|
return $self->add_type($type,{$tag=>$value}); |
|
1049
|
|
|
|
|
|
|
} else { |
|
1050
|
0
|
|
|
|
|
0
|
$self->{config}{$type}{lc $tag} = $value; |
|
1051
|
|
|
|
|
|
|
} |
|
1052
|
|
|
|
|
|
|
} |
|
1053
|
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
# break circular references |
|
1055
|
|
|
|
|
|
|
sub finished { |
|
1056
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
|
1057
|
1
|
|
|
|
|
56
|
delete $self->{features}; |
|
1058
|
|
|
|
|
|
|
} |
|
1059
|
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
sub DESTROY { |
|
1061
|
1
|
|
|
1
|
|
2785
|
my $self = shift; |
|
1062
|
1
|
|
|
|
|
5
|
$self->finished(@_); |
|
1063
|
|
|
|
|
|
|
# $self->{safe_context}->unlink_all_worlds |
|
1064
|
|
|
|
|
|
|
# if $self->{safe_context}; |
|
1065
|
|
|
|
|
|
|
} |
|
1066
|
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
=over 4 |
|
1068
|
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
=item $value = $features-Esetting($stanza =E $option) |
|
1070
|
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
In the two-element form, the setting() method returns the value of an |
|
1072
|
|
|
|
|
|
|
option in the configuration stanza indicated by $stanza. For example: |
|
1073
|
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
$value = $features->setting(general => 'height') |
|
1075
|
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
will return the value of the "height" option in the [general] stanza. |
|
1077
|
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
Call with one element to retrieve all the option names in a stanza: |
|
1079
|
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
@options = $features->setting('general'); |
|
1081
|
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
Call with no elements to retrieve all stanza names: |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
@stanzas = $features->setting; |
|
1085
|
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
=back |
|
1087
|
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
=cut |
|
1089
|
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
sub setting { |
|
1091
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1092
|
0
|
0
|
|
|
|
0
|
if (@_ > 2) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1093
|
0
|
|
|
|
|
0
|
$self->{config}->{$_[0]}{$_[1]} = $_[2]; |
|
1094
|
|
|
|
|
|
|
} |
|
1095
|
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
elsif (@_ <= 1) { |
|
1097
|
0
|
|
|
|
|
0
|
return $self->_setting(@_); |
|
1098
|
|
|
|
|
|
|
} |
|
1099
|
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
elsif ($self->safe) { |
|
1101
|
0
|
|
|
|
|
0
|
return $self->code_setting(@_); |
|
1102
|
|
|
|
|
|
|
} |
|
1103
|
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
elsif ($self->safe_world) { |
|
1105
|
0
|
|
|
|
|
0
|
return $self->safe_setting(@_); |
|
1106
|
|
|
|
|
|
|
} |
|
1107
|
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
else { |
|
1109
|
0
|
0
|
|
|
|
0
|
$self->{code_check}++ && $self->clean_code(); # not safe; clean coderefs |
|
1110
|
0
|
|
|
|
|
0
|
return $self->_setting(@_); |
|
1111
|
|
|
|
|
|
|
} |
|
1112
|
|
|
|
|
|
|
} |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
=head2 fallback_setting() |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
$value = $browser->setting(gene => 'fgcolor'); |
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
Tries to find the setting for designated label (e.g. "gene") first. If |
|
1119
|
|
|
|
|
|
|
this fails, looks in [TRACK DEFAULTS]. If this fails, looks in [GENERAL]. |
|
1120
|
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
=cut |
|
1122
|
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
sub fallback_setting { |
|
1124
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1125
|
0
|
|
|
|
|
0
|
my ($label,$option) = @_; |
|
1126
|
0
|
|
|
|
|
0
|
for my $key ($label,'TRACK DEFAULTS','GENERAL') { |
|
1127
|
0
|
|
|
|
|
0
|
my $value = $self->setting($key,$option); |
|
1128
|
0
|
0
|
|
|
|
0
|
return $value if defined $value; |
|
1129
|
|
|
|
|
|
|
} |
|
1130
|
0
|
|
|
|
|
0
|
return; |
|
1131
|
|
|
|
|
|
|
} |
|
1132
|
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
# return configuration information |
|
1135
|
|
|
|
|
|
|
# arguments are ($type) => returns tags for type |
|
1136
|
|
|
|
|
|
|
# ($type=>$tag) => returns values of tag on type |
|
1137
|
|
|
|
|
|
|
# ($type=>$tag,$value) => sets value of tag |
|
1138
|
|
|
|
|
|
|
sub _setting { |
|
1139
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1140
|
0
|
0
|
|
|
|
0
|
my $config = $self->{config} or return; |
|
1141
|
0
|
0
|
|
|
|
0
|
return keys %{$config} unless @_; |
|
|
0
|
|
|
|
|
0
|
|
|
1142
|
0
|
0
|
|
|
|
0
|
return keys %{$config->{$_[0]}} if @_ == 1; |
|
|
0
|
|
|
|
|
0
|
|
|
1143
|
0
|
0
|
0
|
|
|
0
|
return $config->{$_[0]}{$_[1]} if @_ == 2 && defined $_[0] && exists $config->{$_[0]}; |
|
|
|
|
0
|
|
|
|
|
|
1144
|
0
|
0
|
|
|
|
0
|
return $config->{$_[0]}{$_[1]} = $_[2] if @_ > 2; |
|
1145
|
0
|
|
|
|
|
0
|
return; |
|
1146
|
|
|
|
|
|
|
} |
|
1147
|
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
=over 4 |
|
1150
|
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=item $value = $features-Ecode_setting($stanza=E$option); |
|
1152
|
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
This works like setting() except that it is also able to evaluate code |
|
1154
|
|
|
|
|
|
|
references. These are options whose values begin with the characters |
|
1155
|
|
|
|
|
|
|
"sub {". In this case the value will be passed to an eval() and the |
|
1156
|
|
|
|
|
|
|
resulting codereference returned. Use this with care! |
|
1157
|
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
=back |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
=cut |
|
1161
|
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
sub code_setting { |
|
1163
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1164
|
0
|
|
|
|
|
0
|
my $section = shift; |
|
1165
|
0
|
|
|
|
|
0
|
my $option = shift; |
|
1166
|
0
|
0
|
|
|
|
0
|
croak 'Cannot call code_setting unless feature file is marked as safe' |
|
1167
|
|
|
|
|
|
|
unless $self->safe; |
|
1168
|
|
|
|
|
|
|
|
|
1169
|
0
|
|
|
|
|
0
|
my $setting = $self->_setting($section=>$option); |
|
1170
|
0
|
0
|
|
|
|
0
|
return unless defined $setting; |
|
1171
|
0
|
0
|
|
|
|
0
|
return $setting if ref($setting) eq 'CODE'; |
|
1172
|
0
|
0
|
|
|
|
0
|
if ($setting =~ /^\\&([:\w]+)/) { # coderef in string form |
|
|
|
0
|
|
|
|
|
|
|
1173
|
0
|
|
|
|
|
0
|
my $subroutine_name = $1; |
|
1174
|
0
|
|
|
|
|
0
|
my $package = $self->base2package; |
|
1175
|
0
|
0
|
|
|
|
0
|
my $codestring = $subroutine_name =~ /::/ |
|
1176
|
|
|
|
|
|
|
? "\\&$subroutine_name" |
|
1177
|
|
|
|
|
|
|
: "\\&${package}\:\:${subroutine_name}" ; |
|
1178
|
0
|
|
|
|
|
0
|
my $coderef = eval $codestring; |
|
1179
|
0
|
0
|
|
|
|
0
|
$self->_callback_complain($section,$option) if $@; |
|
1180
|
0
|
|
|
|
|
0
|
$self->set($section,$option,$coderef); |
|
1181
|
0
|
|
|
|
|
0
|
$self->set_callback_source($section,$option,$setting); |
|
1182
|
0
|
|
|
|
|
0
|
return $coderef; |
|
1183
|
|
|
|
|
|
|
} |
|
1184
|
|
|
|
|
|
|
elsif ($setting =~ /^sub\s*(\(\$\$\))*\s*\{/) { |
|
1185
|
0
|
|
|
|
|
0
|
my $package = $self->base2package; |
|
1186
|
0
|
|
|
|
|
0
|
my $coderef = eval "package $package; $setting"; |
|
1187
|
0
|
0
|
|
|
|
0
|
$self->_callback_complain($section,$option) if $@; |
|
1188
|
0
|
|
|
|
|
0
|
$self->set($section,$option,$coderef); |
|
1189
|
0
|
|
|
|
|
0
|
$self->set_callback_source($section,$option,$setting); |
|
1190
|
0
|
|
|
|
|
0
|
return $coderef; |
|
1191
|
|
|
|
|
|
|
} else { |
|
1192
|
0
|
|
|
|
|
0
|
return $setting; |
|
1193
|
|
|
|
|
|
|
} |
|
1194
|
|
|
|
|
|
|
} |
|
1195
|
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
sub _callback_complain { |
|
1197
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1198
|
0
|
|
|
|
|
0
|
my ($section,$option) = @_; |
|
1199
|
0
|
|
|
|
|
0
|
carp "An error occurred while evaluating the callback at section='$section', option='$option':\n => $@"; |
|
1200
|
|
|
|
|
|
|
} |
|
1201
|
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
=over 4 |
|
1203
|
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
=item $value = $features-Esafe_setting($stanza=E$option); |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
This works like code_setting() except that it evaluates anonymous code |
|
1207
|
|
|
|
|
|
|
references in a "Safe::World" compartment. This depends on the |
|
1208
|
|
|
|
|
|
|
L module being installed and the -safe_world option being |
|
1209
|
|
|
|
|
|
|
set to true during object construction. |
|
1210
|
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
=back |
|
1212
|
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
=cut |
|
1214
|
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
sub safe_setting { |
|
1216
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1217
|
|
|
|
|
|
|
|
|
1218
|
0
|
|
|
|
|
0
|
my $section = shift; |
|
1219
|
0
|
|
|
|
|
0
|
my $option = shift; |
|
1220
|
|
|
|
|
|
|
|
|
1221
|
0
|
|
|
|
|
0
|
my $setting = $self->_setting($section=>$option); |
|
1222
|
0
|
0
|
|
|
|
0
|
return unless defined $setting; |
|
1223
|
0
|
0
|
|
|
|
0
|
return $setting if ref($setting) eq 'CODE'; |
|
1224
|
|
|
|
|
|
|
|
|
1225
|
0
|
0
|
0
|
|
|
0
|
if ($setting =~ /^sub\s*(\(\$\$\))*\s*\{/ |
|
1226
|
|
|
|
|
|
|
&& (my $context = $self->{safe_context})) { |
|
1227
|
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
# turn setting from an anonymous sub into a named |
|
1230
|
|
|
|
|
|
|
# sub in the context namespace |
|
1231
|
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
# create proper symbol name |
|
1233
|
0
|
|
|
|
|
0
|
my $subname = "${section}_${option}"; |
|
1234
|
0
|
|
|
|
|
0
|
$subname =~ tr/a-zA-Z0-9_//cd; |
|
1235
|
0
|
|
|
|
|
0
|
$subname =~ s/^\d+//; |
|
1236
|
|
|
|
|
|
|
|
|
1237
|
0
|
|
|
|
|
0
|
my ($prototype) |
|
1238
|
|
|
|
|
|
|
= $setting =~ /^sub\s*\(\$\$\)/; |
|
1239
|
|
|
|
|
|
|
|
|
1240
|
0
|
|
|
|
|
0
|
$setting =~ s/^sub?.*?\{/sub $subname {/; |
|
1241
|
|
|
|
|
|
|
|
|
1242
|
0
|
|
|
|
|
0
|
my $success = $context->eval("$setting; 1"); |
|
1243
|
0
|
0
|
|
|
|
0
|
$self->_callback_complain($section,$option) if $@; |
|
1244
|
0
|
0
|
|
|
|
0
|
unless ($success) { |
|
1245
|
0
|
|
|
|
|
0
|
$self->set($section,$option,1); # if call fails, it becomes a generic "true" value |
|
1246
|
0
|
|
|
|
|
0
|
return 1; |
|
1247
|
|
|
|
|
|
|
} |
|
1248
|
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
my $coderef = $prototype |
|
1250
|
0
|
|
|
0
|
|
0
|
? sub ($$) { return $context->call($subname,$_[0],$_[1]) } |
|
1251
|
|
|
|
|
|
|
: sub { |
|
1252
|
0
|
0
|
|
0
|
|
0
|
if ($_[-1]->isa('Bio::Graphics::Glyph')) { |
|
1253
|
0
|
|
|
|
|
0
|
my %newglyph = %{$_[-1]}; |
|
|
0
|
|
|
|
|
0
|
|
|
1254
|
0
|
|
|
|
|
0
|
$_[-1] = bless \%newglyph,'Bio::Graphics::Glyph'; # make generic |
|
1255
|
|
|
|
|
|
|
} |
|
1256
|
0
|
|
|
|
|
0
|
$context->call($subname,@_); |
|
1257
|
0
|
0
|
|
|
|
0
|
}; |
|
1258
|
0
|
|
|
|
|
0
|
$self->set($section,$option,$coderef); |
|
1259
|
0
|
|
|
|
|
0
|
$self->set_callback_source($section,$option,$setting); |
|
1260
|
0
|
|
|
|
|
0
|
return $coderef; |
|
1261
|
|
|
|
|
|
|
} |
|
1262
|
|
|
|
|
|
|
else { |
|
1263
|
0
|
|
|
|
|
0
|
return $setting; |
|
1264
|
|
|
|
|
|
|
} |
|
1265
|
|
|
|
|
|
|
} |
|
1266
|
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
=over 4 |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
=item $flag = $features-Esafe([$flag]); |
|
1270
|
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
This gets or sets and "safe" flag. If the safe flag is set, then |
|
1272
|
|
|
|
|
|
|
calls to setting() will invoke code_setting(), allowing values that |
|
1273
|
|
|
|
|
|
|
begin with the string "sub {" to be interpreted as anonymous |
|
1274
|
|
|
|
|
|
|
subroutines. This is a potential security risk when used with |
|
1275
|
|
|
|
|
|
|
untrusted files of features, so use it with care. |
|
1276
|
|
|
|
|
|
|
|
|
1277
|
|
|
|
|
|
|
=back |
|
1278
|
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=cut |
|
1280
|
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
sub safe { |
|
1282
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1283
|
0
|
|
|
|
|
0
|
my $d = $self->{safe}; |
|
1284
|
0
|
0
|
|
|
|
0
|
$self->{safe} = shift if @_; |
|
1285
|
0
|
0
|
0
|
|
|
0
|
$self->evaluate_coderefs if $self->{safe} && !$d; |
|
1286
|
0
|
|
|
|
|
0
|
$d; |
|
1287
|
|
|
|
|
|
|
} |
|
1288
|
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
=over 4 |
|
1290
|
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
=item $flag = $features-Esafe_world([$flag]); |
|
1292
|
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
This gets or sets and "safe_world" flag. If the safe_world flag is |
|
1294
|
|
|
|
|
|
|
set, then values that begin with the string "sub {" will be evaluated |
|
1295
|
|
|
|
|
|
|
in a "safe" compartment that gives minimal access to the system. This |
|
1296
|
|
|
|
|
|
|
is not a panacea for security risks, so use with care. |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
=back |
|
1299
|
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
=cut |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
sub safe_world { |
|
1303
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1304
|
0
|
|
|
|
|
0
|
my $safe = shift; |
|
1305
|
|
|
|
|
|
|
|
|
1306
|
0
|
0
|
0
|
|
|
0
|
if ($safe && !$self->{safe_content}) { # initialise the thing |
|
1307
|
|
|
|
|
|
|
|
|
1308
|
0
|
|
|
|
|
0
|
eval "require Safe::World; 1"; |
|
1309
|
0
|
0
|
|
|
|
0
|
unless (Safe::World->can('new')) { |
|
1310
|
0
|
|
|
|
|
0
|
warn "The Safe::World module is not installed on this system. Can't use it to evaluate codesubs in a safe context"; |
|
1311
|
0
|
|
|
|
|
0
|
return; |
|
1312
|
|
|
|
|
|
|
} |
|
1313
|
|
|
|
|
|
|
|
|
1314
|
0
|
0
|
|
|
|
0
|
unless ($self->{safe_lib}) { |
|
1315
|
0
|
0
|
|
|
|
0
|
$self->{safe_lib} = Safe::World->new(sharepack => ['Bio::DB::SeqFeature', |
|
1316
|
|
|
|
|
|
|
'Bio::Graphics::Feature', |
|
1317
|
|
|
|
|
|
|
'Bio::SeqFeature::Lite', |
|
1318
|
|
|
|
|
|
|
'Bio::Graphics::Glyph', |
|
1319
|
|
|
|
|
|
|
]) or return; |
|
1320
|
|
|
|
|
|
|
|
|
1321
|
0
|
0
|
|
|
|
0
|
$self->{safe_lib}->eval(<
|
|
1322
|
|
|
|
|
|
|
use Bio::DB::SeqFeature; |
|
1323
|
|
|
|
|
|
|
use Bio::Graphics::Feature; |
|
1324
|
|
|
|
|
|
|
use Bio::SeqFeature::Lite; |
|
1325
|
|
|
|
|
|
|
use Bio::Graphics::Glyph; |
|
1326
|
|
|
|
|
|
|
1; |
|
1327
|
|
|
|
|
|
|
END |
|
1328
|
|
|
|
|
|
|
} |
|
1329
|
|
|
|
|
|
|
|
|
1330
|
0
|
0
|
|
|
|
0
|
$self->{safe_context} = Safe::World->new(root => $self->base2package) or return; |
|
1331
|
0
|
|
|
|
|
0
|
$self->{safe_context}->op_permit_only(':default'); |
|
1332
|
0
|
|
|
|
|
0
|
$self->{safe_context}->link_world($self->{safe_lib}); |
|
1333
|
0
|
|
|
|
|
0
|
$self->{safe_world} = $safe; |
|
1334
|
|
|
|
|
|
|
} |
|
1335
|
0
|
|
|
|
|
0
|
return $self->{safe_world}; |
|
1336
|
|
|
|
|
|
|
} |
|
1337
|
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
=over 4 |
|
1339
|
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
=item $features-Eset_callback_source($type,$tag,$value) |
|
1341
|
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
=item $features-Eget_callback_source($type,$tag) |
|
1343
|
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
These routines are used internally to get and set the source of a sub |
|
1345
|
|
|
|
|
|
|
{} callback. |
|
1346
|
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
=back |
|
1348
|
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
=cut |
|
1350
|
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
sub set_callback_source { |
|
1352
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1353
|
0
|
|
|
|
|
0
|
my ($type,$tag,$value) = @_; |
|
1354
|
0
|
|
|
|
|
0
|
$self->{source}{$type}{lc $tag} = $value; |
|
1355
|
|
|
|
|
|
|
} |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
sub get_callback_source { |
|
1358
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1359
|
0
|
|
|
|
|
0
|
my ($type,$tag) = @_; |
|
1360
|
0
|
|
|
|
|
0
|
$self->{source}{$type}{lc $tag}; |
|
1361
|
|
|
|
|
|
|
} |
|
1362
|
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
=over 4 |
|
1364
|
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
=item @args = $features-Estyle($type) |
|
1366
|
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
Given a feature type, returns a list of track configuration arguments |
|
1368
|
|
|
|
|
|
|
suitable for suitable for passing to the |
|
1369
|
|
|
|
|
|
|
Bio::Graphics::Panel-Eadd_track() method. |
|
1370
|
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
=back |
|
1372
|
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
=cut |
|
1374
|
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
# turn configuration into a set of -name=>value pairs suitable for add_track() |
|
1376
|
|
|
|
|
|
|
sub style { |
|
1377
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1378
|
0
|
|
|
|
|
0
|
my $type = shift; |
|
1379
|
|
|
|
|
|
|
|
|
1380
|
0
|
0
|
|
|
|
0
|
my $config = $self->{config} or return; |
|
1381
|
0
|
|
|
|
|
0
|
my $hashref = $config->{$type}; |
|
1382
|
0
|
0
|
|
|
|
0
|
unless ($hashref) { |
|
1383
|
0
|
|
|
|
|
0
|
$type =~ s/:.+$//; |
|
1384
|
0
|
0
|
|
|
|
0
|
$hashref = $config->{$type} or return; |
|
1385
|
|
|
|
|
|
|
} |
|
1386
|
|
|
|
|
|
|
|
|
1387
|
0
|
|
|
|
|
0
|
return map {("-$_" => $hashref->{$_})} keys %$hashref; |
|
|
0
|
|
|
|
|
0
|
|
|
1388
|
|
|
|
|
|
|
} |
|
1389
|
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
=over 4 |
|
1392
|
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
=item $glyph = $features-Eglyph($type); |
|
1394
|
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
Return the name of the glyph corresponding to the given type (same as |
|
1396
|
|
|
|
|
|
|
$features-Esetting($type=E'glyph')). |
|
1397
|
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
=back |
|
1399
|
|
|
|
|
|
|
|
|
1400
|
|
|
|
|
|
|
=cut |
|
1401
|
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
# retrieve just the glyph part of the configuration |
|
1403
|
|
|
|
|
|
|
sub glyph { |
|
1404
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1405
|
0
|
|
|
|
|
0
|
my $type = shift; |
|
1406
|
0
|
0
|
|
|
|
0
|
my $config = $self->{config} or return; |
|
1407
|
0
|
0
|
|
|
|
0
|
my $hashref = $config->{$type} or return; |
|
1408
|
0
|
|
|
|
|
0
|
return $hashref->{glyph}; |
|
1409
|
|
|
|
|
|
|
} |
|
1410
|
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
=over 4 |
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
=item @types = $features-Econfigured_types() |
|
1415
|
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
Return a list of all the feature types currently known to the feature |
|
1417
|
|
|
|
|
|
|
file set. Roughly equivalent to: |
|
1418
|
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
@types = grep {$_ ne 'general'} $features->setting; |
|
1420
|
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
=back |
|
1422
|
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
=cut |
|
1424
|
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
# return list of configured types, in proper order |
|
1426
|
|
|
|
|
|
|
sub configured_types { |
|
1427
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1428
|
0
|
0
|
|
|
|
0
|
my $types = $self->{types} or return; |
|
1429
|
0
|
|
|
|
|
0
|
return @$types; |
|
1430
|
|
|
|
|
|
|
} |
|
1431
|
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
sub labels { |
|
1433
|
0
|
|
|
0
|
0
|
0
|
return shift->configured_types; |
|
1434
|
|
|
|
|
|
|
} |
|
1435
|
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
=over 4 |
|
1437
|
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
=item @types = $features-Etypes() |
|
1439
|
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
This is similar to the previous method, but will return *all* feature |
|
1441
|
|
|
|
|
|
|
types, including those that are not configured with a stanza. |
|
1442
|
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
=back |
|
1444
|
|
|
|
|
|
|
|
|
1445
|
|
|
|
|
|
|
=cut |
|
1446
|
|
|
|
|
|
|
|
|
1447
|
|
|
|
|
|
|
sub types { |
|
1448
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1449
|
0
|
|
|
|
|
0
|
my $db = $self->db; |
|
1450
|
0
|
|
|
|
|
0
|
$self->_patch_old_bioperl; |
|
1451
|
0
|
|
|
|
|
0
|
return $self->db->types; |
|
1452
|
|
|
|
|
|
|
} |
|
1453
|
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
sub _patch_old_bioperl { |
|
1455
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1456
|
0
|
0
|
0
|
|
|
0
|
if ($Bio::Root::Version::VERSION >= 1.0069 && |
|
1457
|
|
|
|
|
|
|
$Bio::Root::Version::VERSION <= 1.006901 |
|
1458
|
|
|
|
|
|
|
) { # bad version! |
|
1459
|
0
|
|
|
|
|
0
|
local $^W=0; |
|
1460
|
|
|
|
|
|
|
*Bio::DB::SeqFeature::Store::memory::types = sub { |
|
1461
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1462
|
0
|
0
|
|
|
|
0
|
eval "require Bio::DB::GFF::Typename" unless Bio::DB::GFF::Typename->can('new'); |
|
1463
|
0
|
|
|
|
|
0
|
my @types; |
|
1464
|
0
|
|
|
|
|
0
|
for my $primary_tag ( keys %{$$self{_index}{type}} ) { |
|
|
0
|
|
|
|
|
0
|
|
|
1465
|
0
|
|
|
|
|
0
|
for my $source_tag ( keys %{$$self{_index}{type}{$primary_tag}} ) { |
|
|
0
|
|
|
|
|
0
|
|
|
1466
|
0
|
|
|
|
|
0
|
push @types, Bio::DB::GFF::Typename->new($primary_tag,$source_tag); |
|
1467
|
|
|
|
|
|
|
} |
|
1468
|
|
|
|
|
|
|
} |
|
1469
|
0
|
|
|
|
|
0
|
return @types; |
|
1470
|
|
|
|
|
|
|
} |
|
1471
|
0
|
|
|
|
|
0
|
} |
|
1472
|
|
|
|
|
|
|
} |
|
1473
|
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
=over 4 |
|
1475
|
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
=item $features = $features-Efeatures($type) |
|
1477
|
|
|
|
|
|
|
|
|
1478
|
|
|
|
|
|
|
Return a list of all the feature types of type "$type". If the |
|
1479
|
|
|
|
|
|
|
featurefile object was created by parsing a file or text scalar, then |
|
1480
|
|
|
|
|
|
|
the features will be of type Bio::Graphics::Feature (which follow the |
|
1481
|
|
|
|
|
|
|
Bio::FeatureI interface). Otherwise the list will contain objects of |
|
1482
|
|
|
|
|
|
|
whatever type you added with calls to add_feature(). |
|
1483
|
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
Two APIs: |
|
1485
|
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
1) original API: |
|
1487
|
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
# Reference to an array of all features of type "$type" |
|
1489
|
|
|
|
|
|
|
$features = $features-Efeatures($type) |
|
1490
|
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
# Reference to an array of all features of all types |
|
1492
|
|
|
|
|
|
|
$features = $features-Efeatures() |
|
1493
|
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
# A list when called in a list context |
|
1495
|
|
|
|
|
|
|
@features = $features-Efeatures() |
|
1496
|
|
|
|
|
|
|
|
|
1497
|
|
|
|
|
|
|
2) Bio::Das::SegmentI API: |
|
1498
|
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
@features = $features-Efeatures(-type=>['list','of','types']); |
|
1500
|
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
# variants |
|
1502
|
|
|
|
|
|
|
$features = $features-Efeatures(-type=>['list','of','types']); |
|
1503
|
|
|
|
|
|
|
$features = $features-Efeatures(-type=>'a type'); |
|
1504
|
|
|
|
|
|
|
$iterator = $features-Efeatures(-type=>'a type',-iterator=>1); |
|
1505
|
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
$iterator = $features-Efeatures(-type=>'a type',-seq_id=>$id,-start=>$start,-end=>$end); |
|
1507
|
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
=back |
|
1509
|
|
|
|
|
|
|
|
|
1510
|
|
|
|
|
|
|
=cut |
|
1511
|
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
# return features |
|
1513
|
|
|
|
|
|
|
sub features { |
|
1514
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1515
|
0
|
0
|
0
|
|
|
0
|
my ($types,$iterator,$seq_id,$start,$end,@rest) = defined($_[0] && $_[0]=~/^-/) |
|
1516
|
|
|
|
|
|
|
? rearrange([['TYPE','TYPES'],'ITERATOR','SEQ_ID','START','END'],@_) : (\@_); |
|
1517
|
|
|
|
|
|
|
|
|
1518
|
0
|
0
|
0
|
|
|
0
|
$types = [$types] if $types && !ref($types); |
|
1519
|
0
|
0
|
0
|
|
|
0
|
my @args = $types && @$types ? (-type=>$types) : (); |
|
1520
|
|
|
|
|
|
|
|
|
1521
|
0
|
0
|
|
|
|
0
|
push @args,(-seq_id => $seq_id) if $seq_id; |
|
1522
|
0
|
0
|
|
|
|
0
|
push @args,(-start => $start) if defined $start; |
|
1523
|
0
|
0
|
|
|
|
0
|
push @args,(-end => $end) if defined $end; |
|
1524
|
|
|
|
|
|
|
|
|
1525
|
0
|
|
|
|
|
0
|
my $db = $self->db; |
|
1526
|
|
|
|
|
|
|
|
|
1527
|
0
|
0
|
|
|
|
0
|
if ($iterator) { |
|
1528
|
0
|
|
|
|
|
0
|
return $db->get_seq_stream(@args); |
|
1529
|
|
|
|
|
|
|
} else { |
|
1530
|
0
|
|
|
|
|
0
|
my @f = $db->features(@args); |
|
1531
|
0
|
0
|
|
|
|
0
|
return wantarray ? @f : \@f; |
|
1532
|
|
|
|
|
|
|
} |
|
1533
|
|
|
|
|
|
|
} |
|
1534
|
|
|
|
|
|
|
|
|
1535
|
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
|
|
1537
|
|
|
|
|
|
|
=over 4 |
|
1538
|
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
=item @features = $features-Efeatures($type) |
|
1540
|
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
Return a list of all the feature types of type "$type". If the |
|
1542
|
|
|
|
|
|
|
featurefile object was created by parsing a file or text scalar, then |
|
1543
|
|
|
|
|
|
|
the features will be of type Bio::Graphics::Feature (which follow the |
|
1544
|
|
|
|
|
|
|
Bio::FeatureI interface). Otherwise the list will contain objects of |
|
1545
|
|
|
|
|
|
|
whatever type you added with calls to add_feature(). |
|
1546
|
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
=back |
|
1548
|
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
=cut |
|
1550
|
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
sub make_strand { |
|
1552
|
0
|
|
|
0
|
0
|
0
|
local $^W = 0; |
|
1553
|
0
|
0
|
0
|
|
|
0
|
return +1 if $_[0] =~ /^\+/ || $_[0] > 0; |
|
1554
|
0
|
0
|
0
|
|
|
0
|
return -1 if $_[0] =~ /^\-/ || $_[0] < 0; |
|
1555
|
0
|
|
|
|
|
0
|
return 0; |
|
1556
|
|
|
|
|
|
|
} |
|
1557
|
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
=head2 get_seq_stream |
|
1559
|
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
Title : get_seq_stream |
|
1561
|
|
|
|
|
|
|
Usage : $stream = $s->get_seq_stream(@args) |
|
1562
|
|
|
|
|
|
|
Function: get a stream of features that overlap this segment |
|
1563
|
|
|
|
|
|
|
Returns : a Bio::SeqIO::Stream-compliant stream |
|
1564
|
|
|
|
|
|
|
Args : see below |
|
1565
|
|
|
|
|
|
|
Status : Public |
|
1566
|
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
This is the same as feature_stream(), and is provided for Bioperl |
|
1568
|
|
|
|
|
|
|
compatibility. Use like this: |
|
1569
|
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
$stream = $s->get_seq_stream('exon'); |
|
1571
|
|
|
|
|
|
|
while (my $exon = $stream->next_seq) { |
|
1572
|
|
|
|
|
|
|
print $exon->start,"\n"; |
|
1573
|
|
|
|
|
|
|
} |
|
1574
|
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
=cut |
|
1576
|
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
sub get_seq_stream { |
|
1578
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1579
|
0
|
|
|
|
|
0
|
local $^W = 0; |
|
1580
|
0
|
0
|
|
|
|
0
|
my @args = $_[0] =~ /^-/ ? (@_,-iterator=>1) : (-types=>\@_,-iterator=>1); |
|
1581
|
0
|
|
|
|
|
0
|
$self->features(@args); |
|
1582
|
|
|
|
|
|
|
} |
|
1583
|
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
=head2 get_feature_by_name |
|
1585
|
|
|
|
|
|
|
|
|
1586
|
|
|
|
|
|
|
Usage : $db->get_feature_by_name(-name => $name) |
|
1587
|
|
|
|
|
|
|
Function: fetch features by their name |
|
1588
|
|
|
|
|
|
|
Returns : a list of Bio::DB::GFF::Feature objects |
|
1589
|
|
|
|
|
|
|
Args : the name of the desired feature |
|
1590
|
|
|
|
|
|
|
Status : public |
|
1591
|
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
This method can be used to fetch a named feature from the file. |
|
1593
|
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
The full syntax is as follows. Features can be filtered by |
|
1595
|
|
|
|
|
|
|
their reference, start and end positions |
|
1596
|
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
@f = $db->get_feature_by_name(-name => $name, |
|
1598
|
|
|
|
|
|
|
-ref => $sequence_name, |
|
1599
|
|
|
|
|
|
|
-start => $start, |
|
1600
|
|
|
|
|
|
|
-end => $end); |
|
1601
|
|
|
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
This method may return zero, one, or several Bio::Graphics::Feature |
|
1603
|
|
|
|
|
|
|
objects. |
|
1604
|
|
|
|
|
|
|
|
|
1605
|
|
|
|
|
|
|
=cut |
|
1606
|
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
sub get_feature_by_name { |
|
1608
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1609
|
0
|
|
|
|
|
0
|
my ($name,$ref,$start,$end) = rearrange(['NAME','REF','START','END'],@_); |
|
1610
|
0
|
|
|
|
|
0
|
my @args; |
|
1611
|
0
|
0
|
|
|
|
0
|
push @args,(-name => $name) if defined $name; |
|
1612
|
0
|
0
|
|
|
|
0
|
push @args,(-seq_id => $ref) if defined $ref; |
|
1613
|
0
|
0
|
|
|
|
0
|
push @args,(-start => $start)if defined $start; |
|
1614
|
0
|
0
|
|
|
|
0
|
push @args,(-end => $end) if defined $end; |
|
1615
|
0
|
|
|
|
|
0
|
return $self->db->features(@args); |
|
1616
|
|
|
|
|
|
|
} |
|
1617
|
|
|
|
|
|
|
|
|
1618
|
0
|
|
|
0
|
0
|
0
|
sub get_features_by_name { shift->get_feature_by_name(@_) } |
|
1619
|
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
=head2 search_notes |
|
1621
|
|
|
|
|
|
|
|
|
1622
|
|
|
|
|
|
|
Title : search_notes |
|
1623
|
|
|
|
|
|
|
Usage : @search_results = $db->search_notes("full text search string",$limit) |
|
1624
|
|
|
|
|
|
|
Function: Search the notes for a text string |
|
1625
|
|
|
|
|
|
|
Returns : array of results |
|
1626
|
|
|
|
|
|
|
Args : full text search string, and an optional row limit |
|
1627
|
|
|
|
|
|
|
Status : public |
|
1628
|
|
|
|
|
|
|
|
|
1629
|
|
|
|
|
|
|
Each row of the returned array is a arrayref containing the following fields: |
|
1630
|
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
column 1 Display name of the feature |
|
1632
|
|
|
|
|
|
|
column 2 The text of the note |
|
1633
|
|
|
|
|
|
|
column 3 A relevance score. |
|
1634
|
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
=cut |
|
1636
|
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
sub search_notes { |
|
1638
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1639
|
0
|
|
|
|
|
0
|
return $self->db->search_notes(@_); |
|
1640
|
|
|
|
|
|
|
} |
|
1641
|
|
|
|
|
|
|
|
|
1642
|
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
=head2 get_feature_stream(), top_SeqFeatures(), all_SeqFeatures() |
|
1644
|
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
Provided for compatibility with older BioPerl and/or Bio::DB::GFF |
|
1646
|
|
|
|
|
|
|
APIs. |
|
1647
|
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
=cut |
|
1649
|
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
*get_feature_stream = \&get_seq_stream; |
|
1651
|
|
|
|
|
|
|
*top_SeqFeatures = *all_SeqFeatures = \&features; |
|
1652
|
|
|
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
=over 4 |
|
1655
|
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
=item @refs = $features-Erefs |
|
1657
|
|
|
|
|
|
|
|
|
1658
|
|
|
|
|
|
|
Return the list of reference sequences referred to by this data file. |
|
1659
|
|
|
|
|
|
|
|
|
1660
|
|
|
|
|
|
|
=back |
|
1661
|
|
|
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
=cut |
|
1663
|
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
sub refs { |
|
1665
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1666
|
0
|
0
|
|
|
|
0
|
my $refs = $self->{refs} or return; |
|
1667
|
0
|
|
|
|
|
0
|
keys %$refs; |
|
1668
|
|
|
|
|
|
|
} |
|
1669
|
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
=over 4 |
|
1671
|
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
=item $min = $features-Emin |
|
1673
|
|
|
|
|
|
|
|
|
1674
|
|
|
|
|
|
|
Return the minimum coordinate of the leftmost feature in the data set. |
|
1675
|
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
=back |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
=cut |
|
1679
|
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
sub min { |
|
1681
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1682
|
0
|
|
|
|
|
0
|
$self->_min_max(); |
|
1683
|
0
|
|
|
|
|
0
|
$self->{min}; |
|
1684
|
|
|
|
|
|
|
} |
|
1685
|
|
|
|
|
|
|
|
|
1686
|
|
|
|
|
|
|
=over 4 |
|
1687
|
|
|
|
|
|
|
|
|
1688
|
|
|
|
|
|
|
=item $max = $features-Emax |
|
1689
|
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
Return the maximum coordinate of the rightmost feature in the data set. |
|
1691
|
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
=back |
|
1693
|
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
=cut |
|
1695
|
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
sub max { |
|
1697
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1698
|
0
|
|
|
|
|
0
|
$self->_min_max(); |
|
1699
|
0
|
|
|
|
|
0
|
$self->{max}; |
|
1700
|
|
|
|
|
|
|
} |
|
1701
|
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
sub _min_max { |
|
1703
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1704
|
0
|
0
|
0
|
|
|
0
|
return if defined $self->{min} and defined $self->{max}; |
|
1705
|
|
|
|
|
|
|
|
|
1706
|
0
|
|
|
|
|
0
|
my ($min,$max); |
|
1707
|
0
|
0
|
|
|
|
0
|
if (my $bases = $self->setting(general=>'bases')) { |
|
1708
|
0
|
|
|
|
|
0
|
($min,$max) = $bases =~ /^(-?\d+)(?:\.\.|-)(-?\d+)/; |
|
1709
|
|
|
|
|
|
|
} |
|
1710
|
|
|
|
|
|
|
|
|
1711
|
0
|
0
|
|
|
|
0
|
if (!defined $min) { |
|
1712
|
|
|
|
|
|
|
# otherwise sort through the features |
|
1713
|
0
|
|
|
|
|
0
|
my $fs = $self->get_seq_stream; |
|
1714
|
0
|
|
|
|
|
0
|
while (my $f = $fs->next_seq) { |
|
1715
|
0
|
0
|
0
|
|
|
0
|
$min = $f->start if !defined $min or $min > $f->start; |
|
1716
|
0
|
0
|
0
|
|
|
0
|
$max = $f->end if !defined $max or $max < $f->start; |
|
1717
|
|
|
|
|
|
|
} |
|
1718
|
|
|
|
|
|
|
} |
|
1719
|
|
|
|
|
|
|
|
|
1720
|
0
|
|
|
|
|
0
|
@{$self}{'min','max'} = ($min,$max); |
|
|
0
|
|
|
|
|
0
|
|
|
1721
|
|
|
|
|
|
|
} |
|
1722
|
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
sub init_parse { |
|
1724
|
1
|
|
|
1
|
0
|
2
|
my $s = shift; |
|
1725
|
|
|
|
|
|
|
|
|
1726
|
1
|
|
|
|
|
2
|
$s->{max} = $s->{min} = undef; |
|
1727
|
1
|
|
|
|
|
2
|
$s->{types} = []; |
|
1728
|
1
|
|
|
|
|
2
|
$s->{features} = {}; |
|
1729
|
1
|
|
|
|
|
2
|
$s->{config} = {}; |
|
1730
|
1
|
|
|
|
|
2
|
$s->{loader} = undef; |
|
1731
|
1
|
|
|
|
|
3
|
$s->{state} = 'config'; |
|
1732
|
1
|
|
|
|
|
3
|
$s->{feature_count}= 0; |
|
1733
|
|
|
|
|
|
|
} |
|
1734
|
|
|
|
|
|
|
|
|
1735
|
|
|
|
|
|
|
sub finish_parse { |
|
1736
|
0
|
|
|
0
|
0
|
|
my $s = shift; |
|
1737
|
0
|
0
|
|
|
|
|
if ($s->safe) { |
|
|
|
0
|
|
|
|
|
|
|
1738
|
0
|
|
|
|
|
|
$s->initialize_code; |
|
1739
|
0
|
|
|
|
|
|
$s->evaluate_coderefs; |
|
1740
|
|
|
|
|
|
|
} |
|
1741
|
|
|
|
|
|
|
elsif ($s->safe_world) { |
|
1742
|
0
|
|
|
|
|
|
$s->evaluate_safecoderefs; |
|
1743
|
|
|
|
|
|
|
} |
|
1744
|
0
|
0
|
|
|
|
|
$s->{loader}->finish_load() if $s->{loader}; |
|
1745
|
0
|
|
|
|
|
|
$s->{loader} = undef; |
|
1746
|
0
|
|
|
|
|
|
$s->{state} = 'config'; |
|
1747
|
|
|
|
|
|
|
} |
|
1748
|
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
sub evaluate_coderefs { |
|
1750
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1751
|
0
|
|
|
|
|
|
for my $s ($self->_setting) { |
|
1752
|
0
|
|
|
|
|
|
for my $o ($self->_setting($s)) { |
|
1753
|
0
|
|
|
|
|
|
$self->code_setting($s,$o); |
|
1754
|
|
|
|
|
|
|
} |
|
1755
|
|
|
|
|
|
|
} |
|
1756
|
|
|
|
|
|
|
} |
|
1757
|
|
|
|
|
|
|
sub evaluate_safecoderefs { |
|
1758
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1759
|
0
|
|
|
|
|
|
for my $s ($self->_setting) { |
|
1760
|
0
|
|
|
|
|
|
for my $o ($self->_setting($s)) { |
|
1761
|
0
|
|
|
|
|
|
$self->safe_setting($s,$o); |
|
1762
|
|
|
|
|
|
|
} |
|
1763
|
|
|
|
|
|
|
} |
|
1764
|
|
|
|
|
|
|
} |
|
1765
|
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
sub clean_code { |
|
1767
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1768
|
0
|
|
|
|
|
|
for my $s ($self->_setting) { |
|
1769
|
0
|
|
|
|
|
|
for my $o ($self->_setting($s)) { |
|
1770
|
0
|
0
|
|
|
|
|
$self->_setting($s,$o,1) if |
|
1771
|
|
|
|
|
|
|
$self->_setting($s,$o) =~ /\Asub\s*{/; |
|
1772
|
|
|
|
|
|
|
} |
|
1773
|
|
|
|
|
|
|
} |
|
1774
|
|
|
|
|
|
|
} |
|
1775
|
|
|
|
|
|
|
|
|
1776
|
|
|
|
|
|
|
sub initialize_code { |
|
1777
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1778
|
0
|
|
|
|
|
|
my $package = $self->base2package; |
|
1779
|
0
|
0
|
|
|
|
|
my $init_code = $self->_setting(general => 'init_code') or return; |
|
1780
|
0
|
|
|
|
|
|
my $code = "package $package; $init_code; 1;"; |
|
1781
|
0
|
|
|
|
|
|
eval $code; |
|
1782
|
0
|
0
|
|
|
|
|
$self->_callback_complain(general=>'init_code') if $@; |
|
1783
|
|
|
|
|
|
|
} |
|
1784
|
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
sub base2package { |
|
1786
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1787
|
0
|
0
|
|
|
|
|
return $self->{base2package} if exists $self->{base2package}; |
|
1788
|
0
|
|
|
|
|
|
my $rand = int rand(1000000); |
|
1789
|
0
|
|
|
|
|
|
return $self->{base2package} = "Bio::Graphics::FeatureFile::CallBack::P$rand"; |
|
1790
|
|
|
|
|
|
|
} |
|
1791
|
|
|
|
|
|
|
|
|
1792
|
|
|
|
|
|
|
sub split_group { |
|
1793
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1794
|
0
|
|
0
|
|
|
|
my $gff = $self->{gff} ||= Bio::DB::GFF->new(-adaptor=>'memory'); |
|
1795
|
0
|
|
|
|
|
|
return $gff->split_group(shift, $self->{gff_version} > 2); |
|
1796
|
|
|
|
|
|
|
} |
|
1797
|
|
|
|
|
|
|
|
|
1798
|
|
|
|
|
|
|
# create a panel if needed |
|
1799
|
|
|
|
|
|
|
sub new_panel { |
|
1800
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1801
|
0
|
|
|
|
|
|
my $options = shift; |
|
1802
|
|
|
|
|
|
|
|
|
1803
|
0
|
0
|
|
|
|
|
eval "require Bio::Graphics::Panel" unless Bio::Graphics::Panel->can('new'); |
|
1804
|
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
# general configuration of the image here |
|
1806
|
0
|
|
0
|
|
|
|
my $width = $self->setting(general => 'pixels') |
|
1807
|
|
|
|
|
|
|
|| $self->setting(general => 'width') |
|
1808
|
|
|
|
|
|
|
|| WIDTH; |
|
1809
|
|
|
|
|
|
|
|
|
1810
|
0
|
|
|
|
|
|
my ($start,$stop); |
|
1811
|
0
|
|
|
|
|
|
my $range_expr = '(-?\d+)(?:-|\.\.)(-?\d+)'; |
|
1812
|
|
|
|
|
|
|
|
|
1813
|
0
|
0
|
|
|
|
|
if (my $bases = $self->setting(general => 'bases')) { |
|
1814
|
0
|
|
|
|
|
|
($start,$stop) = $bases =~ /([\d-]+)(?:-|\.\.)([\d-]+)/; |
|
1815
|
|
|
|
|
|
|
} |
|
1816
|
|
|
|
|
|
|
|
|
1817
|
0
|
0
|
0
|
|
|
|
if (!defined $start || !defined $stop) { |
|
1818
|
0
|
0
|
|
|
|
|
$start = $self->min unless defined $start; |
|
1819
|
0
|
0
|
|
|
|
|
$stop = $self->max unless defined $stop; |
|
1820
|
|
|
|
|
|
|
} |
|
1821
|
|
|
|
|
|
|
|
|
1822
|
0
|
|
|
|
|
|
my $new_segment = Bio::Graphics::Feature->new(-start=>$start,-stop=>$stop); |
|
1823
|
0
|
0
|
0
|
|
|
|
my @panel_options = %$options if $options && ref $options eq 'HASH'; |
|
1824
|
0
|
|
|
|
|
|
my $panel = Bio::Graphics::Panel->new(-segment => $new_segment, |
|
1825
|
|
|
|
|
|
|
-width => $width, |
|
1826
|
|
|
|
|
|
|
-key_style => 'between', |
|
1827
|
|
|
|
|
|
|
$self->style('general'), |
|
1828
|
|
|
|
|
|
|
@panel_options |
|
1829
|
|
|
|
|
|
|
); |
|
1830
|
0
|
|
|
|
|
|
$panel; |
|
1831
|
|
|
|
|
|
|
} |
|
1832
|
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
=over 4 |
|
1834
|
|
|
|
|
|
|
|
|
1835
|
|
|
|
|
|
|
=item $mtime = $features-Emtime |
|
1836
|
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
=item $atime = $features-Eatime |
|
1838
|
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
=item $ctime = $features-Ectime |
|
1840
|
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
=item $size = $features-Esize |
|
1842
|
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
Returns stat() information about the data file, for featurefile |
|
1844
|
|
|
|
|
|
|
objects created using the -file option. Size is in bytes. mtime, |
|
1845
|
|
|
|
|
|
|
atime, and ctime are in seconds since the epoch. |
|
1846
|
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
=back |
|
1848
|
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
=cut |
|
1850
|
|
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
sub mtime { |
|
1852
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1853
|
0
|
|
0
|
|
|
|
my $d = $self->{m_time} || $self->{stat}->[9]; |
|
1854
|
0
|
0
|
|
|
|
|
$self->{m_time} = shift if @_; |
|
1855
|
0
|
|
|
|
|
|
$d; |
|
1856
|
|
|
|
|
|
|
} |
|
1857
|
0
|
|
|
0
|
1
|
|
sub atime { shift->{stat}->[8]; } |
|
1858
|
0
|
|
|
0
|
1
|
|
sub ctime { shift->{stat}->[10]; } |
|
1859
|
0
|
|
|
0
|
1
|
|
sub size { shift->{stat}->[7]; } |
|
1860
|
|
|
|
|
|
|
|
|
1861
|
|
|
|
|
|
|
=over 4 |
|
1862
|
|
|
|
|
|
|
|
|
1863
|
|
|
|
|
|
|
=item $label = $features-Efeature2label($feature) |
|
1864
|
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
Given a feature, determines the configuration stanza that bests |
|
1866
|
|
|
|
|
|
|
describes it. Uses the feature's type() method if it has it (DasI |
|
1867
|
|
|
|
|
|
|
interface) or its primary_tag() method otherwise. |
|
1868
|
|
|
|
|
|
|
|
|
1869
|
|
|
|
|
|
|
=back |
|
1870
|
|
|
|
|
|
|
|
|
1871
|
|
|
|
|
|
|
=cut |
|
1872
|
|
|
|
|
|
|
|
|
1873
|
|
|
|
|
|
|
sub feature2label { |
|
1874
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1875
|
0
|
|
|
|
|
|
my $feature = shift; |
|
1876
|
0
|
0
|
|
|
|
|
my $type = $feature->can('type') ? $feature->type |
|
1877
|
|
|
|
|
|
|
: $feature->primary_tag; |
|
1878
|
0
|
0
|
|
|
|
|
$type or return; |
|
1879
|
0
|
|
|
|
|
|
(my $basetype = $type) =~ s/:.+$//; |
|
1880
|
0
|
|
|
|
|
|
my @labels = $self->type2label($type); |
|
1881
|
0
|
0
|
|
|
|
|
@labels = $self->type2label($basetype) unless @labels; |
|
1882
|
0
|
0
|
|
|
|
|
@labels = ($type) unless @labels; |
|
1883
|
0
|
0
|
|
|
|
|
wantarray ? @labels : $labels[0]; |
|
1884
|
|
|
|
|
|
|
} |
|
1885
|
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
=over 4 |
|
1887
|
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
=item $link = $features-Elink_pattern($linkrule,$feature,$panel) |
|
1889
|
|
|
|
|
|
|
|
|
1890
|
|
|
|
|
|
|
Given a feature, tries to generate a URL to link out from it. This |
|
1891
|
|
|
|
|
|
|
uses the 'link' option, if one is present. This method is a |
|
1892
|
|
|
|
|
|
|
convenience for the generic genome browser. |
|
1893
|
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
=back |
|
1895
|
|
|
|
|
|
|
|
|
1896
|
|
|
|
|
|
|
=cut |
|
1897
|
|
|
|
|
|
|
|
|
1898
|
|
|
|
|
|
|
sub link_pattern { |
|
1899
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1900
|
0
|
|
|
|
|
|
my ($linkrule,$feature,$panel,$dont_escape) = @_; |
|
1901
|
|
|
|
|
|
|
|
|
1902
|
0
|
|
0
|
|
|
|
$panel ||= 'Bio::Graphics::Panel'; |
|
1903
|
|
|
|
|
|
|
|
|
1904
|
0
|
0
|
0
|
|
|
|
if (ref($linkrule) && ref($linkrule) eq 'CODE') { |
|
1905
|
0
|
|
|
|
|
|
my $val = eval {$linkrule->($feature,$panel)}; |
|
|
0
|
|
|
|
|
|
|
|
1906
|
0
|
0
|
|
|
|
|
$self->_callback_complain(none=>"linkrule for $feature") if $@; |
|
1907
|
0
|
|
|
|
|
|
return $val; |
|
1908
|
|
|
|
|
|
|
} |
|
1909
|
|
|
|
|
|
|
|
|
1910
|
0
|
0
|
|
|
|
|
require CGI unless defined &CGI::escape; |
|
1911
|
0
|
0
|
|
0
|
|
|
my $escape_method = $dont_escape ? sub {shift} : \&CGI::escape; |
|
|
0
|
|
|
|
|
|
|
|
1912
|
|
|
|
|
|
|
|
|
1913
|
0
|
|
|
|
|
|
my $n; |
|
1914
|
0
|
|
0
|
|
|
|
$linkrule ||= ''; # prevent uninit warning |
|
1915
|
0
|
0
|
|
|
|
|
my $seq_id = $feature->can('seq_id') ? $feature->seq_id() : $feature->location->seq_id(); |
|
1916
|
0
|
|
0
|
|
|
|
$seq_id ||= $feature->seq_id; #fallback |
|
1917
|
0
|
|
|
|
|
|
$linkrule =~ s!\$(\w+)! |
|
1918
|
|
|
|
|
|
|
$escape_method->( |
|
1919
|
|
|
|
|
|
|
$1 eq 'ref' ? (($n = $seq_id) && "$n") || '' |
|
1920
|
|
|
|
|
|
|
: $1 eq 'name' ? (($n = $feature->display_name) && "$n") || '' |
|
1921
|
|
|
|
|
|
|
: $1 eq 'class' ? eval {$feature->class} || '' |
|
1922
|
|
|
|
|
|
|
: $1 eq 'type' ? eval {$feature->method} || $feature->primary_tag || '' |
|
1923
|
|
|
|
|
|
|
: $1 eq 'method' ? eval {$feature->method} || $feature->primary_tag || '' |
|
1924
|
|
|
|
|
|
|
: $1 eq 'source' ? eval {$feature->source} || $feature->source_tag || '' |
|
1925
|
|
|
|
|
|
|
: $1 =~ 'seq_?id' ? eval{$feature->seq_id} || eval{$feature->location->seq_id} || '' |
|
1926
|
|
|
|
|
|
|
: $1 eq 'start' ? $feature->start || '' |
|
1927
|
|
|
|
|
|
|
: $1 eq 'end' ? $feature->end || '' |
|
1928
|
|
|
|
|
|
|
: $1 eq 'stop' ? $feature->end || '' |
|
1929
|
|
|
|
|
|
|
: $1 eq 'segstart' ? $panel->start || '' |
|
1930
|
|
|
|
|
|
|
: $1 eq 'segend' ? $panel->end || '' |
|
1931
|
|
|
|
|
|
|
: $1 eq 'length' ? $feature->length || 0 |
|
1932
|
|
|
|
|
|
|
: $1 eq 'description' ? eval {join '',$feature->notes} || '' |
|
1933
|
0
|
0
|
0
|
|
|
|
: $1 eq 'id' ? eval {$feature->feature_id} || eval {$feature->primary_id} || '' |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
: '$'.$1 |
|
1935
|
|
|
|
|
|
|
) |
|
1936
|
|
|
|
|
|
|
!exg; |
|
1937
|
0
|
|
|
|
|
|
return $linkrule; |
|
1938
|
|
|
|
|
|
|
} |
|
1939
|
|
|
|
|
|
|
|
|
1940
|
|
|
|
|
|
|
sub make_link { |
|
1941
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1942
|
0
|
|
|
|
|
|
my ($feature,$panel) = @_; |
|
1943
|
|
|
|
|
|
|
|
|
1944
|
0
|
|
|
|
|
|
my ($linkrule) = $feature->each_tag_value('link'); |
|
1945
|
|
|
|
|
|
|
|
|
1946
|
0
|
0
|
|
|
|
|
unless ($linkrule) { |
|
1947
|
0
|
|
|
|
|
|
for my $label ($self->feature2label($feature)) { |
|
1948
|
0
|
|
0
|
|
|
|
$linkrule ||= $self->setting($label,'link'); |
|
1949
|
0
|
|
0
|
|
|
|
$linkrule ||= $self->setting(general=>'link'); |
|
1950
|
|
|
|
|
|
|
} |
|
1951
|
|
|
|
|
|
|
} |
|
1952
|
0
|
|
|
|
|
|
return $self->link_pattern($linkrule,$feature,$panel); |
|
1953
|
|
|
|
|
|
|
} |
|
1954
|
|
|
|
|
|
|
|
|
1955
|
|
|
|
|
|
|
sub make_title { |
|
1956
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1957
|
0
|
|
|
|
|
|
my $feature = shift; |
|
1958
|
|
|
|
|
|
|
|
|
1959
|
0
|
|
|
|
|
|
for my $label ($self->feature2label($feature)) { |
|
1960
|
0
|
|
|
|
|
|
my $linkrule = $self->setting($label,'title'); |
|
1961
|
0
|
|
0
|
|
|
|
$linkrule ||= $self->setting(general=>'title'); |
|
1962
|
0
|
0
|
|
|
|
|
next unless $linkrule; |
|
1963
|
0
|
|
|
|
|
|
return $self->link_pattern($linkrule,$feature,undef,1); |
|
1964
|
|
|
|
|
|
|
} |
|
1965
|
|
|
|
|
|
|
|
|
1966
|
0
|
|
0
|
|
|
|
my $method = eval {$feature->method} || $feature->primary_tag; |
|
1967
|
0
|
0
|
|
|
|
|
my $seqid = $feature->can('seq_id') ? $feature->seq_id : $feature->location->seq_id; |
|
1968
|
0
|
|
|
|
|
|
my $title = eval { |
|
1969
|
0
|
0
|
0
|
|
|
|
if ($feature->can('target') && (my $target = $feature->target)) { |
|
1970
|
0
|
0
|
|
|
|
|
join (' ', |
|
1971
|
|
|
|
|
|
|
$method, |
|
1972
|
|
|
|
|
|
|
(defined $seqid ? "$seqid:" : ''). |
|
1973
|
|
|
|
|
|
|
$feature->start."..".$feature->end, |
|
1974
|
|
|
|
|
|
|
$feature->target.':'. |
|
1975
|
|
|
|
|
|
|
$feature->target->start."..".$feature->target->end); |
|
1976
|
|
|
|
|
|
|
} else { |
|
1977
|
0
|
0
|
0
|
|
|
|
join(' ', |
|
|
|
0
|
0
|
|
|
|
|
|
1978
|
|
|
|
|
|
|
$method, |
|
1979
|
|
|
|
|
|
|
$feature->can('display_name') ? $feature->display_name : $feature->info, |
|
1980
|
|
|
|
|
|
|
(defined $seqid ? "$seqid:" : ''). |
|
1981
|
|
|
|
|
|
|
($feature->start||'?')."..".($feature->end||'?') |
|
1982
|
|
|
|
|
|
|
); |
|
1983
|
|
|
|
|
|
|
} |
|
1984
|
|
|
|
|
|
|
}; |
|
1985
|
0
|
0
|
|
|
|
|
warn $@ if $@; |
|
1986
|
0
|
|
|
|
|
|
$title; |
|
1987
|
|
|
|
|
|
|
} |
|
1988
|
|
|
|
|
|
|
|
|
1989
|
|
|
|
|
|
|
# given a feature type, return its label(s) |
|
1990
|
|
|
|
|
|
|
sub type2label { |
|
1991
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
1992
|
0
|
|
|
|
|
|
my $type = shift; |
|
1993
|
0
|
|
0
|
|
|
|
$self->{_type2label} ||= $self->invert_types; |
|
1994
|
0
|
|
|
|
|
|
my @labels = keys %{$self->{_type2label}{lc $type}}; |
|
|
0
|
|
|
|
|
|
|
|
1995
|
0
|
0
|
|
|
|
|
wantarray ? @labels : $labels[0] |
|
1996
|
|
|
|
|
|
|
} |
|
1997
|
|
|
|
|
|
|
|
|
1998
|
|
|
|
|
|
|
sub invert_types { |
|
1999
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
2000
|
0
|
0
|
|
|
|
|
my $config = $self->{config} or return; |
|
2001
|
0
|
|
|
|
|
|
my %inverted; |
|
2002
|
0
|
|
|
|
|
|
for my $label (keys %{$config}) { |
|
|
0
|
|
|
|
|
|
|
|
2003
|
0
|
|
0
|
|
|
|
my $feature = $config->{$label}{feature} || $label; |
|
2004
|
0
|
|
0
|
|
|
|
foreach (shellwords($feature||'')) { |
|
2005
|
0
|
|
|
|
|
|
$inverted{lc $_}{$label}++; |
|
2006
|
|
|
|
|
|
|
} |
|
2007
|
|
|
|
|
|
|
} |
|
2008
|
0
|
|
|
|
|
|
\%inverted; |
|
2009
|
|
|
|
|
|
|
} |
|
2010
|
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
=over 4 |
|
2012
|
|
|
|
|
|
|
|
|
2013
|
|
|
|
|
|
|
=item $citation = $features-Ecitation($feature) |
|
2014
|
|
|
|
|
|
|
|
|
2015
|
|
|
|
|
|
|
Given a feature, tries to generate a citation for it, using the |
|
2016
|
|
|
|
|
|
|
"citation" option if one is present. This method is a convenience for |
|
2017
|
|
|
|
|
|
|
the generic genome browser. |
|
2018
|
|
|
|
|
|
|
|
|
2019
|
|
|
|
|
|
|
=back |
|
2020
|
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
=cut |
|
2022
|
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
# This routine returns the "citation" field. It is here in order to simplify the logic |
|
2024
|
|
|
|
|
|
|
# a bit in the generic browser |
|
2025
|
|
|
|
|
|
|
sub citation { |
|
2026
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
2027
|
0
|
|
0
|
|
|
|
my $feature = shift || 'general'; |
|
2028
|
0
|
|
|
|
|
|
return $self->setting($feature=>'citation'); |
|
2029
|
|
|
|
|
|
|
} |
|
2030
|
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
=over 4 |
|
2032
|
|
|
|
|
|
|
|
|
2033
|
|
|
|
|
|
|
=item $name = $features-Ename([$feature]) |
|
2034
|
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
Get/set the name of this feature set. This is a convenience method |
|
2036
|
|
|
|
|
|
|
useful for keeping track of multiple feature sets. |
|
2037
|
|
|
|
|
|
|
|
|
2038
|
|
|
|
|
|
|
=back |
|
2039
|
|
|
|
|
|
|
|
|
2040
|
|
|
|
|
|
|
=cut |
|
2041
|
|
|
|
|
|
|
|
|
2042
|
|
|
|
|
|
|
# give this feature file a nickname |
|
2043
|
|
|
|
|
|
|
sub name { |
|
2044
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
2045
|
0
|
|
|
|
|
|
my $d = $self->{name}; |
|
2046
|
0
|
0
|
|
|
|
|
$self->{name} = shift if @_; |
|
2047
|
0
|
|
|
|
|
|
$d; |
|
2048
|
|
|
|
|
|
|
} |
|
2049
|
|
|
|
|
|
|
|
|
2050
|
|
|
|
|
|
|
1; |
|
2051
|
|
|
|
|
|
|
|
|
2052
|
|
|
|
|
|
|
__END__ |