Mercurial > repos > dvanzessen > vep_emc
comparison dir_plugins/Draw.pm @ 0:e545d0a25ffe draft
Uploaded
| author | dvanzessen |
|---|---|
| date | Mon, 15 Jul 2019 05:17:17 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:e545d0a25ffe |
|---|---|
| 1 =head1 LICENSE | |
| 2 | |
| 3 Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute | |
| 4 Copyright [2016-2018] EMBL-European Bioinformatics Institute | |
| 5 | |
| 6 Licensed under the Apache License, Version 2.0 (the "License"); | |
| 7 you may not use this file except in compliance with the License. | |
| 8 You may obtain a copy of the License at | |
| 9 | |
| 10 http://www.apache.org/licenses/LICENSE-2.0 | |
| 11 | |
| 12 Unless required by applicable law or agreed to in writing, software | |
| 13 distributed under the License is distributed on an "AS IS" BASIS, | |
| 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 15 See the License for the specific language governing permissions and | |
| 16 limitations under the License. | |
| 17 | |
| 18 =head1 CONTACT | |
| 19 | |
| 20 Ensembl <http://www.ensembl.org/info/about/contact/index.html> | |
| 21 | |
| 22 =cut | |
| 23 | |
| 24 =head1 NAME | |
| 25 | |
| 26 Draw | |
| 27 | |
| 28 =head1 SYNOPSIS | |
| 29 | |
| 30 mv Draw.pm ~/.vep/Plugins | |
| 31 ./vep -i variations.vcf --plugin Draw | |
| 32 | |
| 33 =head1 DESCRIPTION | |
| 34 | |
| 35 A VEP plugin that draws pictures of the transcript model showing the | |
| 36 variant location. Can take five optional paramters: | |
| 37 | |
| 38 1) File name stem for images | |
| 39 2) Image width in pixels (default: 1000px) | |
| 40 3) Image height in pixels (default: 100px) | |
| 41 4) Transcript ID - only draw images for this transcript | |
| 42 5) Variant ID - only draw images for this variant | |
| 43 | |
| 44 e.g. | |
| 45 | |
| 46 ./vep -i variations.vcf --plugin Draw,myimg,2000,100 | |
| 47 | |
| 48 Images are written to [file_stem]_[transcript_id]_[variant_id].png | |
| 49 | |
| 50 Requires GD library installed to run. | |
| 51 | |
| 52 =cut | |
| 53 | |
| 54 package Draw; | |
| 55 | |
| 56 use strict; | |
| 57 use warnings; | |
| 58 | |
| 59 use Bio::EnsEMBL::Variation::Utils::BaseVepPlugin; | |
| 60 | |
| 61 # GD libraries for image creating | |
| 62 use GD; | |
| 63 use GD::Polygon; | |
| 64 | |
| 65 use Bio::EnsEMBL::Variation::Utils::VariationEffect qw(MAX_DISTANCE_FROM_TRANSCRIPT); | |
| 66 | |
| 67 use base qw(Bio::EnsEMBL::Variation::Utils::BaseVepPlugin); | |
| 68 | |
| 69 | |
| 70 sub new { | |
| 71 my $class = shift; | |
| 72 | |
| 73 my $self = $class->SUPER::new(@_); | |
| 74 | |
| 75 # configure | |
| 76 my @params = @{$self->params}; | |
| 77 | |
| 78 $self->{prefix} = $params[0] || $self->{config}->{output_file}; | |
| 79 $self->{width} = $params[1] || 1000; | |
| 80 $self->{height} = $params[2] || 100; | |
| 81 $self->{transcript} = $params[3] || undef; | |
| 82 $self->{variant} = $params[4] || undef; | |
| 83 | |
| 84 return $self; | |
| 85 } | |
| 86 | |
| 87 sub version { | |
| 88 return '2.4'; | |
| 89 } | |
| 90 | |
| 91 sub feature_types { | |
| 92 return ['Transcript']; | |
| 93 } | |
| 94 | |
| 95 sub variant_feature_types { | |
| 96 return ['BaseVariationFeature']; | |
| 97 } | |
| 98 | |
| 99 sub get_header_info { | |
| 100 return {}; | |
| 101 } | |
| 102 | |
| 103 sub run { | |
| 104 my ($self, $tva) = @_; | |
| 105 | |
| 106 my $main_tr = $tva->feature; | |
| 107 my $vf = $tva->base_variation_feature; | |
| 108 | |
| 109 return {} if defined($self->{transcript}) && $main_tr->stable_id ne $self->{transcript}; | |
| 110 return {} if defined($self->{variant}) && $vf->variation_name ne $self->{variant}; | |
| 111 | |
| 112 # if we're showing a gene fusion, get | |
| 113 my $second_tr = $tva->{_fusion_transcripts}->[0] if grep {$_->SO_term =~ /gene_fusion/} @{$tva->get_all_OverlapConsequences}; | |
| 114 | |
| 115 # set up scales etc | |
| 116 my $width = $main_tr->feature_Slice->length + (2 * MAX_DISTANCE_FROM_TRANSCRIPT); | |
| 117 my $tr_start = $main_tr->start - MAX_DISTANCE_FROM_TRANSCRIPT; | |
| 118 my $tr_end = $main_tr->end + MAX_DISTANCE_FROM_TRANSCRIPT; | |
| 119 | |
| 120 if(defined($second_tr)) { | |
| 121 $width = (max(($main_tr->end, $second_tr->end)) - min(($main_tr->start, $second_tr->start))) + 1 + (2 * MAX_DISTANCE_FROM_TRANSCRIPT); | |
| 122 $tr_start = min(($main_tr->start, $second_tr->start)) - MAX_DISTANCE_FROM_TRANSCRIPT; | |
| 123 $tr_end = max(($main_tr->end, $second_tr->end)) + MAX_DISTANCE_FROM_TRANSCRIPT; | |
| 124 } | |
| 125 | |
| 126 my $x_scale = ($self->{width} - 20) / $width; | |
| 127 my $y_scale = ($self->{height} - 30) / 100; | |
| 128 my $x_off = 10; | |
| 129 my $y_off = 20; | |
| 130 | |
| 131 # create GD image object | |
| 132 my $img = GD::Image->new($self->{width}, $self->{height}); | |
| 133 | |
| 134 # set up some colours | |
| 135 my %colours = ( | |
| 136 white => $img->colorAllocate(255,255,255), | |
| 137 black => $img->colorAllocate(0,0,0), | |
| 138 grey => $img->colorAllocate(200,200,200), | |
| 139 darkgrey => $img->colorAllocate(150,150,150), | |
| 140 vlightgrey => $img->colorAllocate(235,235,235), | |
| 141 blue => $img->colorAllocate(0,0,200), | |
| 142 lightblue => $img->colorAllocate(200,200,255), | |
| 143 red => $img->colorAllocate(200,0,0), | |
| 144 lightred => $img->colorAllocate(255,200,200), | |
| 145 green => $img->colorAllocate(0,200,0), | |
| 146 lightgreen => $img->colorAllocate(220,255,220), | |
| 147 yellow => $img->colorAllocate(236,164,26), | |
| 148 purple => $img->colorAllocate(195,50,212), | |
| 149 ); | |
| 150 | |
| 151 # scale bar | |
| 152 my $zero_string = '0' x (length(int(100 / $x_scale)) - 1); | |
| 153 my $bases_per_bar = '1'.$zero_string; | |
| 154 | |
| 155 my $start = int($tr_start / $bases_per_bar) * $bases_per_bar; | |
| 156 my $end = (int($tr_start / $bases_per_bar) + 1) * $bases_per_bar; | |
| 157 my $colour = 0; | |
| 158 | |
| 159 while($start < $tr_end) { | |
| 160 my $method = $colour ? 'rectangle' : 'filledRectangle'; | |
| 161 | |
| 162 $img->$method( | |
| 163 $x_off + (($start - $tr_start) * $x_scale), | |
| 164 $self->{height} - 15, | |
| 165 $x_off + (($end - $tr_start) * $x_scale), | |
| 166 $self->{height} - 10, | |
| 167 $colours{grey}, | |
| 168 ); | |
| 169 | |
| 170 # tick and label | |
| 171 if($start =~ /(5|0)$zero_string$/) { | |
| 172 my $string = $start; | |
| 173 1 while $string =~ s/^(-?\d+)(\d\d\d)/$1,$2/; | |
| 174 | |
| 175 $img->string( | |
| 176 gdTinyFont, | |
| 177 $x_off + (($start - $tr_start) * $x_scale) + 2, | |
| 178 $self->{height} - 8, | |
| 179 $string, | |
| 180 $colours{black} | |
| 181 ); | |
| 182 | |
| 183 $img->line( | |
| 184 $x_off + (($start - $tr_start) * $x_scale), | |
| 185 $self->{height} - 15, | |
| 186 $x_off + (($start - $tr_start) * $x_scale), | |
| 187 $self->{height}, | |
| 188 $start =~ /5$zero_string$/ ? $colours{grey} : $colours{black} | |
| 189 ); | |
| 190 | |
| 191 $img->dashedLine( | |
| 192 $x_off + (($start - $tr_start) * $x_scale), | |
| 193 0, | |
| 194 $x_off + (($start - $tr_start) * $x_scale), | |
| 195 $self->{height} - 15, | |
| 196 $start =~ /5$zero_string$/ ? $colours{vlightgrey} : $colours{grey} | |
| 197 ) | |
| 198 } | |
| 199 | |
| 200 $colour = 1 - $colour; | |
| 201 $start += $bases_per_bar; | |
| 202 $end += $bases_per_bar; | |
| 203 } | |
| 204 | |
| 205 # render transcripts | |
| 206 foreach my $tr($main_tr, $second_tr) { | |
| 207 next unless defined($tr); | |
| 208 | |
| 209 # render introns | |
| 210 foreach my $intron(@{$tr->get_all_Introns}) { | |
| 211 $img->line( | |
| 212 $x_off + (($intron->start - $tr_start) * $x_scale), | |
| 213 $y_off + (20 * $y_scale), | |
| 214 $x_off + (((($intron->start + $intron->end) / 2) - $tr_start) * $x_scale), | |
| 215 $y_off + (10 * $y_scale), | |
| 216 $colours{lightblue} | |
| 217 ); | |
| 218 | |
| 219 $img->line( | |
| 220 $x_off + (((($intron->start + $intron->end) / 2) - $tr_start) * $x_scale), | |
| 221 $y_off + (10 * $y_scale), | |
| 222 $x_off + (($intron->end - $tr_start) * $x_scale), | |
| 223 $y_off + (20 * $y_scale), | |
| 224 $colours{lightblue} | |
| 225 ); | |
| 226 } | |
| 227 | |
| 228 # render exons | |
| 229 foreach my $exon(@{$tr->get_all_Exons}) { | |
| 230 | |
| 231 # non-coding part | |
| 232 $img->rectangle( | |
| 233 $x_off + (($exon->start - $tr_start) * $x_scale), | |
| 234 $y_off + (10 * $y_scale), | |
| 235 $x_off + (($exon->end - $tr_start) * $x_scale), | |
| 236 $y_off + (30 * $y_scale), | |
| 237 $colours{lightblue} | |
| 238 ); | |
| 239 | |
| 240 # coding part | |
| 241 $img->filledRectangle( | |
| 242 $x_off + (($exon->coding_region_start($tr) - $tr_start) * $x_scale), | |
| 243 $y_off + (0 * $y_scale), | |
| 244 $x_off + (($exon->coding_region_end($tr) - $tr_start) * $x_scale), | |
| 245 $y_off + (40 * $y_scale), | |
| 246 $colours{blue} | |
| 247 ) if defined $exon->coding_region_start($tr) && defined $exon->coding_region_end($tr); | |
| 248 } | |
| 249 | |
| 250 # add transcript direction indicator | |
| 251 if($tr->strand == 1) { | |
| 252 | |
| 253 # vertical line | |
| 254 $img->line( | |
| 255 $x_off + (($tr->start - $tr_start) * $x_scale), | |
| 256 $y_off + (-5 * $y_scale), | |
| 257 $x_off + (($tr->start - $tr_start) * $x_scale), | |
| 258 $y_off + (20 * $y_scale), | |
| 259 $colours{lightblue}, | |
| 260 ); | |
| 261 | |
| 262 # horizontal line | |
| 263 $img->line( | |
| 264 $x_off + (($tr->start - $tr_start) * $x_scale), | |
| 265 $y_off + (-5 * $y_scale), | |
| 266 $x_off + (($tr->start - $tr_start) * $x_scale) + 20, | |
| 267 $y_off + (-5 * $y_scale), | |
| 268 $colours{lightblue}, | |
| 269 ); | |
| 270 | |
| 271 # top arrow part | |
| 272 $img->line( | |
| 273 $x_off + (($tr->start - $tr_start) * $x_scale) + 17, | |
| 274 $y_off + (-8 * $y_scale), | |
| 275 $x_off + (($tr->start - $tr_start) * $x_scale) + 20, | |
| 276 $y_off + (-5 * $y_scale), | |
| 277 $colours{lightblue}, | |
| 278 ); | |
| 279 | |
| 280 # bottom arrow part | |
| 281 $img->line( | |
| 282 $x_off + (($tr->start - $tr_start) * $x_scale) + 17, | |
| 283 $y_off + (-1 * $y_scale), | |
| 284 $x_off + (($tr->start - $tr_start) * $x_scale) + 20, | |
| 285 $y_off + (-5 * $y_scale), | |
| 286 $colours{lightblue}, | |
| 287 ); | |
| 288 | |
| 289 # label | |
| 290 $img->string(gdTinyFont, $x_off + (($tr->start - $tr_start) * $x_scale) + 25, $y_off + (-12 * $y_scale), $tr->stable_id, $colours{blue}); | |
| 291 } | |
| 292 | |
| 293 else { | |
| 294 | |
| 295 # vertical line | |
| 296 $img->line( | |
| 297 $x_off + (($tr->end - $tr_start) * $x_scale), | |
| 298 $y_off + (20 * $y_scale), | |
| 299 $x_off + (($tr->end - $tr_start) * $x_scale), | |
| 300 $y_off + (47 * $y_scale), | |
| 301 $colours{lightblue}, | |
| 302 ); | |
| 303 | |
| 304 # horizontal line | |
| 305 $img->line( | |
| 306 $x_off + (($tr->end - $tr_start) * $x_scale), | |
| 307 $y_off + (47 * $y_scale), | |
| 308 $x_off + (($tr->end - $tr_start) * $x_scale) - 20, | |
| 309 $y_off + (47 * $y_scale), | |
| 310 $colours{lightblue}, | |
| 311 ); | |
| 312 | |
| 313 # top arrow part | |
| 314 $img->line( | |
| 315 $x_off + (($tr->end - $tr_start) * $x_scale) - 17, | |
| 316 $y_off + (50 * $y_scale), | |
| 317 $x_off + (($tr->end - $tr_start) * $x_scale) - 20, | |
| 318 $y_off + (47 * $y_scale), | |
| 319 $colours{lightblue}, | |
| 320 ); | |
| 321 | |
| 322 # bottom arrow part | |
| 323 $img->line( | |
| 324 $x_off + (($tr->end - $tr_start) * $x_scale) - 17, | |
| 325 $y_off + (43 * $y_scale), | |
| 326 $x_off + (($tr->end - $tr_start) * $x_scale) - 20, | |
| 327 $y_off + (47 * $y_scale), | |
| 328 $colours{lightblue}, | |
| 329 ); | |
| 330 | |
| 331 # label | |
| 332 $img->string(gdTinyFont, $x_off + (($tr->end - $tr_start) * $x_scale) - 100, $y_off + (43 * $y_scale), $tr->stable_id, $colours{blue}); | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 # render variant | |
| 337 my $var_colour = 'green'; | |
| 338 | |
| 339 if($vf->class_SO_term =~ /deletion/) { | |
| 340 $var_colour = 'red'; | |
| 341 } | |
| 342 | |
| 343 $img->filledRectangle( | |
| 344 $x_off + (($vf->start - $tr_start) * $x_scale), | |
| 345 $y_off + (60 * $y_scale), | |
| 346 $x_off + (($vf->end - $tr_start) * $x_scale), | |
| 347 $y_off + (70 * $y_scale), | |
| 348 $colours{$var_colour}, | |
| 349 ); | |
| 350 | |
| 351 # variant label | |
| 352 $img->string( | |
| 353 gdTinyFont, | |
| 354 $x_off + (($vf->start - $tr_start) * $x_scale), | |
| 355 $y_off + (75 * $y_scale), | |
| 356 $vf->variation_name, | |
| 357 $colours{$var_colour} | |
| 358 ); | |
| 359 | |
| 360 my $vname = $vf->variation_name; | |
| 361 $vname =~ s/\//\_/g; | |
| 362 my $file = $self->{prefix}."_".$main_tr->stable_id."_".(defined($second_tr) ? $second_tr->stable_id."_" : "").$vname."\.png"; | |
| 363 | |
| 364 # check we're allowed to write to it | |
| 365 if(!defined($self->{config}->{force_overwrite}) && -e $file) { | |
| 366 die "ERROR: Image file $file already exists - choose a different file name stem or use --force_overwrite\n"; | |
| 367 return; | |
| 368 } | |
| 369 | |
| 370 open IM, ">$file" or die "ERROR: Could not write to file $file\n"; | |
| 371 binmode IM; | |
| 372 print IM $img->png; | |
| 373 close IM; | |
| 374 | |
| 375 return {}; | |
| 376 } | |
| 377 | |
| 378 sub max { | |
| 379 return (sort {$a <=> $b} @_)[-1]; | |
| 380 } | |
| 381 | |
| 382 sub min { | |
| 383 return (sort {$a <=> $b} @_)[0]; | |
| 384 } | |
| 385 | |
| 386 1; | |
| 387 |
