示例#1
0
 def test_empty_drawing(self):
     dwg = Drawing(profile="tiny")
     result = dwg.tostring()
     self.assertEqual(result, '<svg baseProfile="tiny" height="100%" version="1.2" '\
         'width="100%" xmlns="http://www.w3.org/2000/svg" '\
         'xmlns:ev="http://www.w3.org/2001/xml-events" '\
         'xmlns:xlink="http://www.w3.org/1999/xlink"><defs /></svg>')
示例#2
0
 def test_save_as(self):
     fn = 'test_drawing.svg'
     if os.path.exists(fn):
         os.remove(fn)
     dwg = Drawing()
     dwg.saveas(fn)
     self.assertTrue(os.path.exists(fn))
     os.remove(fn)
示例#3
0
 def test_simple_defs(self):
     dwg = Drawing()
     g = dwg.defs.add(Group(id='test'))
     inner_g = g.add(Group(id='innerTest'))
     result = dwg.tostring()
     self.assertEqual(result, '<svg baseProfile="full" height="100%" version="1.1" '\
         'width="100%" xmlns="http://www.w3.org/2000/svg" '\
         'xmlns:ev="http://www.w3.org/2001/xml-events" '\
         'xmlns:xlink="http://www.w3.org/1999/xlink">' \
         '<defs><g id="test"><g id="innerTest" /></g></defs></svg>')
示例#4
0
 def test_stylesheet(self):
     dwg = Drawing()
     dwg.add_stylesheet('test.css', 'Test')
     f = StringIO()
     dwg.write(f)
     result = f.getvalue()
     f.close()
     self.assertEqual(result, '<?xml version="1.0" encoding="utf-8" ?>\n' \
         '<?xml-stylesheet href="test.css" type="text/css" title="Test" alternate="no" media="screen"?>\n'
         '<svg baseProfile="full" height="100%" version="1.1" width="100%" '\
         'xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" '\
         'xmlns:xlink="http://www.w3.org/1999/xlink"><defs /></svg>')
示例#5
0
 def test_non_us_ascii_chars(self):
     dwg = Drawing()
     dwg.set_desc('öäü')
     f = StringIO()
     dwg.write(f)
     result = f.getvalue()
     f.close()
     self.assertEqual(result,
         '<?xml version="1.0" encoding="utf-8" ?>\n' \
         '<svg baseProfile="full" height="100%" version="1.1" width="100%" '\
         'xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" '\
         'xmlns:xlink="http://www.w3.org/1999/xlink">'
         '<title>öäü</title><defs /></svg>')
示例#6
0
def new_drawing(filename='plot.svg', display_scale=1):
	from svgwrite.drawing   import Drawing
	from svgwrite.container import Group
	size = 2 * PADDING + SCALE
	dwg = Drawing(filename, size=(size * display_scale, size * display_scale))
	dwg.viewbox(minx=0, miny=0, width=size, height=size)
	
	add_stylesheet(dwg)
	
	canvas = Group(id='canvas')
	canvas.translate(PADDING, PADDING)
	canvas.scale(1, -1)
	canvas.translate(0, -SCALE)
	dwg.add(canvas)
	
	return dwg, canvas
    def __init__(self, filename, x_max, y_max, **kwargs):
        '''
        Initialize this object - you need to pass it a mongo object for it to 
        operate on.
        '''
        self.filename = filename
        self.x_max = x_max
        self.y_max = y_max
        if 'x_min' in kwargs:
            self.x_min = kwargs['x_min']
        else:
            self.x_min = 0
        if 'y_min' in kwargs:
            self.y_min = kwargs['y_min']
        else:
            self.y_min = 0
        if 'height' in kwargs:
            self.height = kwargs['height']
        else:
            self.height = 600
        if 'height' in kwargs:
            self.height = kwargs['height']
        else:
            self.height = 1200
        if 'debug' in kwargs:
            self.debug = kwargs['debug']
        else:
            self.debug = True
        if 'margin_top' in kwargs:
            self.margin_top = kwargs['margin_top']
        else:
            self.margin_top = 20
        if 'margin_bottom' in kwargs:
            self.margin_bottom = kwargs['margin_bottom']
        else:
            self.margin_bottom = 20
        if 'margin_left' in kwargs:
            self.margin_left = kwargs['margin_left']
        else:
            self.margin_left = 20
        if 'margin_right' in kwargs:
            self.margin_right = kwargs['margin_right']
        else:
            self.margin_right = 20
        self.max_x = 100
        self.max_y_value = 100
        self.data = None

        # start drawing object
        self.plot = Drawing(self.filename, debug = self.debug,
                            size = (self.height + self.margin_left + self.margin_right, self.height + self.margin_top + self.margin_bottom),
                            # viewBox = ("0 0 " + str(float(length) + 10) + " " + str(float(width) + 10)),
                            preserveAspectRatio = "xMinYMin meet")    # , size=('200mm', '150mm'), viewBox=('0 0 200 150'))
        self.plot.add(Line(start = (self.margin_left - 4, self.margin_top), end = (self.margin_left - 4, self.margin_top + self.height), stroke_width = 0.5, stroke = "black"))
        self.plot.add(Line(start = (self.margin_left, self.margin_top + self.height + 4), end = (self.margin_left + self.height, self.margin_top + self.height + 4), stroke_width = 0.5, stroke = "black"))
 def test_stylesheet(self):
     dwg = Drawing(profile="tiny")
     dwg.add_stylesheet('test.css', 'Test')
     f = StringIO()
     dwg.write(f)
     result = f.getvalue()
     f.close()
     self.assertEqual(
         result, '<?xml version="1.0" encoding="utf-8" ?>\n'
         '<?xml-stylesheet href="test.css" type="text/css" title="Test" alternate="no" media="screen"?>\n'
         '<svg baseProfile="tiny" height="100%" version="1.2" width="100%" '
         'xmlns="http://www.w3.org/2000/svg" '
         'xmlns:ev="http://www.w3.org/2001/xml-events" '
         'xmlns:xlink="http://www.w3.org/1999/xlink"><defs /></svg>')
示例#9
0
    def set_properties(self, filename, title, start, end, width, height):
        '''Set the properties of the canvas on which you'll want to generate your image '''
        self.elements = []
        self.title = title
        self.start = start
        self.end = end
        self.width = width    # default = 200.0

        self.height = height    # default = 60.0

        self.dimension_y = self.height - self.MARGIN - self.BOTTOM_MARGIN    # this is the size of the y field
        self.dimension_x = self.width - self.MARGIN - self.RIGHT_MARGIN
        self.scale_x = float(self.dimension_x) / (self.end - self.start)    # this is a scaling variable
        self.y_bottom = str(round(self.dimension_y + self.MARGIN, 2))


        canvas_size = (str(self.width) + "px" , "100%")    # create drawing # Default is 100%,100% - don't override that, as this allows the svg to fit the data and expand as necessary
        self.plot = Drawing(filename , size = canvas_size)
        background = Rect(insert = (0, 0), size = canvas_size, fill = "white")
        self.plot.add(background)
示例#10
0
    def draw_arrow_w_text_middle(self, scene: Drawing, start: tuple,
                                 point1: tuple, point2: tuple, end: tuple,
                                 height: int, arrowsize: int, is_curved: bool,
                                 text: str, font_size: int, font_family: str,
                                 over: bool, color: tuple):
        # Store the appropriate function ouside of the loop
        if is_curved:
            self._create_curve_arrow(scene, start, point1, point2, end, color)
        else:
            self._create_rect_arrow(scene, start, point1, point2, end, color)

        # Draw arrow
        x_coord = (end[0] - arrowsize, end[1] - arrowsize)
        z_coord = (end[0] + arrowsize, end[1] - arrowsize)
        y_coord = (end[0], end[1])

        # Draw the arrow head
        scene.add(
            Line(x_coord,
                 y_coord,
                 shape_rendering='inherit',
                 stroke=rgb(*color),
                 stroke_width=1))
        scene.add(
            Line(z_coord,
                 y_coord,
                 shape_rendering='inherit',
                 stroke=rgb(*color),
                 stroke_width=1))

        direction = 1
        if over:
            direction = -1

        # Write label in the middle under
        labelx = min(start[0], point2[0]) + abs(start[0] - point2[0]) // 2
        labely = height + direction * font_size  # TODO: Should be font height!

        scene.add(
            Text(text,
                 insert=(labelx, labely),
                 fill=rgb(*color),
                 font_family=font_family,
                 font_size=font_size,
                 text_rendering='inherit',
                 alignment_baseline='central',
                 text_anchor='middle')
        )  # TODO: alignment_baseline should be hanging or baseline!
示例#11
0
class ScatterPlot(object):
    '''A simple scatter diagram plot.'''


    def __init__(self, filename, x_max, y_max, **kwargs):
        '''
        Initialize this object - you need to pass it a mongo object for it to 
        operate on.
        '''
        self.filename = filename
        self.x_max = x_max
        self.y_max = y_max
        if 'x_min' in kwargs:
            self.x_min = kwargs['x_min']
        else:
            self.x_min = 0
        if 'y_min' in kwargs:
            self.y_min = kwargs['y_min']
        else:
            self.y_min = 0
        if 'height' in kwargs:
            self.height = kwargs['height']
        else:
            self.height = 600
        if 'height' in kwargs:
            self.height = kwargs['height']
        else:
            self.height = 1200
        if 'debug' in kwargs:
            self.debug = kwargs['debug']
        else:
            self.debug = True
        if 'margin_top' in kwargs:
            self.margin_top = kwargs['margin_top']
        else:
            self.margin_top = 20
        if 'margin_bottom' in kwargs:
            self.margin_bottom = kwargs['margin_bottom']
        else:
            self.margin_bottom = 20
        if 'margin_left' in kwargs:
            self.margin_left = kwargs['margin_left']
        else:
            self.margin_left = 20
        if 'margin_right' in kwargs:
            self.margin_right = kwargs['margin_right']
        else:
            self.margin_right = 20
        self.max_x = 100
        self.max_y_value = 100
        self.data = None

        # start drawing object
        self.plot = Drawing(self.filename, debug = self.debug,
                            size = (self.height + self.margin_left + self.margin_right, self.height + self.margin_top + self.margin_bottom),
                            # viewBox = ("0 0 " + str(float(length) + 10) + " " + str(float(width) + 10)),
                            preserveAspectRatio = "xMinYMin meet")    # , size=('200mm', '150mm'), viewBox=('0 0 200 150'))
        self.plot.add(Line(start = (self.margin_left - 4, self.margin_top), end = (self.margin_left - 4, self.margin_top + self.height), stroke_width = 0.5, stroke = "black"))
        self.plot.add(Line(start = (self.margin_left, self.margin_top + self.height + 4), end = (self.margin_left + self.height, self.margin_top + self.height + 4), stroke_width = 0.5, stroke = "black"))

    def add_and_zip_data(self, x, y):
        '''adds the data to the scatter plot, using zip to asseble the x and y's.'''
        self.data = zip(x, y)

    def add_data(self, x):
        '''adds the data to the scatter plot - no zipping applied.'''
        self.data = x

    def add_regression(self, slope):
        '''place a regression line on the plot.'''
        self.max_min()
        x = self.max_x
        y = slope * x
        print "x %f y %f" % (x, y)

        if y > self.max_y_value:
            y = self.max_y_value
            x = y / slope
            print "x %f y %f" % (x, y)


        self.plot.add(Line(start = (self.margin_left, self.margin_top + self.height),
                           end = (self.x_to_printx(x), self.y_to_printy(y)),
                                  stroke_width = 1, stroke = "black"))

    def x_to_printx(self, x):
        '''transforms the x value to an x coordinate'''
        return self.margin_left + ((float(x) / self.max_x) * self.height)

    def y_to_printy(self, y):
        '''transforms the y value to a y coordinate'''
        return (self.margin_top + self.height) - ((float(y) / self.max_y_value) * self.height)


    def max_min(self):
        '''Find Max values for x and y dimensions'''
        self.max_x = self.data[0][0]
        self.max_y_value = self.data[0][1]
        for x, y in self.data:
            if x > self.max_x:
                self.max_x = x
            if y > self.max_y_value:
                self.max_y_value = y
        print "max x y : %f %f" % (self.max_x, self.max_y_value)


    def build(self):
        '''assembles the data in the scatterplot, adding the points as circles.'''
        self.max_min()
        for x, y in self.data:
            self.plot.add(Circle(center = (self.margin_left + ((x / self.max_x) * self.height),
                                          (self.margin_top + self.height) - ((y / self.max_y_value) * self.height)),
                                 r = 2, stroke_width = 0.1, stroke_linecap = 'round',
                                 stroke_opacity = 0.3, fill = "dodgerblue",
                                 fill_opacity = 0.2))
        self.plot.add(Text(self.max_x, insert = (self.margin_left + self.height - 50, self.margin_top + self.height + 20.0),
                fill = "midnightblue", font_size = "15"))
        self.data = None


    def save(self):
        '''save the plot and reset.'''
        self.plot.save()
        self.plot = None

    def to_string(self):
        '''convert the plot to string, and reset.'''
        z = self.plot.tostring()
        self.plot = None
        return z
示例#12
0
class Histogram(object):
    '''
    classdocs
    '''
    def __init__(self, filename, bins, **kwargs):
        '''
        Initialize this object - you need to pass it a mongo object for it to 
        operate on.
        '''
        self.filename = filename
        self.bins = bins
        self.data_max = 0
        self.data_min = 0
        self.x_min = 0

        if 'x_max' in kwargs:
            self.x_max = kwargs['x_max']
        else:
            self.x_max = 0
        if 'x_min' in kwargs:
            self.x_min = kwargs['x_min']
        else:
            self.x_min = 0
        if 'gap' in kwargs:
            self.gap = kwargs['gap']
        else:
            self.gap = 5
        if 'height' in kwargs:
            self.height = kwargs['height']
        else:
            self.height = 600
        if 'height' in kwargs:
            self.height = kwargs['height']
        else:
            self.height = 1200
        if 'debug' in kwargs:
            self.debug = kwargs['debug']
        else:
            self.debug = True
        if 'margin_top' in kwargs:
            self.margin_top = kwargs['margin_top']
        else:
            self.margin_top = 20
        if 'margin_bottom' in kwargs:
            self.margin_bottom = kwargs['margin_bottom']
        else:
            self.margin_bottom = 20
        if 'margin_left' in kwargs:
            self.margin_left = kwargs['margin_left']
        else:
            self.margin_left = 20
        if 'margin_right' in kwargs:
            self.margin_right = kwargs['margin_right']
        else:
            self.margin_right = 20
        self.data = None
        self.binned_data = None

        # start drawing object
        self.plot = Drawing(self.filename, debug = self.debug,
                            size = (self.height + self.margin_left + self.margin_right,
                                    self.height + self.margin_top + self.margin_bottom),
                            # viewBox = ("0 0 " + str(float(length) + 10) + " " + str(float(width) + 10)),
                            preserveAspectRatio = "xMinYMin meet")    # , size=('200mm', '150mm'), viewBox=('0 0 200 150'))
        self.plot.add(Line(start = (self.margin_left - 4, self.margin_top), \
                           end = (self.margin_left - 4, self.margin_top + self.height),
                           stroke_width = 0.5, stroke = "black"))
        self.plot.add(Line(start = (self.margin_left, self.margin_top + self.height + 4),
                           end = (self.margin_left + self.height, self.margin_top + self.height + 4),
                           stroke_width = 0.5, stroke = "black"))

    def add_data(self, x):
        self.data = x

    def bin_data(self):
        self.binned_data = {}
        for i in range(self.bins + 1):
            self.binned_data[i] = 0
        if self.x_max == 0 and self.x_min == 0 :
            self.x_min = self.data[0]
            for x in self.data:
                if x > self.x_max:
                    self.x_max = x
                if x < self.x_min:
                    self.x_min = x
        # print "self.x_max = %f" % (self.x_max)
        bin_size = (float(self.x_max) - self.x_min) / self.bins
        print "bin_size: %f" % (bin_size)
        for x in self.data:
            # print "x: %i - bin_size: %f" % (x, bin_size)
            # print "x//bin_size = %f int() = %i" % (x // bin_size, int(x // bin_size))
            k = 0
            if x > self.x_max:
                k = self.bins + 1
            else:
                k = int(x // bin_size)
            if k > self.bins:
                k = self.bins
            self.binned_data[k] += 1    # floored division.
            if self.binned_data[k] > self.data_max:
                self.data_max = self.binned_data[k]
        for i in range(self.bins + 1):
            print "%i %i" % (i, self.binned_data[i])

    def x_to_printx(self, x):
        return self.margin_left + ((float(x) / self.x_max) * self.height)


    def build(self):
        bin_width = (self.height - ((self.bins + 1) * self.gap)) // self.bins    # floored division
        for i in range(self.bins):
            self.plot.add(Rect(insert = (self.margin_left + self.gap + (i * (bin_width + self.gap)),
                                       (self.margin_top + self.height) - ((float(self.binned_data[i]) / self.data_max) * self.height)),
                               size = (bin_width, ((float(self.binned_data[i]) / self.data_max) * self.height)),
                               fill = "red"))
            self.plot.add(Text(i , insert = (self.margin_left + self.gap + (i * (bin_width + self.gap)), self.height + self.margin_top + 20), fill = "midnightblue", font_size = "15"))
        self.data = None


    def save(self):
        self.plot.save()
        self.plot = None

    def to_string(self):
        z = self.plot.tostring()
        self.plot = None
        return z
示例#13
0
def get_4cat_canvas(path,
                    width,
                    height,
                    header=None,
                    footer="made with 4CAT",
                    fontsize_normal=None,
                    fontsize_small=None,
                    fontsize_large=None):
    """
	Get a standard SVG canvas to draw 4CAT graphs to

	Adds a border, footer, header, and some basic text styling

	:param path:  The path where the SVG graph will be saved
	:param width:  Width of the canvas
	:param height:  Height of the canvas
	:param header:  Header, if necessary to draw
	:param footer:  Footer text, if necessary to draw. Defaults to shameless
	4CAT advertisement.
	:param fontsize_normal:  Font size of normal text
	:param fontsize_small:  Font size of small text (e.g. footer)
	:param fontsize_large:  Font size of large text (e.g. header)
	:return SVG:  SVG canvas (via svgwrite) that can be drawn to
	"""
    from svgwrite.container import SVG
    from svgwrite.drawing import Drawing
    from svgwrite.shapes import Rect
    from svgwrite.text import Text

    if fontsize_normal is None:
        fontsize_normal = width / 75

    if fontsize_small is None:
        fontsize_small = width / 100

    if fontsize_large is None:
        fontsize_large = width / 50

    # instantiate with border and white background
    canvas = Drawing(str(path),
                     size=(width, height),
                     style="font-family:monospace;font-size:%ipx" %
                     fontsize_normal)
    canvas.add(
        Rect(insert=(0, 0),
             size=(width, height),
             stroke="#000",
             stroke_width=2,
             fill="#FFF"))

    # header
    if header:
        header_shape = SVG(insert=(0, 0), size=("100%", fontsize_large * 2))
        header_shape.add(
            Rect(insert=(0, 0), size=("100%", "100%"), fill="#000"))
        header_shape.add(
            Text(insert=("50%", "50%"),
                 text=header,
                 dominant_baseline="middle",
                 text_anchor="middle",
                 fill="#FFF",
                 style="font-size:%ipx" % fontsize_large))
        canvas.add(header_shape)

    # footer (i.e. 4cat banner)
    if footer:
        footersize = (fontsize_small * len(footer) * 0.7, fontsize_small * 2)
        footer_shape = SVG(insert=(width - footersize[0],
                                   height - footersize[1]),
                           size=footersize)
        footer_shape.add(
            Rect(insert=(0, 0), size=("100%", "100%"), fill="#000"))
        footer_shape.add(
            Text(insert=("50%", "50%"),
                 text=footer,
                 dominant_baseline="middle",
                 text_anchor="middle",
                 fill="#FFF",
                 style="font-size:%ipx" % fontsize_small))
        canvas.add(footer_shape)

    return canvas
def _blank_drawing(width: int, height: int) -> Drawing:
    base = Drawing(viewBox=f'0 0 {int(width)} {int(height)}', size=(None, None), debug=False, profile='tiny')
    return base
示例#15
0
    def __init__(self,
                 width=600,
                 height=400,
                 margin_top=20,
                 margin_bottom=40,
                 margin_left=40,
                 margin_right=20,
                 x_min=0,
                 y_min=None,
                 x_max=1,
                 y_max=None,
                 debug=False,
                 graph_colour="midnightblue",
                 background=None,
                 x_label=None,
                 y_label=None,
                 y_label_max_min=True,
                 title=None):

        self.width = width
        self.height = height
        self.debug = debug
        self.margin_top = margin_top
        self.margin_bottom = margin_bottom
        self.margin_left = margin_left
        self.margin_right = margin_right
        self.x_max = x_max
        self.y_max = y_max
        self.x_min = x_min
        self.y_min = y_min
        self.graph_colour = graph_colour
        self.x_label = x_label
        self.y_label = y_label
        self.y_label_max_min = y_label_max_min
        self.title = title
        self.font_size = 15

        self.plottable_x = self.width - (self.margin_left + self.margin_right)
        self.plottable_y = self.height - (self.margin_top + self.margin_bottom)

        # start drawing object
        self.plot = Drawing(
            debug=self.debug,
            size=(self.width, self.height),
            # viewBox = ("0 0 " + str(float(length) + 10) + " " + str(float(width) + 10)),
            preserveAspectRatio="xMinYMin meet"
        )  # , size=('200mm', '150mm'), viewBox=('0 0 200 150'))

        if background:
            self.plot.add(
                Rect(insert=(0, 0),
                     size=(self.width, self.height),
                     fill=background))

        if x_label:
            self.plottable_y -= self.font_size
            self.add_x_label()

        if y_label:
            self.plottable_x -= self.font_size
            self.margin_left += self.font_size
            self.add_y_label()

        if title:
            self.plottable_y -= self.font_size + 5
            self.margin_top += self.font_size + 5
            self.add_title()

        self.plot.add(
            Line(start=(self.margin_left - 4, self.margin_top),
                 end=(self.margin_left - 4,
                      self.margin_top + self.plottable_y),
                 stroke_width=1,
                 stroke=self.graph_colour))
        self.plot.add(
            Line(start=(self.margin_left,
                        self.margin_top + self.plottable_y + 4),
                 end=(self.margin_left + self.plottable_x,
                      self.margin_top + self.plottable_y + 4),
                 stroke_width=1,
                 stroke=self.graph_colour))

        if self.y_label_max_min and self.y_max and self.y_min:
            self.add_y_max_min(self.y_max, self.y_min)
示例#16
0
    def make_svg(self, data):
        layers = data["layers"]
        _PROPORTION = 10
        _SHIFT_PROP = _PROPORTION / 1.5
        for f in self.forms:
            structures = [None] * len(f.id.split("_"))
            linkers    = []
            centers    = {}
            order      = {}
            maxW, maxH = 0, 0
            for cx, x in enumerate(f.id.split("_")):
                order[x] = cx
            for cl, l in enumerate(layers):
                for cs, s in enumerate(l):
                    name = f.id + "__" + s["id"]
                    if s["type"] == "H":
                        color  = "cornflowerblue" if "ref" not in s else "gainsboro"
                        shape = Circle( center = (s["shift_x"] * _SHIFT_PROP, s["shift_z"] * _SHIFT_PROP),
                                        r = 2.3 * _PROPORTION, id = name,
                                        fill = color, stroke="black",
                                        stroke_width = "2")
                    elif s["type"] == "E":
                        color  = "indianred" if "ref" not in s else "salmon"
                        rotate = f.get_ss_by_id(s["id"]).struc.goes_down()
                        shape = Triangle(center = (s["shift_x"] * _SHIFT_PROP, s["shift_z"] * _SHIFT_PROP),
                                         rc = 2.3 * _PROPORTION, id = name, fill=color,
                                         stroke = "black", stroke_width = "2",
                                         rotate = rotate)
                    else:
                        color  = "darkgreen" if "ref" not in s else "lightgreen"
                        shape = Cross(center = (s["shift_x"] * _SHIFT_PROP, s["shift_z"] * _SHIFT_PROP), r = 2.3 * _PROPORTION,
                                      fill=color, stroke="black", stroke_width = "2",
                                      id = name)
                    structures[order[s["id"]]] = shape
                    centers[s["id"]] = [s["shift_x"] * _SHIFT_PROP, s["shift_z"] * _SHIFT_PROP]
                    if s["shift_x"] * _SHIFT_PROP > maxW: maxW = s["shift_x"] * _SHIFT_PROP
                    if s["shift_z"] * _SHIFT_PROP > maxH: maxH = s["shift_z"] * _SHIFT_PROP

            for cx, x in enumerate(f.id.split("_")):
                if cx == 0 or cx == len(f.id.split("_")) - 1:
                    init = [0, 0]
                    if (ord(x[0]) - 64) <= len(layers) / 2:
                        init[1] = centers[x][1] - (2.3 * _PROPORTION * 2)
                    else:
                        init[1] = centers[x][1] + (2.3 * _PROPORTION * 2)
                    if int(re.search("(\d+)", x).group(1)) <= len(layers[ord(x[0]) - 65]) / 2:
                        init[0] = centers[x][0] - (2.3 * _PROPORTION * 2)
                    else:
                        init[0] = centers[x][0] + (2.3 * _PROPORTION * 2)
                    if cx == 0:
                        shape = Line(init, centers[x], stroke="darkblue", stroke_width = "4")
                    elif cx == len(f.id.split("_")) - 1:
                        shape = Line(centers[x], init, stroke="darkred", stroke_width = "4")
                    linkers.append(shape)
                for cy, y in enumerate(f.id.split("_")):
                    if cy == cx + 1:
                        shape = Line(centers[x], centers[y], stroke="black", stroke_width = "4")
                        linkers.append(shape)

            # Intercalate
            toplink = []
            dowlink = []
            if f.sslist[0].struc.goes_up():
                dowlink = linkers[0:][::2]
                toplink = linkers[1:][::2]
            else:
                dowlink = linkers[1:][::2]
                toplink = linkers[0:][::2]

            g = Group()
            for x in dowlink:
                g.add(x)
            for x in structures:
                g.add(x)
            for x in toplink:
                g.add(x)
            g.translate(2.3 * _PROPORTION * 3, 2.3 * _PROPORTION * 3)
            d = Drawing(size = (maxW + ((2.3 * _PROPORTION * 3) * 2),
                                maxH + ((2.3 * _PROPORTION * 3) * 2)),
                        id = f.id + "__image")
            d.add(g)
            for x in data["forms"]:
                if x["id"] == f.id:
                    x["svg"] = d.tostring()
 def _func(drawing: Drawing) -> None:
     drawing.add(drawing.rect(insert=(x, y), size=(width, height), fill='white'))
def _draw_elements(drawing: Drawing, drawables: List[Drawable]) -> Drawing:
    drawing = drawing.copy()
    for drawable in drawables:
        drawable(drawing)
    return drawing
 def _func(drawing: Drawing) -> None:
     drawing.add(drawing.text(text, (x, y), font_size=size))
 def _func(drawing: Drawing) -> None:
     drawing.add(drawing.line((x, y), (x_, y_), stroke='black'))
class Plot(object):
    """
    Called byrapper object to plot methylation data.
    """

    METHYLATION_DOT_RADIUS = 2
    METHYLATION_DISTR_HT_MED = 12.0
    METHYLATION_DISTR_HT_BIG = 24.0
    DISTR_SHIFT = 0
    DISTR_STROKE = 0.75
    BOTTOM_MARGIN = 120  # 120 pixels
    RIGHT_MARGIN = 30
    MARGIN = 30
    GENE_OFFSET = 20

    def __init__(self):
        """Simple initiation of all of the self parameters... Does not do anything unexpected."""
        self.elements = []
        self.title = None
        self.sample_grouping = {}  # store sample id/sample group.
        self.gausian_colour = {}
        self.last_hash = 0
        self.start = 0
        self.end = 0
        self.width = 200  # default = 200.0

        self.height = 60  # default = 60.0
        self.message = None

        self.dimension_y = 0  # this is the size of the y field
        self.dimension_x = 0
        self.scale_x = 0
        self.y_bottom = "0"

        self.scale_y = 0
        self.maxh = 0
        self.plot = None

        self.gene_offset = self.GENE_OFFSET

    def set_properties(self, filename, title, start, end, width, height):
        """Set the properties of the canvas on which you'll want to generate your image """
        self.elements = []
        self.title = title
        self.start = start
        self.end = end
        self.width = width  # default = 200.0

        self.height = height  # default = 60.0

        self.dimension_y = self.height - self.MARGIN - self.BOTTOM_MARGIN  # this is the size of the y field
        self.dimension_x = self.width - self.MARGIN - self.RIGHT_MARGIN
        self.scale_x = float(self.dimension_x) / (
            self.end - self.start)  # this is a scaling variable
        self.y_bottom = str(round(self.dimension_y + self.MARGIN, 2))

        canvas_size = (str(self.width) + "px", "100%")
        # create drawing # Default is 100%,100% - don't override that,
        # as this allows the svg to fit the data and expand as necessary
        self.plot = Drawing(filename, size=canvas_size)
        background = Rect(insert=(0, 0), size=canvas_size, fill="white")
        self.plot.add(background)

    def convert_xcoord_to_pos(self, xcoord):
        return round(float(xcoord - self.start) * self.scale_x,
                     2) + self.MARGIN

    def make_gausian(self,
                     pos,
                     height,
                     stddev,
                     horizontal=True,
                     y_median=0,
                     sigmas=3):
        """ path points will be at (-3stddev,0), (0,height), (3stddev,0)
            Control points at (-1stddev,0), (-1stddev,height), (1stddev,height), (1stddev,0)
         """
        x_values = [
            -sigmas * stddev, -1 * stddev, -1 * stddev, 0, stddev, stddev,
            sigmas * stddev
        ]
        y_values = [0, 0, height, height, height, 0, 0]

        if horizontal is True:
            x_values = [
                round((x - self.start + pos) * self.scale_x, 2) + self.MARGIN
                for x in x_values
            ]
            # Scale Y and inverse the coordinates
            y_values = [round(y1 * self.scale_y, 2) for y1 in y_values]
            y_values = [(self.height - self.BOTTOM_MARGIN - y2)
                        for y2 in y_values]
        else:
            switch = [
                round(((pos - self.start) * self.scale_x) + y + self.MARGIN +
                      self.DISTR_SHIFT, 2) for y in y_values
            ]
            y_values = [
                round(((x + y_median) * self.scale_y), 2) for x in x_values
            ]
            x_values = switch
        d_str = "M " + str(x_values[0]) + "," + str(
            y_values[0]) + " C"  # point 1
        for i in range(1, 7):
            d_str += " " + str(x_values[i]) + "," + str(y_values[i])
        return d_str

    @staticmethod
    def filter_waves(waves):
        i = 0
        while i < len(waves) - 1:
            if i > 0:  # check if wave is smaller than the last and should be removed
                a_pos, a_ht, a_sd = waves[i - 1]
                b_pos, b_ht, _b_sd = waves[i]
                if b_ht < a_ht and b_pos < a_pos + 3 * a_sd:
                    waves.pop(i)
                    continue
            if i < len(
                    waves
            ) - 2:  # check if wave is smaller than the next and should be removed
                a_pos, a_ht, _a_sd = waves[i]
                b_pos, b_ht, b_sd = waves[i + 1]
                if a_ht < b_ht and a_pos < b_pos - 3 * b_sd:
                    waves.pop(i)
                    continue
            i += 1

        return waves

    def save(self):
        """ push loaded elements to the the plot, clear out the elements. """
        for element in self.elements:
            self.plot.add(element)
        self.elements = None  # may want to remove this, if we ever want to do fancy stuff with the elements.
        self.plot.save()

    def to_string(self):
        """ convert the loaded elements to strings and return the list of elements"""
        magic_box = Text(" ",
                         id="sample_name",
                         insert=((self.MARGIN + 20), (self.MARGIN + 20)),
                         fill="black",
                         font_size=MED_FONT)
        self.elements.append(magic_box)

        t_0 = time.time()
        temp = [self.plot.tostring().replace("</svg>", "")]
        for element in self.elements:
            temp.append(element.tostring())
        temp.append("</svg>")
        print(f" Conversion of SVG to string took {time.time() - t_0} seconds")
        return ''.join(t for t in temp)

    def get_xml(self):
        """ Convert the loaded elements into XML strings and then return them."""
        strings = ""
        for element in self.elements:
            strings += (element.get_xml().decode('utf-8'))
        return strings

    def add_legends(self, get_cpg, annotations):
        """ Add annotations, title, axis, tic marks and labels """

        title = Text(self.title,
                     insert=(BIG_FONT + ((float(self.MARGIN) - BIG_FONT) / 3),
                             BIG_FONT + ((float(self.MARGIN) - BIG_FONT) / 3)),
                     fill=LEGEND_COLOUR,
                     font_size=BIG_FONT)
        self.elements.append(title)

        for axis in get_axis(self.width, self.MARGIN, self.height,
                             self.BOTTOM_MARGIN, self.RIGHT_MARGIN):
            self.elements.append(axis)

        # print("add_legends, messages are: {}".format(self.message)

        if self.message is None:
            self.add_xtics()
            # self.add_sample_labels(self.width - self.RIGHT_MARGIN + 20)
            if get_cpg:
                for cpg in add_cpg(annotations, self.MARGIN, self.height,
                                   self.scale_x, self.start, self.end,
                                   self.BOTTOM_MARGIN):
                    self.elements.insert(0, cpg)

    def add_xtics(self):
        """ Create X tics on the plot"""
        scale_tics = 1

        while scale_tics * 10 < self.end - self.start:
            scale_tics *= 10
        xtics = [
            i for i in range(self.start, self.end + 1) if i % scale_tics == 0
        ]
        while len(xtics) < 4:
            scale_tics /= 2
            xtics += [
                j for j in range(self.start, self.end + 1)
                if j % scale_tics == 0 and j not in xtics
            ]
        xtics.sort()
        spacing = fabs((self.MARGIN + (xtics[1] - self.start) * self.scale_x) -
                       (self.MARGIN +
                        (xtics[0] - self.start) * self.scale_x)) / 4
        for tic in xtics:
            tic_x = (self.MARGIN + (tic - self.start) * self.scale_x)
            tic_y = self.height - self.BOTTOM_MARGIN + SMALL_FONT * 1.5
            ticmarker = (Text(str(tic),
                              insert=(tic_x, tic_y),
                              fill=LEGEND_COLOUR,
                              font_size=SMALL_FONT))
            ticline = Rect(insert=(tic_x,
                                   self.height - self.BOTTOM_MARGIN - 2),
                           size=(1, 5),
                           fill=LEGEND_COLOUR)
            for i in range(1, 4):
                if tic_x - spacing * i > self.MARGIN - 5:
                    ticline2 = Rect(insert=(tic_x - spacing * i, self.height -
                                            self.BOTTOM_MARGIN - 2),
                                    size=(1, 2),
                                    fill=LEGEND_COLOUR)
                    self.elements.append(ticline2)
            self.elements.append(ticline)
            self.elements.append(ticmarker)

    def add_ytics_chipseq(self):
        """ Add Y ticks to the svg plot """

        steps = round(self.maxh / 5, 1) if self.maxh else 1

        labels = [0, steps, 2 * steps, 3 * steps, 4 * steps, 5 * steps]
        ytics = [
            round(
                self.height - self.BOTTOM_MARGIN - (self.dimension_y / 5 * y),
                3) for y in range(0, 6)
        ]
        spacing = (ytics[0] - ytics[1]) / 2
        for tic, label in zip(ytics, labels):
            ticline = Rect(insert=(self.MARGIN - 2, tic),
                           size=(5, 1),
                           fill=LEGEND_COLOUR)
            if tic - spacing > self.MARGIN:
                ticline2 = Rect(insert=(self.MARGIN - 2, tic - spacing),
                                size=(2, 1),
                                fill=LEGEND_COLOUR)
                self.elements.append(ticline2)
            tic_x = self.MARGIN - SMALL_FONT * 2
            tic_y = tic + 1
            if len(str(label)) == 1:
                tic_x += 3
            if len(str(label)) == 2:
                tic_x += 2
            if len(str(label)) >= 3:
                tic_x -= 10
            ticmarker = (Text(str(label),
                              insert=(tic_x, tic_y),
                              fill=LEGEND_COLOUR,
                              font_size=SMALL_FONT))
            self.elements.append(ticline)
            self.elements.append(ticmarker)

    def add_ytics_methylation(self):
        """ Add Y ticks to the svg plot """
        labels = [0, 0.2, 0.4, 0.6, 0.8, 1]
        ytics = [
            round((self.MARGIN + self.dimension_y) - (y * self.dimension_y), 3)
            for y in labels
        ]
        spacing = (ytics[0] - ytics[1]) / 2
        for tic, label in zip(ytics, labels):

            ticline = Rect(insert=(self.width - self.RIGHT_MARGIN, tic),
                           size=(5, 1),
                           fill=LEGEND_COLOUR)
            if tic - spacing > self.MARGIN:
                ticline2 = Rect(insert=(self.width - self.RIGHT_MARGIN,
                                        tic - spacing),
                                size=(2, 1),
                                fill=LEGEND_COLOUR)
                self.elements.append(ticline2)
            tic_x = self.width - self.RIGHT_MARGIN + SMALL_FONT
            tic_y = tic + 1
            if len(str(label)) == 1:
                tic_x += 3
            if len(str(label)) == 2:
                tic_x += 2
            ticmarker = (Text(str(label),
                              insert=(tic_x, tic_y),
                              fill=LEGEND_COLOUR,
                              font_size=SMALL_FONT))
            self.elements.append(ticline)
            self.elements.append(ticmarker)

    def draw_genes(self, genes):
        for gene in genes:
            for transcript in gene['transcripts']:

                start = self.convert_xcoord_to_pos(gene['start'])
                length = self.convert_xcoord_to_pos(gene['end']) - start
                if start < self.MARGIN:
                    if start < 0:  # first, remove anything before zero.
                        length += start
                        start = 0
                    length -= (
                        self.MARGIN + start
                    )  # then remove anything before the margin begins
                    start = self.MARGIN
                if start + length > (self.width - self.RIGHT_MARGIN):
                    length = (self.width - self.RIGHT_MARGIN) - start
                if gene['strand'] == 1:
                    text = gene['name'] + " ->>>"
                else:
                    text = " <<<- " + gene['name']
                # print "(self.width + self.RIGHT_MARGIN + self.MARGIN)", (self.width - self.RIGHT_MARGIN)
                # print "gene -> text:{} chrend:{} chrstart:{} start:{} length:{}"
                #       .format(gene['name'], gene['end'], gene['start'], start, length)_
                g_rect = Rect(insert=(start, self.height - self.BOTTOM_MARGIN +
                                      self.gene_offset + 4),
                              size=(length, 2),
                              fill="grey")
                g_text = (Text(text,
                               insert=(start,
                                       self.height - self.BOTTOM_MARGIN +
                                       self.gene_offset + 9),
                               fill=LEGEND_COLOUR,
                               font_size=SMALL_FONT))
                self.elements.append(g_rect)
                self.elements.append(g_text)

                for exon in gene["transcripts"][transcript]["exons"]:
                    exon_info = gene["transcripts"][transcript]["exons"][exon]
                    e_start = self.convert_xcoord_to_pos(exon_info["start"])
                    e_len = self.convert_xcoord_to_pos(
                        exon_info['end']) - e_start
                    if e_start > (self.width - self.RIGHT_MARGIN) or (
                            e_start + e_len) < self.MARGIN:
                        continue
                    if e_start < self.MARGIN:
                        e_start = self.MARGIN
                    if e_start + e_len > (self.width - self.RIGHT_MARGIN):
                        e_len = (self.width - self.RIGHT_MARGIN) - e_start

                    e_rect = Rect(insert=(e_start,
                                          self.height - self.BOTTOM_MARGIN +
                                          self.gene_offset),
                                  size=(e_len, 9),
                                  fill="grey")
                    self.elements.append(e_rect)

                self.gene_offset += 12
                if self.gene_offset > 250:
                    self.gene_offset = self.GENE_OFFSET
示例#22
0
class Figure(object):
    def __init__(self,
                 width=600,
                 height=400,
                 margin_top=20,
                 margin_bottom=40,
                 margin_left=40,
                 margin_right=20,
                 x_min=0,
                 y_min=None,
                 x_max=1,
                 y_max=None,
                 debug=False,
                 graph_colour="midnightblue",
                 background=None,
                 x_label=None,
                 y_label=None,
                 y_label_max_min=True,
                 title=None):

        self.width = width
        self.height = height
        self.debug = debug
        self.margin_top = margin_top
        self.margin_bottom = margin_bottom
        self.margin_left = margin_left
        self.margin_right = margin_right
        self.x_max = x_max
        self.y_max = y_max
        self.x_min = x_min
        self.y_min = y_min
        self.graph_colour = graph_colour
        self.x_label = x_label
        self.y_label = y_label
        self.y_label_max_min = y_label_max_min
        self.title = title
        self.font_size = 15

        self.plottable_x = self.width - (self.margin_left + self.margin_right)
        self.plottable_y = self.height - (self.margin_top + self.margin_bottom)

        # start drawing object
        self.plot = Drawing(
            debug=self.debug,
            size=(self.width, self.height),
            # viewBox = ("0 0 " + str(float(length) + 10) + " " + str(float(width) + 10)),
            preserveAspectRatio="xMinYMin meet"
        )  # , size=('200mm', '150mm'), viewBox=('0 0 200 150'))

        if background:
            self.plot.add(
                Rect(insert=(0, 0),
                     size=(self.width, self.height),
                     fill=background))

        if x_label:
            self.plottable_y -= self.font_size
            self.add_x_label()

        if y_label:
            self.plottable_x -= self.font_size
            self.margin_left += self.font_size
            self.add_y_label()

        if title:
            self.plottable_y -= self.font_size + 5
            self.margin_top += self.font_size + 5
            self.add_title()

        self.plot.add(
            Line(start=(self.margin_left - 4, self.margin_top),
                 end=(self.margin_left - 4,
                      self.margin_top + self.plottable_y),
                 stroke_width=1,
                 stroke=self.graph_colour))
        self.plot.add(
            Line(start=(self.margin_left,
                        self.margin_top + self.plottable_y + 4),
                 end=(self.margin_left + self.plottable_x,
                      self.margin_top + self.plottable_y + 4),
                 stroke_width=1,
                 stroke=self.graph_colour))

        if self.y_label_max_min and self.y_max and self.y_min:
            self.add_y_max_min(self.y_max, self.y_min)

    def save(self, reset=True, filename=None):
        if filename:
            self.plot.filename = filename
        elif not self.plot.filename:
            raise Exception("No Filename set to save SVG image.")

        self.plot.save()
        print(f"wrote to {self.plot.filename}")

        if reset:
            self.plot = None

    def add_y_label(self):
        y_coord = self.margin_top + (self.plottable_y / 2)
        text_group = Group(
            transform=f"rotate(270, {self.font_size}, {y_coord})")

        text_group.add(
            Text(self.y_label,
                 insert=(0, y_coord),
                 fill=self.graph_colour,
                 font_size="15",
                 stroke_width=0))
        self.plot.add(text_group)

    def add_x_label(self):
        self.plot.add(
            Text(self.x_label,
                 insert=(self.plottable_x / 2, self.plottable_y +
                         self.margin_top + (self.margin_bottom / 2) + 15),
                 fill=self.graph_colour,
                 font_size=self.font_size))

    def add_title(self):
        self.plot.add(
            Text(self.title,
                 insert=(self.plottable_x / 2, self.font_size + 5),
                 fill=self.graph_colour,
                 font_size=self.font_size))

    def add_y_max_min(self, max_value, min_value):
        self.plot.add(
            Text(str(round(max_value, 2)),
                 insert=(self.margin_left - 8, self.margin_top + 15),
                 text_anchor="end",
                 fill=self.graph_colour,
                 font_size=self.font_size))
        self.plot.add(
            Text(str(min_value),
                 insert=(self.margin_left - 8,
                         self.margin_top + self.plottable_y),
                 text_anchor="end",
                 fill=self.graph_colour,
                 font_size=self.font_size))

    def add_x_column_labels(self,
                            column_positions,
                            column_labels,
                            rotate=None):
        for gene in column_labels:
            text_group = Group(
                transform=
                f"rotate({rotate if rotate else 0},{column_positions[gene]},"
                f"{self.margin_top + self.plottable_y + 17})")

            text_group.add(
                Text(gene,
                     insert=(column_positions[gene],
                             self.margin_top + self.plottable_y + 17),
                     fill=self.graph_colour,
                     font_size=self.font_size,
                     stroke_width=0))
            self.plot.add(text_group)

    def to_string(self, reset=True):
        string_value = self.plot.tostring()
        if reset:
            self.plot = None
        return string_value

    def add_max_min_text(self):
        self.plot.add(
            Text(str(self.x_max),
                 insert=(self.margin_left + self.plottable_x,
                         self.margin_top + self.plottable_y + 20.0),
                 text_anchor="end",
                 fill=self.graph_colour,
                 font_size=self.font_size))
        self.plot.add(
            Text(str(self.x_min),
                 insert=(self.margin_left,
                         self.margin_top + self.plottable_y + 20.0),
                 fill=self.graph_colour,
                 font_size=self.font_size))

        self.plot.add(
            Text(str(self.y_max),
                 insert=(self.margin_left - 8, self.margin_top + 10),
                 text_anchor="end",
                 fill=self.graph_colour,
                 font_size=self.font_size))
        self.plot.add(
            Text(str(self.y_min),
                 insert=(self.margin_left - 8,
                         self.margin_top + self.plottable_y),
                 text_anchor="end",
                 fill=self.graph_colour,
                 font_size=self.font_size))
def create_image(filename):
    image = Drawing(filename)
    image.update({"stroke": "black", "stroke_width": 1})
    return image
示例#24
0
class Plot(object):
    '''
    Called by a MongoEpigeneticsWrapper object to plot methylation data.
    '''

    METHYLATION_DOT_RADIUS = 2
    METHYLATION_DISTR_HT_MED = 12.0
    METHYLATION_DISTR_HT_BIG = 24.0
    DISTR_SHIFT = 0
    DISTR_STROKE = 0.75
    BOTTOM_MARGIN = 120    # 120 pixels
    RIGHT_MARGIN = 30
    MARGIN = 30
    GENE_OFFSET = 20
    palette = Color_Palette.ColorPalette()    # APF - reset on each iteration through the sorter.


    def __init__(self):
        '''Simple initiation of all of the self parameters... Does not do anything unexpected.'''
        self.elements = []
        self.title = None
        self.palette.samples_color = {}    # reset the methylation plot colour assignment on initialization.
        self.sample_grouping = {}    # store sample id/sample group.
        self.gausian_colour = {}
        self.last_hash = 0
        self.start = 0
        self.end = 0
        self.width = 200    # default = 200.0

        self.height = 60    # default = 60.0
        self.message = None

        self.dimension_y = 0    # this is the size of the y field
        self.dimension_x = 0
        self.scale_x = 0
        self.y_bottom = 0

        self.scale_y = 0
        self.maxh = 0
        self.plot = None

        self.gene_offset = self.GENE_OFFSET


    def set_properties(self, filename, title, start, end, width, height):
        '''Set the properties of the canvas on which you'll want to generate your image '''
        self.elements = []
        self.title = title
        self.start = start
        self.end = end
        self.width = width    # default = 200.0

        self.height = height    # default = 60.0

        self.dimension_y = self.height - self.MARGIN - self.BOTTOM_MARGIN    # this is the size of the y field
        self.dimension_x = self.width - self.MARGIN - self.RIGHT_MARGIN
        self.scale_x = float(self.dimension_x) / (self.end - self.start)    # this is a scaling variable
        self.y_bottom = str(round(self.dimension_y + self.MARGIN, 2))


        canvas_size = (str(self.width) + "px" , "100%")    # create drawing # Default is 100%,100% - don't override that, as this allows the svg to fit the data and expand as necessary
        self.plot = Drawing(filename , size = canvas_size)
        background = Rect(insert = (0, 0), size = canvas_size, fill = "white")
        self.plot.add(background)


    def set_sample_index(self, types, samples):
        '''overwrite the sample_index to preserve colours from previous set.'''
        self.palette.set_colors_dict(types, samples)

    def get_sample_index(self):
        ''' Return the sample index so that it can be used in HTML'''
        return self.palette.get_colors_dict()

    def get_types_index(self):
        ''' Return the sample index so that it can be used in HTML'''
        return self.palette.get_type_colors()

    def convert_xcoord_to_pos(self, xcoord):
        return round(float(xcoord - self.start) * self.scale_x, 2) + self.MARGIN


    def make_gausian(self, pos, height, stddev, horizontal = True, y_median = 0, sigmas = 3):
        ''' path points will be at (-3stddev,0), (0,height), (3stddev,0)
            Control points at (-1stddev,0), (-1stddev,height), (1stddev,height), (1stddev,0)
         '''
        X = [-sigmas * stddev, -1 * stddev, -1 * stddev, 0, stddev, stddev, sigmas * stddev]
        Y = [0, 0, height, height, height, 0, 0]

        if horizontal is True:
            X = [round((x - self.start + pos) * self.scale_x, 2) + self.MARGIN for x in X]
            # Scale Y and inverse the coordinates
            Y = [round(y1 * self.scale_y, 2) for y1 in Y]
            Y = [(self.height - self.BOTTOM_MARGIN - y2) for y2 in Y]
        else:
            S = [round(((pos - self.start) * self.scale_x) + y + self.MARGIN + self.DISTR_SHIFT, 2) for y in Y]
            Y = [round(((x + y_median) * self.scale_y), 2) for x in X]
            X = S
        d = "M " + str(X[0]) + "," + str(Y[0]) + " C"    # point 1
        for i in range(1, 7):
            d += " " + str(X[i]) + "," + str(Y[i])
        return d


    def filter_waves(self, waves):
        i = 0
        while i < len(waves) - 1:
            if i > 0:    # check if wave is smaller than the last and should be removed
                a_pos, a_ht, a_sd = waves[i - 1]
                b_pos, b_ht, _b_sd = waves[i]
                if b_ht < a_ht and b_pos < a_pos + 3 * a_sd:
                    waves.pop(i)
                    continue
            if i < len(waves) - 2:    # check if wave is smaller than the next and should be removed
                a_pos, a_ht, _a_sd = waves[i]
                b_pos, b_ht, b_sd = waves[i + 1]
                if a_ht < b_ht and a_pos < b_pos - 3 * b_sd:
                    waves.pop(i)
                    continue
            i += 1

        return waves




    def make_trace_chipseq(self, waves):    # pos, height, stddev, horizontal = True, y_median = 0, sigmas = 3):
        ''' path points will be at (-3stddev,0), (0,height), (3stddev,0)
            Control points at (-1stddev,0), (-1stddev,height), (1stddev,height), (1stddev,0)
         '''

        waves = sorted(waves)

        waves = self.filter_waves(waves)


        X = [round((pos - self.start) * self.scale_x, 2) + self.MARGIN for (pos, _height, _stddev) in waves]
            # Scale Y and inverse the coordinates
        Y = [round(height * self.scale_y, 2) for (_pos, height, _stddev)  in waves]
        Y = [(self.height - self.BOTTOM_MARGIN - y2) for y2 in Y]
        S = [round(stddev * self.scale_x, 2) for (_pos, _height, stddev) in waves]



        if (X[0] > S[0]):
            d = "M " + str(X[0] - (2 * S[0])) + "," + self.y_bottom + " C"    # point 1
            d += " " + str(X[0] - (S[0])) + "," + self.y_bottom    # control point 1
            d += " " + str(X[0] - (S[0])) + "," + str(Y[0])    # control point 2
            d += " " + str(X[0]) + "," + str(Y[0])    # actual coordinate
        else:
            d = "M " + str(X[0]) + "," + str(Y[0]) + " C"    # point 1
        for i in range(1, len(X)):
            if ((3 * S[i]) + (3 * S[i - 1])) < (X[i] - X[i - 1]):
                d += " " + str(X[i - 1] + S[i - 1]) + "," + str(Y[i - 1])    # control point 1
                d += " " + str(X[i - 1] + 2 * S[i - 1]) + "," + self.y_bottom    # control point 2
                d += " " + str(X[i - 1] + 3 * S[i - 1]) + "," + self.y_bottom    # actual coordinate

                d += " " + str(X[i] - S[i]) + "," + self.y_bottom    # control point 2
                d += " " + str(X[i] - 2 * S[i]) + "," + self.y_bottom    # control point 1
                d += " " + str(X[i] - 3 * S[i]) + "," + self.y_bottom    # actual coordinate

                d += " " + str(X[i] - (S[i])) + "," + self.y_bottom    # control point 1
                d += " " + str(X[i] - (S[i])) + "," + str(Y[i])    # control point 2
                d += " " + str(X[i]) + "," + str(Y[i])    # actual coordinate

            else:
                d += " " + str(X[i - 1] + round((X[i] - X[i - 1]) / 4, 2)) + "," + str(Y[i - 1])    # control point 1
                d += " " + str(X[i] - round((X[i] - X[i - 1]) / 4, 2)) + "," + str(Y[i])    # control point 2
                d += " " + str(X[i]) + "," + str(Y[i])    # actual coordinate
        last = len(X) - 1
        d += " " + str(X[last] + (S[last])) + "," + str(Y[last])    # control point 1
        d += " " + str(X[last] + (S[last])) + "," + self.y_bottom    # control point 2
        d += " " + str(X[last] + (2 * S[last])) + "," + self.y_bottom    # actual coordinate

        return d


    def make_trace_meth(self, data):    # pos, height, stddev, horizontal = True, y_median = 0, sigmas = 3):
        ''' path points will be at (-3stddev,0), (0,height), (3stddev,0)
            Control points at (-1stddev,0), (-1stddev,height), (1stddev,height), (1stddev,0)
         '''

        data = sorted(data)
        X = [round((pos - self.start) * self.scale_x, 2) + self.MARGIN for (pos, _mean) in data]
            # Scale Y and inverse the coordinates
        Y = [mean for (_pos, mean) in data]


        d = "M " + str(X[0]) + "," + str(Y[0]) + " C"    # point 1
        for i in range(1, len(X)):
            d += " " + str(X[i - 1] + round((X[i] - X[i - 1]) / 4, 2)) + "," + str(Y[i - 1])    # control point 1
            d += " " + str(X[i] - round((X[i] - X[i - 1]) / 4, 2)) + "," + str(Y[i])    # control point 2
            d += " " + str(X[i]) + "," + str(Y[i])    # actual coordinate
        return d


    def build_chipseq(self, message, waves, trace):
        '''convert loaded data into svg images'''
        if message:
            Message = Text('[ ' + message + ' ]', insert = (float(self.width) / 3.0, float(self.height) / 2.0),
                    fill = "black", font_size = bigfont * 2)
            self.elements.append(Message)
        heights = []    # create path objects for each peak
        if not waves:
            return

        for (pos, height, stddev, sample_id) in waves:
            heights.append(height)
        self.maxh = max(heights)
        self.scale_y = self.dimension_y / self.maxh

        samples = {}
        if trace:
            for (pos, height, stddev, sample_id) in waves:
                if sample_id in samples:
                    samples[sample_id].append((pos, height, stddev))
                else:
                    samples[sample_id] = []
                    samples[sample_id].append((pos, height, stddev))
            for sampleid in samples:
                w = samples[sampleid]
                d = self.make_trace_chipseq(w)
                types_color, _new = self.palette.colour_assignment_group(sampleid)
                self.elements.append(Path(stroke = types_color, stroke_width = 2,
                       stroke_linecap = 'round', stroke_opacity = 0.8, d = d, fill = 'none',
                       onmouseover = "evt.target.ownerDocument.getElementById('sample_name').firstChild.data = \'%s\'" %
                                    (''.join(s for s in sampleid if s in string.printable))))
        else:

            for (pos, height, stddev, sample_id) in waves:
                d = self.make_gausian(pos, height, stddev)
                types_color, _new = self.palette.colour_assignment_group(sample_id)
                self.elements.append(Path(stroke = types_color, stroke_width = 0.1,
                           stroke_linecap = 'round', stroke_opacity = 0.8,
                           fill = types_color, fill_opacity = 0.5, d = d,
                        onmouseover = "evt.target.ownerDocument.getElementById('sample_name').firstChild.data = \'%s\'" %
                                    (''.join(s for s in sample_id if s in string.printable))))

        # fix to truncate curves at border (to hide them)
        self.elements.append(Rect(insert = (0, self.BOTTOM_MARGIN), size = (self.MARGIN + 1, self.height - self.MARGIN), stroke = types_color, stroke_width = 0.0, fill = "#ffffff", fill_opacity = 1))
        self.elements.append(Rect(insert = (self.width - self.RIGHT_MARGIN, 0), size = (self.RIGHT_MARGIN, self.height - self.MARGIN), stroke = types_color, stroke_width = 0.0, fill = "#ffffff", fill_opacity = 1))
        self.palette.purge_unused()

    def build_methylation(self, message, pos_betas_dict, sample_peaks, show_points, show_peaks, show_groups, probes_by_pos, probe_details, bigger_dists, trace):
        '''convert this information into elements of the svg image'''
        self.scale_y = 1

        ht = 0
        if bigger_dists:
            ht = self.METHYLATION_DISTR_HT_BIG
        else:
            ht = self.METHYLATION_DISTR_HT_MED

        if message:
            Message = Text('[ ' + message + ' ]', insert = (float(self.width) / 3.0, float(self.height) / 2.0),
                    fill = "black", font_size = bigfont * 2)
            self.elements.append(Message)

        samples = {}
        for position in pos_betas_dict.keys():
            x = round(float(position - self.start) * self.scale_x, 2) + self.MARGIN

            probeid = probes_by_pos[position]

            if show_points:
                for beta, sample_id, sample_type in pos_betas_dict[position]:
                    sample_id = str(sample_id)
                    sample_type = str(sample_type)
                    y = round((1 - beta) * self.dimension_y, 2) + self.MARGIN
                    type_color, sample_color, new = self.palette.colour_assignment(sample_type, sample_id)
                    if new:
                        show_groups.append(sample_type)
                    if not show_groups or sample_type in show_groups:
                        point = Circle(center = (x, y), r = self.METHYLATION_DOT_RADIUS, fill = sample_color,
                                   onmouseover = "evt.target.ownerDocument.getElementById('sample_name').firstChild.data = \'%s %s-%s \'" %
                                    (probeid, sample_type, sample_id))
                        self.elements.append(point)


            if show_peaks or trace:
                for sample_type in sample_peaks[position]:
                    if not show_groups or sample_type in show_groups:
                        if self.gausian_colour.has_key(sample_type):
                            type_color = self.gausian_colour[sample_type]
                        else:
                            type_color, new = self.palette.colour_assignment_group(sample_type)
                            self.gausian_colour[sample_type] = type_color
                        m, s = sample_peaks[position][sample_type]
                        m = round((1 - m) * self.dimension_y, 2) + self.MARGIN
                        s = round(s * self.dimension_y, 3)
                        if trace:
                            if sample_type in samples:
                                samples[sample_type].append((position, m))
                            else:
                                samples[sample_type] = []
                                samples[sample_type].append((position, m))

                        if show_peaks and s != 0.0:
                            d = self.make_gausian(position, ht, s, False, m)
                            gaussian = (Path(stroke = type_color,
                                         stroke_width = self.DISTR_STROKE,
                                         stroke_linecap = 'round',
                                         stroke_opacity = 0.8,
                                         fill = type_color,
                                         fill_opacity = 0.1,
                                         d = d))

                            self.elements.insert(1, gaussian)
        if trace:
            for sample_type in samples:
                if self.gausian_colour.has_key(sample_type):
                    type_color = self.gausian_colour[sample_type]
                else:
                    type_color, new = self.palette.colour_assignment_group(sample_type)
                    self.gausian_colour[sample_type] = type_color
                d = self.make_trace_meth(samples[sample_type])
                t = (Path(stroke = type_color,
                                stroke_width = 2,
                                stroke_linecap = 'round',
                                stroke_opacity = 0.8,
                                fill = "none",
                                onmouseover = "evt.target.ownerDocument.getElementById('sample_name').firstChild.data = \'%s\'" % (sample_type) ,
                                d = d))
                self.elements.insert(1, t)


            # fix to truncate curves at border (to hide them)
        if show_peaks:
            self.elements.append(Rect(insert = (0, 0), size = (self.width, self.MARGIN), stroke_width = 0, fill = "#ffffff", fill_opacity = 1))
            self.elements.append(Rect(insert = (0, self.height - self.BOTTOM_MARGIN), size = (self.width, self.dimension_x - self.height), stroke_width = 0, fill = "#ffffff", fill_opacity = 1))

        for position in pos_betas_dict.keys():
            probeid = probes_by_pos[position]
            x = round(float(position - self.start) * self.scale_x, 2) + self.MARGIN
            if probe_details[probeid]['n_snpcpg'] > 0:    # must process this after the rect, which would cover them otherwise
                point = Rect(insert = (x - 1.0, self.MARGIN + self.dimension_y), size = (2.0, 8.0), stroke_width = 0, fill = 'red',
                               onmouseover = "evt.target.ownerDocument.getElementById('sample_name').firstChild.data = \'SNP in cpg: %s\'" %
                                    (probeid))
                self.elements.append(point)
            elif probe_details[probeid]['n_snpprobe'] > 0:
                point = Rect(insert = (x - 1.0, self.MARGIN + self.dimension_y), size = (2.0, 8.0), stroke_width = 0, fill = 'blue',
                               onmouseover = "evt.target.ownerDocument.getElementById('sample_name').firstChild.data = \'SNP in probe %s\'" %
                                    (probeid))
                self.elements.append(point)
        self.palette.purge_unused()

    def save(self):
        ''' push loaded elements to the the plot, clear out the elements. '''
        for element in self.elements:
            self.plot.add(element)
        self.elements = None    # may want to remove this, if we ever want to do fancy stuff with the elements.
        self.plot.save()

    def to_string(self):
        ''' convert the loaded elements to strings and return the list of elements'''
        magic_box = Text(" ", id = "sample_name", insert = ((self.MARGIN + 20), (self.MARGIN + 20)),
                    fill = "black", font_size = medfont)
        self.elements.append(magic_box)

        t0 = time.time()
        temp = []
        temp.append(self.plot.tostring().replace("</svg>", ""))
        for element in self.elements:
            temp.append(element.tostring())
        temp.append("</svg>")
        print " Conversion of SVG to string took %f seconds" % (time.time() - t0)
        return ''.join(t for t in temp)


    def get_elements(self):
        ''' call sample labels and delete loaded elements. '''
        self.add_sample_labels(self.MARGIN * 3.2 + self.width)
        return self.elements

    def get_xml(self):
        ''' Convert the loaded elements into XML strings and then return them.'''
        strings = ""
        for element in self.elements:
            strings += (element.get_xml().decode('utf-8'))
        return strings

    def add_legends(self, get_cpg, annotations):
        ''' Add annotations, title, axis, tic marks and labels '''

        Title = Text(self.title, insert = (bigfont + ((float(self.MARGIN) - bigfont) / 3),
                                           bigfont + ((float(self.MARGIN) - bigfont) / 3)),
                                           fill = legend_color, font_size = bigfont)
        self.elements.append(Title)

        for axis in get_axis(self.width, self.MARGIN, self.height, self.BOTTOM_MARGIN, self.RIGHT_MARGIN):
            self.elements.append(axis)

        # print "add_legends, messages are: %s" % self.message

        if self.message is None:
            self.add_xtics()
            # self.add_sample_labels(self.width - self.RIGHT_MARGIN + 20)
            if get_cpg:
                for cpg in add_cpg(annotations, self.MARGIN, self.height, self.scale_x, self.start, self.end, self.BOTTOM_MARGIN):
                    self.elements.insert(0, cpg)

    def add_xtics(self):
        ''' Create X tics on the plot'''
        scale_tics = 1

        while((scale_tics * 10) < self.end - self.start):
            scale_tics *= 10
        xtics = [i for i in range(self.start, self.end + 1) if i % (scale_tics) == 0]
        while len(xtics) < 4:
            scale_tics /= 2
            xtics += [j for j in range(self.start, self.end + 1) if j % (scale_tics) == 0 and j not in xtics]
        xtics.sort()
        spacing = fabs((self.MARGIN + (xtics[1] - self.start) * self.scale_x) - (self.MARGIN + (xtics[0] - self.start) * self.scale_x)) / 4
        for tic in xtics:
            tic_x = (self.MARGIN + (tic - self.start) * self.scale_x)
            tic_y = self.height - self.BOTTOM_MARGIN + smallfont * 1.5
            ticmarker = (Text(str(tic), insert = (tic_x, tic_y), fill = legend_color, font_size = smallfont))
            ticline = Rect(insert = (tic_x, self.height - self.BOTTOM_MARGIN - 2), size = (1, 5), fill = legend_color)
            for i in range (1, 4):
                if tic_x - spacing * i > self.MARGIN - 5:
                    ticline2 = Rect(insert = (tic_x - spacing * i, self.height - self.BOTTOM_MARGIN - 2), size = (1, 2), fill = legend_color)
                    self.elements.append(ticline2)
            self.elements.append(ticline)
            self.elements.append(ticmarker)

    def add_ytics_chipseq(self):
        ''' Add Y ticks to the svg plot '''

        if self.maxh: steps = round(self.maxh / 5, 1)
        labels = [0, steps, 2 * steps, 3 * steps, 4 * steps, 5 * steps]
        ytics = [round(self.height - self.BOTTOM_MARGIN - (self.dimension_y / 5 * y), 3) for y in range(0, 6)]
        spacing = (ytics[0] - ytics[1]) / 2
        for tic, label in zip(ytics, labels):
            ticline = Rect(insert = (self.MARGIN - 2, tic), size = (5, 1), fill = legend_color)
            if tic - spacing > self.MARGIN:
                ticline2 = Rect(insert = (self.MARGIN - 2, tic - spacing), size = (2, 1), fill = legend_color)
                self.elements.append(ticline2)
            tic_x = self.MARGIN - smallfont * 2
            tic_y = tic + 1
            if len(str(label)) == 1:
                tic_x += 3
            if len(str(label)) == 2:
                tic_x += 2
            if len(str(label)) >= 3:
                tic_x -= 10
            ticmarker = (Text(label, insert = (tic_x, tic_y), fill = legend_color, font_size = smallfont))
            self.elements.append(ticline)
            self.elements.append(ticmarker)

    def add_ytics_methylation(self):
        ''' Add Y ticks to the svg plot '''
        labels = [0, 0.2, 0.4, 0.6, 0.8, 1]
        ytics = [round((self.MARGIN + self.dimension_y) - (y * self.dimension_y), 3) for y in labels]
        spacing = (ytics[0] - ytics[1]) / 2
        for tic, label in zip(ytics, labels):

            ticline = Rect(insert = (self.width - self.RIGHT_MARGIN, tic), size = (5, 1), fill = legend_color)
            if tic - spacing > self.MARGIN:
                ticline2 = Rect(insert = (self.width - self.RIGHT_MARGIN, tic - spacing), size = (2, 1), fill = legend_color)
                self.elements.append(ticline2)
            tic_x = self.width - self.RIGHT_MARGIN + smallfont
            tic_y = tic + 1
            if len(str(label)) == 1:
                tic_x += 3
            if len(str(label)) == 2:
                tic_x += 2
            ticmarker = (Text(label, insert = (tic_x, tic_y), fill = legend_color, font_size = smallfont))
            self.elements.append(ticline)
            self.elements.append(ticmarker)

    def draw_genes(self, genes):
        for gene in genes:
            for transcript in gene['transcripts']:

                start = self.convert_xcoord_to_pos(gene['start'])
                length = self.convert_xcoord_to_pos(gene['end']) - start
                if start < self.MARGIN:
                    if start < 0:    # first, remove anything before zero.
                        length += start
                        start = 0
                    length -= (self.MARGIN + start)    # then remove anything before the margin begins
                    start = self.MARGIN
                if start + length > (self.width - self.RIGHT_MARGIN):
                    length = (self.width - self.RIGHT_MARGIN) - start
                text = None
                if gene['strand'] == 1:
                    text = gene['name'] + " ->>>"
                else:
                    text = " <<<- " + gene['name']
                # print "(self.width + self.RIGHT_MARGIN + self.MARGIN)", (self.width - self.RIGHT_MARGIN)
                # print "gene -> text:%s chrend:%i chrstart:%i start:%i length:%i" % (gene['name'], gene['end'], gene['start'], start, length)
                g = Rect(insert = (start, self.height - self.BOTTOM_MARGIN + self.gene_offset + 4), size = (length, 2), fill = "grey")
                t = (Text(text, insert = (start, self.height - self.BOTTOM_MARGIN + self.gene_offset + 9), fill = legend_color, font_size = smallfont))
                self.elements.append(g)

                for exon in gene["transcripts"][transcript]["exons"]:
                    e = gene["transcripts"][transcript]["exons"][exon]
                    e_start = self.convert_xcoord_to_pos(e["start"])
                    e_len = self.convert_xcoord_to_pos(e['end']) - e_start
                    if e_start > (self.width - self.RIGHT_MARGIN) or (e_start + e_len) < self.MARGIN:
                        continue
                    if e_start < self.MARGIN:
                        e_start = self.MARGIN
                    if e_start + e_len > (self.width - self.RIGHT_MARGIN):
                        e_len = (self.width - self.RIGHT_MARGIN) - e_start

                    e = Rect(insert = (e_start, self.height - self.BOTTOM_MARGIN + self.gene_offset), size = (e_len, 9), fill = "grey")
                    self.elements.append(e)

                self.elements.append(t)
                self.gene_offset += 12
                if self.gene_offset > 250:
                    self.gene_offset = self.GENE_OFFSET