コード例 #1
0
def create_layout(n2lo, layout_model, layout_plugin, ub_sps, model):
    h_min, (x_shift, y_shift), (w, h) = get_layout_characteristics(n2lo)
    scale_factor = MARGIN * 1.0 / (h_min if h_min else 1)
    (w, h) = scale((w, h), scale_factor)
    (x_shift, y_shift) = shift(scale((x_shift, y_shift), scale_factor), MARGIN, MARGIN)

    layout = layout_plugin.createLayout()
    layout.setId(generate_unique_id(layout_model, "l_"))
    l_id = layout.getId()
    layout.setDimensions(create_dimensions(w + 2 * MARGIN, h + 2 * MARGIN))

    for comp in model.getListOfCompartments():
        c_id = comp.getId()
        c_name = comp.getName()
        if c_id in n2lo:
            (x, y), (w, h) = n2lo[c_id]
            (x, y), (w, h) = shift(scale((x, y), scale_factor), x_shift, y_shift), scale((w, h), scale_factor)
            comp_glyph = layout.createCompartmentGlyph()
            comp_glyph.setId("cg_%s_%s" % (l_id, c_id))
            comp_glyph.setCompartmentId(c_id)
            comp_glyph.setBoundingBox(create_bounding_box(x, y, w, h))
            add_label(c_name, layout, comp_glyph, c_id, w, h, x, y)

    for species in model.getListOfSpecies():
        s_id = species.getId()
        s_name = species.getName()
        if s_id in n2lo:
            if isinstance(n2lo[s_id], dict):
                elements = n2lo[s_id].items()
            else:
                elements = [('', n2lo[s_id])]
            for r_ids, [(x, y), (w, h)] in elements:
                if not r_ids or next((it for it in (model.getReaction(r_id) for r_id in r_ids) if it), False):
                    (x, y), (w, h) = shift(scale((x, y), scale_factor), x_shift, y_shift), scale((w, h), scale_factor)
                    s_glyph = layout.createSpeciesGlyph()
                    s_glyph.setSpeciesId(s_id)
                    s_glyph_suffix = "%s_%s" % (s_id, '_'.join(r_ids)) if r_ids else s_id
                    s_glyph.setId("sg_%s_%s" % (l_id, s_glyph_suffix))
                    s_glyph.setBoundingBox(create_bounding_box(x, y, w, h))
                    add_label(s_name, layout, s_glyph, s_id, w, h, x, y)

    for reaction in model.getListOfReactions():
        r_id = reaction.getId()
        r_name = reaction.getName()
        if r_id in n2lo:
            (x, y), (w, h) = n2lo[r_id]
            (x, y), (w, h) = shift(scale((x, y), scale_factor), x_shift, y_shift), scale((w, h), scale_factor)
            r_glyph = layout.createReactionGlyph()
            r_glyph.setReactionId(r_id)
            r_glyph.setId("rg_%s_%s" % (l_id, r_id))
            r_glyph.setBoundingBox(create_bounding_box(x, y, w, h))
            add_label(r_name, layout, r_glyph, r_id, w, h, x, y)
            link_reaction_to_species(reaction.getListOfReactants(), r_glyph, l_id, r_id, n2lo,
                                     lambda s_id: libsbml.SPECIES_ROLE_SIDESUBSTRATE
                                     if s_id in ub_sps else libsbml.SPECIES_ROLE_SUBSTRATE)
            link_reaction_to_species(reaction.getListOfProducts(), r_glyph, l_id, r_id, n2lo,
                                     lambda s_id: libsbml.SPECIES_ROLE_SIDEPRODUCT
                                     if s_id in ub_sps else libsbml.SPECIES_ROLE_PRODUCT)
コード例 #2
0
def save_as_cytoscape_json(n2lo, model, out_json, ub_sp_ids):
    """
    Converts a model with the given node and edge layout to a json file readable by Cytoscape.
    :param ub_sp_ids: collection of ubiquitous species ids
    :param n2lo: node layout as a dictionary    {node_id: ((x, y), (w, h)) if node is not ubiquitous
                                                else node_id : {r_ids: ((x, y), (w, h)) for r_ids of
                                                reactions using each duplicated metabolite}}
    :param model: SBML model
    :param out_json: path where to save the resulting json file.
    """
    # let's scale the map so that a minimal node has a width == 16 (so that the labels fit)
    h_min, (x_shift, y_shift), _ = get_layout_characteristics(n2lo)
    scale_factor = MARGIN * 1.0 / h_min if h_min else 1
    (x_shift, y_shift) = shift(scale((x_shift, y_shift), scale_factor), MARGIN, MARGIN)

    nodes, edges = [], []

    for comp in model.getListOfCompartments():
        c_id = comp.getId()
        c_name = get_name(comp)
        if c_id in n2lo:
            (x, y), (w, h) = transform(n2lo[c_id], x_shift, y_shift, scale_factor)
            nodes.append(get_node(x=x + w / 2, y=y + h / 2, z=-1,
                                  **{NAME: c_name, ID: c_id, WEIGHT: w, HEIGHT: h, TYPE: TYPE_COMPARTMENT}))

    for species in model.getListOfSpecies():
        s_id = species.getId()
        s_name = get_name(species)
        glyph_type = UNSPECIFIED_ENTITY
        sbo_term = species.getSBOTermID()
        if sbo_term:
            sbo_term = sbo_term.upper().strip()
            if sbo_term in SBO_2_GLYPH_TYPE:
                glyph_type = SBO_2_GLYPH_TYPE[sbo_term]
        if s_id in n2lo:
            if isinstance(n2lo[s_id], dict):
                elements = n2lo[s_id].items()
            else:
                elements = [('', n2lo[s_id])]
            for r_ids, coords in elements:
                if not r_ids or next((it for it in (model.getReaction(r_id) for r_id in r_ids) if it), False):
                    (x, y), (w, h) = transform(coords, x_shift, y_shift, scale_factor)
                    nodes.append(
                        get_node(x=x + w / 2, y=y + h / 2, z=1,
                                 **{GLYPH_TYPE: glyph_type, ID: "%s_%s" % (s_id, '_'.join(r_ids)) if r_ids else s_id,
                                    COMPARTMENT: species.getCompartment(), NAME: s_name, WEIGHT: w, HEIGHT: h,
                                    TYPE: TYPE_SPECIES, UBIQUITOUS: s_id in ub_sp_ids,
                                    'ChEBI': get_chebi_id(species)}))

    def get_sref_id(s_id):
        if isinstance(n2lo[s_id], dict):
            for r_ids in n2lo[s_id].keys():
                if r_id in r_ids:
                    return "%s_%s" % (s_id, '_'.join(r_ids))
        return s_id

    for reaction in model.getListOfReactions():
        r_id = reaction.getId()
        r_name = get_name(reaction)
        ga = get_gene_association(reaction)
        if r_id in n2lo:
            (x, y), (w, h) = transform(n2lo[r_id], x_shift, y_shift, scale_factor)
            nodes.append(get_node(x=x + w / 2, y=y + h / 2, z=1,
                                  **{ID: r_id, NAME: ga, WEIGHT: w, HEIGHT: h, TYPE: TYPE_REACTION,
                                     REVERSIBLE: reaction.getReversible(),
                                     UBIQUITOUS:
                                         next((False for s_id in chain(get_reactants(reaction), get_products(reaction))
                                               if s_id not in ub_sp_ids), True),
                                     'genes': ga, 'r_name': r_name}))

            for s_id in get_reactants(reaction):
                edges.append(get_edge(**{ID: "%s_%s" % (r_id, s_id), SOURCE: r_id, TARGET: get_sref_id(s_id),
                                             NAME: '%s is a substrate of %s' % (get_name(model.getSpecies(s_id)), r_name),
                                             UBIQUITOUS: s_id in ub_sp_ids, INTERACTION: SUBSTRATE}))

            for s_id in get_products(reaction):
                edges.append(get_edge(**{ID: "%s_%s" % (r_id, s_id), SOURCE: r_id, TARGET: get_sref_id(s_id),
                                             NAME: '%s is a product of %s' % (get_name(model.getSpecies(s_id)), r_name),
                                             UBIQUITOUS: s_id in ub_sp_ids, INTERACTION: PRODUCT}))

    save_cyjson(nodes, edges, out_json)
コード例 #3
0
ファイル: mimoza_pipeline.py プロジェクト: annazhukova/mimoza
def process_sbml(sbml,
                 verbose,
                 ub_ch_ids=None,
                 web_page_prefix=None,
                 generalize=True,
                 log_file=None,
                 id2mask=None,
                 layer2mask=DEFAULT_LAYER2MASK,
                 tab2html=None,
                 title=None,
                 h1=None,
                 id2color=None,
                 tabs={ABOUT_TAB, DOWNLOAD_TAB},
                 info='',
                 invisible_layers=None,
                 sbgn=True,
                 cytoscape=True):
    """
    Generalizes and visualizes a given SBML model.
    :param sbml: a path to the input SBML file
    :param verbose: if logging information should be printed
    :param ub_ch_ids: optional, ChEBI ids to be considered as ubiquitous. If left None, will be calculated automatically.
    :param web_page_prefix: optional, how this model's webpage will be identified.
    If left None an identifier will be generated based on the SBML file's md5.
    :param generalize: optional, whether the generalization should be performed. The default is True
    :param log_file: optional, a file where the logging information should be redirected
    (only needed if verbose is set to True)
    :param id2mask: optional,
    :param layer2mask: optional, a dict storing the correspondence between a layer name and an its id mask
    :param tab2html: optional,
    :param title: optional, the title for the web page
    :param h1: optional, the main header of the web page
    :param id2color: optional,
    :param tabs: optional, a set of names of tabs that should be shown
    :param info: optional, additional information to be displayed in the bottom of the web page
    :param invisible_layers: optional, the layers of the visualized metabolic map that should be hidden
    :return: void
    """
    # Read the SBML
    reader = libsbml.SBMLReader()
    doc = reader.readSBML(sbml)
    model = doc.getModel()
    if not model:
        raise Exception(
            "The model should be in SBML format, check your file %s" % sbml)
    model_id = model.getId()
    if not model_id:
        sbml_name = os.path.splitext(os.path.basename(sbml))[0]
        model.setId(sbml_name)
        model_id = sbml_name

    # Prepare the output directories
    web_page_prefix = web_page_prefix if web_page_prefix else check_md5(sbml)
    sbml_dir = dirname(abspath(sbml))
    directory = os.path.join(sbml_dir, web_page_prefix)
    if not os.path.exists(directory):
        os.makedirs(directory)
    lib_path = os.path.join(directory, 'lib')
    if not os.path.exists(lib_path):
        copytree(get_lib(), lib_path)

    # Prepare the logger
    if verbose:
        logging.captureWarnings(True)
        logging.basicConfig(level=logging.INFO,
                            format='%(asctime)s: %(message)s',
                            datefmt="%Y-%m-%d %H:%M:%S",
                            filename=log_file)

    # Generalize the model if needed
    groups_sbml = os.path.join(directory, '%s_with_groups.xml' % model_id)
    gen_sbml = os.path.join(directory, '%s_generalized.xml' % model_id)
    if check_for_groups(sbml, SBO_CHEMICAL_MACROMOLECULE,
                        GROUP_TYPE_UBIQUITOUS):
        if sbml != groups_sbml:
            if not libsbml.SBMLWriter().writeSBMLToFile(doc, groups_sbml):
                raise Exception("Could not write your model to %s" %
                                groups_sbml)
    else:
        chebi = parse_simple(get_chebi())
        if generalize:
            logging.info('Generalizing the model...')
            generalize_model(sbml,
                             chebi,
                             groups_sbml,
                             gen_sbml,
                             ub_chebi_ids=ub_ch_ids)
        else:
            gen_sbml = None
            logging.info('Ubiquitizing the model...')
            ubiquitize_model(sbml, chebi, groups_sbml, ub_chebi_ids=ub_ch_ids)

    # Visualize the model
    reader = libsbml.SBMLReader()
    input_document = reader.readSBML(groups_sbml)
    input_model = input_document.getModel()

    root, c_id2info, c_id2outs, chebi, ub_sps = import_sbml(
        input_model, groups_sbml)

    c_id2out_c_id = {}
    for c_id, c_info in c_id2info.items():
        _, _, (_, out_c_id) = c_info
        if out_c_id:
            c_id2out_c_id[c_id] = out_c_id
    try:
        n2xy = parse_layout_sbml(sbml)
        if n2xy:
            logging.info('Found layout in the model...')
            r_size = next((n2xy[r.getId()][1][0]
                           for r in input_model.getListOfReactions()
                           if r.getId() in n2xy), None)
            if r_size:
                scale_factor = REACTION_SIZE / r_size
                if scale_factor != 1:
                    keys = n2xy.keys()
                    for n_id in keys:
                        value = n2xy[n_id]
                        if isinstance(value, dict):
                            value = {
                                r_id:
                                (scale(xy,
                                       scale_factor), scale(wh, scale_factor))
                                for (r_id, (xy, wh)) in value.items()
                            }
                        else:
                            xy, wh = value
                            value = scale(xy, scale_factor), scale(
                                wh, scale_factor)
                        n2xy[n_id] = value
    except LoPlError:
        n2xy = None
    fc, (n2lo, e2lo), hidden_c_ids, c_id_hidden_ubs = \
        graph2geojson(c_id2info, c_id2outs, root, n2xy, id2mask=id2mask, onto=chebi,
                      colorer=color if not id2color else lambda graph: color_by_id(graph, id2color))
    if n2lo:
        groups_document = reader.readSBML(groups_sbml)
        groups_model = groups_document.getModel()
        if gen_sbml:
            gen_document = reader.readSBML(gen_sbml)
            gen_model = gen_document.getModel()
        else:
            gen_model = False
        save_as_layout_sbml(groups_model, gen_model, groups_sbml, gen_sbml,
                            n2lo, ub_sps)

        if sbgn:
            groups_sbgn = os.path.join(directory, '%s.sbgn' % model_id)
            gen_sbgn = os.path.join(directory,
                                    '%s_generalized.sbgn' % model_id)

            try:
                save_as_sbgn(n2lo, e2lo, groups_model, groups_sbgn)
                logging.info('   exported as SBGN %s' % groups_sbgn)
            except Exception as e:
                logging.error("Didn't manage to save to SBGN: %s" % e)

            if gen_model:
                try:
                    save_as_sbgn(n2lo, e2lo, gen_model, gen_sbgn)
                    logging.info('   exported as SBGN %s' % groups_sbgn)
                except Exception as e:
                    logging.error("Didn't manage to save to SBGN: %s" % e)

        if cytoscape:
            out_json = os.path.join(directory, '%s.cyjs' % model_id)
            save_as_cytoscape_json(n2lo, model, out_json, ub_sps)
            logging.info('   exported as Cytoscape json %s' % out_json)

            if gen_model:
                out_json = os.path.join(directory,
                                        '%s_generalized.cyjs' % model_id)
                save_as_cytoscape_json(n2lo, gen_model, out_json, ub_sps)

    # Serialize the result
    serialize(directory=directory,
              m_dir_id=web_page_prefix,
              input_model=input_model,
              c_id2level2features=fc,
              c_id2out_c_id=c_id2out_c_id,
              hidden_c_ids=hidden_c_ids,
              c_id_hidden_ubs=c_id_hidden_ubs,
              tabs=tabs,
              groups_sbml=groups_sbml,
              layer2mask=layer2mask,
              tab2html=tab2html,
              title=title,
              h1=h1,
              invisible_layers=invisible_layers)
コード例 #4
0
ファイル: sbgn_helper.py プロジェクト: annazhukova/mimoza
def save_as_sbgn(n2lo, e2lo, model, out_sbgn):
    """
    Converts a model with the given node and edge layout to SBGN PD (see http://www.sbgn.org/).
    :param n2lo: node layout as a dictionary    {node_id: ((x, y), (w, h)) if node is not ubiquitous
                                                else node_id : {r_ids: ((x, y), (w, h)) for r_ids of
                                                reactions using each duplicated metabolite}}
    :param e2lo: edge layout as a dictionary    {edge_id: [(x_start, y_start), (x_bend_0, y_bend_0),..,(x_end, y_end)]},
                                                where edge_id = "-".join(sorted((metabolite_id, reaction_id))).
    :param model: SBML model
    :param out_sbgn: path where to save the resulting SBGN file.
    """
    # let's scale the map so that a minimal node has a width == 16 (so that the labels fit)
    h_min, (x_shift, y_shift), (w, h) = get_layout_characteristics(n2lo)
    scale_factor = MARGIN * 1.0 / h_min if h_min else 1
    (w, h) = scale((w, h), scale_factor)
    (x_shift, y_shift) = shift(scale((x_shift, y_shift), scale_factor), MARGIN, MARGIN)

    # create empty sbgn
    sbgn = libsbgn.sbgn()

    # create map, set language and set in sbgn
    sbgn_map = libsbgn.map()
    sbgn_map.set_language(Language.PD)
    sbgn.set_map(sbgn_map)

    # create a bounding box for the map
    box = libsbgn.bbox(0, 0, w + 2 * MARGIN, h + 2 * MARGIN)
    sbgn_map.set_bbox(box)

    # glyphs with labels
    for comp in model.getListOfCompartments():
        c_id = comp.getId()
        c_name = comp.getName()
        if not c_name:
            c_name = c_id
        if c_id in n2lo:
            (x, y), (w, h) = transform(n2lo[c_id], x_shift, y_shift, scale_factor)
            g = libsbgn.glyph(class_=GlyphClass.COMPARTMENT, id=c_id)
            g.set_label(libsbgn.label(text=c_name, bbox=libsbgn.bbox(x, y, w, h)))
            g.set_bbox(libsbgn.bbox(x, y, w, h))
            sbgn_map.add_glyph(g)

    for species in model.getListOfSpecies():
        s_id = species.getId()
        s_name = species.getName()
        glyph_type = GlyphClass.UNSPECIFIED_ENTITY
        sbo_term = species.getSBOTermID()
        if sbo_term:
            sbo_term = sbo_term.upper().strip()
            if sbo_term in SBO_2_GLYPH_TYPE:
                glyph_type = SBO_2_GLYPH_TYPE[sbo_term]
        if not s_name:
            s_name = s_id
        if s_id in n2lo:
            if isinstance(n2lo[s_id], dict):
                elements = n2lo[s_id].items()
            else:
                elements = [('', n2lo[s_id])]
            for r_ids, coords in elements:
                if not r_ids or next((it for it in (model.getReaction(r_id) for r_id in r_ids) if it), False):
                    (x, y), (w, h) = transform(coords, x_shift, y_shift, scale_factor)
                    g = libsbgn.glyph(class_=glyph_type, id="%s_%s" % (s_id, '_'.join(r_ids)) if r_ids else s_id,
                                      compartmentRef=species.getCompartment())
                    g.set_label(libsbgn.label(text=s_name,
                                              bbox=libsbgn.bbox(x + w * 0.1, y + h * 0.1, w * 0.8, h * 0.8)))
                    g.set_bbox(libsbgn.bbox(x, y, w, h))
                    sbgn_map.add_glyph(g)

    # glyph with ports (process)
    for reaction in model.getListOfReactions():
        r_id = reaction.getId()
        if r_id in n2lo:
            (x, y), (w, h) = transform(n2lo[r_id], x_shift, y_shift, scale_factor)
            g = libsbgn.glyph(class_=GlyphClass.PROCESS, id=r_id)
            g.set_bbox(libsbgn.bbox(x, y, w, h))
            rev = reaction.getReversible()

            in_port = None
            for s_id in (species_ref.getSpecies() for species_ref in reaction.getListOfReactants()):
                edge_id = "-".join(sorted((s_id, r_id)))
                if edge_id in e2lo:
                    xy_list = e2lo[edge_id]
                    if not in_port:
                        port_x, port_y = shift(scale(xy_list[-2] if len(xy_list) > 2 else xy_list[-1], scale_factor),
                                               x_shift, y_shift)
                        in_port = libsbgn.port(x=port_x, y=port_y, id="%s__in" % r_id)
                        g.add_port(in_port)
                    sref_id = s_id
                    if isinstance(n2lo[s_id], dict):
                        for r_ids in n2lo[s_id].keys():
                            if r_id in r_ids:
                                sref_id = "%s_%s" % (s_id, '_'.join(r_ids))
                    a = libsbgn.arc(class_=ArcClass.PRODUCTION if rev else ArcClass.CONSUMPTION,
                                    target=sref_id if rev else in_port.get_id(),
                                    source=in_port.get_id() if rev else sref_id, id="a_%s_%s" % (s_id, r_id))
                    s_x, s_y = shift(scale(xy_list[0], scale_factor), x_shift, y_shift)
                    a.set_start(libsbgn.startType(x=in_port.get_x() if rev else s_x, y=in_port.get_y() if rev else s_y))
                    a.set_end(libsbgn.endType(x=s_x if rev else in_port.get_x(), y=s_y if rev else in_port.get_y()))
                    sbgn_map.add_arc(a)
            out_port = None
            for s_id in (species_ref.getSpecies() for species_ref in reaction.getListOfProducts()):
                edge_id = "-".join(sorted((s_id, r_id)))
                if edge_id in e2lo:
                    xy_list = e2lo[edge_id]
                    if not out_port:
                        port_x, port_y = shift(scale(xy_list[1] if len(xy_list) > 2 else xy_list[0], scale_factor),
                                               x_shift, y_shift)
                        out_port = libsbgn.port(x=port_x, y=port_y, id="%s__out" % r_id)
                        g.add_port(out_port)
                    sref_id = s_id
                    if isinstance(n2lo[s_id], dict):
                        for r_ids in n2lo[s_id].keys():
                            if r_id in r_ids:
                                sref_id = "%s_%s" % (s_id, '_'.join(r_ids))
                    a = libsbgn.arc(class_=ArcClass.PRODUCTION, target=sref_id, source=out_port.get_id(),
                                    id="a_%s_%s" % (r_id, s_id))
                    s_x, s_y = shift(scale(xy_list[-1], scale_factor), x_shift, y_shift)
                    a.set_end(libsbgn.startType(x=s_x, y=s_y))
                    a.set_start(libsbgn.endType(x=out_port.get_x(), y=out_port.get_y()))
                    sbgn_map.add_arc(a)
            sbgn_map.add_glyph(g)

    # write everything to a file
    sbgn.write_file(out_sbgn)
コード例 #5
0
def save_as_sbgn(n2lo, e2lo, model, out_sbgn):
    """
    Converts a model with the given node and edge layout to SBGN PD (see http://www.sbgn.org/).
    :param n2lo: node layout as a dictionary    {node_id: ((x, y), (w, h)) if node is not ubiquitous
                                                else node_id : {r_ids: ((x, y), (w, h)) for r_ids of
                                                reactions using each duplicated metabolite}}
    :param e2lo: edge layout as a dictionary    {edge_id: [(x_start, y_start), (x_bend_0, y_bend_0),..,(x_end, y_end)]},
                                                where edge_id = "-".join(sorted((metabolite_id, reaction_id))).
    :param model: SBML model
    :param out_sbgn: path where to save the resulting SBGN file.
    """
    # let's scale the map so that a minimal node has a width == 16 (so that the labels fit)
    h_min, (x_shift, y_shift), (w, h) = get_layout_characteristics(n2lo)
    scale_factor = MARGIN * 1.0 / h_min if h_min else 1
    (w, h) = scale((w, h), scale_factor)
    (x_shift, y_shift) = shift(scale((x_shift, y_shift), scale_factor), MARGIN,
                               MARGIN)

    # create empty sbgn
    sbgn = libsbgn.sbgn()

    # create map, set language and set in sbgn
    sbgn_map = libsbgn.map()
    sbgn_map.set_language(Language.PD)
    sbgn.set_map(sbgn_map)

    # create a bounding box for the map
    box = libsbgn.bbox(0, 0, w + 2 * MARGIN, h + 2 * MARGIN)
    sbgn_map.set_bbox(box)

    # glyphs with labels
    for comp in model.getListOfCompartments():
        c_id = comp.getId()
        c_name = comp.getName()
        if not c_name:
            c_name = c_id
        if c_id in n2lo:
            (x, y), (w, h) = transform(n2lo[c_id], x_shift, y_shift,
                                       scale_factor)
            g = libsbgn.glyph(class_=GlyphClass.COMPARTMENT, id=c_id)
            g.set_label(
                libsbgn.label(text=c_name, bbox=libsbgn.bbox(x, y, w, h)))
            g.set_bbox(libsbgn.bbox(x, y, w, h))
            sbgn_map.add_glyph(g)

    for species in model.getListOfSpecies():
        s_id = species.getId()
        s_name = species.getName()
        glyph_type = GlyphClass.UNSPECIFIED_ENTITY
        sbo_term = species.getSBOTermID()
        if sbo_term:
            sbo_term = sbo_term.upper().strip()
            if sbo_term in SBO_2_GLYPH_TYPE:
                glyph_type = SBO_2_GLYPH_TYPE[sbo_term]
        if not s_name:
            s_name = s_id
        if s_id in n2lo:
            if isinstance(n2lo[s_id], dict):
                elements = n2lo[s_id].items()
            else:
                elements = [('', n2lo[s_id])]
            for r_ids, coords in elements:
                if not r_ids or next(
                    (it for it in (model.getReaction(r_id)
                                   for r_id in r_ids) if it), False):
                    (x, y), (w, h) = transform(coords, x_shift, y_shift,
                                               scale_factor)
                    g = libsbgn.glyph(
                        class_=glyph_type,
                        id="%s_%s" %
                        (s_id, '_'.join(r_ids)) if r_ids else s_id,
                        compartmentRef=species.getCompartment())
                    g.set_label(
                        libsbgn.label(text=s_name,
                                      bbox=libsbgn.bbox(
                                          x + w * 0.1, y + h * 0.1, w * 0.8,
                                          h * 0.8)))
                    g.set_bbox(libsbgn.bbox(x, y, w, h))
                    sbgn_map.add_glyph(g)

    # glyph with ports (process)
    for reaction in model.getListOfReactions():
        r_id = reaction.getId()
        if r_id in n2lo:
            (x, y), (w, h) = transform(n2lo[r_id], x_shift, y_shift,
                                       scale_factor)
            g = libsbgn.glyph(class_=GlyphClass.PROCESS, id=r_id)
            g.set_bbox(libsbgn.bbox(x, y, w, h))
            rev = reaction.getReversible()

            in_port = None
            for s_id in (species_ref.getSpecies()
                         for species_ref in reaction.getListOfReactants()):
                edge_id = "-".join(sorted((s_id, r_id)))
                if edge_id in e2lo:
                    xy_list = e2lo[edge_id]
                    if not in_port:
                        port_x, port_y = shift(
                            scale(
                                xy_list[-2] if len(xy_list) > 2 else
                                xy_list[-1], scale_factor), x_shift, y_shift)
                        in_port = libsbgn.port(x=port_x,
                                               y=port_y,
                                               id="%s__in" % r_id)
                        g.add_port(in_port)
                    sref_id = s_id
                    if isinstance(n2lo[s_id], dict):
                        for r_ids in n2lo[s_id].keys():
                            if r_id in r_ids:
                                sref_id = "%s_%s" % (s_id, '_'.join(r_ids))
                    a = libsbgn.arc(
                        class_=ArcClass.PRODUCTION
                        if rev else ArcClass.CONSUMPTION,
                        target=sref_id if rev else in_port.get_id(),
                        source=in_port.get_id() if rev else sref_id,
                        id="a_%s_%s" % (s_id, r_id))
                    s_x, s_y = shift(scale(xy_list[0], scale_factor), x_shift,
                                     y_shift)
                    a.set_start(
                        libsbgn.startType(x=in_port.get_x() if rev else s_x,
                                          y=in_port.get_y() if rev else s_y))
                    a.set_end(
                        libsbgn.endType(x=s_x if rev else in_port.get_x(),
                                        y=s_y if rev else in_port.get_y()))
                    sbgn_map.add_arc(a)
            out_port = None
            for s_id in (species_ref.getSpecies()
                         for species_ref in reaction.getListOfProducts()):
                edge_id = "-".join(sorted((s_id, r_id)))
                if edge_id in e2lo:
                    xy_list = e2lo[edge_id]
                    if not out_port:
                        port_x, port_y = shift(
                            scale(
                                xy_list[1] if len(xy_list) > 2 else xy_list[0],
                                scale_factor), x_shift, y_shift)
                        out_port = libsbgn.port(x=port_x,
                                                y=port_y,
                                                id="%s__out" % r_id)
                        g.add_port(out_port)
                    sref_id = s_id
                    if isinstance(n2lo[s_id], dict):
                        for r_ids in n2lo[s_id].keys():
                            if r_id in r_ids:
                                sref_id = "%s_%s" % (s_id, '_'.join(r_ids))
                    a = libsbgn.arc(class_=ArcClass.PRODUCTION,
                                    target=sref_id,
                                    source=out_port.get_id(),
                                    id="a_%s_%s" % (r_id, s_id))
                    s_x, s_y = shift(scale(xy_list[-1], scale_factor), x_shift,
                                     y_shift)
                    a.set_end(libsbgn.startType(x=s_x, y=s_y))
                    a.set_start(
                        libsbgn.endType(x=out_port.get_x(),
                                        y=out_port.get_y()))
                    sbgn_map.add_arc(a)
            sbgn_map.add_glyph(g)

    # write everything to a file
    sbgn.write_file(out_sbgn)