Spaces:
No application file
No application file
DrVai-Rag-Testing
/
myenv
/lib
/python3.10
/site-packages
/Bio
/Graphics
/GenomeDiagram
/_Diagram.py
# Copyright 2003-2008 by Leighton Pritchard. All rights reserved. | |
# | |
# This file is part of the Biopython distribution and governed by your | |
# choice of the "Biopython License Agreement" or the "BSD 3-Clause License". | |
# Please see the LICENSE file that should have been included as part of this | |
# package. | |
# | |
# Contact: Leighton Pritchard, The James Hutton Institute, | |
# Invergowrie, Dundee, Scotland, DD2 5DA, UK | |
# [email protected] | |
"""Provides a container for information concerning the tracks to be drawn in a diagram. | |
It also provides 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 objects containing SeqFeature objects. | |
""" | |
try: | |
from reportlab.graphics import renderPM | |
except ImportError: | |
# This is an optional part of ReportLab, so may not be installed. | |
renderPM = None | |
from ._LinearDrawer import LinearDrawer | |
from ._CircularDrawer import CircularDrawer | |
from ._Track import Track | |
from Bio.Graphics import _write | |
def _first_defined(*args): | |
"""Return the first non-null argument (PRIVATE).""" | |
for arg in args: | |
if arg is not None: | |
return arg | |
return None | |
class Diagram: | |
"""Diagram container. | |
Arguments: | |
- name - a string, identifier for the diagram. | |
- tracks - a list of Track objects comprising the diagram. | |
- format - a string, format of the diagram 'circular' or | |
'linear', depending on the sort of diagram required. | |
- pagesize - a string, the pagesize of output describing the ISO | |
size of the image, or a tuple of pixels. | |
- orientation - a string describing the required orientation of the | |
final drawing ('landscape' or 'portrait'). | |
- x - a float (0->1), the proportion of the page to take | |
up with even X margins t the page. | |
- y - a float (0->1), the proportion of the page to take | |
up with even Y margins to the page. | |
- xl - a float (0->1), the proportion of the page to take | |
up with the left X margin to the page (overrides x). | |
- xr - a float (0->1), the proportion of the page to take | |
up with the right X margin to the page (overrides x). | |
- yt - a float (0->1), the proportion of the page to take | |
up with the top Y margin to the page (overrides y). | |
- yb - a float (0->1), the proportion of the page to take | |
up with the bottom Y margin to the page (overrides y). | |
- circle_core - a float, the proportion of the available radius to | |
leave empty at the center of a circular diagram (0 to 1). | |
- start - an integer, the base/aa position to start the diagram at. | |
- end - an integer, the base/aa position to end the diagram at. | |
- tracklines - a boolean, True if track guidelines are to be drawn. | |
- fragments - and integer, for a linear diagram, the number of equal | |
divisions into which the sequence is divided. | |
- fragment_size - a float (0->1), the proportion of the space | |
available to each fragment that should be used in drawing. | |
- track_size - a float (0->1), the proportion of the space | |
available to each track that should be used in drawing with sigils. | |
- circular - a boolean, True if the genome/sequence to be drawn | |
is, in reality, circular. | |
""" | |
def __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=None, | |
track_size=0.75, | |
circular=True, | |
circle_core=0.0, | |
): | |
"""Initialize. | |
gdd = Diagram(name=None) | |
""" | |
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 | |
if fragment_size is not None: | |
self.fragment_size = fragment_size | |
else: | |
if self.fragments == 1: | |
# For single fragments, default to full height | |
self.fragment_size = 1 | |
else: | |
# Otherwise keep a 10% gap between fragments | |
self.fragment_size = 0.9 | |
self.track_size = track_size | |
self.circular = circular | |
self.circle_core = circle_core | |
self.cross_track_links = [] | |
self.drawing = None | |
def set_all_tracks(self, attr, value): | |
"""Set the passed attribute of all tracks in the set to the passed value. | |
Arguments: | |
- attr - An attribute of the Track class. | |
- value - The value to set that attribute. | |
set_all_tracks(self, attr, value) | |
""" | |
for track in self.tracks.values(): | |
if hasattr(track, attr): | |
# If the feature has the attribute set it to the passed value | |
setattr(track, attr, value) | |
def 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, | |
circle_core=None, | |
cross_track_links=None, | |
): | |
"""Draw the diagram, with passed parameters overriding existing attributes. | |
gdd.draw(format='circular') | |
""" | |
# 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, | |
_first_defined(pagesize, self.pagesize), | |
_first_defined(orientation, self.orientation), | |
_first_defined(x, self.x), | |
_first_defined(y, self.y), | |
_first_defined(xl, self.xl), | |
_first_defined(xr, self.xr), | |
_first_defined(yt, self.yt), | |
_first_defined(yb, self.yb), | |
_first_defined(start, self.start), | |
_first_defined(end, self.end), | |
_first_defined(tracklines, self.tracklines), | |
_first_defined(fragments, self.fragments), | |
_first_defined(fragment_size, self.fragment_size), | |
_first_defined(track_size, self.track_size), | |
_first_defined(cross_track_links, self.cross_track_links), | |
) | |
else: | |
drawer = CircularDrawer( | |
self, | |
_first_defined(pagesize, self.pagesize), | |
_first_defined(orientation, self.orientation), | |
_first_defined(x, self.x), | |
_first_defined(y, self.y), | |
_first_defined(xl, self.xl), | |
_first_defined(xr, self.xr), | |
_first_defined(yt, self.yt), | |
_first_defined(yb, self.yb), | |
_first_defined(start, self.start), | |
_first_defined(end, self.end), | |
_first_defined(tracklines, self.tracklines), | |
_first_defined(track_size, self.track_size), | |
_first_defined(circular, self.circular), | |
_first_defined(circle_core, self.circle_core), | |
_first_defined(cross_track_links, self.cross_track_links), | |
) | |
drawer.draw() # Tell the drawer to complete the drawing | |
self.drawing = drawer.drawing # Get the completed drawing | |
def write(self, filename="test1.ps", output="PS", dpi=72): | |
"""Write the drawn diagram to a specified file, in a specified format. | |
Arguments: | |
- filename - a string indicating the name of the output file, | |
or a handle to write to. | |
- output - a 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. | |
- dpi - an integer. Resolution (dots per inch) for bitmap formats. | |
Returns: | |
No return value. | |
write(self, filename='test1.ps', output='PS', dpi=72) | |
""" | |
return _write(self.drawing, filename, output, dpi=dpi) | |
def write_to_string(self, output="PS", dpi=72): | |
"""Return a byte string containing the diagram in the requested format. | |
Arguments: | |
- output - a string indicating output format, one of PS, PDF, | |
SVG, JPG, BMP, GIF, PNG, TIFF or TIFF (as specified for the write | |
method). | |
- dpi - Resolution (dots per inch) for bitmap formats. | |
Returns: | |
Return the completed drawing as a bytes string in a prescribed | |
format. | |
""" | |
# The ReportLab drawToString method, which this function used to call, | |
# originally just used a StringIO handle with the drawToFile method. | |
# | |
# TODO - Rename this method to include keyword bytes? | |
from io import BytesIO | |
handle = BytesIO() | |
self.write(handle, output, dpi) | |
return handle.getvalue() | |
def add_track(self, track, track_level): | |
"""Add a Track object to the diagram. | |
It also accepts instructions to place it at a particular level on the | |
diagram. | |
Arguments: | |
- track - Track object to draw. | |
- track_level - an integer. The level at which the track will be | |
drawn (above an arbitrary baseline). | |
add_track(self, track, track_level) | |
""" | |
if track is None: | |
raise ValueError("Must specify track") | |
if track_level not in self.tracks: # No track at that level | |
self.tracks[track_level] = track # so just add it | |
else: # Already a track there, so shunt all higher tracks up one | |
occupied_levels = sorted( | |
self.get_levels() | |
) # Get list of occupied levels... | |
occupied_levels.reverse() # ...reverse it (highest first) | |
for val in occupied_levels: | |
# If track value >= that to be added | |
if 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 | |
def new_track(self, track_level, **args): | |
"""Add a new Track to the diagram at a given level. | |
The track is returned for further user manipulation. | |
Arguments: | |
- track_level - an integer. The level at which the track will be | |
drawn (above an arbitrary baseline). | |
new_track(self, track_level) | |
""" | |
newtrack = Track() | |
for key in args: | |
setattr(newtrack, key, args[key]) | |
if track_level not in self.tracks: # No track at that level | |
self.tracks[track_level] = newtrack # so just add it | |
else: # Already a track there, so shunt all higher tracks up one | |
occupied_levels = sorted( | |
self.get_levels() | |
) # Get list of occupied levels... | |
occupied_levels.reverse() # ...reverse (highest first)... | |
for val in occupied_levels: | |
if val >= track_level: | |
# Track value >= that to be added, increment by 1 | |
self.tracks[val + 1] = self.tracks[val] | |
self.tracks[track_level] = newtrack # And put the new track in | |
self.tracks[track_level].track_level = track_level | |
return newtrack | |
def del_track(self, track_level): | |
"""Remove the track to be drawn at a particular level on the diagram. | |
Arguments: | |
- track_level - an integer. The level of the track on the diagram | |
to delete. | |
del_track(self, track_level) | |
""" | |
del self.tracks[track_level] | |
def get_tracks(self): | |
"""Return a list of the tracks contained in the diagram.""" | |
return list(self.tracks.values()) | |
def move_track(self, from_level, to_level): | |
"""Move a track from one level on the diagram to another. | |
Arguments: | |
- from_level - an integer. The level at which the track to be | |
moved is found. | |
- to_level - an integer. The level to move the track to. | |
""" | |
aux = self.tracks[from_level] | |
del self.tracks[from_level] | |
self.add_track(aux, to_level) | |
def renumber_tracks(self, low=1, step=1): | |
"""Renumber all tracks consecutively. | |
Optionally from a passed lowest number. | |
Arguments: | |
- low - an integer. The track number to start from. | |
- step - an integer. The track interval for separation of | |
tracks. | |
""" | |
track = low # Start numbering from here | |
levels = self.get_levels() | |
conversion = {} # Holds new set of levels | |
for 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 set | |
def get_levels(self): | |
"""Return a sorted list of levels occupied by tracks in the diagram.""" | |
return sorted(self.tracks) | |
def get_drawn_levels(self): | |
"""Return a sorted list of levels occupied by tracks. | |
These tracks are not explicitly hidden. | |
""" | |
return sorted(key for key in self.tracks if not self.tracks[key].hide) | |
def range(self): | |
"""Return lowest and highest base numbers from track features. | |
Returned type is 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 tracks | |
def __getitem__(self, key): | |
"""Return the track contained at the level of the passed key.""" | |
return self.tracks[key] | |
def __str__(self): | |
"""Return a formatted string describing the diagram.""" | |
outstr = [f"\n<{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 | |