Source code for mavis.illustrate.diagram

"""
This is the primary module responsible for generating svg visualizations

"""
from svgwrite import Drawing

from .elements import draw_exon_track, draw_genes, draw_template, draw_ustranscript, draw_vmarker
from .scatter import draw_scatter
from .util import generate_interval_mapping, LabelMapping

from ..annotate.genomic import IntergenicRegion
from ..interval import Interval

# draw gene level view
# draw gene box
HEX_WHITE = '#FFFFFF'
HEX_BLACK = '#000000'


[docs]def draw_sv_summary_diagram( config, ann, reference_genome=None, templates=None, ignore_absent_templates=True, user_friendly_labels=True, template_display_label_prefix='', draw_reference_transcripts=True, draw_reference_genes=True, draw_reference_templates=True, draw_fusion_transcript=True): """ this is the main drawing function. It decides between layouts where each view-level is split into one or two diagrams (side-by-side) dependant on whether the event is interchromosomal, within a single transcript, etc. Diagrams have four levels - template - gene - transcript - fusion transcript/translation Args: ann (Annotation): the annotation object to be illustrated reference_genome (dict of str by str): reference sequences templates (list of Template): list of templates, used in drawing the template-level view ignore_absent_templates (bool): if true then will not raise an error if the template information is not given but will not draw the template instead show_template (bool): if false the template-level view is not drawn user_friendly_labels (bool): if True, genes are labelled by their aliases (where possible) and domains are labeled by their names (where possible) template_display_label_prefix (str): the character to precede the template label """ if not any([draw_reference_templates, draw_reference_genes, draw_reference_transcripts, draw_fusion_transcript]): raise AssertionError('nothing to draw') fusion_transcript = ann.fusion templates = dict() if templates is None else templates canvas = Drawing(size=(config.width, 1000)) # just set the height for now and change later labels = LabelMapping() # keep labels consistent within the drawing y = config.top_margin x = config.left_margin dx_label_shift = config.label_left_margin x += dx_label_shift drawing_width = config.width - dx_label_shift - config.left_margin - config.right_margin # calculate the half-width for transcripts and genes etc half_drawing_width = (drawing_width - config.inner_margin - dx_label_shift) / 2 second_drawing_shift = x + half_drawing_width + config.inner_margin + dx_label_shift if draw_reference_templates: try: template1 = templates[ann.transcript1.get_chr()] template2 = templates[ann.transcript2.get_chr()] if user_friendly_labels and template1.name: labels.set_key(template_display_label_prefix + template1.name, template1) if user_friendly_labels and template2.name: labels.set_key(template_display_label_prefix + template2.name, template2) height = [0] if template1 == template2: # single template svg_group = draw_template( config, canvas, template1, drawing_width, breakpoints=[ann.break1, ann.break2], labels=labels) canvas.add(svg_group) svg_group.translate(x, y) height.append(svg_group.height) else: # multiple templates svg_group = draw_template(config, canvas, template1, half_drawing_width, breakpoints=[ann.break1], labels=labels) canvas.add(svg_group) svg_group.translate(x, y) height.append(svg_group.height) svg_group = draw_template(config, canvas, template2, half_drawing_width, breakpoints=[ann.break2], labels=labels) canvas.add(svg_group) svg_group.translate(second_drawing_shift, y) height.append(svg_group.height) y += max(height) + config.inner_margin except KeyError as err: if not ignore_absent_templates: raise err colors = dict() genes1 = set() genes2 = set() legend = dict() for gene in ann.genes_overlapping_break1: genes1.add(gene) colors[gene] = config.gene1_color for gene, _ in ann.genes_proximal_to_break1: genes1.add(gene) colors[gene] = config.gene1_color for gene in ann.genes_overlapping_break2: genes2.add(gene) colors.setdefault(gene, config.gene2_color) for gene, _ in ann.genes_proximal_to_break2: genes2.add(gene) colors.setdefault(gene, config.gene2_color) if ann.transcript1: try: genes1.add(ann.transcript1.gene) colors[ann.transcript1.gene] = config.gene1_color_selected for exon in ann.transcript1.exons: colors[exon] = config.exon1_color except AttributeError: genes1.add(ann.transcript1) colors[ann.transcript1] = config.gene1_color_selected if ann.transcript2: try: genes2.add(ann.transcript2.gene) colors.setdefault(ann.transcript2.gene, config.gene2_color_selected) for exon in ann.transcript2.exons: colors.setdefault(exon, config.exon2_color) except AttributeError: genes2.add(ann.transcript2) colors[ann.transcript2] = config.gene2_color_selected if draw_reference_genes: # set all the labels so that they are re-used correctly aliases = {} alias_failure = False for gene in sorted(genes1 | genes2, key=lambda x: (str(x.get_chr()), x.start)): if alias_failure: break try: for alias in gene.aliases: if alias in aliases and aliases[alias] != gene: alias_failure = True break if len(gene.aliases) == 1: aliases[gene.aliases[0]] = gene else: for alias in gene.aliases: # can't label when multiple aliases[alias] = None except AttributeError: pass alias_by_gene = {} for alias, gene in aliases.items(): alias_by_gene[gene] = alias for gene in sorted(genes1 | genes2, key=lambda x: (str(x.get_chr()), x.start)): if isinstance(gene, IntergenicRegion): labels.add(gene, config.region_label_prefix) elif user_friendly_labels and not alias_failure and gene in alias_by_gene: labels[alias_by_gene[gene]] = gene else: labels.add(gene, config.gene_label_prefix) gheights = [0] if ann.interchromosomal: svg_group = draw_genes(config, canvas, genes1, half_drawing_width, [ann.break1], colors=colors, labels=labels) svg_group.translate(x, y) canvas.add(svg_group) gheights.append(svg_group.height) # second gene view svg_group = draw_genes(config, canvas, genes2, half_drawing_width, [ann.break2], colors=colors, labels=labels) svg_group.translate(second_drawing_shift, y) canvas.add(svg_group) gheights.append(svg_group.height) else: svg_group = draw_genes( config, canvas, genes1 | genes2, drawing_width, [ann.break1, ann.break2], colors=colors, labels=labels) svg_group.translate(x, y) canvas.add(svg_group) gheights.append(svg_group.height) y += max(gheights) + config.inner_margin if draw_reference_transcripts: theights = [] # now the transcript level drawings if any([ ann.transcript1 == ann.transcript2, ann.transcript1 is None, ann.transcript2 is None, isinstance(ann.transcript1, IntergenicRegion), isinstance(ann.transcript2, IntergenicRegion) ]): breaks = [ann.break1, ann.break2] transcript = ann.transcript1 if ann.transcript1 is None or isinstance(ann.transcript1, IntergenicRegion): transcript = ann.transcript2 breaks = [ann.break2] elif ann.transcript2 is None or isinstance(ann.transcript2, IntergenicRegion): breaks = [ann.break1] try: svg_group = canvas.g(class_='transcript') svg_group = draw_ustranscript( config, canvas, transcript, drawing_width, breakpoints=breaks, labels=labels, colors=colors, reference_genome=reference_genome ) theights.append(svg_group.height) svg_group.translate(x, y) canvas.add(svg_group) except AttributeError: pass # Intergenic region or None else: # separate drawings try: svg_group = canvas.g(class_='transcript') svg_group = draw_ustranscript( config, canvas, ann.transcript1, half_drawing_width, breakpoints=[ann.break1], labels=labels, colors=colors, reference_genome=reference_genome ) theights.append(svg_group.height) svg_group.translate(x, y) canvas.add(svg_group) except AttributeError: pass # Intergenic region or None try: svg_group = canvas.g(class_='transcript') svg_group = draw_ustranscript( config, canvas, ann.transcript2, half_drawing_width, breakpoints=[ann.break2], labels=labels, colors=colors, reference_genome=reference_genome ) theights.append(svg_group.height) svg_group.translate(second_drawing_shift, y) canvas.add(svg_group) except AttributeError: pass # Intergenic region or None if len(theights) > 0: y += max(theights) + config.inner_margin # finally the fusion transcript level drawing if fusion_transcript and draw_fusion_transcript: for exon in fusion_transcript.exons: colors[exon] = config.novel_exon_color try: old_ex = fusion_transcript.exon_mapping[exon.position] colors[exon] = colors[old_ex] except KeyError: pass svg_group = canvas.g(class_='transcript') svg_group = draw_ustranscript( config, canvas, fusion_transcript, drawing_width, colors=colors, labels=labels, reference_genome=reference_genome ) svg_group.translate(x, y) canvas.add(svg_group) y += svg_group.height + config.inner_margin y += config.bottom_margin - config.inner_margin canvas.attribs['height'] = y for label, obj in labels.items(): if label in legend: continue try: legend[label] = obj.to_dict() except AttributeError: legend[label] = str(obj) # now make the json legend return canvas, legend
[docs]def draw_multi_transcript_overlay(config, gene, vmarkers=None, window_buffer=0, plots=None): vmarkers = [] if vmarkers is None else vmarkers plots = [] if plots is None else plots canvas = Drawing(size=(config.width, 1000)) # just set the height for now and change later width = config.width - config.left_margin - config.right_margin - config.overlay_left_label - config.padding labels = LabelMapping() # keep labels consistent within the drawing all_exons = set() colors = dict() for us_tx in gene.transcripts: for ex in us_tx.exons: all_exons.add(ex) colors[ex] = config.exon1_color if us_tx.is_best_transcript else config.exon2_color for spl_tx in us_tx.transcripts: for translation in spl_tx.translations: for dom in translation.domains: labels.set_key(dom.name, dom.name) st = min([max([gene.start - window_buffer, 1])] + [m.start for m in vmarkers] + [p.xmin for p in plots]) end = max([gene.end + window_buffer] + [m.end for m in vmarkers] + [p.xmax for p in plots]) mapping = generate_interval_mapping( all_exons, width, config.exon_intron_ratio, config.exon_min_width, min_inter_width=config.min_width, start=st, end=end ) main_group = canvas.g(class_='overlay') x = config.overlay_left_label + config.padding y = config.marker_top_margin for plot in plots: plot_group = draw_scatter(config, canvas, plot, mapping) main_group.add(plot_group) plot_group.translate(x, y) y += plot.height + config.padding * 2 regular_transcripts = sorted([us_tx for us_tx in gene.transcripts if not us_tx.is_best_transcript], key=lambda x: x.name) for us_tx in regular_transcripts: group_element = draw_exon_track(config, canvas, us_tx, mapping, colors=colors) main_group.add(group_element) group_element.translate(x, y) text_element = canvas.text( us_tx.name, insert=( x - config.padding, y + config.track_height / 2 + config.font_central_shift_ratio * config.label_font_size ), fill=config.label_color, style=config.font_style.format(font_size=config.label_font_size, text_anchor='end'), class_='label' ) main_group.add(text_element) y += config.padding + config.track_height best_transcripts = sorted([us_tx for us_tx in gene.transcripts if us_tx.is_best_transcript], key=lambda x: x.name) for us_tx in best_transcripts: for spl_tx in us_tx.transcripts: labels[us_tx.name] = spl_tx group_element = draw_ustranscript(config, canvas, us_tx, mapping=mapping, colors=colors, labels=labels) main_group.add(group_element) group_element.translate(x, y) y += config.padding + group_element.height y += config.marker_bottom_margin # now draw the breakpoints overtop for marker in sorted(vmarkers): px_itvl = Interval( Interval.convert_ratioed_pos(mapping, marker.start).start, Interval.convert_ratioed_pos(mapping, marker.end).end) group_element = draw_vmarker( config, canvas, marker, px_itvl.length(), y, label=marker.name) group_element.translate(x + px_itvl.start, 0) main_group.add(group_element) main_group.translate(config.left_margin, config.top_margin) y += config.bottom_margin canvas.add(main_group) canvas.attribs['height'] = y return canvas