1
2
3
4
5
6
7
8
9
10
11 """ Diagram module
12
13 Provides:
14
15 o Diagram - Container for information concerning the tracks to be
16 drawn in a diagram, and the interface for defining the
17 diagram (possibly split these functions in later version?)
18
19 For drawing capabilities, this module uses reportlab to draw and write
20 the diagram:
21
22 http://www.reportlab.com
23
24 For dealing with biological information, the package expects BioPython
25 objects - namely SeqRecord ojbects containing SeqFeature objects.
26 """
27
28
29
30
31
32 try:
33 from reportlab.graphics import renderPM
34 except ImportError:
35
36 renderPM=None
37
38
39 from _LinearDrawer import LinearDrawer
40 from _CircularDrawer import CircularDrawer
41 from _Track import Track
42
43 from Bio.Graphics import _write
44
45
46
47
48
49
50
51
53 """ Diagram
54
55 Provides:
56
57 Attributes:
58
59 o name String, identifier for the diagram
60
61 o tracks List of Track objects comprising the diagram
62
63 o format String, format of the diagram (circular/linear)
64
65 o pagesize String, the pagesize of output
66
67 o orientation String, the page orientation (landscape/portrait)
68
69 o x Float, the proportion of the page to take up with even
70 X margins
71
72 o y Float, the proportion of the page to take up with even
73 Y margins
74
75 o xl Float, the proportion of the page to take up with the
76 left X margin
77
78 o xr Float, the proportion of the page to take up with the
79 right X margin
80
81 o yt Float, the proportion of the page to take up with the
82 top Y margin
83
84 o yb Float, the proportion of the page to take up with the
85 bottom Y margin
86
87 o circle_core Float, the proportion of the available radius to leave
88 empty at the center of a circular diagram (0 to 1).
89
90 o start Int, the base/aa position to start the diagram at
91
92 o end Int, the base/aa position to end the diagram at
93
94 o tracklines Boolean, True if track guidelines are to be drawn
95
96 o fragments Int, for a linear diagram, the number of equal divisions
97 into which the sequence is divided
98
99 o fragment_size Float, the proportion of the space available to each
100 fragment that should be used in drawing
101
102 o track_size Float, the proportion of the space available to each
103 track that should be used in drawing
104
105 o circular Boolean, True if the genome/sequence to be drawn is, in
106 reality, circular.
107
108 Methods:
109
110 o __init__(self, name=None) Called on instantiation
111
112 o draw(self, format='circular', ...) Instructs the package to draw
113 the diagram
114
115 o write(self, filename='test1.ps', output='PS') Writes the drawn
116 diagram to a specified file, in a specified format.
117
118 o add_track(self, track, track_level) Adds a Track object to the
119 diagram, with instructions to place it at a particular level on
120 the diagram
121
122 o del_track(self, track_level) Removes the track that is to be drawn
123 at a particular level on the diagram
124
125 o get_tracks(self) Returns the list of Track objects to be drawn
126 contained in the diagram
127
128 o renumber_tracks(self, low=1) Renumbers all tracks consecutively,
129 optionally from a passed lowest number
130
131 o get_levels(self) Returns a list of levels currently occupied by
132 Track objects
133
134 o get_drawn_levels(self) Returns a list of levels currently occupied
135 by Track objects that will be shown in the drawn diagram (i.e.
136 are not hidden)
137
138 o range(self) Returns the lowest- and highest-numbered positions
139 contained within features in all tracks on the diagram as a tuple.
140
141 o __getitem__(self, key) Returns the track contained at the level of
142 the passed key
143
144 o __str__(self) Returns a formatted string describing the diagram
145
146 """
147 - def __init__(self, name=None, format='circular', pagesize='A3',
148 orientation='landscape', x=0.05, y=0.05, xl=None,
149 xr=None, yt=None, yb=None, start=None, end=None,
150 tracklines=False, fragments=10, fragment_size=0.9,
151 track_size=0.75, circular=True, circle_core=0.0):
152 """ __init__(self, name=None)
153
154 o name String describing the diagram
155
156 o format String: 'circular' or 'linear', depending on the sort of
157 diagram required
158
159 o pagesize String describing the ISO size of the image, or a tuple
160 of pixels
161
162 o orientation String describing the required orientation of the
163 final drawing ('landscape' or 'portrait')
164
165 o x Float (0->1) describing the relative size of the X
166 margins to the page
167
168 o y Float (0->1) describing the relative size of the Y
169 margins to the page
170
171 o xl Float (0->1) describing the relative size of the left X
172 margin to the page (overrides x)
173
174 o xl Float (0->1) describing the relative size of the left X
175 margin to the page (overrides x)
176
177 o xr Float (0->1) describing the relative size of the right X
178 margin to the page (overrides x)
179
180 o yt Float (0->1) describing the relative size of the top Y
181 margin to the page (overrides y)
182
183 o yb Float (0->1) describing the relative size of the lower Y
184 margin to the page (overrides y)
185
186 o start Int, the position to begin drawing the diagram at
187
188
189 o end Int, the position to stop drawing the diagram at
190
191 o tracklines Boolean flag to show (or not) lines delineating
192 tracks on the diagram
193
194 o fragments Int, for linear diagrams, the number of sections into
195 which to break the sequence being drawn
196
197 o fragment_size Float (0->1), for linear diagrams, describing
198 the proportion of space in a fragment to take
199 up with tracks
200
201 o track_size Float (0->1) describing the proportion of space
202 in a track to take up with sigils
203
204 o circular Boolean flag to indicate whether the sequence being
205 drawn is circular
206
207 """
208 self.tracks = {}
209 self.name = name
210
211 self.format = format
212 self.pagesize = pagesize
213 self.orientation = orientation
214 self.x = x
215 self.y = y
216 self.xl = xl
217 self.xr = xr
218 self.yt = yt
219 self.yb = yb
220 self.start = start
221 self.end = end
222 self.tracklines = tracklines
223 self.fragments = fragments
224 self.fragment_size = fragment_size
225 self.track_size = track_size
226 self.circular = circular
227 self.circle_core = circle_core
228 self.cross_track_links = []
229
231 """ set_all_tracks(self, attr, value)
232
233 o attr An attribute of the Track class
234
235 o value The value to set that attribute
236
237 Set the passed attribute of all tracks in the set to the
238 passed value
239 """
240 for track in self.tracks.values():
241 if hasattr(track, attr):
242 if getattr(track, attr) != value:
243 setattr(track, attr, value)
244
245 - def draw(self, format=None, pagesize=None, orientation=None,
246 x=None, y=None, xl=None, xr=None, yt=None, yb=None,
247 start=None, end=None, tracklines=None, fragments=None,
248 fragment_size=None, track_size=None, circular=None,
249 circle_core=None, cross_track_links=None):
250 """Draw the diagram, with passed parameters overriding existing attributes.
251 """
252
253
254
255
256 if format == 'linear':
257 drawer = LinearDrawer(self, pagesize or self.pagesize,
258 orientation or self.orientation,
259 x or self.x, y or self.y, xl or self.xl,
260 xr or self.xr, yt or self.yt,
261 yb or self.yb, start or self.start,
262 end or self.end,
263 tracklines or self.tracklines,
264 fragments or self.fragments,
265 fragment_size or self.fragment_size,
266 track_size or self.track_size,
267 cross_track_links or self.cross_track_links)
268 else:
269 drawer = CircularDrawer(self, pagesize or self.pagesize,
270 orientation or self.orientation,
271 x or self.x, y or self.y, xl or self.xl,
272 xr or self.xr, yt or self.yt,
273 yb or self.yb, start or self.start,
274 end or self.end,
275 tracklines or self.tracklines,
276 track_size or self.track_size,
277 circular or self.circular,
278 circle_core or self.circle_core,
279 cross_track_links or self.cross_track_links)
280 drawer.draw()
281 self.drawing = drawer.drawing
282
283 - def write(self, filename='test1.ps', output='PS', dpi=72):
284 """ write(self, filename='test1.ps', output='PS', dpi=72)
285
286 o filename String indicating the name of the output file,
287 or a handle to write to.
288
289 o output String indicating output format, one of PS, PDF,
290 SVG, or provided the ReportLab renderPM module is
291 installed, one of the bitmap formats JPG, BMP,
292 GIF, PNG, TIFF or TIFF. The format can be given
293 in upper or lower case.
294
295 o dpi Resolution (dots per inch) for bitmap formats.
296
297 Write the completed drawing out to a file in a prescribed format
298
299 No return value.
300 """
301 return _write(self.drawing, filename, output, dpi=dpi)
302
304 """ write(self, output='PS')
305
306 o output String indicating output format, one of PS, PDF,
307 SVG, JPG, BMP, GIF, PNG, TIFF or TIFF (as
308 specified for the write method).
309
310 o dpi Resolution (dots per inch) for bitmap formats.
311
312 Return the completed drawing as a string in a prescribed format
313 """
314
315
316
317
318 from StringIO import StringIO
319 handle = StringIO()
320 self.write(handle, output, dpi)
321 return handle.getvalue()
322
324 """ add_track(self, track, track_level)
325
326 o track Track object to draw
327
328 o track_level Int, the level at which the track will be drawn
329 (above an arbitrary baseline)
330
331 Add a pre-existing Track to the diagram at a given level
332 """
333 if track is None:
334 raise ValueError("Must specify track")
335 if track_level not in self.tracks:
336 self.tracks[track_level] = track
337 else:
338 occupied_levels = self.get_levels()
339 occupied_levels.sort()
340 occupied_levels.reverse()
341 for val in occupied_levels:
342
343 if val >= track.track_level:
344 self.tracks[val+1] = self.tracks[val]
345 self.tracks[track_level] = track
346 self.tracks[track_level].track_level = track_level
347
349 """ new_track(self, track_level) -> Track
350
351 o track_level Int, the level at which the track will be drawn
352 (above an arbitrary baseline)
353
354 Add a new Track to the diagram at a given level and returns it for
355 further user manipulation.
356 """
357 newtrack = Track()
358 for key in args:
359 setattr(newtrack, key, args[key])
360 if track_level not in self.tracks:
361 self.tracks[track_level] = newtrack
362 else:
363 occupied_levels = self.get_levels()
364 occupied_levels.sort()
365 occupied_levels.reverse()
366 for val in occupied_levels:
367 if val >= track_level:
368 self.tracks[val+1] = self.tracks[val]
369 self.tracks[track_level] = newtrack
370 self.tracks[track_level].track_level = track_level
371 return newtrack
372
374 """ del_track(self, track_level)
375
376 o track_level Int, the level of the track on the diagram to delete
377
378 Remove the track at the passed level from the diagram
379 """
380 del self.tracks[track_level]
381
383 """ get_tracks(self) -> list
384
385 Returns a list of the tracks contained in the diagram
386 """
387 return self.tracks.values()
388
390 """ move_track(self, from_level, to_level)
391
392 o from_level Int, the level at which the track to be moved is
393 found
394
395 o to_level Int, the level to move the track to
396
397 Moves a track from one level on the diagram to another
398 """
399 aux = self.tracks[from_level]
400 del self.tracks[from_level]
401 self.add_track(aux, to_level)
402
404 """ renumber_tracks(self, low=1, step=1)
405
406 o low Int, the track number to start from
407
408 o step Int, the track interval for separation of tracks
409
410 Reassigns all the tracks to run consecutively from the lowest
411 value (low)
412 """
413 track = low
414 levels = self.get_levels()
415
416 conversion = {}
417 for level in levels:
418 conversion[track] = self.tracks[level]
419 conversion[track].track_level = track
420 track += step
421 self.tracks = conversion
422
424 """ get_levels(self) -> [int, int, ...]
425
426 Return a sorted list of levels occupied by tracks in the diagram
427 """
428 levels = self.tracks.keys()
429 levels.sort()
430 return levels
431
433 """ get_drawn_levels(self) -> [int, int, ...]
434
435 Return a sorted list of levels occupied by tracks that are not
436 explicitly hidden
437 """
438 drawn_levels = [key for key in self.tracks.keys() if
439 not self.tracks[key].hide]
440 drawn_levels.sort()
441 return drawn_levels
442
444 """ range(self) -> (int, int)
445
446 Returns the lowest and highest base (or mark) numbers containd in
447 track features as a tuple
448 """
449 lows, highs = [], []
450 for track in self.tracks.values():
451 low, high = track.range()
452 lows.append(low)
453 highs.append(high)
454 return (min(lows), max(highs))
455
457 """ __getitem__(self, key) -> Track
458
459 o key The id of a track in the diagram
460
461 Return the Track object with the passed id
462 """
463 return self.tracks[key]
464
466 """ __str__(self) -> ""
467
468 Returns a formatted string with information about the diagram
469 """
470 outstr = ["\n<%s: %s>" % (self.__class__, self.name)]
471 outstr.append("%d tracks" % len(self.tracks))
472 for level in self.get_levels():
473 outstr.append("Track %d: %s\n" % (level, self.tracks[level]))
474 outstr = '\n'.join(outstr)
475 return outstr
476