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>')
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)
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>')
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>')
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>')
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>')
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 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!
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
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
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
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 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
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
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