1# Copyright 2003-2008 by Leighton Pritchard.  All rights reserved.
2# Revisions copyright 2008-2010 by Peter Cock.
3#
4# This file is part of the Biopython distribution and governed by your
5# choice of the "Biopython License Agreement" or the "BSD 3-Clause License".
6# Please see the LICENSE file that should have been included as part of this
7# package.
8#
9# Contact:       Leighton Pritchard, The James Hutton Institute,
10#                Invergowrie, Dundee, Scotland, DD2 5DA, UK
11#                Leighton.Pritchard@hutton.ac.uk
12################################################################################
13#
14# TODO: Make representation of Ymax and Ymin values at this level, so that
15#       calculation of graph/axis drawing is simplified
16
17"""GraphSet module.
18
19Provides:
20 - GraphSet - container for GraphData objects
21
22For drawing capabilities, this module uses reportlab to draw and write
23the diagram: http://www.reportlab.com
24"""
25
26# ReportLab imports
27
28from reportlab.lib import colors
29
30from ._Graph import GraphData
31
32
33class GraphSet:
34    """Graph Set.
35
36    Attributes:
37     - id        Unique identifier for the set
38     - name      String describing the set
39
40    """
41
42    def __init__(self, name=None):
43        """Initialize.
44
45        Arguments:
46         - name      String identifying the graph set sensibly
47
48        """
49        self.id = id  # Unique identifier for the set
50        self._next_id = 0  # Holds unique ids for graphs
51        self._graphs = {}  # Holds graphs, keyed by unique id
52        self.name = name  # Holds description of graph
53
54    def new_graph(
55        self,
56        data,
57        name=None,
58        style="bar",
59        color=colors.lightgreen,
60        altcolor=colors.darkseagreen,
61        linewidth=1,
62        center=None,
63        colour=None,
64        altcolour=None,
65        centre=None,
66    ):
67        """Add a GraphData object to the diagram.
68
69        Arguments:
70         - data      List of (position, value) int tuples
71         - name      String, description of the graph
72         - style     String ('bar', 'heat', 'line') describing how the graph
73           will be drawn
74         - color    colors.Color describing the color to draw all or 'high'
75           (some styles) data (overridden by backwards compatible
76           argument with UK spelling, colour).
77         - altcolor  colors.Color describing the color to draw 'low' (some
78           styles) data (overridden by backwards compatible argument
79           with UK spelling, colour).
80         - linewidth     Float describing linewidth for graph
81         - center        Float setting the value at which the x-axis
82           crosses the y-axis (overridden by backwards
83           compatible argument with UK spelling, centre)
84
85        Add a GraphData object to the diagram (will be stored internally).
86        """
87        # Let the UK spelling (colour) override the USA spelling (color)
88        if colour is not None:
89            color = colour
90        if altcolour is not None:
91            altcolor = altcolour
92        if centre is not None:
93            center = centre
94
95        id = self._next_id  # get id number
96        graph = GraphData(id, data, name, style, color, altcolor, center)
97        graph.linewidth = linewidth
98        self._graphs[id] = graph  # add graph data
99        self._next_id += 1  # increment next id
100        return graph
101
102    def del_graph(self, graph_id):
103        """Remove a graph from the set, indicated by its id."""
104        del self._graphs[graph_id]
105
106    def get_graphs(self):
107        """Return list of all graphs in the graph set, sorted by id.
108
109        Sorting is to ensure reliable stacking.
110        """
111        return [self._graphs[id] for id in sorted(self._graphs)]
112
113    def get_ids(self):
114        """Return a list of all ids for the graph set."""
115        return list(self._graphs.keys())
116
117    def range(self):
118        """Return the lowest and highest base (or mark) numbers as a tuple."""
119        lows, highs = [], []
120        for graph in self._graphs.values():
121            low, high = graph.range()
122            lows.append(low)
123            highs.append(high)
124        return (min(lows), max(highs))
125
126    def data_quartiles(self):
127        """Return (minimum, lowerQ, medianQ, upperQ, maximum) values as a tuple."""
128        data = []
129        for graph in self._graphs.values():
130            data += list(graph.data.values())
131        data.sort()
132        datalen = len(data)
133        return (
134            data[0],
135            data[datalen / 4],
136            data[datalen / 2],
137            data[3 * datalen / 4],
138            data[-1],
139        )
140
141    def to_string(self, verbose=0):
142        """Return a formatted string with information about the set.
143
144        Arguments:
145            - verbose - Flag indicating whether a short or complete account
146              of the set is required
147
148        """
149        if not verbose:
150            return "%s" % self
151        else:
152            outstr = ["\n<%s: %s>" % (self.__class__, self.name)]
153            outstr.append("%d graphs" % len(self._graphs))
154            for key in self._graphs:
155                outstr.append("%s" % self._graphs[key])
156            return "\n".join(outstr)
157
158    def __len__(self):
159        """Return the number of graphs in the set."""
160        return len(self._graphs)
161
162    def __getitem__(self, key):
163        """Return a graph, keyed by id."""
164        return self._graphs[key]
165
166    def __str__(self):
167        """Return a formatted string with information about the feature set."""
168        outstr = ["\n<%s: %s>" % (self.__class__, self.name)]
169        outstr.append("%d graphs" % len(self._graphs))
170        outstr = "\n".join(outstr)
171        return outstr
172