Merge branch 'JAL-1483_29dev' into develop
[jalview.git] / src / jalview / analysis / scoremodels / FeatureScoreModel.java
diff --git a/src/jalview/analysis/scoremodels/FeatureScoreModel.java b/src/jalview/analysis/scoremodels/FeatureScoreModel.java
new file mode 100644 (file)
index 0000000..e2a8b9a
--- /dev/null
@@ -0,0 +1,138 @@
+package jalview.analysis.scoremodels;
+
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.ViewBasedAnalysisI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+
+public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
+{
+  jalview.api.FeatureRenderer fr;
+
+  @Override
+  public boolean configureFromAlignmentView(
+          jalview.api.AlignmentViewPanel view)
+  {
+    fr = view.cloneFeatureRenderer();
+    return true;
+  }
+
+  @Override
+  public float[][] findDistances(AlignmentView seqData)
+  {
+    int nofeats = 0;
+    List<String> dft = Arrays.asList(fr.getDisplayedFeatureTypes());
+
+    if (dft != null)
+    {
+      nofeats = dft.size();
+    }
+
+    SequenceI[] sequenceString = seqData.getVisibleAlignment(
+            Comparison.GapChars.charAt(0)).getSequencesArray();
+    int noseqs = sequenceString.length;
+    int cpwidth = seqData.getWidth();
+    float[][] distance = new float[noseqs][noseqs];
+    if (nofeats == 0)
+    {
+      for (float[] d : distance)
+      {
+        for (int i = 0; i < d.length; d[i++] = 0f)
+        {
+          ;
+        }
+      }
+      return distance;
+    }
+    float max = 0;
+    for (int cpos = 0; cpos < cpwidth; cpos++)
+    {
+      // get visible features at cpos under view's display settings and compare
+      // them
+      List<Hashtable<String, SequenceFeature>> sfap = new ArrayList<Hashtable<String, SequenceFeature>>();
+      for (int i = 0; i < noseqs; i++)
+      {
+        Hashtable<String, SequenceFeature> types = new Hashtable<String, SequenceFeature>();
+        List<SequenceFeature> sfs = fr.findFeaturesAtRes(sequenceString[i],
+                sequenceString[i].findPosition(cpos));
+        for (SequenceFeature sf : sfs)
+        {
+          types.put(sf.getType(), sf);
+        }
+        sfap.add(types);
+      }
+      for (int i = 0; i < (noseqs - 1); i++)
+      {
+        if (cpos == 0)
+        {
+          distance[i][i] = 0f;
+        }
+        for (int j = i + 1; j < noseqs; j++)
+        {
+          int sfcommon = 0;
+          // compare the two lists of features...
+          Hashtable<String, SequenceFeature> fi = sfap.get(i), fk, fj = sfap
+                  .get(j);
+          if (fi.size() > fj.size())
+          {
+            fk = fj;
+          }
+          else
+          {
+            fk = fi;
+            fi = fj;
+          }
+          for (String k : fi.keySet())
+          {
+            SequenceFeature sfj = fk.get(k);
+            if (sfj != null)
+            {
+              sfcommon++;
+            }
+          }
+          distance[i][j] += (fi.size() + fk.size() - 2f * sfcommon);
+          distance[j][i] += distance[i][j];
+        }
+      }
+    }
+    for (int i = 0; i < noseqs; i++)
+    {
+      for (int j = i + 1; j < noseqs; j++)
+      {
+        distance[i][j] /= cpwidth;
+        distance[j][i] = distance[i][j];
+      }
+    }
+    return distance;
+  }
+
+  @Override
+  public String getName()
+  {
+    return "Sequence Feature Similarity";
+  }
+
+  @Override
+  public boolean isDNA()
+  {
+    return true;
+  }
+
+  @Override
+  public boolean isProtein()
+  {
+    return true;
+  }
+
+  public String toString()
+  {
+    return "Score between sequences based on hamming distance between binary vectors marking features displayed at each column";
+  }
+}