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  """Provides a container for information concerning the tracks to be drawn in a diagram. 
 10   
 11  It also provides the interface for defining the diagram (possibly split these 
 12  functions in later version?). 
 13   
 14  For drawing capabilities, this module uses reportlab to draw and write the 
 15  diagram: 
 16   
 17  http://www.reportlab.com 
 18   
 19  For dealing with biological information, the package expects BioPython 
 20  objects - namely SeqRecord objects containing SeqFeature objects. 
 21  """ 
 22   
 23  try: 
 24      from reportlab.graphics import renderPM 
 25  except ImportError: 
 26      # This is an optional part of ReportLab, so may not be installed. 
 27      renderPM = None 
 28   
 29  from ._LinearDrawer import LinearDrawer 
 30  from ._CircularDrawer import CircularDrawer 
 31  from ._Track import Track 
 32   
 33  from Bio.Graphics import _write 
 34   
 35   
36 -class Diagram(object):
37 """Diagram container. 38 39 Arguments: 40 - name - a string, identifier for the diagram. 41 - tracks - a list of Track objects comprising the diagram. 42 - format - a string, format of the diagram 'circular' or 43 'linear', depending on the sort of diagram required. 44 - pagesize - a string, the pagesize of output describing the ISO 45 size of the image, or a tuple of pixels. 46 - orientation - a string describing the required orientation of the 47 final drawing ('landscape' or 'portrait'). 48 - x - a float (0->1), the proportion of the page to take 49 up with even X margins t the page. 50 - y - a float (0->1), the proportion of the page to take 51 up with even Y margins to the page. 52 - xl - a float (0->1), the proportion of the page to take 53 up with the left X margin to the page (overrides x). 54 - xr - a float (0->1), the proportion of the page to take 55 up with the right X margin to the page (overrides x). 56 - yt - a float (0->1), the proportion of the page to take 57 up with the top Y margin to the page (overrides y). 58 - yb - a float (0->1), the proportion of the page to take 59 up with the bottom Y margin to the page (overrides y). 60 - circle_core - a float, the proportion of the available radius to 61 leave empty at the center of a circular diagram (0 to 1). 62 - start - an integer, the base/aa position to start the diagram at. 63 - end - an integer, the base/aa position to end the diagram at. 64 - tracklines - a boolean, True if track guidelines are to be drawn. 65 - fragments - and integer, for a linear diagram, the number of equal 66 divisions into which the sequence is divided. 67 - fragment_size - a float (0->1), the proportion of the space 68 available to each fragment that should be used in drawing. 69 - track_size - a float (0->1), the proportion of the space 70 available to each track that should be used in drawing with sigils. 71 - circular - a boolean, True if the genome/sequence to be drawn 72 is, in reality, circular. 73 """
74 - def __init__(self, name=None, format='circular', pagesize='A3', 75 orientation='landscape', x=0.05, y=0.05, xl=None, 76 xr=None, yt=None, yb=None, start=None, end=None, 77 tracklines=False, fragments=10, fragment_size=0.9, 78 track_size=0.75, circular=True, circle_core=0.0):
79 """Called on instantiation. 80 81 gdd = Diagram(name=None) 82 """ 83 self.tracks = {} # Holds all Track objects, keyed by level 84 self.name = name # Description of the diagram 85 # Diagram page setup attributes 86 self.format = format 87 self.pagesize = pagesize 88 self.orientation = orientation 89 self.x = x 90 self.y = y 91 self.xl = xl 92 self.xr = xr 93 self.yt = yt 94 self.yb = yb 95 self.start = start 96 self.end = end 97 self.tracklines = tracklines 98 self.fragments = fragments 99 self.fragment_size = fragment_size 100 self.track_size = track_size 101 self.circular = circular 102 self.circle_core = circle_core 103 self.cross_track_links = [] 104 self.drawing = None
105
106 - def set_all_tracks(self, attr, value):
107 """Set the passed attribute of all tracks in the set to the passed value. 108 109 Arguments: 110 - attr - An attribute of the Track class. 111 - value - The value to set that attribute. 112 113 set_all_tracks(self, attr, value) 114 """ 115 for track in self.tracks.values(): 116 if hasattr(track, attr): # If the feature has the attribute 117 if getattr(track, attr) != value: 118 setattr(track, attr, value) # set it to the passed value
119
120 - def draw(self, format=None, pagesize=None, orientation=None, 121 x=None, y=None, xl=None, xr=None, yt=None, yb=None, 122 start=None, end=None, tracklines=None, fragments=None, 123 fragment_size=None, track_size=None, circular=None, 124 circle_core=None, cross_track_links=None):
125 """Draw the diagram, with passed parameters overriding existing attributes. 126 127 gdd.draw(format='circular') 128 """ 129 # Pass the parameters to the drawer objects that will build the 130 # diagrams. At the moment, we detect overrides with an or in the 131 # Instantiation arguments, but I suspect there's a neater way to do 132 # this. 133 if format == 'linear': 134 drawer = LinearDrawer(self, pagesize or self.pagesize, 135 orientation or self.orientation, 136 x or self.x, y or self.y, xl or self.xl, 137 xr or self.xr, yt or self.yt, 138 yb or self.yb, start or self.start, 139 end or self.end, 140 tracklines or self.tracklines, 141 fragments or self.fragments, 142 fragment_size or self.fragment_size, 143 track_size or self.track_size, 144 cross_track_links or self.cross_track_links) 145 else: 146 drawer = CircularDrawer(self, pagesize or self.pagesize, 147 orientation or self.orientation, 148 x or self.x, y or self.y, xl or self.xl, 149 xr or self.xr, yt or self.yt, 150 yb or self.yb, start or self.start, 151 end or self.end, 152 tracklines or self.tracklines, 153 track_size or self.track_size, 154 circular or self.circular, 155 circle_core or self.circle_core, 156 cross_track_links or self.cross_track_links) 157 drawer.draw() # Tell the drawer to complete the drawing 158 self.drawing = drawer.drawing # Get the completed drawing
159
160 - def write(self, filename='test1.ps', output='PS', dpi=72):
161 """Writes the drawn diagram to a specified file, in a specified format. 162 163 Arguments: 164 - filename - a string indicating the name of the output file, 165 or a handle to write to. 166 - output - a string indicating output format, one of PS, PDF, 167 SVG, or provided the ReportLab renderPM module is installed, one 168 of the bitmap formats JPG, BMP, GIF, PNG, TIFF or TIFF. The 169 format can be given in upper or lower case. 170 - dpi - an integer. Resolution (dots per inch) for bitmap formats. 171 172 Returns: 173 No return value. 174 175 write(self, filename='test1.ps', output='PS', dpi=72) 176 """ 177 return _write(self.drawing, filename, output, dpi=dpi)
178
179 - def write_to_string(self, output='PS', dpi=72):
180 """Returns a byte string containing the diagram in the requested format. 181 182 Arguments: 183 - output - a string indicating output format, one of PS, PDF, 184 SVG, JPG, BMP, GIF, PNG, TIFF or TIFF (as specified for the write 185 method). 186 - dpi - Resolution (dots per inch) for bitmap formats. 187 188 Returns: 189 Return the completed drawing as a bytes string in a prescribed 190 format. 191 """ 192 # The ReportLab drawToString method, which this function used to call, 193 # just used a cStringIO or StringIO handle with the drawToFile method. 194 # In order to put all our complicated file format specific code in one 195 # place we just used a StringIO handle here, later a BytesIO handle 196 # for Python 3 compatibility. 197 # 198 # TODO - Rename this method to include keyword bytes? 199 from io import BytesIO 200 handle = BytesIO() 201 self.write(handle, output, dpi) 202 return handle.getvalue()
203
204 - def add_track(self, track, track_level):
205 """Adds a Track object to the diagram. 206 207 It also accepts instructions to place it at a particular level on the 208 diagram. 209 210 Arguments: 211 - track - Track object to draw. 212 - track_level - an integer. The level at which the track will be 213 drawn (above an arbitrary baseline). 214 215 add_track(self, track, track_level) 216 """ 217 if track is None: 218 raise ValueError("Must specify track") 219 if track_level not in self.tracks: # No track at that level 220 self.tracks[track_level] = track # so just add it 221 else: # Already a track there, so shunt all higher tracks up one 222 occupied_levels = sorted(self.get_levels()) # Get list of occupied levels... 223 occupied_levels.reverse() # ...reverse it (highest first) 224 for val in occupied_levels: 225 # If track value >= that to be added 226 if val >= track.track_level: 227 self.tracks[val + 1] = self.tracks[val] # ...increment by 1 228 self.tracks[track_level] = track # And put the new track in 229 self.tracks[track_level].track_level = track_level
230
231 - def new_track(self, track_level, **args):
232 """Add a new Track to the diagram at a given level. 233 234 The track is returned for further user manipulation. 235 236 Arguments: 237 - track_level - an integer. The level at which the track will be 238 drawn (above an arbitrary baseline). 239 240 new_track(self, track_level) 241 """ 242 newtrack = Track() 243 for key in args: 244 setattr(newtrack, key, args[key]) 245 if track_level not in self.tracks: # No track at that level 246 self.tracks[track_level] = newtrack # so just add it 247 else: # Already a track there, so shunt all higher tracks up one 248 occupied_levels = sorted(self.get_levels()) # Get list of occupied levels... 249 occupied_levels.reverse() # ...reverse (highest first)... 250 for val in occupied_levels: 251 if val >= track_level: # Track value >= that to be added 252 self.tracks[val + 1] = self.tracks[val] # ..increment by 1 253 self.tracks[track_level] = newtrack # And put the new track in 254 self.tracks[track_level].track_level = track_level 255 return newtrack
256
257 - def del_track(self, track_level):
258 """Removes the track to be drawn at a particular level on the diagram. 259 260 Arguments: 261 - track_level - an integer. The level of the track on the diagram 262 to delete. 263 264 del_track(self, track_level) 265 """ 266 del self.tracks[track_level]
267
268 - def get_tracks(self):
269 """Returns a list of the tracks contained in the diagram. 270 271 get_tracks(self) 272 """ 273 return list(self.tracks.values())
274
275 - def move_track(self, from_level, to_level):
276 """Moves a track from one level on the diagram to another. 277 278 Arguments: 279 - from_level - an integer. The level at which the track to be 280 moved is found. 281 - to_level - an integer. The level to move the track to. 282 283 move_track(self, from_level, to_level) 284 """ 285 aux = self.tracks[from_level] 286 del self.tracks[from_level] 287 self.add_track(aux, to_level)
288
289 - def renumber_tracks(self, low=1, step=1):
290 """Renumbers all tracks consecutively. 291 292 Optionally from a passed lowest number. 293 294 Arguments: 295 - low - an integer. The track number to start from. 296 - step - an integer. The track interval for separation of 297 tracks. 298 299 renumber_tracks(self, low=1, step=1) 300 """ 301 track = low # Start numbering from here 302 levels = self.get_levels() 303 304 conversion = {} # Holds new set of levels 305 for level in levels: # Starting at low... 306 conversion[track] = self.tracks[level] # Add old tracks to new set 307 conversion[track].track_level = track 308 track += step # step interval 309 self.tracks = conversion # Replace old set of levels with new set
310
311 - def get_levels(self):
312 """Return a sorted list of levels occupied by tracks in the diagram. 313 314 get_levels(self) 315 """ 316 return sorted(self.tracks)
317
318 - def get_drawn_levels(self):
319 """Return a sorted list of levels occupied by tracks. 320 321 These tracks are not explicitly hidden. 322 323 get_drawn_levels(self) 324 """ 325 return sorted(key for key in self.tracks if not self.tracks[key].hide)
326
327 - def range(self):
328 """Returns lowest and highest base numbers from track features. 329 330 Returned type is a tuple. 331 332 range(self) 333 """ 334 lows, highs = [], [] 335 for track in self.tracks.values(): # Get ranges for each track 336 low, high = track.range() 337 lows.append(low) 338 highs.append(high) 339 return min(lows), max(highs) # Return extremes from all tracks
340
341 - def __getitem__(self, key):
342 """Returns the track contained at the level of the passed key. 343 344 Arguments: 345 - key - The id of a track in the diagram. 346 347 __getitem__(self, key) 348 """ 349 return self.tracks[key]
350
351 - def __str__(self):
352 """Returns a formatted string describing the diagram. 353 354 __str__(self) 355 """ 356 outstr = ["\n<%s: %s>" % (self.__class__, self.name)] 357 outstr.append("%d tracks" % len(self.tracks)) 358 for level in self.get_levels(): 359 outstr.append("Track %d: %s\n" % (level, self.tracks[level])) 360 outstr = '\n'.join(outstr) 361 return outstr
362