JAL-2349 allow PAE or other contact matrices to hold a coordinate mapping allowing...
[jalview.git] / src / jalview / datamodel / ContactMatrix.java
1 package jalview.datamodel;
2
3 import java.awt.Color;
4 import java.math.BigInteger;
5 import java.util.ArrayList;
6 import java.util.BitSet;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Spliterator;
10 import java.util.StringTokenizer;
11
12 import jalview.bin.Console;
13
14 public abstract class ContactMatrix implements ContactMatrixI
15 {
16   /**
17    * are contacts reflexive ?
18    */
19   boolean symmetric = true;
20
21   public ContactMatrix(boolean symmetric)
22   {
23     this.symmetric = symmetric;
24   }
25
26   List<List<Float>> contacts = null;
27
28   int width = 0, numcontacts = 0;
29
30   float min = 0f, max = 0f;
31
32   public void addContact(int left, int right, float strength)
33   {
34     if (left < 0 || right < 0)
35     {
36       throw new Error(new RuntimeException(
37               "Cannot have negative indices for contact left=" + left
38                       + " right=" + right + " strength=" + strength));
39     }
40     if (symmetric)
41     {
42       if (left > right)
43       {
44         // swap
45         int r = right;
46         right = left;
47         left = r;
48       }
49     }
50     if (contacts == null)
51     {
52       // TODO: use sparse list for efficiency ?
53       contacts = new ArrayList<List<Float>>();
54     }
55     List<Float> clist = contacts.get(left);
56     if (clist == null)
57     {
58       clist = new ArrayList<Float>();
59       contacts.set(left, clist);
60     }
61     Float last = clist.set(right, strength);
62     // TODO: if last is non null, may need to recompute range
63     checkBounds(strength);
64     if (last == null)
65     {
66       numcontacts++;
67     }
68   }
69
70   private void checkBounds(float strength)
71   {
72     if (min > strength)
73     {
74       min = strength;
75     }
76     if (max < strength)
77     {
78       max = strength;
79     }
80   }
81
82   @Override
83   public ContactListI getContactList(final int column)
84   {
85     if (column < 0 || column >= width)
86     {
87       return null;
88     }
89
90     return new ContactListImpl(new ContactListProviderI()
91     {
92       int p = column;
93
94       @Override
95       public int getPosition()
96       {
97         return p;
98       }
99
100       @Override
101       public int getContactHeight()
102       {
103         return width;
104
105       }
106
107       @Override
108       public double getContactAt(int column)
109       {
110         List<Float> clist;
111         Float cl = null;
112         if (symmetric)
113         {
114           if (p < column)
115           {
116             clist = contacts.get(p);
117             cl = clist.get(column);
118           }
119           else
120           {
121             clist = contacts.get(column);
122             cl = clist.get(p);
123           }
124         }
125         else
126         {
127           clist = contacts.get(p);
128           cl = clist.get(column);
129         }
130         if (cl == null)
131         {
132           // return 0 not NaN ?
133           return Double.NaN;
134         }
135         return cl.doubleValue();
136       }
137     });
138   }
139
140   @Override
141   public float getMin()
142   {
143     return min;
144   }
145
146   @Override
147   public float getMax()
148   {
149     return max;
150   }
151
152   @Override
153   public String getAnnotLabel()
154   {
155     return "Contact Matrix";
156   }
157
158   @Override
159   public String getAnnotDescr()
160   {
161     return "Contact Matrix";
162   }
163   List<BitSet> groups=null;
164   @Override
165   public void updateGroups(List<BitSet> colGroups)
166   {
167     groups = colGroups;
168     colorMap=new HashMap<>();
169   }
170   @Override
171   public boolean hasGroups()
172   {
173     return groups!=null && groups.size()>0;
174   }
175   @Override
176   public List<BitSet> getGroups()
177   {
178     return groups;
179   }
180   @Override
181   public BitSet getGroupsFor(int column)
182   {
183     for (BitSet gp:groups) {
184       if (gp.get(column))
185       {
186         return gp;
187       }
188     }
189     return ContactMatrixI.super.getGroupsFor(column);
190   }
191   HashMap<BitSet,Color> colorMap = new HashMap<>();
192   @Override 
193   public Color getColourForGroup(BitSet bs)
194   {
195     if (bs==null) {
196       return Color.white;
197     }
198     Color groupCol=colorMap.get(bs);
199     if (groupCol==null)
200     {
201       return Color.white;
202     }
203     return groupCol;
204   }
205   @Override 
206   public void setColorForGroup(BitSet bs,Color color)
207   {
208     colorMap.put(bs,color);
209   }
210   public static String contactToFloatString(ContactMatrixI cm)
211   {
212     StringBuilder sb = new StringBuilder();
213     for (int c=0;c<cm.getWidth();c++)
214     {
215       ContactListI cl=cm.getContactList(c);
216       if (cl!=null) {
217       for (int h=0;h<=cl.getContactHeight();h++)
218       {
219         if (sb.length()>0) {
220           sb.append('\t');
221         }
222         sb.append(cl.getContactAt(h));
223       }
224       }
225     }
226     return sb.toString();
227   }
228
229   public static float[][] fromFloatStringToContacts(String values, int cols,
230           int rows)
231   {
232     float[][] vals = new float[cols][rows];
233     StringTokenizer tabsep = new StringTokenizer(values,""+'\t');
234     int c=0,r=0;
235     
236     while (tabsep.hasMoreTokens())
237     {
238       double elem = Double.valueOf(tabsep.nextToken());
239       vals[c][r++]=(float) elem;
240       if (r>=vals[c].length)
241       {
242         r=0;
243         c++;
244       }
245       if (c>=vals.length)
246       {
247         
248         break;
249       }
250     }
251     if (tabsep.hasMoreElements())
252     {
253       Console.warn("Ignoring additional elements for Float string to contact matrix parsing.");
254     }
255       
256     return vals;
257   }
258 }