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