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 is None:
339 raise ValueError("Must specify track")
340 if track_level not in self.tracks:
341 self.tracks[track_level] = track
342 else:
343 occupied_levels = self.get_levels()
344 occupied_levels.sort()
345 occupied_levels.reverse()
346 for val in occupied_levels:
347
348 if val >= track.track_level:
349 self.tracks[val+1] = self.tracks[val]
350 self.tracks[track_level] = track
351 self.tracks[track_level].track_level = track_level
352
353
355 """ new_track(self, track_level) -> Track
356
357 o track_level Int, the level at which the track will be drawn
358 (above an arbitrary baseline)
359
360 Add a new Track to the diagram at a given level and returns it for
361 further user manipulation.
362 """
363 newtrack = Track()
364 for key in args:
365 setattr(newtrack, key, args[key])
366 if track_level not in self.tracks:
367 self.tracks[track_level] = newtrack
368 else:
369 occupied_levels = self.get_levels()
370 occupied_levels.sort()
371 occupied_levels.reverse()
372 for val in occupied_levels:
373 if val >= track_level:
374 self.tracks[val+1] = self.tracks[val]
375 self.tracks[track_level] = newtrack
376 self.tracks[track_level].track_level = track_level
377 return newtrack
378
379
381 """ del_track(self, track_level)
382
383 o track_level Int, the level of the track on the diagram to delete
384
385 Remove the track at the passed level from the diagram
386 """
387 del self.tracks[track_level]
388
389
391 """ get_tracks(self) -> list
392
393 Returns a list of the tracks contained in the diagram
394 """
395 return self.tracks.values()
396
397
399 """ move_track(self, from_level, to_level)
400
401 o from_level Int, the level at which the track to be moved is
402 found
403
404 o to_level Int, the level to move the track to
405
406 Moves a track from one level on the diagram to another
407 """
408 aux = self.tracks[from_level]
409 del self.tracks[from_level]
410 self.add_track(aux, to_level)
411
412
414 """ renumber_tracks(self, low=1, step=1)
415
416 o low Int, the track number to start from
417
418 o step Int, the track interval for separation of tracks
419
420 Reassigns all the tracks to run consecutively from the lowest
421 value (low)
422 """
423 track = low
424 levels = self.get_levels()
425
426 conversion = {}
427 for level in levels:
428 conversion[track] = self.tracks[level]
429 conversion[track].track_level = track
430 track += step
431 self.tracks = conversion
432
434 """ get_levels(self) -> [int, int, ...]
435
436 Return a sorted list of levels occupied by tracks in the diagram
437 """
438 levels = self.tracks.keys()
439 levels.sort()
440 return levels
441
442
444 """ get_drawn_levels(self) -> [int, int, ...]
445
446 Return a sorted list of levels occupied by tracks that are not
447 explicitly hidden
448 """
449 drawn_levels = [key for key in self.tracks.keys() if \
450 not self.tracks[key].hide]
451 drawn_levels.sort()
452 return drawn_levels
453
454
456 """ range(self) -> (int, int)
457
458 Returns the lowest and highest base (or mark) numbers containd in
459 track features as a tuple
460 """
461 lows, highs = [], []
462 for track in self.tracks.values():
463 low, high = track.range()
464 lows.append(low)
465 highs.append(high)
466 return (min(lows), max(highs))
467
469 """ __getitem__(self, key) -> Track
470
471 o key The id of a track in the diagram
472
473 Return the Track object with the passed id
474 """
475 return self.tracks[key]
476
478 """ __str__(self) -> ""
479
480 Returns a formatted string with information about the diagram
481 """
482 outstr = ["\n<%s: %s>" % (self.__class__, self.name)]
483 outstr.append("%d tracks" % len(self.tracks))
484 for level in self.get_levels():
485 outstr.append("Track %d: %s\n" % (level, self.tracks[level]))
486 outstr = '\n'.join(outstr)
487 return outstr
488