# Copyright 2003-2008 by Leighton Pritchard. All rights reserved.# This code is part of the Biopython distribution and governed by its# license. Please see the LICENSE file that should have been included# as part of this package.## Contact: Leighton Pritchard, Scottish Crop Research Institute,# Invergowrie, Dundee, Scotland, DD2 5DA, UK# L.Pritchard@scri.ac.uk################################################################################""" Diagram module Provides: o Diagram - Container for information concerning the tracks to be drawn in a diagram, and the interface for defining the diagram (possibly split these functions in later version?) For drawing capabilities, this module uses reportlab to draw and write the diagram: http://www.reportlab.com For dealing with biological information, the package expects BioPython objects - namely SeqRecord ojbects containing SeqFeature objects."""#------------------------------------------------------------------------------# IMPORTS# ReportLabfrom reportlab.graphics import renderPS, renderPDF, renderSVG
try:
from reportlab.graphics import renderPM
except ImportError:
#This is an optional part of ReportLab, so may not be installed.
renderPM=Nonefrom reportlab.lib import pagesizes
# GenomeDiagramfrom _LinearDrawer import LinearDrawer
from _CircularDrawer import CircularDrawer
from _Track import Track
# Builtinsimport sys
from Bio.Graphics import _write
#------------------------------------------------------------------------------# CLASSES#------------------------------------------------------------# Diagram00056class Diagram(object):
""" Diagram Provides: Attributes: o name String, identifier for the diagram o tracks List of Track objects comprising the diagram o format String, format of the diagram (circular/linear) o pagesize String, the pagesize of output o orientation String, the page orientation (landscape/portrait) o x Float, the proportion of the page to take up with even X margins o y Float, the proportion of the page to take up with even Y margins o xl Float, the proportion of the page to take up with the left X margin o xr Float, the proportion of the page to take up with the right X margin o yt Float, the proportion of the page to take up with the top Y margin o yb Float, the proportion of the page to take up with the bottom Y margin o start Int, the base/aa position to start the diagram at o end Int, the base/aa position to end the diagram at o tracklines Boolean, True if track guidelines are to be drawn o fragments Int, for a linear diagram, the number of equal divisions into which the sequence is divided o fragment_size Float, the proportion of the space available to each fragment that should be used in drawing o track_size Float, the proportion of the space available to each track that should be used in drawing o circular Boolean, True if the genome/sequence to be drawn is, in reality, circular. Methods: o __init__(self, name=None) Called on instantiation o draw(self, format='circular', pagesize='A3', orientation='landscape', x=0.05, y=0.05, xl=None, xr=None, yt=None, yb=None, start=None, end=None, tracklines=0, fragments=10, fragment_size=0.9, track_size=0.75) Instructs the package to draw the diagram o write(self, filename='test1.ps', output='PS') Writes the drawn diagram to a specified file, in a specified format. o add_track(self, track, track_level) Adds a Track object to the diagram, with instructions to place it at a particular level on the diagram o del_track(self, track_level) Removes the track that is to be drawn at a particular level on the diagram o get_tracks(self) Returns the list of Track objects to be drawn contained in the diagram o renumber_tracks(self, low=1) Renumbers all tracks consecutively, optionally from a passed lowest number o get_levels(self) Returns a list of levels currently occupied by Track objects o get_drawn_levels(self) Returns a list of levels currently occupied by Track objects that will be shown in the drawn diagram (i.e. are not hidden) o range(self) Returns the lowest- and highest-numbered positions contained within features in all tracks on the diagram as a tuple. o __getitem__(self, key) Returns the track contained at the level of the passed key o __str__(self) Returns a formatted string describing the diagram """00151def __init__(self, name=None, format='circular', pagesize='A3',
orientation='landscape', x=0.05, y=0.05, xl=None,
xr=None, yt=None, yb=None, start=None, end=None,
tracklines=False, fragments=10, fragment_size=0.9,
track_size=0.75, circular=True):
""" __init__(self, name=None) o name String describing the diagram o format String: 'circular' or 'linear', depending on the sort of diagram required o pagesize String describing the ISO size of the image, or a tuple of pixels o orientation String describing the required orientation of the final drawing ('landscape' or 'portrait') o x Float (0->1) describing the relative size of the X margins to the page o y Float (0->1) describing the relative size of the Y margins to the page o xl Float (0->1) describing the relative size of the left X margin to the page (overrides x) o xl Float (0->1) describing the relative size of the left X margin to the page (overrides x) o xr Float (0->1) describing the relative size of the right X margin to the page (overrides x) o yt Float (0->1) describing the relative size of the top Y margin to the page (overrides y) o yb Float (0->1) describing the relative size of the lower Y margin to the page (overrides y) o start Int, the position to begin drawing the diagram at o end Int, the position to stop drawing the diagram at o tracklines Boolean flag to show (or not) lines delineating tracks on the diagram o fragments Int, for linear diagrams, the number of sections into which to break the sequence being drawn o fragment_size Float (0->1), for linear diagrams, describing the proportion of space in a fragment to take up with tracks o track_size Float (0->1) describing the proportion of space in a track to take up with sigils o circular Boolean flag to indicate whether the sequence being drawn is circular """
self.tracks = {} # Holds all Track objects, keyed by level
self.name = name # Description of the diagram# Diagram page setup attributes
self.format = format
self.pagesize = pagesize
self.orientation = orientation
self.x = x
self.y = y
self.xl = xl
self.xr = xr
self.yt = yt
self.yb = yb
self.start = start
self.end = end
self.tracklines = tracklines
self.fragments = fragments
self.fragment_size = fragment_size
self.track_size = track_size
self.circular = circular
00233def set_all_tracks(self, attr, value):
""" set_all_tracks(self, attr, value) o attr An attribute of the Track class o value The value to set that attribute Set the passed attribute of all tracks in the set to the passed value """for track in self.tracks.values():
if hasattr(track, attr): # If the feature has the attributeif getattr(track, attr) != value:
setattr(track, attr, value) # set it to the passed value 00248def draw(self, format=None, pagesize=None, orientation=None,
x=None, y=None, xl=None, xr=None, yt=None, yb=None,
start=None, end=None, tracklines=None, fragments=None,
fragment_size=None, track_size=None, circular=None):
""" draw(self, format=None, pagesize=None, orientation=None, x=None, y=None, xl=None, xr=None, yt=None, yb=None, start=None, end=None, tracklines=None, fragments=None, fragment_size=None, track_size=None) Draws the diagram using the passed parameters, if any, to override previous settings for the diagram object. """# Pass the parameters to the drawer objects that will build the # diagrams. At the moment, we detect overrides with an or in the # Instantiation arguments, but I suspect there's a neater way to do # this.if format == 'linear':
drawer = LinearDrawer(self, pagesize or self.pagesize,
orientation or self.orientation,
x or self.x, y or self.y, xl or self.xl,
xr or self.xr, yt or self.yt,
yb or self.yb, start or self.start,
end or self.end,
tracklines or self.tracklines,
fragments or self.fragments,
fragment_size or self.fragment_size,
track_size or self.track_size)
else:
drawer = CircularDrawer(self, pagesize or self.pagesize,
orientation or self.orientation,
x or self.x, y or self.y, xl or self.xl,
xr or self.xr, yt or self.yt,
yb or self.yb, start or self.start,
end or self.end,
tracklines or self.tracklines,
track_size or self.track_size,
circular or self.circular)
drawer.draw() # Tell the drawer to complete the drawing
self.drawing = drawer.drawing # Get the completed drawing00288def write(self, filename='test1.ps', output='PS', dpi=72):
""" write(self, filename='test1.ps', output='PS', dpi=72) o filename String indicating the name of the output file, or a handle to write to. o output String indicating output format, one of PS, PDF, SVG, or provided the ReportLab renderPM module is installed, one of the bitmap formats JPG, BMP, GIF, PNG, TIFF or TIFF. The format can be given in upper or lower case. o dpi Resolution (dots per inch) for bitmap formats. Write the completed drawing out to a file in a prescribed format No return value. """return _write(self.drawing, filename, output, dpi=dpi)
00308def write_to_string(self, output='PS', dpi=72):
""" write(self, output='PS') o output String indicating output format, one of PS, PDF, SVG, JPG, BMP, GIF, PNG, TIFF or TIFF (as specified for the write method). o dpi Resolution (dots per inch) for bitmap formats. Return the completed drawing as a string in a prescribed format """#The ReportLab drawToString method, which this function used to call,#just uses a cStringIO or StringIO handle with the drawToFile method.#In order to put all our complicated file format specific code in one#place we'll just use a StringIO handle here:from StringIO import StringIO
handle = StringIO()
self.write(handle, output, dpi)
return handle.getvalue()
00328def add_track(self, track, track_level):
""" add_track(self, track, track_level) o track Track object to draw o track_level Int, the level at which the track will be drawn (above an arbitrary baseline) Add a pre-existing Track to the diagram at a given level """if track isNone:
raise ValueError("Must specify track")
if track_level notin self.tracks: # No track at that level
self.tracks[track_level] = track # so just add itelse: # Already a track there, so shunt all higher tracks up one
occupied_levels = self.get_levels() # Get list of occupied levels...
occupied_levels.sort() # ...sort it...
occupied_levels.reverse() # ...reverse it (highest first)for val in occupied_levels:
# If track value >= that to be addedif val >= track.track_level:
self.tracks[val+1] = self.tracks[val] # ...increment by 1
self.tracks[track_level] = track # And put the new track in
self.tracks[track_level].track_level = track_level
00354def new_track(self, track_level, **args):
""" new_track(self, track_level) -> Track o track_level Int, the level at which the track will be drawn (above an arbitrary baseline) Add a new Track to the diagram at a given level and returns it for further user manipulation. """
newtrack = Track()
for key in args:
setattr(newtrack, key, args[key])
if track_level notin self.tracks: # No track at that level
self.tracks[track_level] = newtrack # so just add itelse: # Already a track there, so shunt all higher tracks up one
occupied_levels = self.get_levels() # Get list of occupied levels...
occupied_levels.sort() # ...sort it...
occupied_levels.reverse() # ...reverse (highest first)...for val in occupied_levels:
if val >= track_level: # Track value >= that to be added
self.tracks[val+1] = self.tracks[val] # ..increment by 1
self.tracks[track_level] = newtrack # And put the new track in
self.tracks[track_level].track_level = track_level
return newtrack
00380def del_track(self, track_level):
""" del_track(self, track_level) o track_level Int, the level of the track on the diagram to delete Remove the track at the passed level from the diagram """
del self.tracks[track_level]
00390def get_tracks(self):
""" get_tracks(self) -> list Returns a list of the tracks contained in the diagram """return self.tracks.values()
00398def move_track(self, from_level, to_level):
""" move_track(self, from_level, to_level) o from_level Int, the level at which the track to be moved is found o to_level Int, the level to move the track to Moves a track from one level on the diagram to another """
aux = self.tracks[from_level]
del self.tracks[from_level]
self.add_track(aux, to_level)
00413def renumber_tracks(self, low=1, step=1):
""" renumber_tracks(self, low=1, step=1) o low Int, the track number to start from o step Int, the track interval for separation of tracks Reassigns all the tracks to run consecutively from the lowest value (low) """
track = low # Start numbering from here
levels = self.get_levels() #
conversion = {} # Holds new set of levelsfor level in levels: # Starting at low...
conversion[track] = self.tracks[level] # Add old tracks to new set
conversion[track].track_level = track
track += step # step interval
self.tracks = conversion # Replace old set of levels with new set00433def get_levels(self):
""" get_levels(self) -> [int, int, ...] Return a sorted list of levels occupied by tracks in the diagram """
levels = self.tracks.keys()
levels.sort()
return levels
00443def get_drawn_levels(self):
""" get_drawn_levels(self) -> [int, int, ...] Return a sorted list of levels occupied by tracks that are not explicitly hidden """
drawn_levels = [key for key in self.tracks.keys() if \
not self.tracks[key].hide] # get list of shown levels
drawn_levels.sort()
return drawn_levels
00455def range(self):
""" range(self) -> (int, int) Returns the lowest and highest base (or mark) numbers containd in track features as a tuple """
lows, highs = [], []
for track in self.tracks.values(): # Get ranges for each track
low, high = track.range()
lows.append(low)
highs.append(high)
return (min(lows), max(highs)) # Return extremes from all tracks00468def __getitem__(self, key):
""" __getitem__(self, key) -> Track o key The id of a track in the diagram Return the Track object with the passed id """return self.tracks[key]
00477def __str__(self):
""" __str__(self) -> "" Returns a formatted string with information about the diagram """
outstr = ["\n<%s: %s>" % (self.__class__, self.name)]
outstr.append("%d tracks" % len(self.tracks))
for level in self.get_levels():
outstr.append("Track %d: %s\n" % (level, self.tracks[level]))
outstr = '\n'.join(outstr)
return outstr