diff --git a/CHANGELOG b/CHANGELOG index 1909c941e2dee9c2608c6201331adb66a995a3d3..84a4f00826cc095977f243a75301a3eb02d85d35 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,8 @@ minerva (15.0.0~alpha.1) stable; urgency=medium button icons were missing (regression 15.0.0~alpha.0) * Bug fix: export to CellDesigner names with new line is properly handled (#930) + * Bug fix: layout of the exported reactome pathways is fixed when reactome + exporter violates SBGN specification (#707) minerva (15.0.0~alpha.0) stable; urgency=medium * Improvement: logs provided for validation data model are structurized (#325) diff --git a/converter-SBGNML/src/main/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParser.java b/converter-SBGNML/src/main/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParser.java index 598553d58c86f7b5e188e70a4c80fb53d819a055..67cb9626e0766b663c81428b4892334a7dcb3232 100644 --- a/converter-SBGNML/src/main/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParser.java +++ b/converter-SBGNML/src/main/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParser.java @@ -5,6 +5,7 @@ import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.logging.log4j.LogManager; @@ -13,6 +14,7 @@ import org.sbgn.*; import org.sbgn.bindings.*; import org.sbgn.bindings.Arc.*; +import lcsb.mapviewer.common.Configuration; import lcsb.mapviewer.common.comparator.DoubleComparator; import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.common.geometry.PointTransformation; @@ -21,9 +23,12 @@ import lcsb.mapviewer.converter.ZIndexPopulator; 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; +import lcsb.mapviewer.converter.model.celldesigner.geometry.helper.PolylineDataFactory; import lcsb.mapviewer.converter.model.celldesigner.types.ModifierType; import lcsb.mapviewer.converter.model.celldesigner.types.ModifierTypeUtils; import lcsb.mapviewer.converter.model.sbgnml.structures.Process; +import lcsb.mapviewer.model.LogMarker; +import lcsb.mapviewer.model.ProjectLogEntryType; import lcsb.mapviewer.model.graphics.*; import lcsb.mapviewer.model.map.compartment.Compartment; import lcsb.mapviewer.model.map.compartment.SquareCompartment; @@ -1021,6 +1026,7 @@ public class SbgnmlXmlParser { */ private PolylineData parseLine(Arc a, List<Point2D> pointList) { PolylineData line = new PolylineData(pointList); + line = PolylineDataFactory.removeCollinearPoints(line); ArrowTypeData atd = extractArrowTypeDataFromArc(a); line.setEndAtd(atd.copy()); return line; @@ -1245,16 +1251,15 @@ public class SbgnmlXmlParser { for (Arc a : p.getProductArcs()) { if (((Port) a.getSource()).equals(p.getProductsPort())) { Product product = new Product(); - product.setReaction(reaction); Glyph target = (Glyph) a.getTarget(); product.setElement(model.getElementByElementId(target.getId())); List<Point2D> pointList = getLinePoints(a); PolylineData line = parseLine(a, pointList); product.setLine(line); + if (splitOperator != null) { splitOperator.addOutput(product); } - reaction.addProduct(product); } else { Reactant reactant = new Reactant(); @@ -1273,6 +1278,23 @@ public class SbgnmlXmlParser { } } + Product firstProduct = reaction.getProducts().get(0); + boolean shouldReverse = reaction.getProducts().size() > 1; + for (Product product : reaction.getProducts()) { + if (product.getLine().getBeginPoint().distance(firstProduct.getLine().getBeginPoint()) < Configuration.EPSILON && + product.getLine().getEndPoint().distance(firstProduct.getLine().getEndPoint()) > Configuration.EPSILON) { + shouldReverse = false; + } + } + if (shouldReverse) { + logger.warn(new LogMarker(ProjectLogEntryType.PARSING_ISSUE, "process-arcs", p.getCentralPoint().getId(), + model.getIdModel()), "Product lines should be reversed"); + for (Product product : reaction.getProducts()) { + Collections.reverse(product.getLine().getPoints()); + } + splitOperator.getLine().getBeginPoint().setLocation(reaction.getProducts().get(0).getLine().getBeginPoint()); + } + Point2D centerPointStart = reaction.getReactants().get(0).getLine().getEndPoint(); if (andOperator != null) { andOperator.getLine().trimEnd(ReactionCellDesignerConverter.RECT_SIZE / 2 - 1); diff --git a/converter-SBGNML/src/test/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParserTest2.java b/converter-SBGNML/src/test/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParserTest2.java index 2d0019add6ac9583ce44ab8923441982bb5b971c..4b5ae56c770fe3ee87bafb35d70a91d2b28be768 100644 --- a/converter-SBGNML/src/test/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParserTest2.java +++ b/converter-SBGNML/src/test/java/lcsb/mapviewer/converter/model/sbgnml/SbgnmlXmlParserTest2.java @@ -109,10 +109,35 @@ public class SbgnmlXmlParserTest2 extends SbgnmlTestFunctions { Converter converter = new SbgnmlXmlConverter(); Model model = converter .createModel(new ConverterParams().filename("testFiles/sbgnmlCellDesignerInompatible/stateVariable.sbgn")); - + Protein protein = model.getElementByElementId("glyph_n20"); assertEquals("inactive", protein.getStructuralState().getValue()); assertEquals(0, protein.getModificationResidues().size()); } + @Test + public void testProblematicProduct() throws Exception { + Converter converter = new SbgnmlXmlConverter(); + Model model = converter + .createModel(new ConverterParams() + .filename("testFiles/sbgnmlParserTestFiles/sbgnmlFiles/problematic_product_line.sbgn")); + + Reaction r = model.getReactionByReactionId("reactionVertex_10725167_27892"); + Product p = r.getProducts().get(0); + for (Product product : r.getProducts()) { + assertEquals(2, product.getLine().getPoints().size()); + assertEquals("Product lines should start at the same point", 0, + product.getLine().getBeginPoint().distance(p.getLine().getBeginPoint()), Configuration.EPSILON); + } + SplitOperator operator = null; + for (NodeOperator o : r.getOperators()) { + if (o instanceof SplitOperator) { + operator = (SplitOperator) o; + } + } + + assertEquals(0, operator.getLine().getBeginPoint().distance(p.getLine().getBeginPoint()), Configuration.EPSILON); + + } + } diff --git a/converter-SBGNML/testFiles/sbgnmlParserTestFiles/sbgnmlFiles/problematic_product_line.sbgn b/converter-SBGNML/testFiles/sbgnmlParserTestFiles/sbgnmlFiles/problematic_product_line.sbgn new file mode 100644 index 0000000000000000000000000000000000000000..b7ae37848a4631b5fdf9dc1d67ce00305cc486d8 --- /dev/null +++ b/converter-SBGNML/testFiles/sbgnmlParserTestFiles/sbgnmlFiles/problematic_product_line.sbgn @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<sbgn xmlns="http://sbgn.org/libsbgn/0.2"> + <map language="process description"> + <glyph class="process" id="reactionVertex_10725167_27892"> + <label text="KDM6B demethylates H3K27me3 on p16INK4A promoter"/> + <bbox w="12.0" h="12.0" x="687.0" y="821.0"/> + <port id="reactionVertex_10725167.1" x="694.0" y="790.0"/> + <port id="reactionVertex_10725167.2" x="693.0" y="853.0"/> + </glyph> + <arc class="consumption" id="arc_27893_input_27892" source="entityVertex_113671_27893" target="reactionVertex_10725167.1"> + <glyph class="stoichiometry" id="Stoichiometry_113671_INPUT_10725167"> + <label text="3"/> + <bbox w="12.0" h="12.0" x="708.0" y="782.0"/> + </glyph> + <start x="735.0" y="787.0"/> + <next x="694.0" y="790.0"/> + <end x="694.0" y="790.0"/> + </arc> + <arc class="production" id="arc_27896_output_27892" source="reactionVertex_10725167.2" target="entityVertex_10725157_27896"> + <start x="693.0" y="877.0"/> + <next x="693.0" y="853.0"/> + <end x="693.0" y="853.0"/> + </arc> + <arc class="consumption" id="arc_27894_input_27892" source="entityVertex_10725155_27894" target="reactionVertex_10725167.1"> + <start x="694.0" y="735.0"/> + <next x="694.0" y="790.0"/> + <end x="694.0" y="790.0"/> + </arc> + <arc class="production" id="arc_27895_output_27892" source="reactionVertex_10725167.2" target="entityVertex_159939_27895"> + <glyph class="stoichiometry" id="Stoichiometry_159939_OUTPUT_10725167"> + <label text="3"/> + <bbox w="12.0" h="12.0" x="709.0" y="848.0"/> + </glyph> + <start x="738.0" y="856.0"/> + <next x="693.0" y="853.0"/> + <end x="693.0" y="853.0"/> + </arc> + <arc class="catalysis" id="arc_27897_catalyst_27892" source="entityVertex_10725164_27897" target="reactionVertex_10725167_27892"> + <start x="612.0" y="827.0"/> + <next x="677.0" y="827.0"/> + <end x="677.0" y="827.0"/> + </arc> + + <glyph class="simple chemical" id="entityVertex_113671_27893"> + <label text="2OG"/> + <bbox w="43.0" h="23.0" x="738.0" y="776.0"/> + </glyph> + <glyph class="complex" id="entityVertex_10725157_27896"> + <label text="CDKN2A Gene:Nucleosome"/> + <bbox w="100.0" h="35.0" x="643.0" y="880.0"/> + </glyph> + <glyph class="complex" id="entityVertex_10725155_27894"> + <label text="CDKN2A Gene:H3K27Me3-Nucleosome"/> + <bbox w="103.0" h="54.0" x="643.0" y="678.0"/> + </glyph> + <glyph class="simple chemical" id="entityVertex_159939_27895"> + <label text="SUCCA"/> + <bbox w="63.0" h="26.0" x="741.0" y="845.0"/> + </glyph> + <glyph class="complex" id="entityVertex_10725164_27897"> + <label text="KDM6B:Fe2+"/> + <bbox w="90.0" h="22.0" x="519.0" y="816.0"/> + </glyph> + </map> +</sbgn> \ No newline at end of file