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