biotite 1.5.0__cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of biotite might be problematic. Click here for more details.
- biotite/__init__.py +18 -0
- biotite/application/__init__.py +69 -0
- biotite/application/application.py +276 -0
- biotite/application/autodock/__init__.py +12 -0
- biotite/application/autodock/app.py +500 -0
- biotite/application/blast/__init__.py +14 -0
- biotite/application/blast/alignment.py +92 -0
- biotite/application/blast/webapp.py +428 -0
- biotite/application/clustalo/__init__.py +12 -0
- biotite/application/clustalo/app.py +223 -0
- biotite/application/dssp/__init__.py +12 -0
- biotite/application/dssp/app.py +216 -0
- biotite/application/localapp.py +342 -0
- biotite/application/mafft/__init__.py +12 -0
- biotite/application/mafft/app.py +116 -0
- biotite/application/msaapp.py +363 -0
- biotite/application/muscle/__init__.py +13 -0
- biotite/application/muscle/app3.py +227 -0
- biotite/application/muscle/app5.py +163 -0
- biotite/application/sra/__init__.py +18 -0
- biotite/application/sra/app.py +447 -0
- biotite/application/tantan/__init__.py +12 -0
- biotite/application/tantan/app.py +199 -0
- biotite/application/util.py +77 -0
- biotite/application/viennarna/__init__.py +18 -0
- biotite/application/viennarna/rnaalifold.py +310 -0
- biotite/application/viennarna/rnafold.py +254 -0
- biotite/application/viennarna/rnaplot.py +208 -0
- biotite/application/viennarna/util.py +77 -0
- biotite/application/webapp.py +76 -0
- biotite/copyable.py +71 -0
- biotite/database/__init__.py +23 -0
- biotite/database/afdb/__init__.py +12 -0
- biotite/database/afdb/download.py +197 -0
- biotite/database/entrez/__init__.py +15 -0
- biotite/database/entrez/check.py +60 -0
- biotite/database/entrez/dbnames.py +101 -0
- biotite/database/entrez/download.py +228 -0
- biotite/database/entrez/key.py +44 -0
- biotite/database/entrez/query.py +263 -0
- biotite/database/error.py +16 -0
- biotite/database/pubchem/__init__.py +21 -0
- biotite/database/pubchem/download.py +258 -0
- biotite/database/pubchem/error.py +30 -0
- biotite/database/pubchem/query.py +819 -0
- biotite/database/pubchem/throttle.py +98 -0
- biotite/database/rcsb/__init__.py +13 -0
- biotite/database/rcsb/download.py +161 -0
- biotite/database/rcsb/query.py +963 -0
- biotite/database/uniprot/__init__.py +13 -0
- biotite/database/uniprot/check.py +40 -0
- biotite/database/uniprot/download.py +126 -0
- biotite/database/uniprot/query.py +292 -0
- biotite/file.py +244 -0
- biotite/interface/__init__.py +19 -0
- biotite/interface/openmm/__init__.py +20 -0
- biotite/interface/openmm/state.py +93 -0
- biotite/interface/openmm/system.py +227 -0
- biotite/interface/pymol/__init__.py +201 -0
- biotite/interface/pymol/cgo.py +346 -0
- biotite/interface/pymol/convert.py +185 -0
- biotite/interface/pymol/display.py +267 -0
- biotite/interface/pymol/object.py +1228 -0
- biotite/interface/pymol/shapes.py +178 -0
- biotite/interface/pymol/startup.py +169 -0
- biotite/interface/rdkit/__init__.py +19 -0
- biotite/interface/rdkit/mol.py +490 -0
- biotite/interface/version.py +94 -0
- biotite/interface/warning.py +19 -0
- biotite/sequence/__init__.py +84 -0
- biotite/sequence/align/__init__.py +199 -0
- biotite/sequence/align/alignment.py +702 -0
- biotite/sequence/align/banded.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/banded.pyx +652 -0
- biotite/sequence/align/buckets.py +71 -0
- biotite/sequence/align/cigar.py +425 -0
- biotite/sequence/align/kmeralphabet.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/kmeralphabet.pyx +595 -0
- biotite/sequence/align/kmersimilarity.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/kmersimilarity.pyx +233 -0
- biotite/sequence/align/kmertable.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/kmertable.pyx +3411 -0
- biotite/sequence/align/localgapped.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/localgapped.pyx +892 -0
- biotite/sequence/align/localungapped.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/localungapped.pyx +279 -0
- biotite/sequence/align/matrix.py +631 -0
- biotite/sequence/align/matrix_data/3Di.mat +24 -0
- biotite/sequence/align/matrix_data/BLOSUM100.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM30.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM35.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM40.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM45.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM50.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM50_13p.mat +25 -0
- biotite/sequence/align/matrix_data/BLOSUM50_14.3.mat +25 -0
- biotite/sequence/align/matrix_data/BLOSUM50_5.0.mat +25 -0
- biotite/sequence/align/matrix_data/BLOSUM55.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM60.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM62.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM62_13p.mat +25 -0
- biotite/sequence/align/matrix_data/BLOSUM62_14.3.mat +25 -0
- biotite/sequence/align/matrix_data/BLOSUM62_5.0.mat +25 -0
- biotite/sequence/align/matrix_data/BLOSUM65.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM70.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM75.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM80.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM85.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUM90.mat +31 -0
- biotite/sequence/align/matrix_data/BLOSUMN.mat +31 -0
- biotite/sequence/align/matrix_data/CorBLOSUM49_5.0.mat +25 -0
- biotite/sequence/align/matrix_data/CorBLOSUM57_13p.mat +25 -0
- biotite/sequence/align/matrix_data/CorBLOSUM57_14.3.mat +25 -0
- biotite/sequence/align/matrix_data/CorBLOSUM61_5.0.mat +25 -0
- biotite/sequence/align/matrix_data/CorBLOSUM66_13p.mat +25 -0
- biotite/sequence/align/matrix_data/CorBLOSUM67_14.3.mat +25 -0
- biotite/sequence/align/matrix_data/DAYHOFF.mat +32 -0
- biotite/sequence/align/matrix_data/GONNET.mat +26 -0
- biotite/sequence/align/matrix_data/IDENTITY.mat +25 -0
- biotite/sequence/align/matrix_data/MATCH.mat +25 -0
- biotite/sequence/align/matrix_data/NUC.mat +25 -0
- biotite/sequence/align/matrix_data/PAM10.mat +34 -0
- biotite/sequence/align/matrix_data/PAM100.mat +34 -0
- biotite/sequence/align/matrix_data/PAM110.mat +34 -0
- biotite/sequence/align/matrix_data/PAM120.mat +34 -0
- biotite/sequence/align/matrix_data/PAM130.mat +34 -0
- biotite/sequence/align/matrix_data/PAM140.mat +34 -0
- biotite/sequence/align/matrix_data/PAM150.mat +34 -0
- biotite/sequence/align/matrix_data/PAM160.mat +34 -0
- biotite/sequence/align/matrix_data/PAM170.mat +34 -0
- biotite/sequence/align/matrix_data/PAM180.mat +34 -0
- biotite/sequence/align/matrix_data/PAM190.mat +34 -0
- biotite/sequence/align/matrix_data/PAM20.mat +34 -0
- biotite/sequence/align/matrix_data/PAM200.mat +34 -0
- biotite/sequence/align/matrix_data/PAM210.mat +34 -0
- biotite/sequence/align/matrix_data/PAM220.mat +34 -0
- biotite/sequence/align/matrix_data/PAM230.mat +34 -0
- biotite/sequence/align/matrix_data/PAM240.mat +34 -0
- biotite/sequence/align/matrix_data/PAM250.mat +34 -0
- biotite/sequence/align/matrix_data/PAM260.mat +34 -0
- biotite/sequence/align/matrix_data/PAM270.mat +34 -0
- biotite/sequence/align/matrix_data/PAM280.mat +34 -0
- biotite/sequence/align/matrix_data/PAM290.mat +34 -0
- biotite/sequence/align/matrix_data/PAM30.mat +34 -0
- biotite/sequence/align/matrix_data/PAM300.mat +34 -0
- biotite/sequence/align/matrix_data/PAM310.mat +34 -0
- biotite/sequence/align/matrix_data/PAM320.mat +34 -0
- biotite/sequence/align/matrix_data/PAM330.mat +34 -0
- biotite/sequence/align/matrix_data/PAM340.mat +34 -0
- biotite/sequence/align/matrix_data/PAM350.mat +34 -0
- biotite/sequence/align/matrix_data/PAM360.mat +34 -0
- biotite/sequence/align/matrix_data/PAM370.mat +34 -0
- biotite/sequence/align/matrix_data/PAM380.mat +34 -0
- biotite/sequence/align/matrix_data/PAM390.mat +34 -0
- biotite/sequence/align/matrix_data/PAM40.mat +34 -0
- biotite/sequence/align/matrix_data/PAM400.mat +34 -0
- biotite/sequence/align/matrix_data/PAM410.mat +34 -0
- biotite/sequence/align/matrix_data/PAM420.mat +34 -0
- biotite/sequence/align/matrix_data/PAM430.mat +34 -0
- biotite/sequence/align/matrix_data/PAM440.mat +34 -0
- biotite/sequence/align/matrix_data/PAM450.mat +34 -0
- biotite/sequence/align/matrix_data/PAM460.mat +34 -0
- biotite/sequence/align/matrix_data/PAM470.mat +34 -0
- biotite/sequence/align/matrix_data/PAM480.mat +34 -0
- biotite/sequence/align/matrix_data/PAM490.mat +34 -0
- biotite/sequence/align/matrix_data/PAM50.mat +34 -0
- biotite/sequence/align/matrix_data/PAM500.mat +34 -0
- biotite/sequence/align/matrix_data/PAM60.mat +34 -0
- biotite/sequence/align/matrix_data/PAM70.mat +34 -0
- biotite/sequence/align/matrix_data/PAM80.mat +34 -0
- biotite/sequence/align/matrix_data/PAM90.mat +34 -0
- biotite/sequence/align/matrix_data/PB.license +21 -0
- biotite/sequence/align/matrix_data/PB.mat +18 -0
- biotite/sequence/align/matrix_data/RBLOSUM52_5.0.mat +25 -0
- biotite/sequence/align/matrix_data/RBLOSUM59_13p.mat +25 -0
- biotite/sequence/align/matrix_data/RBLOSUM59_14.3.mat +25 -0
- biotite/sequence/align/matrix_data/RBLOSUM64_5.0.mat +25 -0
- biotite/sequence/align/matrix_data/RBLOSUM69_13p.mat +25 -0
- biotite/sequence/align/matrix_data/RBLOSUM69_14.3.mat +25 -0
- biotite/sequence/align/multiple.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/multiple.pyx +619 -0
- biotite/sequence/align/pairwise.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/pairwise.pyx +585 -0
- biotite/sequence/align/permutation.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/permutation.pyx +313 -0
- biotite/sequence/align/primes.txt +821 -0
- biotite/sequence/align/selector.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/selector.pyx +954 -0
- biotite/sequence/align/statistics.py +264 -0
- biotite/sequence/align/tracetable.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/align/tracetable.pxd +64 -0
- biotite/sequence/align/tracetable.pyx +370 -0
- biotite/sequence/alphabet.py +555 -0
- biotite/sequence/annotation.py +836 -0
- biotite/sequence/codec.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/codec.pyx +155 -0
- biotite/sequence/codon.py +476 -0
- biotite/sequence/codon_tables.txt +202 -0
- biotite/sequence/graphics/__init__.py +33 -0
- biotite/sequence/graphics/alignment.py +1101 -0
- biotite/sequence/graphics/color_schemes/3di_flower.json +48 -0
- biotite/sequence/graphics/color_schemes/autumn.json +51 -0
- biotite/sequence/graphics/color_schemes/blossom.json +51 -0
- biotite/sequence/graphics/color_schemes/clustalx_dna.json +11 -0
- biotite/sequence/graphics/color_schemes/clustalx_protein.json +28 -0
- biotite/sequence/graphics/color_schemes/flower.json +51 -0
- biotite/sequence/graphics/color_schemes/jalview_buried.json +31 -0
- biotite/sequence/graphics/color_schemes/jalview_hydrophobicity.json +31 -0
- biotite/sequence/graphics/color_schemes/jalview_prop_helix.json +31 -0
- biotite/sequence/graphics/color_schemes/jalview_prop_strand.json +31 -0
- biotite/sequence/graphics/color_schemes/jalview_prop_turn.json +31 -0
- biotite/sequence/graphics/color_schemes/jalview_taylor.json +28 -0
- biotite/sequence/graphics/color_schemes/jalview_zappo.json +28 -0
- biotite/sequence/graphics/color_schemes/ocean.json +51 -0
- biotite/sequence/graphics/color_schemes/pb_flower.json +40 -0
- biotite/sequence/graphics/color_schemes/rainbow_dna.json +11 -0
- biotite/sequence/graphics/color_schemes/rainbow_protein.json +30 -0
- biotite/sequence/graphics/color_schemes/spring.json +51 -0
- biotite/sequence/graphics/color_schemes/sunset.json +51 -0
- biotite/sequence/graphics/color_schemes/wither.json +51 -0
- biotite/sequence/graphics/colorschemes.py +170 -0
- biotite/sequence/graphics/dendrogram.py +231 -0
- biotite/sequence/graphics/features.py +544 -0
- biotite/sequence/graphics/logo.py +102 -0
- biotite/sequence/graphics/plasmid.py +712 -0
- biotite/sequence/io/__init__.py +12 -0
- biotite/sequence/io/fasta/__init__.py +22 -0
- biotite/sequence/io/fasta/convert.py +283 -0
- biotite/sequence/io/fasta/file.py +265 -0
- biotite/sequence/io/fastq/__init__.py +19 -0
- biotite/sequence/io/fastq/convert.py +117 -0
- biotite/sequence/io/fastq/file.py +507 -0
- biotite/sequence/io/genbank/__init__.py +17 -0
- biotite/sequence/io/genbank/annotation.py +269 -0
- biotite/sequence/io/genbank/file.py +573 -0
- biotite/sequence/io/genbank/metadata.py +336 -0
- biotite/sequence/io/genbank/sequence.py +173 -0
- biotite/sequence/io/general.py +201 -0
- biotite/sequence/io/gff/__init__.py +26 -0
- biotite/sequence/io/gff/convert.py +128 -0
- biotite/sequence/io/gff/file.py +449 -0
- biotite/sequence/phylo/__init__.py +36 -0
- biotite/sequence/phylo/nj.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/phylo/nj.pyx +221 -0
- biotite/sequence/phylo/tree.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/phylo/tree.pyx +1169 -0
- biotite/sequence/phylo/upgma.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/sequence/phylo/upgma.pyx +164 -0
- biotite/sequence/profile.py +561 -0
- biotite/sequence/search.py +117 -0
- biotite/sequence/seqtypes.py +720 -0
- biotite/sequence/sequence.py +373 -0
- biotite/setup_ccd.py +197 -0
- biotite/structure/__init__.py +135 -0
- biotite/structure/alphabet/__init__.py +25 -0
- biotite/structure/alphabet/encoder.py +332 -0
- biotite/structure/alphabet/encoder_weights_3di.kerasify +0 -0
- biotite/structure/alphabet/i3d.py +109 -0
- biotite/structure/alphabet/layers.py +86 -0
- biotite/structure/alphabet/pb.license +21 -0
- biotite/structure/alphabet/pb.py +170 -0
- biotite/structure/alphabet/unkerasify.py +128 -0
- biotite/structure/atoms.py +1562 -0
- biotite/structure/basepairs.py +1403 -0
- biotite/structure/bonds.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/structure/bonds.pyx +2036 -0
- biotite/structure/box.py +724 -0
- biotite/structure/celllist.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/structure/celllist.pyx +864 -0
- biotite/structure/chains.py +310 -0
- biotite/structure/charges.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/structure/charges.pyx +520 -0
- biotite/structure/compare.py +683 -0
- biotite/structure/density.py +109 -0
- biotite/structure/dotbracket.py +213 -0
- biotite/structure/error.py +39 -0
- biotite/structure/filter.py +591 -0
- biotite/structure/geometry.py +817 -0
- biotite/structure/graphics/__init__.py +13 -0
- biotite/structure/graphics/atoms.py +243 -0
- biotite/structure/graphics/rna.py +298 -0
- biotite/structure/hbond.py +425 -0
- biotite/structure/info/__init__.py +24 -0
- biotite/structure/info/atom_masses.json +121 -0
- biotite/structure/info/atoms.py +98 -0
- biotite/structure/info/bonds.py +149 -0
- biotite/structure/info/ccd.py +200 -0
- biotite/structure/info/components.bcif +0 -0
- biotite/structure/info/groups.py +128 -0
- biotite/structure/info/masses.py +121 -0
- biotite/structure/info/misc.py +137 -0
- biotite/structure/info/radii.py +267 -0
- biotite/structure/info/standardize.py +185 -0
- biotite/structure/integrity.py +213 -0
- biotite/structure/io/__init__.py +29 -0
- biotite/structure/io/dcd/__init__.py +13 -0
- biotite/structure/io/dcd/file.py +67 -0
- biotite/structure/io/general.py +243 -0
- biotite/structure/io/gro/__init__.py +14 -0
- biotite/structure/io/gro/file.py +343 -0
- biotite/structure/io/mol/__init__.py +20 -0
- biotite/structure/io/mol/convert.py +112 -0
- biotite/structure/io/mol/ctab.py +420 -0
- biotite/structure/io/mol/header.py +120 -0
- biotite/structure/io/mol/mol.py +149 -0
- biotite/structure/io/mol/sdf.py +940 -0
- biotite/structure/io/netcdf/__init__.py +13 -0
- biotite/structure/io/netcdf/file.py +64 -0
- biotite/structure/io/pdb/__init__.py +20 -0
- biotite/structure/io/pdb/convert.py +389 -0
- biotite/structure/io/pdb/file.py +1380 -0
- biotite/structure/io/pdb/hybrid36.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/structure/io/pdb/hybrid36.pyx +242 -0
- biotite/structure/io/pdbqt/__init__.py +15 -0
- biotite/structure/io/pdbqt/convert.py +113 -0
- biotite/structure/io/pdbqt/file.py +688 -0
- biotite/structure/io/pdbx/__init__.py +23 -0
- biotite/structure/io/pdbx/bcif.py +674 -0
- biotite/structure/io/pdbx/cif.py +1091 -0
- biotite/structure/io/pdbx/component.py +251 -0
- biotite/structure/io/pdbx/compress.py +362 -0
- biotite/structure/io/pdbx/convert.py +2113 -0
- biotite/structure/io/pdbx/encoding.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/structure/io/pdbx/encoding.pyx +1078 -0
- biotite/structure/io/trajfile.py +696 -0
- biotite/structure/io/trr/__init__.py +13 -0
- biotite/structure/io/trr/file.py +43 -0
- biotite/structure/io/util.py +38 -0
- biotite/structure/io/xtc/__init__.py +13 -0
- biotite/structure/io/xtc/file.py +43 -0
- biotite/structure/mechanics.py +72 -0
- biotite/structure/molecules.py +337 -0
- biotite/structure/pseudoknots.py +622 -0
- biotite/structure/rdf.py +245 -0
- biotite/structure/repair.py +302 -0
- biotite/structure/residues.py +716 -0
- biotite/structure/rings.py +451 -0
- biotite/structure/sasa.cpython-313-x86_64-linux-gnu.so +0 -0
- biotite/structure/sasa.pyx +322 -0
- biotite/structure/segments.py +328 -0
- biotite/structure/sequence.py +110 -0
- biotite/structure/spacegroups.json +1567 -0
- biotite/structure/spacegroups.license +26 -0
- biotite/structure/sse.py +306 -0
- biotite/structure/superimpose.py +511 -0
- biotite/structure/tm.py +581 -0
- biotite/structure/transform.py +736 -0
- biotite/structure/util.py +160 -0
- biotite/version.py +34 -0
- biotite/visualize.py +375 -0
- biotite-1.5.0.dist-info/METADATA +162 -0
- biotite-1.5.0.dist-info/RECORD +354 -0
- biotite-1.5.0.dist-info/WHEEL +6 -0
- biotite-1.5.0.dist-info/licenses/LICENSE.rst +30 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
# This source code is part of the Biotite package and is distributed
|
|
2
|
+
# under the 3-Clause BSD License. Please see 'LICENSE.rst' for further
|
|
3
|
+
# information.
|
|
4
|
+
|
|
5
|
+
__name__ = "biotite.sequence.graphics"
|
|
6
|
+
__author__ = "Patrick Kunzmann"
|
|
7
|
+
__all__ = ["plot_plasmid_map"]
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
import warnings
|
|
11
|
+
import numpy as np
|
|
12
|
+
from biotite.sequence.annotation import Feature, Location
|
|
13
|
+
from biotite.visualize import colors
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def plot_plasmid_map(
|
|
17
|
+
axes,
|
|
18
|
+
annotation,
|
|
19
|
+
plasmid_size,
|
|
20
|
+
tick_length=0.02,
|
|
21
|
+
tick_step=200,
|
|
22
|
+
ring_width=0.01,
|
|
23
|
+
feature_width=0.06,
|
|
24
|
+
spacing=0.01,
|
|
25
|
+
arrow_head_length=0.04,
|
|
26
|
+
label=None,
|
|
27
|
+
face_properties=None,
|
|
28
|
+
label_properties=None,
|
|
29
|
+
omit_oversized_labels=True,
|
|
30
|
+
feature_formatter=None,
|
|
31
|
+
):
|
|
32
|
+
"""
|
|
33
|
+
Plot a plasmid map using the sequence features in the given
|
|
34
|
+
:class:`Annotation`.
|
|
35
|
+
|
|
36
|
+
Each feature location is depicted either by a curved arrow,
|
|
37
|
+
indicating on which strand the feature resides, or an arc.
|
|
38
|
+
The ticks indicate the sequence position.
|
|
39
|
+
|
|
40
|
+
**EXPERIMENTAL**: The API and the resulting plots will probably
|
|
41
|
+
change in future versions.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
axes : PolarAxes
|
|
46
|
+
A *Matplotlib* axes, that is used as plotting area.
|
|
47
|
+
A polar projection is required.
|
|
48
|
+
annotation : Annotation
|
|
49
|
+
The annotation to be visualized.
|
|
50
|
+
plasmid_size : int
|
|
51
|
+
The size of the plasmid, i.e. the length of its sequence.
|
|
52
|
+
tick_length : float, optional
|
|
53
|
+
The length of the axis ticks as percentage of the plot radius.
|
|
54
|
+
tick_step : int, optional
|
|
55
|
+
The interval between the displayed sequence positions.
|
|
56
|
+
For example ``tick_step=200`` means that ticks will be displayed
|
|
57
|
+
at ``1``, ``200``, ``400``, ``600``, etc.
|
|
58
|
+
ring_width : float, optional
|
|
59
|
+
The width of the outer ring as percentage of the plot radius.
|
|
60
|
+
feature_width : float, optional
|
|
61
|
+
The width of each feature arrow/arc as percentage of the
|
|
62
|
+
plot radius.
|
|
63
|
+
spacing : float, optional
|
|
64
|
+
The spacing between the rows of feature arrows/arcs as
|
|
65
|
+
percentage of the plot radius.
|
|
66
|
+
arrow_head_length : float, optional
|
|
67
|
+
The length of the arrow heads as percentage of the radius
|
|
68
|
+
at which the respective arrow is plotted.
|
|
69
|
+
label : str, optional
|
|
70
|
+
The central label of the plot. Ususally the plasmid name.
|
|
71
|
+
face_properties : dict, optional
|
|
72
|
+
A dictionary of properties given to the patches that make up the
|
|
73
|
+
feature arrows/arcs.
|
|
74
|
+
Internally the arrow tail is a *Matplotlib* :class:`Rectangle`,
|
|
75
|
+
and the arrow head is a :class:`Polygon`.
|
|
76
|
+
For example this parameter could be used to add edge lines to
|
|
77
|
+
the arrows/arcs.
|
|
78
|
+
label_properties : dict, optional
|
|
79
|
+
A dictionary of properties given to the feature labels.
|
|
80
|
+
Internally each feature label is one or multiple *Matplotlib*
|
|
81
|
+
:class:`Text` instances.
|
|
82
|
+
For example this parameter could be used to set the font weight
|
|
83
|
+
or size.
|
|
84
|
+
omit_oversized_labels : bool, optional
|
|
85
|
+
If true, a feature label will not be displayed, if the label is
|
|
86
|
+
larger than its containing arc/arrow.
|
|
87
|
+
This ensures, that labels from different features will not
|
|
88
|
+
overlap each other.
|
|
89
|
+
If false, the feature labels will always be displayed.
|
|
90
|
+
feature_formatter : function, optional
|
|
91
|
+
A function that determines *how* each feature is displayed.
|
|
92
|
+
The given function must take a :class:`Feature` and must return
|
|
93
|
+
the following tuple:
|
|
94
|
+
|
|
95
|
+
- *directional* : bool
|
|
96
|
+
|
|
97
|
+
True, if the direction of the feature should be indicated by
|
|
98
|
+
an arrow.
|
|
99
|
+
Otherwise, the feature is plotted is arc.
|
|
100
|
+
|
|
101
|
+
- *face_color* : tuple or str, optional
|
|
102
|
+
|
|
103
|
+
A *Matplotlib* compatible color for the feature arrow/arc.
|
|
104
|
+
|
|
105
|
+
- *label_color* : tuple or str, optional
|
|
106
|
+
|
|
107
|
+
A *Matplotlib* compatible color for the feature label.
|
|
108
|
+
|
|
109
|
+
- *label* : str or None
|
|
110
|
+
|
|
111
|
+
The label to be displayed for this feature.
|
|
112
|
+
None, if no label should be displayed.
|
|
113
|
+
"""
|
|
114
|
+
from matplotlib.projections.polar import PolarAxes
|
|
115
|
+
|
|
116
|
+
if not isinstance(axes, PolarAxes):
|
|
117
|
+
raise TypeError("The given axes must be a 'PolarAxes'")
|
|
118
|
+
|
|
119
|
+
### Setup parameters ###
|
|
120
|
+
if plasmid_size is None:
|
|
121
|
+
# 'stop' of 'get_location_range()' is exclusive -> -1
|
|
122
|
+
plasmid_size = annotation.get_location_range()[1] - 1
|
|
123
|
+
if face_properties is None:
|
|
124
|
+
face_properties = {}
|
|
125
|
+
if label_properties is None:
|
|
126
|
+
label_properties = {}
|
|
127
|
+
if feature_formatter is None:
|
|
128
|
+
feature_formatter = _default_feature_formatter
|
|
129
|
+
|
|
130
|
+
### Setup matplotlib ###
|
|
131
|
+
# The x-coordinate is given as angle (rad)
|
|
132
|
+
# Full circle -> 2*pi
|
|
133
|
+
axes.set_xlim(0, 2 * np.pi)
|
|
134
|
+
axes.set_ylim(0, 1)
|
|
135
|
+
axes.yaxis.set_visible(False)
|
|
136
|
+
axes.xaxis.set_tick_params(bottom=False, labelbottom=True)
|
|
137
|
+
axes.set_theta_zero_location("N")
|
|
138
|
+
axes.set_theta_direction("clockwise")
|
|
139
|
+
axes.spines["polar"].set_visible(False)
|
|
140
|
+
axes.grid(False)
|
|
141
|
+
# Setup ticks
|
|
142
|
+
ticks = [1]
|
|
143
|
+
tick_labels = [str(1)]
|
|
144
|
+
for tick in range(tick_step, plasmid_size, tick_step):
|
|
145
|
+
ticks.append(tick)
|
|
146
|
+
tick_labels.append(str(tick))
|
|
147
|
+
# Sequence location is replaced by angle
|
|
148
|
+
axes.xaxis.set_ticks([_loc_to_rad(tick, plasmid_size) for tick in ticks])
|
|
149
|
+
axes.xaxis.set_ticklabels(tick_labels)
|
|
150
|
+
### Draw plasmid ring with ticks and central label ###
|
|
151
|
+
|
|
152
|
+
# Plasmid ring
|
|
153
|
+
# Use 'barh()' instead of a Rectangle patch to ensure that the axes
|
|
154
|
+
# is properly initialized
|
|
155
|
+
# Otherwise the feature rectangles are not curved, but straight
|
|
156
|
+
axes.barh(
|
|
157
|
+
1 - ring_width - tick_length, 2 * np.pi, ring_width, align="edge", color="black"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Ticks (ticks itself, not the tick labels)
|
|
161
|
+
for tick in ticks:
|
|
162
|
+
angle = _loc_to_rad(tick, plasmid_size)
|
|
163
|
+
axes.plot(
|
|
164
|
+
(angle, angle),
|
|
165
|
+
(1 - tick_length, 1),
|
|
166
|
+
color="black",
|
|
167
|
+
linewidth=1,
|
|
168
|
+
linestyle="-",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Central plasmid label
|
|
172
|
+
if label is not None:
|
|
173
|
+
axes.text(
|
|
174
|
+
0,
|
|
175
|
+
0,
|
|
176
|
+
label,
|
|
177
|
+
ha="center",
|
|
178
|
+
va="center",
|
|
179
|
+
color="black",
|
|
180
|
+
size=32,
|
|
181
|
+
fontweight="bold",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
### Draw plasmid interior ###
|
|
185
|
+
inner_radius = 1 - ring_width - tick_length
|
|
186
|
+
features = sorted(
|
|
187
|
+
[
|
|
188
|
+
_merge_over_periodic_boundary(feature, plasmid_size)
|
|
189
|
+
for feature in annotation
|
|
190
|
+
],
|
|
191
|
+
# Features are sorted by the length of their location range
|
|
192
|
+
# The shortest come first
|
|
193
|
+
key=lambda feature: np.diff(feature.get_location_range())[0],
|
|
194
|
+
reverse=True,
|
|
195
|
+
)
|
|
196
|
+
axes.add_artist(
|
|
197
|
+
PlasmidMap(
|
|
198
|
+
axes,
|
|
199
|
+
0,
|
|
200
|
+
features,
|
|
201
|
+
plasmid_size,
|
|
202
|
+
inner_radius,
|
|
203
|
+
feature_width,
|
|
204
|
+
spacing,
|
|
205
|
+
arrow_head_length,
|
|
206
|
+
label,
|
|
207
|
+
face_properties,
|
|
208
|
+
label_properties,
|
|
209
|
+
omit_oversized_labels,
|
|
210
|
+
feature_formatter,
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
# Only create these classes when matplotlib is installed
|
|
217
|
+
from matplotlib.artist import Artist
|
|
218
|
+
from matplotlib.patches import Polygon, Rectangle
|
|
219
|
+
from matplotlib.transforms import Bbox
|
|
220
|
+
|
|
221
|
+
class PlasmidMap(Artist):
|
|
222
|
+
def __init__(
|
|
223
|
+
self,
|
|
224
|
+
axes,
|
|
225
|
+
zorder,
|
|
226
|
+
features,
|
|
227
|
+
plasmid_size,
|
|
228
|
+
radius,
|
|
229
|
+
feature_width,
|
|
230
|
+
spacing,
|
|
231
|
+
arrow_head_length,
|
|
232
|
+
label,
|
|
233
|
+
face_properties,
|
|
234
|
+
label_properties,
|
|
235
|
+
omit_oversized_labels,
|
|
236
|
+
feature_formatter,
|
|
237
|
+
):
|
|
238
|
+
super().__init__()
|
|
239
|
+
self._axes = axes
|
|
240
|
+
self.zorder = zorder
|
|
241
|
+
self._features = features
|
|
242
|
+
self._plasmid_size = plasmid_size
|
|
243
|
+
self._radius = radius
|
|
244
|
+
self._feature_width = feature_width
|
|
245
|
+
self._spacing = spacing
|
|
246
|
+
|
|
247
|
+
self._all_indicators = []
|
|
248
|
+
for feature in features:
|
|
249
|
+
indicators_for_feature = []
|
|
250
|
+
for loc in feature.locs:
|
|
251
|
+
# Set proper positions in 'draw()' method
|
|
252
|
+
bbox = Bbox.from_extents(0, 0, 0, 0)
|
|
253
|
+
# Draw features as curved arrows (feature indicator)
|
|
254
|
+
indicator = axes.add_artist(
|
|
255
|
+
FeatureIndicator(
|
|
256
|
+
axes,
|
|
257
|
+
self.zorder + 1,
|
|
258
|
+
feature,
|
|
259
|
+
loc,
|
|
260
|
+
bbox,
|
|
261
|
+
arrow_head_length,
|
|
262
|
+
face_properties,
|
|
263
|
+
label_properties,
|
|
264
|
+
omit_oversized_labels,
|
|
265
|
+
feature_formatter,
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
indicators_for_feature.append(indicator)
|
|
269
|
+
self._all_indicators.append(indicators_for_feature)
|
|
270
|
+
|
|
271
|
+
def draw(self, renderer, *args, **kwargs):
|
|
272
|
+
# Find the maximum amount of feature rows
|
|
273
|
+
# (used for overlapping features)
|
|
274
|
+
row_count = int(self._radius // (self._feature_width + self._spacing))
|
|
275
|
+
# Tracks the location ranges of feature that were added to
|
|
276
|
+
# a row in order to check if that row is occupied
|
|
277
|
+
ranges_in_row = [[] for i in range(row_count)]
|
|
278
|
+
# Stores the bottom coordinate (radius) for each row
|
|
279
|
+
row_bottoms = [
|
|
280
|
+
self._radius - (row + 1) * (self._feature_width + self._spacing)
|
|
281
|
+
for row in range(row_count)
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
# Arrange the feature indicators in an way,
|
|
285
|
+
# that there is no overlap between them
|
|
286
|
+
for feature, indicators_for_feature in zip(
|
|
287
|
+
self._features, self._all_indicators
|
|
288
|
+
):
|
|
289
|
+
row_bottom = None
|
|
290
|
+
first, last = feature.get_location_range()
|
|
291
|
+
|
|
292
|
+
for row_i, curr_range in enumerate(ranges_in_row):
|
|
293
|
+
is_occupied = False
|
|
294
|
+
if curr_range is not None:
|
|
295
|
+
# Check if row is occupied
|
|
296
|
+
for curr_first, curr_last in curr_range:
|
|
297
|
+
# If the location extends over periodic
|
|
298
|
+
# boundary the 'first' location is negative
|
|
299
|
+
if first > 0:
|
|
300
|
+
# 'Normal feature'
|
|
301
|
+
if first <= curr_last and last >= curr_first:
|
|
302
|
+
is_occupied = True
|
|
303
|
+
else: # first < 1
|
|
304
|
+
# Location is over periodic boundary
|
|
305
|
+
if (
|
|
306
|
+
first + self._plasmid_size <= curr_last
|
|
307
|
+
or last >= curr_first
|
|
308
|
+
):
|
|
309
|
+
is_occupied = True
|
|
310
|
+
if not is_occupied:
|
|
311
|
+
# Row is not occupied by another feature
|
|
312
|
+
# in the location range of the new feature
|
|
313
|
+
# -> Use this row
|
|
314
|
+
if first > 0:
|
|
315
|
+
# 'Normal feature'
|
|
316
|
+
ranges_in_row[row_i].append((first, last))
|
|
317
|
+
else:
|
|
318
|
+
# Location is over periodic boundary
|
|
319
|
+
# Split into 'end' and 'start' part
|
|
320
|
+
ranges_in_row[row_i].append(
|
|
321
|
+
(first + self._plasmid_size, self._plasmid_size)
|
|
322
|
+
)
|
|
323
|
+
ranges_in_row[row_i].append((1, last))
|
|
324
|
+
row_bottom = row_bottoms[row_i]
|
|
325
|
+
break
|
|
326
|
+
if row_bottom is None:
|
|
327
|
+
# No free row -> ignore feature and raise warning
|
|
328
|
+
warnings.warn(
|
|
329
|
+
"Too many feature overlaps, try to increase the "
|
|
330
|
+
"radius or decrease the feature width or spacing"
|
|
331
|
+
)
|
|
332
|
+
else:
|
|
333
|
+
for loc, indicator in zip(feature.locs, indicators_for_feature):
|
|
334
|
+
# Calculate arrow shape parameters
|
|
335
|
+
row_top = row_bottom + self._feature_width
|
|
336
|
+
start_ang = _loc_to_rad(loc.first, self._plasmid_size)
|
|
337
|
+
stop_ang = _loc_to_rad(loc.last, self._plasmid_size)
|
|
338
|
+
bbox = Bbox.from_extents(
|
|
339
|
+
start_ang, row_bottom, stop_ang, row_top
|
|
340
|
+
)
|
|
341
|
+
indicator.set_bbox(bbox)
|
|
342
|
+
|
|
343
|
+
class FeatureIndicator(Artist):
|
|
344
|
+
def __init__(
|
|
345
|
+
self,
|
|
346
|
+
axes,
|
|
347
|
+
zorder,
|
|
348
|
+
feature,
|
|
349
|
+
loc,
|
|
350
|
+
bbox,
|
|
351
|
+
head_length,
|
|
352
|
+
arrow_properties,
|
|
353
|
+
label_properties,
|
|
354
|
+
omit_oversized_labels,
|
|
355
|
+
feature_formatter,
|
|
356
|
+
):
|
|
357
|
+
super().__init__()
|
|
358
|
+
self._axes = axes
|
|
359
|
+
self.zorder = zorder
|
|
360
|
+
self._direction = loc.strand
|
|
361
|
+
self._bbox = bbox
|
|
362
|
+
self._head_length = head_length
|
|
363
|
+
self._omit_oversized_labels = omit_oversized_labels
|
|
364
|
+
|
|
365
|
+
# Determine how to draw the feature
|
|
366
|
+
directional, face_color, label_color, label = feature_formatter(feature)
|
|
367
|
+
|
|
368
|
+
# Draw arrow as composition of a rectangle and a triangle,
|
|
369
|
+
# as FancyArrow does not properly work for polar plots
|
|
370
|
+
|
|
371
|
+
self._arrow_tail = axes.add_patch(
|
|
372
|
+
Rectangle(
|
|
373
|
+
# Set positions in 'draw()' method
|
|
374
|
+
(0, 0),
|
|
375
|
+
0,
|
|
376
|
+
0,
|
|
377
|
+
# Line width is set to 1 to avoid strange artifact in
|
|
378
|
+
# the transition from rectangle (tail) to polygon (head)
|
|
379
|
+
color=face_color,
|
|
380
|
+
linewidth=1,
|
|
381
|
+
zorder=self.zorder + 1,
|
|
382
|
+
**arrow_properties,
|
|
383
|
+
)
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
if directional:
|
|
387
|
+
# Only draw any arrow head when feature has a direction,
|
|
388
|
+
# otherwise simply draw the tail (rectangle)
|
|
389
|
+
self._arrow_head = axes.add_patch(
|
|
390
|
+
Polygon(
|
|
391
|
+
# Set positions in 'draw()' method
|
|
392
|
+
[(0, 0), (0, 0), (0, 0)],
|
|
393
|
+
color=face_color,
|
|
394
|
+
linewidth=1,
|
|
395
|
+
zorder=self.zorder + 1,
|
|
396
|
+
**arrow_properties,
|
|
397
|
+
)
|
|
398
|
+
)
|
|
399
|
+
else:
|
|
400
|
+
self._arrow_head = None
|
|
401
|
+
|
|
402
|
+
if label is not None:
|
|
403
|
+
label_properties["color"] = label_color
|
|
404
|
+
self._label = axes.add_artist(
|
|
405
|
+
CurvedText(
|
|
406
|
+
# Set positions in 'draw()' method
|
|
407
|
+
axes,
|
|
408
|
+
self.zorder + 1,
|
|
409
|
+
0,
|
|
410
|
+
0,
|
|
411
|
+
label,
|
|
412
|
+
label_properties,
|
|
413
|
+
)
|
|
414
|
+
)
|
|
415
|
+
else:
|
|
416
|
+
self._label = None
|
|
417
|
+
|
|
418
|
+
def set_bbox(self, bbox):
|
|
419
|
+
self._bbox = bbox
|
|
420
|
+
center_x = (bbox.x0 + bbox.x1) / 2
|
|
421
|
+
center_y = (bbox.y0 + bbox.y1) / 2
|
|
422
|
+
if self._label is not None:
|
|
423
|
+
self._label.set_position(center_x, center_y)
|
|
424
|
+
|
|
425
|
+
def draw(self, renderer, *args, **kwargs):
|
|
426
|
+
bbox = self._bbox
|
|
427
|
+
center_y = (bbox.y0 + bbox.y1) / 2
|
|
428
|
+
|
|
429
|
+
# Constant absolute width for all arrows
|
|
430
|
+
# irrespective of the radius in the polar plot
|
|
431
|
+
# Calculate actual angle from given absolute width
|
|
432
|
+
head_length = self._head_length / center_y
|
|
433
|
+
|
|
434
|
+
# Check if the head should be drawn
|
|
435
|
+
if self._arrow_head is None:
|
|
436
|
+
head_length = 0
|
|
437
|
+
# Check if the feature location is too small for
|
|
438
|
+
elif head_length > bbox.width:
|
|
439
|
+
# Limit size of arrow head to range of location
|
|
440
|
+
head_length = bbox.width
|
|
441
|
+
|
|
442
|
+
if self._direction == Location.Strand.FORWARD:
|
|
443
|
+
rect_pos = (bbox.x0, bbox.y0)
|
|
444
|
+
# (x0, y0), (x1, y1), (x2, y2)
|
|
445
|
+
triangle_coord = [
|
|
446
|
+
(bbox.x1 - head_length, bbox.y0), # base 1
|
|
447
|
+
(bbox.x1 - head_length, bbox.y1), # base 2
|
|
448
|
+
(bbox.x1, center_y), # tip
|
|
449
|
+
]
|
|
450
|
+
else:
|
|
451
|
+
rect_pos = (bbox.x0 + head_length, bbox.y0)
|
|
452
|
+
triangle_coord = [
|
|
453
|
+
(bbox.x0 + head_length, bbox.y0), # base 1
|
|
454
|
+
(bbox.x0 + head_length, bbox.y1), # base 2
|
|
455
|
+
(bbox.x0, center_y), # tip
|
|
456
|
+
]
|
|
457
|
+
|
|
458
|
+
# Update coordinates of sub-artists
|
|
459
|
+
self._arrow_tail.set_xy(rect_pos)
|
|
460
|
+
self._arrow_tail.set_width(bbox.width - head_length)
|
|
461
|
+
self._arrow_tail.set_height(bbox.height)
|
|
462
|
+
if self._arrow_head is not None:
|
|
463
|
+
self._arrow_head.set_xy(triangle_coord)
|
|
464
|
+
|
|
465
|
+
if self._label is not None:
|
|
466
|
+
# Do not draw the labels if it is larger than the
|
|
467
|
+
# indicator
|
|
468
|
+
if (
|
|
469
|
+
self._omit_oversized_labels
|
|
470
|
+
and self._label.get_total_angle(renderer) > bbox.width
|
|
471
|
+
):
|
|
472
|
+
self._label.set_visible(False)
|
|
473
|
+
else:
|
|
474
|
+
self._label.set_visible(True)
|
|
475
|
+
|
|
476
|
+
class CurvedText(Artist):
|
|
477
|
+
def __init__(self, axes, zorder, angle, radius, string, text_properties):
|
|
478
|
+
super().__init__()
|
|
479
|
+
self._axes = axes
|
|
480
|
+
self.zorder = zorder
|
|
481
|
+
self._angle = angle
|
|
482
|
+
self._radius = radius
|
|
483
|
+
|
|
484
|
+
self._texts = []
|
|
485
|
+
for word in _split_into_words(string):
|
|
486
|
+
text = axes.text(
|
|
487
|
+
# Set position in 'draw()' method
|
|
488
|
+
0,
|
|
489
|
+
0,
|
|
490
|
+
word,
|
|
491
|
+
ha="center",
|
|
492
|
+
va="center",
|
|
493
|
+
zorder=self.zorder + 1,
|
|
494
|
+
**text_properties,
|
|
495
|
+
)
|
|
496
|
+
self._texts.append(text)
|
|
497
|
+
|
|
498
|
+
def set_visible(self, visible):
|
|
499
|
+
super().set_visible(visible)
|
|
500
|
+
for text in self._texts:
|
|
501
|
+
text.set_visible(visible)
|
|
502
|
+
|
|
503
|
+
def set_position(self, angle, radius):
|
|
504
|
+
self._angle = angle
|
|
505
|
+
self._radius = radius
|
|
506
|
+
|
|
507
|
+
def get_total_angle(self, renderer):
|
|
508
|
+
return np.sum(self.get_word_angles(renderer))
|
|
509
|
+
|
|
510
|
+
def get_word_angles(self, renderer):
|
|
511
|
+
ax_px_radius = self._axes.get_window_extent(renderer).width / 2
|
|
512
|
+
ax_unit_radius = self._axes.get_ylim()[1]
|
|
513
|
+
circle_px_circumference = (
|
|
514
|
+
ax_px_radius * 2 * np.pi * (self._radius / ax_unit_radius)
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
angles = []
|
|
518
|
+
for text in self._texts:
|
|
519
|
+
orig_rot = text.get_rotation()
|
|
520
|
+
orig_visible = text.get_visible()
|
|
521
|
+
# Reset rotation and visibility
|
|
522
|
+
# for correct window extent calculation
|
|
523
|
+
text.set_rotation(0)
|
|
524
|
+
text.set_visible(True)
|
|
525
|
+
word_px_width = text.get_window_extent(renderer).width
|
|
526
|
+
# In some Matplotlib versions the window extent of
|
|
527
|
+
# whitespace characters is 'nan'
|
|
528
|
+
# In this case, assign a fixed width
|
|
529
|
+
if np.isnan(word_px_width):
|
|
530
|
+
word_px_width = 5.0
|
|
531
|
+
word_angle = 2 * np.pi * word_px_width / circle_px_circumference
|
|
532
|
+
angles.append(word_angle)
|
|
533
|
+
# Restore
|
|
534
|
+
text.set_rotation(orig_rot)
|
|
535
|
+
text.set_visible(orig_visible)
|
|
536
|
+
return angles
|
|
537
|
+
|
|
538
|
+
def draw(self, renderer, *args, **kwargs):
|
|
539
|
+
angles = self.get_word_angles(renderer)
|
|
540
|
+
total_angle = np.sum(angles)
|
|
541
|
+
|
|
542
|
+
rad_angle = 360 - np.rad2deg(self._angle)
|
|
543
|
+
# Avoid to draw the text upside down, when drawn on the
|
|
544
|
+
# bottom half of the map
|
|
545
|
+
if rad_angle > 90 and rad_angle < 270:
|
|
546
|
+
turn_around = True
|
|
547
|
+
else:
|
|
548
|
+
turn_around = False
|
|
549
|
+
|
|
550
|
+
# Now that the angle for each word is known,
|
|
551
|
+
# the appropriate position and rotation can be set
|
|
552
|
+
if turn_around:
|
|
553
|
+
# curr_angle is the left-aligned position of the
|
|
554
|
+
# upcoming word
|
|
555
|
+
curr_angle = self._angle + total_angle / 2
|
|
556
|
+
else:
|
|
557
|
+
curr_angle = self._angle - total_angle / 2
|
|
558
|
+
for text, angle in zip(self._texts, angles):
|
|
559
|
+
if turn_around:
|
|
560
|
+
# The text itself is centered
|
|
561
|
+
# -> The position itself must be corrected with
|
|
562
|
+
# half of the word angle
|
|
563
|
+
angle_corrected = curr_angle - angle / 2
|
|
564
|
+
text_rot = 360 - np.rad2deg(angle_corrected) + 180
|
|
565
|
+
curr_angle -= angle
|
|
566
|
+
else:
|
|
567
|
+
angle_corrected = curr_angle + angle / 2
|
|
568
|
+
text_rot = 360 - np.rad2deg(angle_corrected)
|
|
569
|
+
curr_angle += angle
|
|
570
|
+
text.set_position((angle_corrected, self._radius))
|
|
571
|
+
text.set_rotation(text_rot)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
except ImportError:
|
|
575
|
+
pass
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _loc_to_rad(loc, plasmid_size):
|
|
579
|
+
if loc > plasmid_size:
|
|
580
|
+
raise ValueError(
|
|
581
|
+
f"Location {loc} is larger then the plasmid size of {plasmid_size}"
|
|
582
|
+
)
|
|
583
|
+
# Location starts at 1 -> (loc-1)
|
|
584
|
+
return ((loc - 1) / plasmid_size) * 2 * np.pi
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _rad_to_loc(rad, plasmid_size):
|
|
588
|
+
# Location starts at 1 -> + 1
|
|
589
|
+
return rad / (2 * np.pi) * plasmid_size + 1
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def _merge_over_periodic_boundary(feature, plasmid_size):
|
|
593
|
+
if len(feature.locs) == 1:
|
|
594
|
+
# Only one location -> no merge possible
|
|
595
|
+
return feature
|
|
596
|
+
first_loc = None
|
|
597
|
+
last_loc = None
|
|
598
|
+
# Find total first location of the feature
|
|
599
|
+
for loc in feature.locs:
|
|
600
|
+
if first_loc is None or loc.first < first_loc.first:
|
|
601
|
+
first_loc = loc
|
|
602
|
+
# Find total last location of the feature
|
|
603
|
+
for loc in feature.locs:
|
|
604
|
+
if last_loc is None or loc.last > last_loc.last:
|
|
605
|
+
last_loc = loc
|
|
606
|
+
# If the first and last location meet at the periodic boundary of
|
|
607
|
+
# the plasmid -> merge them
|
|
608
|
+
if (
|
|
609
|
+
first_loc.first == 1
|
|
610
|
+
and last_loc.last == plasmid_size
|
|
611
|
+
and first_loc.strand == last_loc.strand
|
|
612
|
+
):
|
|
613
|
+
new_locs = set(feature.locs)
|
|
614
|
+
new_locs.remove(first_loc)
|
|
615
|
+
new_locs.remove(last_loc)
|
|
616
|
+
new_locs.add(
|
|
617
|
+
Location(
|
|
618
|
+
# the fist base is now at negative location
|
|
619
|
+
# by shifting by one plasmid 'period'
|
|
620
|
+
first=last_loc.first - plasmid_size,
|
|
621
|
+
last=first_loc.last,
|
|
622
|
+
strand=first_loc.strand,
|
|
623
|
+
defect=first_loc.defect | last_loc.defect,
|
|
624
|
+
)
|
|
625
|
+
)
|
|
626
|
+
return Feature(feature.key, new_locs, feature.qual)
|
|
627
|
+
else:
|
|
628
|
+
return feature
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
# ' ', '-' and '_' are word delimiters
|
|
632
|
+
separators = re.compile(r"\s|_|-")
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
def _split_into_words(string):
|
|
636
|
+
match_indices = sorted([match.start() for match in separators.finditer(string)])
|
|
637
|
+
current_index = 0
|
|
638
|
+
words = []
|
|
639
|
+
for i in match_indices:
|
|
640
|
+
# Add word up to delimiter
|
|
641
|
+
words.append(string[current_index:i])
|
|
642
|
+
# Add delimiter
|
|
643
|
+
words.append(string[i : i + 1])
|
|
644
|
+
current_index = i + 1
|
|
645
|
+
# If there is a word after the last delimiter, add it too
|
|
646
|
+
if current_index < len(string):
|
|
647
|
+
words.append(string[current_index:])
|
|
648
|
+
return words
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def _default_feature_formatter(f):
|
|
652
|
+
"""
|
|
653
|
+
Returns
|
|
654
|
+
-------
|
|
655
|
+
directional : bool
|
|
656
|
+
True, if the direction of the feature should be indicated by
|
|
657
|
+
an arrow.
|
|
658
|
+
face_color: tuple or str, optional
|
|
659
|
+
A matplotlib compatible color for the feature indicator.
|
|
660
|
+
label_color: tuple or str, optional
|
|
661
|
+
A matplotlib compatible color for the feature label.
|
|
662
|
+
label: str or None
|
|
663
|
+
The label to be displayed for this feature.
|
|
664
|
+
None, if no label should be displayed.
|
|
665
|
+
"""
|
|
666
|
+
# Source
|
|
667
|
+
if f.key == "source":
|
|
668
|
+
if f.qual.get("organism") is not None:
|
|
669
|
+
label = f"Source: {f.qual.get('organism')}"
|
|
670
|
+
else:
|
|
671
|
+
label = None
|
|
672
|
+
return False, "black", "white", label
|
|
673
|
+
|
|
674
|
+
# Origin of Replication
|
|
675
|
+
elif f.key == "rep_origin":
|
|
676
|
+
return False, "indigo", "white", f.qual.get("standard_name", "ori")
|
|
677
|
+
|
|
678
|
+
# Coding sequences
|
|
679
|
+
elif f.key in ["gene", "CDS", "rRNA"]:
|
|
680
|
+
label = f.qual.get("product")
|
|
681
|
+
if label is None:
|
|
682
|
+
label = f.qual.get("gene")
|
|
683
|
+
return True, colors["orange"], "black", label
|
|
684
|
+
|
|
685
|
+
elif f.key == "regulatory":
|
|
686
|
+
# Promoters
|
|
687
|
+
if f.qual.get("regulatory_class") in [
|
|
688
|
+
"promoter",
|
|
689
|
+
"TATA_box",
|
|
690
|
+
"minus_35_signal",
|
|
691
|
+
"minus_10_signal",
|
|
692
|
+
]:
|
|
693
|
+
return True, colors["dimgreen"], "black", f.qual.get("note")
|
|
694
|
+
|
|
695
|
+
# Terminators
|
|
696
|
+
elif f.qual.get("regulatory_class") in "terminator":
|
|
697
|
+
return False, "firebrick", "white", f.qual.get("note")
|
|
698
|
+
|
|
699
|
+
# RBS
|
|
700
|
+
elif f.qual.get("regulatory_class") == "ribosome_binding_site":
|
|
701
|
+
return False, colors["brightorange"], "white", None
|
|
702
|
+
|
|
703
|
+
# Primers
|
|
704
|
+
elif f.key == "primer_bind":
|
|
705
|
+
return True, "royalblue", "black", f.qual.get("note")
|
|
706
|
+
|
|
707
|
+
# Binding proteins
|
|
708
|
+
elif f.key == "protein_bind":
|
|
709
|
+
return False, colors["lightgreen"], "black", f.qual.get("note")
|
|
710
|
+
|
|
711
|
+
# Misc
|
|
712
|
+
return True, "dimgray", "white", f.qual.get("note")
|