Commit 6224cc0c authored by Piotr Gawron's avatar Piotr Gawron
Browse files

export/import annotations from sbgnml

parent 41491483
Pipeline #28551 failed with stage
in 11 minutes and 44 seconds
minerva (15.1.0) unstable; urgency=medium
* Small improvement: annotations are exported in SBGN extension that can be
opened by newt (#1296)
minerva (15.0.0~beta.5) unstable; urgency=medium
* Backward incompatible: truncation/association/dissocation points are
represented as a dot in all new images/uploaded maps (#1265)
......
......@@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
import lcsb.mapviewer.annotation.cache.*;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.model.map.*;
/**
......@@ -36,6 +37,8 @@ public final class MiriamConnector extends CachableInterface implements IExterna
* Default class logger.
*/
private Logger logger = LogManager.getLogger(MiriamConnector.class);
XmlAnnotationParser xap = new XmlAnnotationParser();
/**
* Default class constructor. Prevent initialization.
......@@ -90,13 +93,6 @@ public final class MiriamConnector extends CachableInterface implements IExterna
} else if (miriamData.getDataType().getUris().size() == 0) {
throw new InvalidArgumentException("Url for " + miriamData.getDataType() + " cannot be retreived.");
}
String id;
if (miriamData.getDataType().getNamespace().isEmpty()) {
id = miriamData.getResource();
} else {
id = miriamData.getDataType().getNamespace() + ":" + miriamData.getResource();
}
String query = LINK_DB_PREFIX + miriamData.getDataType().getUris().get(0) + "\n" + miriamData.getResource();
String result = getCacheValue(query);
if (result != null) {
......@@ -106,7 +102,8 @@ public final class MiriamConnector extends CachableInterface implements IExterna
return result;
}
try {
result = getRedirectURL("https://identifiers.org/" + id);
String identifiersOrgUrl = xap.getIdentifiersOrgUrl(miriamData);
result = getRedirectURL(identifiersOrgUrl);
if (result != null) {
setCacheValue(query, result);
......
......@@ -2,12 +2,13 @@ package lcsb.mapviewer.converter.model.sbgnml;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.*;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.lang3.RandomStringUtils;
......@@ -18,12 +19,14 @@ import org.sbgn.*;
import org.sbgn.bindings.*;
import org.sbgn.bindings.Arc.*;
import org.sbgn.bindings.Map;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import lcsb.mapviewer.common.comparator.DoubleComparator;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.geometry.ColorParser;
import lcsb.mapviewer.converter.ConverterException;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.graphics.bioEntity.element.species.SpeciesConverter;
import lcsb.mapviewer.converter.graphics.bioEntity.reaction.ReactionConverter;
import lcsb.mapviewer.model.LogMarker;
......@@ -98,6 +101,8 @@ public class SbgnmlXmlExporter {
ColorParser colorParser = new ColorParser();
XmlAnnotationParser xap = new XmlAnnotationParser(new ArrayList<>(), true);
/**
* Transforms model into SBGN-ML xml.
*
......@@ -213,11 +218,39 @@ public class SbgnmlXmlExporter {
}
}
addRenderInformation(newGlyph, element);
addExtensions(newGlyph, element);
sourceTargetMap.put(element.getElementId(), newGlyph);
return newGlyph;
}
private void addExtensions(Glyph newGlyph, Element element) {
SBGNBase.Extension extension = new SBGNBase.Extension();
newGlyph.setExtension(extension);
org.w3c.dom.Element annotation = getAnnotationExtension(element);
if (annotation != null) {
extension.getAny().add(annotation);
}
}
private org.w3c.dom.Element getAnnotationExtension(Element element) {
try {
String content =xap.dataSetToXmlString(element.getMiriamData(), element.getElementId());
content = "<annotation>" + content + "</annotation>";
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db;
db = dbf.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(content.getBytes()));
return doc.getDocumentElement();
} catch (Exception e) {
logger.error(new LogMarker(ProjectLogEntryType.EXPORT_ISSUE, element), "Problem with exporting annotations", e);
return null;
}
}
private void addRenderInformation(Glyph glyph, Element element) {
float lineWidth = 1.0f;
if (element instanceof Species) {
......
......@@ -12,14 +12,18 @@ import org.sbgn.*;
import org.sbgn.bindings.*;
import org.sbgn.bindings.Arc.*;
import org.sbgn.bindings.Map;
import org.w3c.dom.Node;
import lcsb.mapviewer.common.Configuration;
import lcsb.mapviewer.common.XmlParser;
import lcsb.mapviewer.common.comparator.DoubleComparator;
import lcsb.mapviewer.common.exception.InvalidArgumentException;
import lcsb.mapviewer.common.exception.InvalidXmlSchemaException;
import lcsb.mapviewer.common.geometry.ColorParser;
import lcsb.mapviewer.common.geometry.PointTransformation;
import lcsb.mapviewer.converter.InvalidInputDataExecption;
import lcsb.mapviewer.converter.ZIndexPopulator;
import lcsb.mapviewer.converter.annotation.XmlAnnotationParser;
import lcsb.mapviewer.converter.graphics.bioEntity.element.species.SpeciesConverter;
import lcsb.mapviewer.converter.model.celldesigner.geometry.CellDesignerAliasConverter;
import lcsb.mapviewer.converter.model.celldesigner.geometry.ReactionCellDesignerConverter;
......@@ -874,6 +878,12 @@ public class SbgnmlXmlParser {
* model to be updated
*/
private void parseSpecies(Glyph glyph, Species species, Model model) {
if (glyph.getExtension() != null) {
for (org.w3c.dom.Element element : glyph.getExtension().getAny()) {
parseExtensionNode(element, species);
}
}
species.setHeight(new Double(glyph.getBbox().getH()));
species.setWidth(new Double(glyph.getBbox().getW()));
species.setX(new Double(glyph.getBbox().getX()));
......@@ -910,6 +920,29 @@ public class SbgnmlXmlParser {
model.addElement(species);
}
private void parseExtensionNode(org.w3c.dom.Element node, Element element) {
if (node.getNodeName().equals("annotation")) {
parseAnnotationNode(node, element);
} else {
logger.warn(new LogMarker(ProjectLogEntryType.PARSING_ISSUE, element),
"Unknown extension: " + node.getNodeName());
}
}
private void parseAnnotationNode(org.w3c.dom.Element node, Element element) {
Node rdf = XmlParser.getNode("rdf:RDF", node);
if (rdf == null) {
logger.warn(new LogMarker(ProjectLogEntryType.PARSING_ISSUE, element),
"No rdf node provided in annotation extension");
} else {
try {
element.addMiriamData(new XmlAnnotationParser().parseRdfNode(rdf));
} catch (InvalidXmlSchemaException e) {
logger.error(e, e);
}
}
}
private void assignRenderInformation(Element element, Glyph glyph) {
if (renderInformation != null) {
Style style = RenderUtil.getStyle(renderInformation, glyph);
......
......@@ -8,6 +8,7 @@ import org.junit.runners.Suite.SuiteClasses;
@SuiteClasses({ CellDesigner2SbgnmlConversionTest.class,
CellDesignerToSbgnTest.class,
DbSerializationTest.class,
ParseNewtExtensionTest.class,
SbgnmlSerializationTest.class,
SbgnmlXmlExporterTest.class,
SbgnmlXmlParserTest.class,
......
package lcsb.mapviewer.converter.model.sbgnml;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
import lcsb.mapviewer.converter.ConverterParams;
import lcsb.mapviewer.model.map.model.Model;
import lcsb.mapviewer.model.map.model.ModelComparator;
public class ParseNewtExtensionTest extends SbgnmlTestFunctions {
Logger logger = LogManager.getLogger();
@Test
public void createModelTest() throws Exception {
SbgnmlXmlConverter parser = new SbgnmlXmlConverter();
Model model = parser.createModel(new ConverterParams().filename("testFiles/newt/annotation.nwt"));
assertEquals(1, model.getElements().iterator().next().getMiriamData().size());
String xml = parser.model2String(model);
Model model2 = parser.createModel(new ConverterParams().inputStream(new ByteArrayInputStream(xml.getBytes()), "testFiles/newt/annotation.nwt"));
assertEquals(0, new ModelComparator().compare(model, model2));
}
}
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<sbgn xmlns="http://sbgn.org/libsbgn/0.2">
<map language="process description">
<extension>
<renderInformation
xmlns="http://www.sbml.org/sbml/level3/version1/render/version1" id="renderInformation" program-name="sbgnviz" program-version="6.0.1" background-color="#00000000">
<listOfColorDefinitions>
<colorDefinition id="color_1" value="#ffffffff"/>
<colorDefinition id="color_2" value="#555555"/>
</listOfColorDefinitions>
<listOfBackgroundImages/>
<listOfStyles>
<style id="nodeffffff5555551.2511normalnormalHelvetica00011" idList="nwtN_d184ed92-00d1-47a2-a230-0ff177c120ba">
<g font-size="11" font-family="Helvetica" font-weight="normal" font-style="normal" font-color="#000" stroke="color_2" stroke-width="1.25" fill="color_1" background-image-opacity="1" background-opacity="1"/>
</style>
</listOfStyles>
</renderInformation>
<mapProperties>
<compoundPadding>0</compoundPadding>
<extraCompartmentPadding>14</extraCompartmentPadding>
<extraComplexPadding>10</extraComplexPadding>
<arrowScale>1.25</arrowScale>
<showComplexName>true</showComplexName>
<dynamicLabelSize>regular</dynamicLabelSize>
<inferNestingOnLoad>false</inferNestingOnLoad>
<fitLabelsToNodes>false</fitLabelsToNodes>
<fitLabelsToInfoboxes>false</fitLabelsToInfoboxes>
<recalculateLayoutOnComplexityManagement>true</recalculateLayoutOnComplexityManagement>
<rearrangeOnComplexityManagement>true</rearrangeOnComplexityManagement>
<animateOnDrawingChanges>true</animateOnDrawingChanges>
<adjustNodeLabelFontSizeAutomatically>false</adjustNodeLabelFontSizeAutomatically>
<enablePorts>true</enablePorts>
<enableSIFTopologyGrouping>false</enableSIFTopologyGrouping>
<allowCompoundNodeResize>true</allowCompoundNodeResize>
<mapColorScheme>black_white</mapColorScheme>
<mapColorSchemeStyle>solid</mapColorSchemeStyle>
<mapName>Pathway #1</mapName>
<mapDescription/>
</mapProperties>
</extension>
<glyph id="nwtN_d184ed92-00d1-47a2-a230-0ff177c120ba" class="macromolecule">
<extension>
<annotation>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:bqmodel="http://biomodels.net/model-qualifiers/">
<rdf:Description rdf:about="#nwtN_d184ed92-00d1-47a2-a230-0ff177c120ba">
<bqmodel:is>
<rdf:Bag>
<rdf:li rdf:resource="http://identifiers.org/CHEBI:12345"/>
</rdf:Bag>
</bqmodel:is>
</rdf:Description>
</rdf:RDF>
</annotation>
</extension>
<label text="xyz"/>
<bbox x="770" y="149" w="60" h="30"/>
</glyph>
</map>
</sbgn>
\ No newline at end of file
......@@ -32,6 +32,8 @@ public class XmlAnnotationParser {
private Set<MiriamRelationType> supportedRelationTypes = new HashSet<>();
private boolean useIdentifiersOrgFormat = false;
/**
* Default constructor.
*/
......@@ -47,6 +49,11 @@ public class XmlAnnotationParser {
}
}
public XmlAnnotationParser(Collection<MiriamRelationType> supportedRelationTypes, boolean useIdentifiersOrgFormat) {
this(supportedRelationTypes);
this.useIdentifiersOrgFormat = useIdentifiersOrgFormat;
}
/**
* This method parse the xml string passed as an argument. All information
* obtained by the method are stored in local variables: miriamDataSet,
......@@ -374,8 +381,13 @@ public class XmlAnnotationParser {
}
result.append("<" + relationType.getStringRepresentation() + ">\n");
result.append("<rdf:Bag>\n");
result.append("<rdf:li rdf:resource=\"" + data.getDataType().getUris().get(0) + ":"
+ data.getResource().replaceAll(":", "%3A") + "\"/>\n");
String uri;
if (useIdentifiersOrgFormat) {
uri = getIdentifiersOrgUrl(data);
} else {
uri = data.getDataType().getUris().get(0) + ":" + data.getResource().replaceAll(":", "%3A");
}
result.append("<rdf:li rdf:resource=\"" + uri + "\"/>\n");
result.append("</rdf:Bag>\n");
result.append("</" + relationType.getStringRepresentation() + ">\n");
......@@ -410,4 +422,14 @@ public class XmlAnnotationParser {
return result;
}
public String getIdentifiersOrgUrl(MiriamData miriamData) {
String id;
if (miriamData.getDataType().getNamespace().isEmpty()) {
id = miriamData.getResource();
} else {
id = miriamData.getDataType().getNamespace() + ":" + miriamData.getResource();
}
return "https://identifiers.org/" + id;
}
}
......@@ -115,7 +115,8 @@ public enum MiriamType {
CHEBI("Chebi",
"http://www.ebi.ac.uk/chebi/",
new String[] { "urn:miriam:obo.chebi", "urn:miriam:chebi",
"http://identifiers.org/chebi/", "http://identifiers.org/obo.chebi/" },
"http://identifiers.org/chebi/", "http://identifiers.org/obo.chebi/",
"http://identifiers.org/chebi", "https://identifiers.org/chebi" },
new Class<?>[] { Chemical.class, Drug.class, }, "MIR:00000002",
new Class<?>[] { Chemical.class },
"",
......@@ -229,7 +230,10 @@ public enum MiriamType {
ECO("Evidence Code Ontology",
"https://www.ebi.ac.uk/ols/ontologies/eco",
new String[] { "urn:miriam:obo.eco", "http://identifiers.org/obo.eco/" },
new String[] { "urn:miriam:obo.eco", "http://identifiers.org/obo.eco/",
"http://identifiers.org/ec-code",
"https://identifiers.org/ec-code",
},
new Class<?>[] { Protein.class, Gene.class, Rna.class }, "MIR:00000055",
new Class<?>[] {},
"",
......@@ -270,7 +274,10 @@ public enum MiriamType {
*/
ENTREZ("Entrez Gene",
"http://www.ncbi.nlm.nih.gov/gene",
new String[] { "urn:miriam:ncbigene", "urn:miriam:entrez.gene", "http://identifiers.org/ncbigene/" },
new String[] { "urn:miriam:ncbigene", "urn:miriam:entrez.gene", "http://identifiers.org/ncbigene/",
"http://identifiers.org/ncbigene",
"https://identifiers.org/ncbigene",
},
new Class<?>[] { Protein.class, Gene.class, Rna.class }, "MIR:00000069",
new Class<?>[] {},
"ncbigene",
......@@ -298,7 +305,10 @@ public enum MiriamType {
GO("Gene Ontology",
"http://amigo.geneontology.org/amigo",
new String[] { "urn:miriam:obo.go", "urn:miriam:go", "http://identifiers.org/go/",
"http://identifiers.org/obo.go/" },
"http://identifiers.org/obo.go/",
"http://identifiers.org/GO",
"https://identifiers.org/GO",
},
new Class<?>[] { Phenotype.class, Compartment.class, Complex.class }, "MIR:00000022",
new Class<?>[] {},
"",
......@@ -309,7 +319,10 @@ public enum MiriamType {
*/
HGNC("HGNC",
"http://www.genenames.org",
new String[] { "urn:miriam:hgnc", "https://identifiers.org/hgnc/" },
new String[] { "urn:miriam:hgnc", "https://identifiers.org/hgnc/",
"http://identifiers.org/hgnc",
"https://identifiers.org/hgnc",
},
new Class<?>[] { Protein.class, Gene.class, Rna.class }, "MIR:00000080",
new Class<?>[] { Protein.class, Gene.class, Rna.class },
"hgnc",
......@@ -350,7 +363,10 @@ public enum MiriamType {
*/
INTERPRO("InterPro",
"http://www.ebi.ac.uk/interpro/",
new String[] { "urn:miriam:interpro", "http://identifiers.org/interpro/" },
new String[] { "urn:miriam:interpro", "http://identifiers.org/interpro/",
"http://identifiers.org/interpro",
"https://identifiers.org/interpro",
},
new Class<?>[] { Protein.class, Complex.class }, "MIR:00000011",
new Class<?>[] {},
"interpro",
......@@ -456,7 +472,10 @@ public enum MiriamType {
*/
MESH_2012("MeSH",
"http://www.nlm.nih.gov/mesh/",
new String[] { "urn:miriam:mesh", "urn:miriam:mesh.2012", "https://identifiers.org/mesh/" },
new String[] { "urn:miriam:mesh", "urn:miriam:mesh.2012", "https://identifiers.org/mesh/",
"http://identifiers.org/mesh",
"https://identifiers.org/mesh",
},
new Class<?>[] { Phenotype.class, Compartment.class, Complex.class }, "MIR:00000560",
new Class<?>[] {},
"mesh",
......@@ -598,7 +617,10 @@ public enum MiriamType {
*/
PUBCHEM("PubChem-compound",
"http://pubchem.ncbi.nlm.nih.gov/",
new String[] { "urn:miriam:pubchem.compound", "https://identifiers.org/pubchem.compound/" },
new String[] { "urn:miriam:pubchem.compound", "https://identifiers.org/pubchem.compound/",
"http://identifiers.org/pubchem.compound",
"https://identifiers.org/pubchem.compound",
},
new Class<?>[] { Chemical.class }, "MIR:00000034",
new Class<?>[] { Chemical.class },
"pubchem.compound",
......@@ -620,7 +642,10 @@ public enum MiriamType {
*/
PUBMED("PubMed",
"http://www.ncbi.nlm.nih.gov/PubMed/",
new String[] { "urn:miriam:pubmed", "http://identifiers.org/pubmed/" },
new String[] { "urn:miriam:pubmed", "http://identifiers.org/pubmed/",
"http://identifiers.org/pubmed",
"https://identifiers.org/pubmed",
},
new Class<?>[] { BioEntity.class }, "MIR:00000015",
new Class<?>[] { Reaction.class },
"pubmed",
......@@ -631,7 +656,10 @@ public enum MiriamType {
*/
REACTOME("Reactome",
"http://www.reactome.org/",
new String[] { "urn:miriam:reactome", "http://identifiers.org/reactome/" },
new String[] { "urn:miriam:reactome", "http://identifiers.org/reactome/",
"http://identifiers.org/reactome",
"https://identifiers.org/reactome",
},
new Class<?>[] { Reaction.class }, "MIR:00000018",
new Class<?>[] {},
"reactome",
......@@ -785,7 +813,10 @@ public enum MiriamType {
*/
UNIPROT("Uniprot",
"http://www.uniprot.org/",
new String[] { "urn:miriam:uniprot", "http://identifiers.org/uniprot/" },
new String[] { "urn:miriam:uniprot", "http://identifiers.org/uniprot/",
"http://identifiers.org/uniprot",
"https://identifiers.org/uniprot",
},
new Class<?>[] { Protein.class, Gene.class, Rna.class }, "MIR:00000005",
new Class<?>[] {},
"uniprot",
......@@ -852,7 +883,9 @@ public enum MiriamType {
*/
WIKIPATHWAYS("WikiPathways",
"http://www.wikipathways.org/",
new String[] { "urn:miriam:wikipathways", "https://identifiers.org/wikipathways/" },
new String[] { "urn:miriam:wikipathways", "https://identifiers.org/wikipathways/",
"http://identifiers.org/wikipathways",
"https://identifiers.org/wikipathways" },
new Class<?>[] {}, "MIR:00000076",
new Class<?>[] {},
"wikipathways",
......@@ -1071,22 +1104,25 @@ public enum MiriamType {
// "%3A" and also the last ":"
miriamUri = miriamUri.replace("%3A", ":");
String miriamUriLowerCase = miriamUri.toLowerCase();
String foundUri = "";
MiriamType foundType = null;
for (MiriamType type : MiriamType.values()) {
for (String uri : type.getUris()) {
if (miriamUri.startsWith(uri + ":")) {
uri = uri.toLowerCase();
if (miriamUriLowerCase.startsWith(uri + ":")) {
if (uri.length() > foundUri.length()) {
foundType = type;
foundUri = uri;
}
} else if (miriamUri.startsWith(uri) && uri.endsWith("/")) {
} else if (miriamUriLowerCase.startsWith(uri) && uri.endsWith("/")) {
if (uri.length() > foundUri.length()) {
foundType = type;
foundUri = uri;
}
} else if (miriamUri.startsWith(uri) && uri.endsWith("/")) {
} else if (miriamUriLowerCase.startsWith(uri) && uri.endsWith("/")) {
if (uri.length() > foundUri.length()) {
foundType = type;
foundUri = uri;
......@@ -1099,7 +1135,11 @@ public enum MiriamType {
throw new InvalidArgumentException("Invalid miriam uri: " + miriamUri);
}
int uriLength = foundUri.length();
if (!foundUri.endsWith("/")) { // add one character for separator
if (foundUri.contains("://identifiers.org/") && !foundUri.replaceAll("://identifiers.org/", "").contains("/")
&& foundType.getNamespace() == null
|| foundType.getNamespace().isEmpty()) {
uriLength = foundUri.lastIndexOf("/") + 1;
} else if (!foundUri.endsWith("/")) { // add one character for separator
uriLength++;
}
String resource = miriamUri.substring(uriLength);
......
......@@ -100,6 +100,12 @@ public class MiriamTypeTest extends ModelTestFunctions {
assertEquals(new MiriamData(MiriamType.UNIPROT, "P42224"), md);
}
@Test
public void testGetMiriamByChebiIdentifiersOrgUri() throws Exception {
MiriamData md = MiriamType.getMiriamByUri("http://identifiers.org/CHEBI:12345");
assertEquals(new MiriamData(MiriamType.CHEBI, "CHEBI:12345"), md);
}
@Test(expected = InvalidArgumentException.class)
public void testGetMiriamByInvalidUri() throws Exception {
MiriamType.getMiriamByUri("invalid_uri");
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment