From 00130dba29ed9561b0e1b21b7fd174e6aee217e5 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Wed, 6 Nov 2019 10:28:59 +0100
Subject: [PATCH] problem with reactome sbgn product layout fixed

---
 CHANGELOG                                     |  2 +
 .../model/sbgnml/SbgnmlXmlParser.java         | 26 +++++++-
 .../model/sbgnml/SbgnmlXmlParserTest2.java    | 27 +++++++-
 .../sbgnmlFiles/problematic_product_line.sbgn | 65 +++++++++++++++++++
 4 files changed, 117 insertions(+), 3 deletions(-)
 create mode 100644 converter-SBGNML/testFiles/sbgnmlParserTestFiles/sbgnmlFiles/problematic_product_line.sbgn

diff --git a/CHANGELOG b/CHANGELOG
index 1909c941e2..84a4f00826 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 598553d58c..67cb9626e0 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 2d0019add6..4b5ae56c77 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 0000000000..b7ae37848a
--- /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
-- 
GitLab