biotite 0.41.2__cp312-cp312-macosx_11_0_arm64.whl → 1.0.1__cp312-cp312-macosx_11_0_arm64.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 +2 -3
- biotite/application/__init__.py +1 -1
- biotite/application/application.py +20 -10
- biotite/application/autodock/__init__.py +1 -1
- biotite/application/autodock/app.py +74 -79
- biotite/application/blast/__init__.py +1 -1
- biotite/application/blast/alignment.py +19 -10
- biotite/application/blast/webapp.py +92 -85
- biotite/application/clustalo/__init__.py +1 -1
- biotite/application/clustalo/app.py +46 -61
- biotite/application/dssp/__init__.py +1 -1
- biotite/application/dssp/app.py +8 -11
- biotite/application/localapp.py +62 -60
- biotite/application/mafft/__init__.py +1 -1
- biotite/application/mafft/app.py +16 -22
- biotite/application/msaapp.py +78 -89
- biotite/application/muscle/__init__.py +1 -1
- biotite/application/muscle/app3.py +50 -64
- biotite/application/muscle/app5.py +23 -31
- biotite/application/sra/__init__.py +1 -1
- biotite/application/sra/app.py +64 -68
- biotite/application/tantan/__init__.py +1 -1
- biotite/application/tantan/app.py +22 -45
- biotite/application/util.py +7 -9
- biotite/application/viennarna/rnaalifold.py +34 -28
- biotite/application/viennarna/rnafold.py +24 -39
- biotite/application/viennarna/rnaplot.py +36 -21
- biotite/application/viennarna/util.py +17 -12
- biotite/application/webapp.py +13 -14
- biotite/copyable.py +13 -13
- biotite/database/__init__.py +1 -1
- biotite/database/entrez/__init__.py +1 -1
- biotite/database/entrez/check.py +2 -3
- biotite/database/entrez/dbnames.py +7 -5
- biotite/database/entrez/download.py +55 -49
- biotite/database/entrez/key.py +1 -1
- biotite/database/entrez/query.py +62 -23
- biotite/database/error.py +2 -1
- biotite/database/pubchem/__init__.py +1 -1
- biotite/database/pubchem/download.py +43 -45
- biotite/database/pubchem/error.py +2 -2
- biotite/database/pubchem/query.py +34 -31
- biotite/database/pubchem/throttle.py +3 -4
- biotite/database/rcsb/__init__.py +1 -1
- biotite/database/rcsb/download.py +44 -52
- biotite/database/rcsb/query.py +85 -80
- biotite/database/uniprot/check.py +6 -3
- biotite/database/uniprot/download.py +6 -11
- biotite/database/uniprot/query.py +115 -31
- biotite/file.py +12 -31
- biotite/sequence/__init__.py +3 -3
- biotite/sequence/align/__init__.py +2 -2
- biotite/sequence/align/alignment.py +99 -90
- biotite/sequence/align/banded.cpython-312-darwin.so +0 -0
- biotite/sequence/align/buckets.py +12 -10
- biotite/sequence/align/cigar.py +43 -52
- biotite/sequence/align/kmeralphabet.cpython-312-darwin.so +0 -0
- biotite/sequence/align/kmeralphabet.pyx +55 -51
- biotite/sequence/align/kmersimilarity.cpython-312-darwin.so +0 -0
- biotite/sequence/align/kmertable.cpython-312-darwin.so +0 -0
- biotite/sequence/align/kmertable.pyx +3 -2
- biotite/sequence/align/localgapped.cpython-312-darwin.so +0 -0
- biotite/sequence/align/localungapped.cpython-312-darwin.so +0 -0
- biotite/sequence/align/matrix.py +81 -82
- biotite/sequence/align/multiple.cpython-312-darwin.so +0 -0
- biotite/sequence/align/multiple.pyx +1 -1
- biotite/sequence/align/pairwise.cpython-312-darwin.so +0 -0
- biotite/sequence/align/permutation.cpython-312-darwin.so +0 -0
- biotite/sequence/align/permutation.pyx +12 -4
- biotite/sequence/align/selector.cpython-312-darwin.so +0 -0
- biotite/sequence/align/selector.pyx +52 -54
- biotite/sequence/align/statistics.py +32 -33
- biotite/sequence/align/tracetable.cpython-312-darwin.so +0 -0
- biotite/sequence/alphabet.py +51 -65
- biotite/sequence/annotation.py +78 -77
- biotite/sequence/codec.cpython-312-darwin.so +0 -0
- biotite/sequence/codon.py +90 -79
- biotite/sequence/graphics/__init__.py +1 -1
- biotite/sequence/graphics/alignment.py +184 -103
- biotite/sequence/graphics/colorschemes.py +10 -12
- biotite/sequence/graphics/dendrogram.py +79 -34
- biotite/sequence/graphics/features.py +133 -99
- biotite/sequence/graphics/logo.py +22 -28
- biotite/sequence/graphics/plasmid.py +229 -178
- biotite/sequence/io/fasta/__init__.py +1 -1
- biotite/sequence/io/fasta/convert.py +44 -33
- biotite/sequence/io/fasta/file.py +42 -55
- biotite/sequence/io/fastq/__init__.py +1 -1
- biotite/sequence/io/fastq/convert.py +11 -14
- biotite/sequence/io/fastq/file.py +68 -112
- biotite/sequence/io/genbank/__init__.py +2 -2
- biotite/sequence/io/genbank/annotation.py +12 -20
- biotite/sequence/io/genbank/file.py +74 -76
- biotite/sequence/io/genbank/metadata.py +74 -62
- biotite/sequence/io/genbank/sequence.py +13 -14
- biotite/sequence/io/general.py +39 -30
- biotite/sequence/io/gff/__init__.py +2 -2
- biotite/sequence/io/gff/convert.py +10 -15
- biotite/sequence/io/gff/file.py +81 -65
- biotite/sequence/phylo/__init__.py +1 -1
- biotite/sequence/phylo/nj.cpython-312-darwin.so +0 -0
- biotite/sequence/phylo/tree.cpython-312-darwin.so +0 -0
- biotite/sequence/phylo/upgma.cpython-312-darwin.so +0 -0
- biotite/sequence/profile.py +57 -28
- biotite/sequence/search.py +17 -15
- biotite/sequence/seqtypes.py +200 -164
- biotite/sequence/sequence.py +15 -17
- biotite/structure/__init__.py +3 -3
- biotite/structure/atoms.py +246 -236
- biotite/structure/basepairs.py +260 -271
- biotite/structure/bonds.cpython-312-darwin.so +0 -0
- biotite/structure/bonds.pyx +29 -32
- biotite/structure/box.py +67 -71
- biotite/structure/celllist.cpython-312-darwin.so +0 -0
- biotite/structure/chains.py +55 -39
- biotite/structure/charges.cpython-312-darwin.so +0 -0
- biotite/structure/compare.py +32 -32
- biotite/structure/density.py +13 -18
- biotite/structure/dotbracket.py +20 -22
- biotite/structure/error.py +10 -2
- biotite/structure/filter.py +83 -78
- biotite/structure/geometry.py +130 -119
- biotite/structure/graphics/atoms.py +60 -43
- biotite/structure/graphics/rna.py +81 -68
- biotite/structure/hbond.py +112 -93
- biotite/structure/info/__init__.py +0 -2
- biotite/structure/info/atoms.py +10 -11
- biotite/structure/info/bonds.py +41 -43
- biotite/structure/info/ccd.py +4 -5
- biotite/structure/info/groups.py +1 -3
- biotite/structure/info/masses.py +5 -10
- biotite/structure/info/misc.py +1 -1
- biotite/structure/info/radii.py +20 -20
- biotite/structure/info/standardize.py +15 -26
- biotite/structure/integrity.py +18 -71
- biotite/structure/io/__init__.py +3 -4
- biotite/structure/io/dcd/__init__.py +1 -1
- biotite/structure/io/dcd/file.py +22 -20
- biotite/structure/io/general.py +47 -61
- biotite/structure/io/gro/__init__.py +1 -1
- biotite/structure/io/gro/file.py +73 -72
- biotite/structure/io/mol/__init__.py +1 -1
- biotite/structure/io/mol/convert.py +8 -11
- biotite/structure/io/mol/ctab.py +37 -36
- biotite/structure/io/mol/header.py +14 -10
- biotite/structure/io/mol/mol.py +9 -53
- biotite/structure/io/mol/sdf.py +47 -50
- biotite/structure/io/netcdf/__init__.py +1 -1
- biotite/structure/io/netcdf/file.py +24 -23
- biotite/structure/io/pdb/__init__.py +1 -1
- biotite/structure/io/pdb/convert.py +32 -20
- biotite/structure/io/pdb/file.py +151 -172
- biotite/structure/io/pdb/hybrid36.cpython-312-darwin.so +0 -0
- biotite/structure/io/pdbqt/__init__.py +1 -1
- biotite/structure/io/pdbqt/convert.py +17 -11
- biotite/structure/io/pdbqt/file.py +128 -80
- biotite/structure/io/pdbx/__init__.py +1 -2
- biotite/structure/io/pdbx/bcif.py +36 -44
- biotite/structure/io/pdbx/cif.py +140 -110
- biotite/structure/io/pdbx/component.py +10 -16
- biotite/structure/io/pdbx/convert.py +260 -258
- biotite/structure/io/pdbx/encoding.cpython-312-darwin.so +0 -0
- biotite/structure/io/trajfile.py +90 -107
- biotite/structure/io/trr/__init__.py +1 -1
- biotite/structure/io/trr/file.py +12 -15
- biotite/structure/io/xtc/__init__.py +1 -1
- biotite/structure/io/xtc/file.py +11 -14
- biotite/structure/mechanics.py +9 -11
- biotite/structure/molecules.py +3 -4
- biotite/structure/pseudoknots.py +53 -67
- biotite/structure/rdf.py +23 -21
- biotite/structure/repair.py +137 -86
- biotite/structure/residues.py +26 -16
- biotite/structure/sasa.cpython-312-darwin.so +0 -0
- biotite/structure/{resutil.py → segments.py} +24 -23
- biotite/structure/sequence.py +10 -11
- biotite/structure/sse.py +100 -119
- biotite/structure/superimpose.py +39 -77
- biotite/structure/transform.py +97 -71
- biotite/structure/util.py +11 -13
- biotite/version.py +2 -2
- biotite/visualize.py +69 -55
- {biotite-0.41.2.dist-info → biotite-1.0.1.dist-info}/METADATA +6 -5
- biotite-1.0.1.dist-info/RECORD +322 -0
- biotite/structure/io/ctab.py +0 -72
- biotite/structure/io/mmtf/__init__.py +0 -21
- biotite/structure/io/mmtf/assembly.py +0 -214
- biotite/structure/io/mmtf/convertarray.cpython-312-darwin.so +0 -0
- biotite/structure/io/mmtf/convertarray.pyx +0 -341
- biotite/structure/io/mmtf/convertfile.cpython-312-darwin.so +0 -0
- biotite/structure/io/mmtf/convertfile.pyx +0 -501
- biotite/structure/io/mmtf/decode.cpython-312-darwin.so +0 -0
- biotite/structure/io/mmtf/decode.pyx +0 -152
- biotite/structure/io/mmtf/encode.cpython-312-darwin.so +0 -0
- biotite/structure/io/mmtf/encode.pyx +0 -183
- biotite/structure/io/mmtf/file.py +0 -233
- biotite/structure/io/npz/__init__.py +0 -20
- biotite/structure/io/npz/file.py +0 -152
- biotite/structure/io/pdbx/legacy.py +0 -267
- biotite/structure/io/tng/__init__.py +0 -13
- biotite/structure/io/tng/file.py +0 -46
- biotite/temp.py +0 -86
- biotite-0.41.2.dist-info/RECORD +0 -340
- {biotite-0.41.2.dist-info → biotite-1.0.1.dist-info}/WHEEL +0 -0
- {biotite-0.41.2.dist-info → biotite-1.0.1.dist-info}/licenses/LICENSE.rst +0 -0
|
@@ -6,20 +6,29 @@ __name__ = "biotite.sequence.graphics"
|
|
|
6
6
|
__author__ = "Patrick Kunzmann"
|
|
7
7
|
__all__ = ["plot_plasmid_map"]
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import re
|
|
10
10
|
import warnings
|
|
11
|
-
import abc
|
|
12
11
|
import numpy as np
|
|
13
|
-
import
|
|
14
|
-
from
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
+
):
|
|
23
32
|
"""
|
|
24
33
|
Plot a plasmid map using the sequence features in the given
|
|
25
34
|
:class:`Annotation`.
|
|
@@ -84,26 +93,26 @@ def plot_plasmid_map(axes, annotation, plasmid_size, tick_length=0.02,
|
|
|
84
93
|
the following tuple:
|
|
85
94
|
|
|
86
95
|
- *directional* : bool
|
|
87
|
-
|
|
96
|
+
|
|
88
97
|
True, if the direction of the feature should be indicated by
|
|
89
98
|
an arrow.
|
|
90
99
|
Otherwise, the feature is plotted is arc.
|
|
91
|
-
|
|
100
|
+
|
|
92
101
|
- *face_color* : tuple or str, optional
|
|
93
|
-
|
|
102
|
+
|
|
94
103
|
A *Matplotlib* compatible color for the feature arrow/arc.
|
|
95
|
-
|
|
104
|
+
|
|
96
105
|
- *label_color* : tuple or str, optional
|
|
97
|
-
|
|
106
|
+
|
|
98
107
|
A *Matplotlib* compatible color for the feature label.
|
|
99
|
-
|
|
108
|
+
|
|
100
109
|
- *label* : str or None
|
|
101
|
-
|
|
110
|
+
|
|
102
111
|
The label to be displayed for this feature.
|
|
103
112
|
None, if no label should be displayed.
|
|
104
113
|
"""
|
|
105
114
|
from matplotlib.projections.polar import PolarAxes
|
|
106
|
-
|
|
115
|
+
|
|
107
116
|
if not isinstance(axes, PolarAxes):
|
|
108
117
|
raise TypeError("The given axes must be a 'PolarAxes'")
|
|
109
118
|
|
|
@@ -118,16 +127,13 @@ def plot_plasmid_map(axes, annotation, plasmid_size, tick_length=0.02,
|
|
|
118
127
|
if feature_formatter is None:
|
|
119
128
|
feature_formatter = _default_feature_formatter
|
|
120
129
|
|
|
121
|
-
|
|
122
130
|
### Setup matplotlib ###
|
|
123
131
|
# The x-coordinate is given as angle (rad)
|
|
124
132
|
# Full circle -> 2*pi
|
|
125
|
-
axes.set_xlim(0, 2*np.pi)
|
|
133
|
+
axes.set_xlim(0, 2 * np.pi)
|
|
126
134
|
axes.set_ylim(0, 1)
|
|
127
135
|
axes.yaxis.set_visible(False)
|
|
128
|
-
axes.xaxis.set_tick_params(
|
|
129
|
-
bottom=False, labelbottom=True
|
|
130
|
-
)
|
|
136
|
+
axes.xaxis.set_tick_params(bottom=False, labelbottom=True)
|
|
131
137
|
axes.set_theta_zero_location("N")
|
|
132
138
|
axes.set_theta_direction("clockwise")
|
|
133
139
|
axes.spines["polar"].set_visible(False)
|
|
@@ -142,32 +148,39 @@ def plot_plasmid_map(axes, annotation, plasmid_size, tick_length=0.02,
|
|
|
142
148
|
axes.xaxis.set_ticks([_loc_to_rad(tick, plasmid_size) for tick in ticks])
|
|
143
149
|
axes.xaxis.set_ticklabels(tick_labels)
|
|
144
150
|
### Draw plasmid ring with ticks and central label ###
|
|
145
|
-
|
|
151
|
+
|
|
146
152
|
# Plasmid ring
|
|
147
153
|
# Use 'barh()' instead of a Rectangle patch to ensure that the axes
|
|
148
154
|
# is properly initialized
|
|
149
155
|
# Otherwise the feature rectangles are not curved, but straight
|
|
150
156
|
axes.barh(
|
|
151
|
-
1-ring_width-tick_length, 2*np.pi, ring_width,
|
|
152
|
-
align="edge", color="black"
|
|
157
|
+
1 - ring_width - tick_length, 2 * np.pi, ring_width, align="edge", color="black"
|
|
153
158
|
)
|
|
154
|
-
|
|
159
|
+
|
|
155
160
|
# Ticks (ticks itself, not the tick labels)
|
|
156
161
|
for tick in ticks:
|
|
157
162
|
angle = _loc_to_rad(tick, plasmid_size)
|
|
158
163
|
axes.plot(
|
|
159
|
-
(angle, angle),
|
|
160
|
-
|
|
164
|
+
(angle, angle),
|
|
165
|
+
(1 - tick_length, 1),
|
|
166
|
+
color="black",
|
|
167
|
+
linewidth=1,
|
|
168
|
+
linestyle="-",
|
|
161
169
|
)
|
|
162
|
-
|
|
170
|
+
|
|
163
171
|
# Central plasmid label
|
|
164
172
|
if label is not None:
|
|
165
173
|
axes.text(
|
|
166
|
-
0,
|
|
167
|
-
|
|
174
|
+
0,
|
|
175
|
+
0,
|
|
176
|
+
label,
|
|
177
|
+
ha="center",
|
|
178
|
+
va="center",
|
|
179
|
+
color="black",
|
|
180
|
+
size=32,
|
|
181
|
+
fontweight="bold",
|
|
168
182
|
)
|
|
169
183
|
|
|
170
|
-
|
|
171
184
|
### Draw plasmid interior ###
|
|
172
185
|
inner_radius = 1 - ring_width - tick_length
|
|
173
186
|
features = sorted(
|
|
@@ -177,28 +190,51 @@ def plot_plasmid_map(axes, annotation, plasmid_size, tick_length=0.02,
|
|
|
177
190
|
],
|
|
178
191
|
# Features are sorted by the length of their location range
|
|
179
192
|
# The shortest come first
|
|
180
|
-
key
|
|
181
|
-
reverse
|
|
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
|
+
)
|
|
182
212
|
)
|
|
183
|
-
axes.add_artist(PlasmidMap(
|
|
184
|
-
axes, 0, features, plasmid_size, inner_radius, feature_width, spacing,
|
|
185
|
-
arrow_head_length, label, face_properties, label_properties,
|
|
186
|
-
omit_oversized_labels, feature_formatter
|
|
187
|
-
))
|
|
188
213
|
|
|
189
214
|
|
|
190
215
|
try:
|
|
191
216
|
# Only create these classes when matplotlib is installed
|
|
192
217
|
from matplotlib.artist import Artist
|
|
218
|
+
from matplotlib.patches import Polygon, Rectangle
|
|
193
219
|
from matplotlib.transforms import Bbox
|
|
194
|
-
from matplotlib.patches import Rectangle, Polygon
|
|
195
|
-
|
|
196
220
|
|
|
197
221
|
class PlasmidMap(Artist):
|
|
198
|
-
def __init__(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
+
):
|
|
202
238
|
super().__init__()
|
|
203
239
|
self._axes = axes
|
|
204
240
|
self.zorder = zorder
|
|
@@ -212,30 +248,36 @@ try:
|
|
|
212
248
|
for feature in features:
|
|
213
249
|
indicators_for_feature = []
|
|
214
250
|
for loc in feature.locs:
|
|
215
|
-
|
|
251
|
+
# Set proper positions in 'draw()' method
|
|
216
252
|
bbox = Bbox.from_extents(0, 0, 0, 0)
|
|
217
253
|
# Draw features as curved arrows (feature indicator)
|
|
218
|
-
indicator = axes.add_artist(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
+
)
|
|
223
268
|
indicators_for_feature.append(indicator)
|
|
224
269
|
self._all_indicators.append(indicators_for_feature)
|
|
225
270
|
|
|
226
|
-
|
|
227
271
|
def draw(self, renderer, *args, **kwargs):
|
|
228
272
|
# Find the maximum amount of feature rows
|
|
229
273
|
# (used for overlapping features)
|
|
230
|
-
row_count = int(
|
|
231
|
-
self._radius // (self._feature_width + self._spacing)
|
|
232
|
-
)
|
|
274
|
+
row_count = int(self._radius // (self._feature_width + self._spacing))
|
|
233
275
|
# Tracks the location ranges of feature that were added to
|
|
234
276
|
# a row in order to check if that row is occupied
|
|
235
277
|
ranges_in_row = [[] for i in range(row_count)]
|
|
236
278
|
# Stores the bottom coordinate (radius) for each row
|
|
237
279
|
row_bottoms = [
|
|
238
|
-
self._radius - (row+1) * (self._feature_width + self._spacing)
|
|
280
|
+
self._radius - (row + 1) * (self._feature_width + self._spacing)
|
|
239
281
|
for row in range(row_count)
|
|
240
282
|
]
|
|
241
283
|
|
|
@@ -258,11 +300,13 @@ try:
|
|
|
258
300
|
# 'Normal feature'
|
|
259
301
|
if first <= curr_last and last >= curr_first:
|
|
260
302
|
is_occupied = True
|
|
261
|
-
else:
|
|
303
|
+
else: # first < 1
|
|
262
304
|
# Location is over periodic boundary
|
|
263
|
-
if
|
|
264
|
-
|
|
265
|
-
|
|
305
|
+
if (
|
|
306
|
+
first + self._plasmid_size <= curr_last
|
|
307
|
+
or last >= curr_first
|
|
308
|
+
):
|
|
309
|
+
is_occupied = True
|
|
266
310
|
if not is_occupied:
|
|
267
311
|
# Row is not occupied by another feature
|
|
268
312
|
# in the location range of the new feature
|
|
@@ -273,12 +317,10 @@ try:
|
|
|
273
317
|
else:
|
|
274
318
|
# Location is over periodic boundary
|
|
275
319
|
# Split into 'end' and 'start' part
|
|
276
|
-
ranges_in_row[row_i].append(
|
|
277
|
-
first + self._plasmid_size, self._plasmid_size
|
|
278
|
-
)
|
|
279
|
-
ranges_in_row[row_i].append((
|
|
280
|
-
1, last
|
|
281
|
-
))
|
|
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))
|
|
282
324
|
row_bottom = row_bottoms[row_i]
|
|
283
325
|
break
|
|
284
326
|
if row_bottom is None:
|
|
@@ -288,24 +330,30 @@ try:
|
|
|
288
330
|
"radius or decrease the feature width or spacing"
|
|
289
331
|
)
|
|
290
332
|
else:
|
|
291
|
-
for loc, indicator in zip(
|
|
292
|
-
feature.locs, indicators_for_feature
|
|
293
|
-
):
|
|
333
|
+
for loc, indicator in zip(feature.locs, indicators_for_feature):
|
|
294
334
|
# Calculate arrow shape parameters
|
|
295
|
-
row_center = row_bottom + self._feature_width/2
|
|
296
335
|
row_top = row_bottom + self._feature_width
|
|
297
336
|
start_ang = _loc_to_rad(loc.first, self._plasmid_size)
|
|
298
|
-
stop_ang
|
|
337
|
+
stop_ang = _loc_to_rad(loc.last, self._plasmid_size)
|
|
299
338
|
bbox = Bbox.from_extents(
|
|
300
339
|
start_ang, row_bottom, stop_ang, row_top
|
|
301
340
|
)
|
|
302
341
|
indicator.set_bbox(bbox)
|
|
303
342
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
+
):
|
|
309
357
|
super().__init__()
|
|
310
358
|
self._axes = axes
|
|
311
359
|
self.zorder = zorder
|
|
@@ -313,44 +361,59 @@ try:
|
|
|
313
361
|
self._bbox = bbox
|
|
314
362
|
self._head_length = head_length
|
|
315
363
|
self._omit_oversized_labels = omit_oversized_labels
|
|
316
|
-
|
|
364
|
+
|
|
317
365
|
# Determine how to draw the feature
|
|
318
|
-
directional, face_color, label_color, label
|
|
319
|
-
|
|
320
|
-
|
|
366
|
+
directional, face_color, label_color, label = feature_formatter(feature)
|
|
367
|
+
|
|
321
368
|
# Draw arrow as composition of a rectangle and a triangle,
|
|
322
369
|
# as FancyArrow does not properly work for polar plots
|
|
323
370
|
|
|
324
|
-
self._arrow_tail = axes.add_patch(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
+
|
|
333
386
|
if directional:
|
|
334
387
|
# Only draw any arrow head when feature has a direction,
|
|
335
388
|
# otherwise simply draw the tail (rectangle)
|
|
336
|
-
self._arrow_head = axes.add_patch(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
+
)
|
|
342
399
|
else:
|
|
343
400
|
self._arrow_head = None
|
|
344
401
|
|
|
345
402
|
if label is not None:
|
|
346
403
|
label_properties["color"] = label_color
|
|
347
|
-
self._label = axes.add_artist(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
+
)
|
|
351
415
|
else:
|
|
352
416
|
self._label = None
|
|
353
|
-
|
|
354
417
|
|
|
355
418
|
def set_bbox(self, bbox):
|
|
356
419
|
self._bbox = bbox
|
|
@@ -359,17 +422,15 @@ try:
|
|
|
359
422
|
if self._label is not None:
|
|
360
423
|
self._label.set_position(center_x, center_y)
|
|
361
424
|
|
|
362
|
-
|
|
363
425
|
def draw(self, renderer, *args, **kwargs):
|
|
364
426
|
bbox = self._bbox
|
|
365
|
-
center_x = (bbox.x0 + bbox.x1) / 2
|
|
366
427
|
center_y = (bbox.y0 + bbox.y1) / 2
|
|
367
428
|
|
|
368
429
|
# Constant absolute width for all arrows
|
|
369
430
|
# irrespective of the radius in the polar plot
|
|
370
431
|
# Calculate actual angle from given absolute width
|
|
371
432
|
head_length = self._head_length / center_y
|
|
372
|
-
|
|
433
|
+
|
|
373
434
|
# Check if the head should be drawn
|
|
374
435
|
if self._arrow_head is None:
|
|
375
436
|
head_length = 0
|
|
@@ -382,39 +443,38 @@ try:
|
|
|
382
443
|
rect_pos = (bbox.x0, bbox.y0)
|
|
383
444
|
# (x0, y0), (x1, y1), (x2, y2)
|
|
384
445
|
triangle_coord = [
|
|
385
|
-
(bbox.x1 - head_length, bbox.y0),
|
|
386
|
-
(bbox.x1 - head_length, bbox.y1),
|
|
387
|
-
(bbox.x1,
|
|
446
|
+
(bbox.x1 - head_length, bbox.y0), # base 1
|
|
447
|
+
(bbox.x1 - head_length, bbox.y1), # base 2
|
|
448
|
+
(bbox.x1, center_y), # tip
|
|
388
449
|
]
|
|
389
450
|
else:
|
|
390
|
-
rect_pos = (bbox.x0+head_length, bbox.y0)
|
|
451
|
+
rect_pos = (bbox.x0 + head_length, bbox.y0)
|
|
391
452
|
triangle_coord = [
|
|
392
|
-
(bbox.x0 + head_length, bbox.y0),
|
|
393
|
-
(bbox.x0 + head_length, bbox.y1),
|
|
394
|
-
(bbox.x0,
|
|
453
|
+
(bbox.x0 + head_length, bbox.y0), # base 1
|
|
454
|
+
(bbox.x0 + head_length, bbox.y1), # base 2
|
|
455
|
+
(bbox.x0, center_y), # tip
|
|
395
456
|
]
|
|
396
|
-
|
|
457
|
+
|
|
397
458
|
# Update coordinates of sub-artists
|
|
398
459
|
self._arrow_tail.set_xy(rect_pos)
|
|
399
|
-
self._arrow_tail.set_width(bbox.width-head_length)
|
|
460
|
+
self._arrow_tail.set_width(bbox.width - head_length)
|
|
400
461
|
self._arrow_tail.set_height(bbox.height)
|
|
401
462
|
if self._arrow_head is not None:
|
|
402
463
|
self._arrow_head.set_xy(triangle_coord)
|
|
403
|
-
|
|
464
|
+
|
|
404
465
|
if self._label is not None:
|
|
405
466
|
# Do not draw the labels if it is larger than the
|
|
406
467
|
# indicator
|
|
407
|
-
if
|
|
408
|
-
|
|
409
|
-
|
|
468
|
+
if (
|
|
469
|
+
self._omit_oversized_labels
|
|
470
|
+
and self._label.get_total_angle(renderer) > bbox.width
|
|
471
|
+
):
|
|
472
|
+
self._label.set_visible(False)
|
|
410
473
|
else:
|
|
411
474
|
self._label.set_visible(True)
|
|
412
475
|
|
|
413
|
-
|
|
414
|
-
|
|
415
476
|
class CurvedText(Artist):
|
|
416
|
-
def __init__(self, axes, zorder, angle, radius, string,
|
|
417
|
-
text_properties):
|
|
477
|
+
def __init__(self, axes, zorder, angle, radius, string, text_properties):
|
|
418
478
|
super().__init__()
|
|
419
479
|
self._axes = axes
|
|
420
480
|
self.zorder = zorder
|
|
@@ -425,44 +485,35 @@ try:
|
|
|
425
485
|
for word in _split_into_words(string):
|
|
426
486
|
text = axes.text(
|
|
427
487
|
# Set position in 'draw()' method
|
|
428
|
-
0,
|
|
488
|
+
0,
|
|
489
|
+
0,
|
|
429
490
|
word,
|
|
430
|
-
ha="center",
|
|
491
|
+
ha="center",
|
|
492
|
+
va="center",
|
|
431
493
|
zorder=self.zorder + 1,
|
|
432
494
|
**text_properties,
|
|
433
495
|
)
|
|
434
496
|
self._texts.append(text)
|
|
435
|
-
|
|
436
497
|
|
|
437
498
|
def set_visible(self, visible):
|
|
438
499
|
super().set_visible(visible)
|
|
439
500
|
for text in self._texts:
|
|
440
501
|
text.set_visible(visible)
|
|
441
502
|
|
|
442
|
-
|
|
443
503
|
def set_position(self, angle, radius):
|
|
444
504
|
self._angle = angle
|
|
445
505
|
self._radius = radius
|
|
446
|
-
|
|
447
506
|
|
|
448
507
|
def get_total_angle(self, renderer):
|
|
449
508
|
return np.sum(self.get_word_angles(renderer))
|
|
450
|
-
|
|
451
509
|
|
|
452
510
|
def get_word_angles(self, renderer):
|
|
453
511
|
ax_px_radius = self._axes.get_window_extent(renderer).width / 2
|
|
454
512
|
ax_unit_radius = self._axes.get_ylim()[1]
|
|
455
|
-
circle_px_circumference =
|
|
456
|
-
|
|
513
|
+
circle_px_circumference = (
|
|
514
|
+
ax_px_radius * 2 * np.pi * (self._radius / ax_unit_radius)
|
|
515
|
+
)
|
|
457
516
|
|
|
458
|
-
rad_angle = 360 - np.rad2deg(self._angle)
|
|
459
|
-
# Avoid to draw the text upside down, when drawn on the
|
|
460
|
-
# bottom half of the map
|
|
461
|
-
if rad_angle > 90 and rad_angle < 270:
|
|
462
|
-
turn_around = True
|
|
463
|
-
else:
|
|
464
|
-
turn_around = False
|
|
465
|
-
|
|
466
517
|
angles = []
|
|
467
518
|
for text in self._texts:
|
|
468
519
|
orig_rot = text.get_rotation()
|
|
@@ -477,14 +528,12 @@ try:
|
|
|
477
528
|
# In this case, assign a fixed width
|
|
478
529
|
if np.isnan(word_px_width):
|
|
479
530
|
word_px_width = 5.0
|
|
480
|
-
word_angle
|
|
481
|
-
= 2*np.pi * word_px_width / circle_px_circumference
|
|
531
|
+
word_angle = 2 * np.pi * word_px_width / circle_px_circumference
|
|
482
532
|
angles.append(word_angle)
|
|
483
533
|
# Restore
|
|
484
534
|
text.set_rotation(orig_rot)
|
|
485
535
|
text.set_visible(orig_visible)
|
|
486
536
|
return angles
|
|
487
|
-
|
|
488
537
|
|
|
489
538
|
def draw(self, renderer, *args, **kwargs):
|
|
490
539
|
angles = self.get_word_angles(renderer)
|
|
@@ -497,7 +546,7 @@ try:
|
|
|
497
546
|
turn_around = True
|
|
498
547
|
else:
|
|
499
548
|
turn_around = False
|
|
500
|
-
|
|
549
|
+
|
|
501
550
|
# Now that the angle for each word is known,
|
|
502
551
|
# the appropriate position and rotation can be set
|
|
503
552
|
if turn_around:
|
|
@@ -526,20 +575,18 @@ except ImportError:
|
|
|
526
575
|
pass
|
|
527
576
|
|
|
528
577
|
|
|
529
|
-
|
|
530
|
-
|
|
531
578
|
def _loc_to_rad(loc, plasmid_size):
|
|
532
579
|
if loc > plasmid_size:
|
|
533
580
|
raise ValueError(
|
|
534
581
|
f"Location {loc} is larger then the plasmid size of {plasmid_size}"
|
|
535
582
|
)
|
|
536
583
|
# Location starts at 1 -> (loc-1)
|
|
537
|
-
return ((loc-1) / plasmid_size) * 2*np.pi
|
|
584
|
+
return ((loc - 1) / plasmid_size) * 2 * np.pi
|
|
538
585
|
|
|
539
586
|
|
|
540
587
|
def _rad_to_loc(rad, plasmid_size):
|
|
541
588
|
# Location starts at 1 -> + 1
|
|
542
|
-
return rad / (2*np.pi) * plasmid_size + 1
|
|
589
|
+
return rad / (2 * np.pi) * plasmid_size + 1
|
|
543
590
|
|
|
544
591
|
|
|
545
592
|
def _merge_over_periodic_boundary(feature, plasmid_size):
|
|
@@ -547,7 +594,7 @@ def _merge_over_periodic_boundary(feature, plasmid_size):
|
|
|
547
594
|
# Only one location -> no merge possible
|
|
548
595
|
return feature
|
|
549
596
|
first_loc = None
|
|
550
|
-
last_loc
|
|
597
|
+
last_loc = None
|
|
551
598
|
# Find total first location of the feature
|
|
552
599
|
for loc in feature.locs:
|
|
553
600
|
if first_loc is None or loc.first < first_loc.first:
|
|
@@ -558,38 +605,43 @@ def _merge_over_periodic_boundary(feature, plasmid_size):
|
|
|
558
605
|
last_loc = loc
|
|
559
606
|
# If the first and last location meet at the periodic boundary of
|
|
560
607
|
# the plasmid -> merge them
|
|
561
|
-
if
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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(
|
|
567
618
|
# the fist base is now at negative location
|
|
568
619
|
# by shifting by one plasmid 'period'
|
|
569
|
-
first
|
|
570
|
-
last
|
|
571
|
-
strand
|
|
572
|
-
defect
|
|
573
|
-
)
|
|
574
|
-
|
|
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)
|
|
575
627
|
else:
|
|
576
628
|
return feature
|
|
577
629
|
|
|
578
630
|
|
|
579
631
|
# ' ', '-' and '_' are word delimiters
|
|
580
632
|
separators = re.compile(r"\s|_|-")
|
|
633
|
+
|
|
634
|
+
|
|
581
635
|
def _split_into_words(string):
|
|
582
|
-
match_indices = sorted(
|
|
583
|
-
[match.start() for match in separators.finditer(string)]
|
|
584
|
-
)
|
|
636
|
+
match_indices = sorted([match.start() for match in separators.finditer(string)])
|
|
585
637
|
current_index = 0
|
|
586
638
|
words = []
|
|
587
639
|
for i in match_indices:
|
|
588
640
|
# Add word up to delimiter
|
|
589
|
-
words.append(string[current_index
|
|
641
|
+
words.append(string[current_index:i])
|
|
590
642
|
# Add delimiter
|
|
591
|
-
words.append(string[i : i+1])
|
|
592
|
-
current_index = i+1
|
|
643
|
+
words.append(string[i : i + 1])
|
|
644
|
+
current_index = i + 1
|
|
593
645
|
# If there is a word after the last delimiter, add it too
|
|
594
646
|
if current_index < len(string):
|
|
595
647
|
words.append(string[current_index:])
|
|
@@ -618,44 +670,43 @@ def _default_feature_formatter(f):
|
|
|
618
670
|
else:
|
|
619
671
|
label = None
|
|
620
672
|
return False, "black", "white", label
|
|
621
|
-
|
|
673
|
+
|
|
622
674
|
# Origin of Replication
|
|
623
675
|
elif f.key == "rep_origin":
|
|
624
|
-
return False, "indigo", "white",
|
|
625
|
-
|
|
626
|
-
|
|
676
|
+
return False, "indigo", "white", f.qual.get("standard_name", "ori")
|
|
677
|
+
|
|
627
678
|
# Coding sequences
|
|
628
679
|
elif f.key in ["gene", "CDS", "rRNA"]:
|
|
629
680
|
label = f.qual.get("product")
|
|
630
681
|
if label is None:
|
|
631
682
|
label = f.qual.get("gene")
|
|
632
683
|
return True, colors["orange"], "black", label
|
|
633
|
-
|
|
684
|
+
|
|
634
685
|
elif f.key == "regulatory":
|
|
635
686
|
# Promoters
|
|
636
687
|
if f.qual.get("regulatory_class") in [
|
|
637
688
|
"promoter",
|
|
638
689
|
"TATA_box",
|
|
639
690
|
"minus_35_signal",
|
|
640
|
-
"minus_10_signal"
|
|
691
|
+
"minus_10_signal",
|
|
641
692
|
]:
|
|
642
693
|
return True, colors["dimgreen"], "black", f.qual.get("note")
|
|
643
|
-
|
|
694
|
+
|
|
644
695
|
# Terminators
|
|
645
696
|
elif f.qual.get("regulatory_class") in "terminator":
|
|
646
697
|
return False, "firebrick", "white", f.qual.get("note")
|
|
647
|
-
|
|
698
|
+
|
|
648
699
|
# RBS
|
|
649
700
|
elif f.qual.get("regulatory_class") == "ribosome_binding_site":
|
|
650
701
|
return False, colors["brightorange"], "white", None
|
|
651
|
-
|
|
702
|
+
|
|
652
703
|
# Primers
|
|
653
704
|
elif f.key == "primer_bind":
|
|
654
705
|
return True, "royalblue", "black", f.qual.get("note")
|
|
655
|
-
|
|
706
|
+
|
|
656
707
|
# Binding proteins
|
|
657
708
|
elif f.key == "protein_bind":
|
|
658
709
|
return False, colors["lightgreen"], "black", f.qual.get("note")
|
|
659
|
-
|
|
710
|
+
|
|
660
711
|
# Misc
|
|
661
|
-
return True, "dimgray", "white", f.qual.get("note")
|
|
712
|
+
return True, "dimgray", "white", f.qual.get("note")
|