Package Bio :: Package Graphics :: Package GenomeDiagram :: Module _Diagram
[hide private]
[frames] | no frames]

Source Code for Module Bio.Graphics.GenomeDiagram._Diagram

  1  # Copyright 2003-2008 by Leighton Pritchard.  All rights reserved. 
  2  # This code is part of the Biopython distribution and governed by its 
  3  # license.  Please see the LICENSE file that should have been included 
  4  # as part of this package. 
  5  # 
  6  # Contact:       Leighton Pritchard, Scottish Crop Research Institute, 
  7  #                Invergowrie, Dundee, Scotland, DD2 5DA, UK 
  8  #                L.Pritchard@scri.ac.uk 
  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  # IMPORTS 
 30   
 31  # ReportLab 
 32  try: 
 33      from reportlab.graphics import renderPM 
 34  except ImportError: 
 35      #This is an optional part of ReportLab, so may not be installed. 
 36      renderPM=None 
 37   
 38  # GenomeDiagram 
 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  # CLASSES 
 47   
 48  #------------------------------------------------------------ 
 49  # Diagram 
 50   
 51   
52 -class Diagram(object):
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 = {} # Holds all Track objects, keyed by level 209 self.name = name # Description of the diagram 210 # Diagram page setup attributes 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
230 - def set_all_tracks(self, attr, value):
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): # If the feature has the attribute 242 if getattr(track, attr) != value: 243 setattr(track, attr, value) # set it to the passed 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 # Pass the parameters to the drawer objects that will build the 253 # diagrams. At the moment, we detect overrides with an or in the 254 # Instantiation arguments, but I suspect there's a neater way to do 255 # this. 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() # Tell the drawer to complete the drawing 281 self.drawing = drawer.drawing # Get the completed 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
303 - def write_to_string(self, output='PS', dpi=72):
304 """Returns a byte string containing the diagram in the requested format. 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 bytes string in a prescribed format 313 """ 314 #The ReportLab drawToString method, which this function used to call, 315 #just used a cStringIO or StringIO handle with the drawToFile method. 316 #In order to put all our complicated file format specific code in one 317 #place we just used a StringIO handle here, later a BytesIO handle 318 #for Python 3 compatibility. 319 # 320 #TODO - Rename this method to include keyword bytes? 321 from io import BytesIO 322 handle = BytesIO() 323 self.write(handle, output, dpi) 324 return handle.getvalue()
325
326 - def add_track(self, track, track_level):
327 """ add_track(self, track, track_level) 328 329 o track Track object to draw 330 331 o track_level Int, the level at which the track will be drawn 332 (above an arbitrary baseline) 333 334 Add a pre-existing Track to the diagram at a given level 335 """ 336 if track is None: 337 raise ValueError("Must specify track") 338 if track_level not in self.tracks: # No track at that level 339 self.tracks[track_level] = track # so just add it 340 else: # Already a track there, so shunt all higher tracks up one 341 occupied_levels = sorted(self.get_levels()) # Get list of occupied levels... 342 occupied_levels.reverse() # ...reverse it (highest first) 343 for val in occupied_levels: 344 # If track value >= that to be added 345 if val >= track.track_level: 346 self.tracks[val+1] = self.tracks[val] # ...increment by 1 347 self.tracks[track_level] = track # And put the new track in 348 self.tracks[track_level].track_level = track_level
349
350 - def new_track(self, track_level, **args):
351 """ new_track(self, track_level) -> Track 352 353 o track_level Int, the level at which the track will be drawn 354 (above an arbitrary baseline) 355 356 Add a new Track to the diagram at a given level and returns it for 357 further user manipulation. 358 """ 359 newtrack = Track() 360 for key in args: 361 setattr(newtrack, key, args[key]) 362 if track_level not in self.tracks: # No track at that level 363 self.tracks[track_level] = newtrack # so just add it 364 else: # Already a track there, so shunt all higher tracks up one 365 occupied_levels = sorted(self.get_levels()) # Get list of occupied levels... 366 occupied_levels.reverse() # ...reverse (highest first)... 367 for val in occupied_levels: 368 if val >= track_level: # Track value >= that to be added 369 self.tracks[val+1] = self.tracks[val] # ..increment by 1 370 self.tracks[track_level] = newtrack # And put the new track in 371 self.tracks[track_level].track_level = track_level 372 return newtrack
373
374 - def del_track(self, track_level):
375 """ del_track(self, track_level) 376 377 o track_level Int, the level of the track on the diagram to delete 378 379 Remove the track at the passed level from the diagram 380 """ 381 del self.tracks[track_level]
382
383 - def get_tracks(self):
384 """ get_tracks(self) -> list 385 386 Returns a list of the tracks contained in the diagram 387 """ 388 return list(self.tracks.values())
389
390 - def move_track(self, from_level, to_level):
391 """ move_track(self, from_level, to_level) 392 393 o from_level Int, the level at which the track to be moved is 394 found 395 396 o to_level Int, the level to move the track to 397 398 Moves a track from one level on the diagram to another 399 """ 400 aux = self.tracks[from_level] 401 del self.tracks[from_level] 402 self.add_track(aux, to_level)
403
404 - def renumber_tracks(self, low=1, step=1):
405 """ renumber_tracks(self, low=1, step=1) 406 407 o low Int, the track number to start from 408 409 o step Int, the track interval for separation of tracks 410 411 Reassigns all the tracks to run consecutively from the lowest 412 value (low) 413 """ 414 track = low # Start numbering from here 415 levels = self.get_levels() 416 417 conversion = {} # Holds new set of levels 418 for level in levels: # Starting at low... 419 conversion[track] = self.tracks[level] # Add old tracks to new set 420 conversion[track].track_level = track 421 track += step # step interval 422 self.tracks = conversion # Replace old set of levels with new set
423
424 - def get_levels(self):
425 """ get_levels(self) -> [int, int, ...] 426 427 Return a sorted list of levels occupied by tracks in the diagram 428 """ 429 return sorted(self.tracks)
430
431 - def get_drawn_levels(self):
432 """ get_drawn_levels(self) -> [int, int, ...] 433 434 Return a sorted list of levels occupied by tracks that are not 435 explicitly hidden 436 """ 437 return sorted(key for key in self.tracks if not self.tracks[key].hide)
438
439 - def range(self):
440 """ range(self) -> (int, int) 441 442 Returns the lowest and highest base (or mark) numbers containd in 443 track features as a tuple 444 """ 445 lows, highs = [], [] 446 for track in self.tracks.values(): # Get ranges for each track 447 low, high = track.range() 448 lows.append(low) 449 highs.append(high) 450 return (min(lows), max(highs)) # Return extremes from all tracks
451
452 - def __getitem__(self, key):
453 """ __getitem__(self, key) -> Track 454 455 o key The id of a track in the diagram 456 457 Return the Track object with the passed id 458 """ 459 return self.tracks[key]
460
461 - def __str__(self):
462 """ __str__(self) -> "" 463 464 Returns a formatted string with information about the diagram 465 """ 466 outstr = ["\n<%s: %s>" % (self.__class__, self.name)] 467 outstr.append("%d tracks" % len(self.tracks)) 468 for level in self.get_levels(): 469 outstr.append("Track %d: %s\n" % (level, self.tracks[level])) 470 outstr = '\n'.join(outstr) 471 return outstr
472