Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
MappableContactMatrix | 38 | 129 | 80 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.ws.datamodel.alphafold; | |
22 | ||
23 | import java.awt.Color; | |
24 | import java.util.ArrayList; | |
25 | import java.util.BitSet; | |
26 | ||
27 | import jalview.datamodel.ContactListI; | |
28 | import jalview.datamodel.ContactListImpl; | |
29 | import jalview.datamodel.ContactListProviderI; | |
30 | import jalview.datamodel.ContactMatrixI; | |
31 | import jalview.datamodel.GroupSet; | |
32 | import jalview.datamodel.GroupSetI; | |
33 | import jalview.datamodel.Mapping; | |
34 | import jalview.datamodel.SequenceI; | |
35 | import jalview.util.MapList; | |
36 | import jalview.ws.datamodel.MappableContactMatrixI; | |
37 | ||
38 | public abstract class MappableContactMatrix<T extends MappableContactMatrix<T>> | |
39 | implements MappableContactMatrixI | |
40 | { | |
41 | /** | |
42 | * the matrix that is being mapped to | |
43 | */ | |
44 | protected ContactMatrixI mappedMatrix = null; | |
45 | ||
46 | 3917 | public ContactListI getContactList(int column) |
47 | { | |
48 | 3917 | return mappedMatrix.getContactList(column); |
49 | } | |
50 | ||
51 | 120 | public float getMin() |
52 | { | |
53 | 120 | return mappedMatrix.getMin(); |
54 | } | |
55 | ||
56 | 127 | public float getMax() |
57 | { | |
58 | 127 | return mappedMatrix.getMax(); |
59 | } | |
60 | ||
61 | 2142 | public int getWidth() |
62 | { | |
63 | 2142 | return mappedMatrix.getWidth(); |
64 | } | |
65 | ||
66 | 103448 | public int getHeight() |
67 | { | |
68 | 103448 | return mappedMatrix.getHeight(); |
69 | } | |
70 | ||
71 | 0 | @Override |
72 | public ContactMatrixI getMappedMatrix() | |
73 | { | |
74 | 0 | return mappedMatrix; |
75 | } | |
76 | ||
77 | 14270 | @Override |
78 | public GroupSetI getGroupSet() | |
79 | { | |
80 | 14270 | return mappedMatrix.getGroupSet(); |
81 | }; | |
82 | ||
83 | 2 | @Override |
84 | public void setGroupSet(GroupSet makeGroups) | |
85 | { | |
86 | 2 | mappedMatrix.setGroupSet(makeGroups); |
87 | } | |
88 | ||
89 | /** | |
90 | * the sequence and how it is mapped to the matrix | |
91 | */ | |
92 | ||
93 | SequenceI refSeq = null; | |
94 | ||
95 | MapList toSeq = null; | |
96 | ||
97 | /** | |
98 | * the length that refSeq is expected to be (excluding gaps, of course) | |
99 | */ | |
100 | int length; | |
101 | ||
102 | 142233 | @Override |
103 | public boolean hasReferenceSeq() | |
104 | { | |
105 | 142233 | return (refSeq != null); |
106 | } | |
107 | ||
108 | 118 | @Override |
109 | public SequenceI getReferenceSeq() | |
110 | { | |
111 | 118 | return refSeq; |
112 | } | |
113 | ||
114 | 11 | @Override |
115 | public MapList getMapFor(SequenceI mapSeq) | |
116 | { | |
117 | 11 | if (refSeq != null) |
118 | { | |
119 | 14 | while (mapSeq != refSeq && mapSeq.getDatasetSequence() != null) |
120 | { | |
121 | 3 | mapSeq = mapSeq.getDatasetSequence(); |
122 | } | |
123 | 11 | if (mapSeq != refSeq) |
124 | { | |
125 | 0 | return null; |
126 | } | |
127 | } | |
128 | else | |
129 | { | |
130 | 0 | if (mapSeq != null) |
131 | { | |
132 | // our MapList does not concern this seq | |
133 | 0 | return null; |
134 | } | |
135 | } | |
136 | ||
137 | 11 | return toSeq; |
138 | } | |
139 | ||
140 | /** | |
141 | * set the reference sequence and construct the mapping between the start-end | |
142 | * positions of given sequence and row/columns of contact matrix | |
143 | * | |
144 | * @param _refSeq | |
145 | */ | |
146 | 161 | public void setRefSeq(SequenceI _refSeq) |
147 | { | |
148 | 161 | refSeq = _refSeq; |
149 | 163 | while (refSeq.getDatasetSequence() != null) |
150 | { | |
151 | 2 | refSeq = refSeq.getDatasetSequence(); |
152 | } | |
153 | 161 | length = _refSeq.getEnd() - _refSeq.getStart() + 1; |
154 | // if (length!=refSeq.getLength() || _refSeq.getStart()!=1) | |
155 | { | |
156 | 161 | toSeq = new MapList( |
157 | new int[] | |
158 | { _refSeq.getStart(), _refSeq.getEnd() }, | |
159 | new int[] | |
160 | { 0, length - 1 }, 1, 1); | |
161 | } | |
162 | } | |
163 | ||
164 | 40 | public T liftOver(SequenceI newRefSeq, Mapping sp2sq) |
165 | { | |
166 | 40 | if (sp2sq.getMappedWidth() != sp2sq.getWidth()) |
167 | { | |
168 | // TODO: employ getWord/MappedWord to transfer annotation between cDNA and | |
169 | // Protein reference frames | |
170 | 0 | throw new Error( |
171 | "liftOver currently not implemented for transfer of annotation between different types of seqeunce"); | |
172 | } | |
173 | 40 | boolean mapIsTo = (sp2sq != null) ? (sp2sq.getTo() == refSeq) : false; |
174 | ||
175 | /** | |
176 | * map from matrix to toSeq's coordinate frame | |
177 | */ | |
178 | 40 | int[] refMap = toSeq.locateInFrom(0, length - 1); |
179 | 40 | ArrayList<Integer> newFromMap = new ArrayList<Integer>(); |
180 | 40 | int last = -1; |
181 | 80 | for (int i = 0; i < refMap.length; i += 2) |
182 | { | |
183 | /* | |
184 | * for each contiguous range in toSeq, locate corresponding range in sequence mapped to toSeq by sp2sq | |
185 | */ | |
186 | 40 | int[] sp2map = mapIsTo |
187 | ? sp2sq.getMap().locateInFrom(refMap[i], refMap[i + 1]) | |
188 | : sp2sq.getMap().locateInTo(refMap[i], refMap[i + 1]); | |
189 | 40 | if (sp2map == null) |
190 | { | |
191 | 0 | continue; |
192 | } | |
193 | ||
194 | 81 | for (int spm = 0; spm < sp2map.length; spm += 2) |
195 | { | |
196 | ||
197 | 41 | if (last > -1) |
198 | { | |
199 | 1 | if (sp2map[spm] != last + 1) |
200 | { | |
201 | 1 | newFromMap.add(sp2map[spm]); |
202 | } | |
203 | else | |
204 | { | |
205 | 0 | newFromMap.remove(newFromMap.size() - 1); |
206 | } | |
207 | } | |
208 | else | |
209 | { | |
210 | 40 | newFromMap.add(sp2map[spm]); |
211 | } | |
212 | 41 | last = sp2map[spm + 1]; |
213 | 41 | newFromMap.add(last); |
214 | } | |
215 | } | |
216 | 40 | if ((newFromMap.size() % 2) != 0) |
217 | { | |
218 | // should have had an even number of int ranges! | |
219 | 0 | throw new Error("PAEMatrix liftover failed."); |
220 | } | |
221 | 40 | int fromIntMap[] = new int[newFromMap.size()]; |
222 | 40 | int ipos = 0; |
223 | 40 | for (Integer i : newFromMap) |
224 | { | |
225 | 82 | fromIntMap[ipos++] = i; |
226 | } | |
227 | 40 | MapList newFromMapList = new MapList(fromIntMap, |
228 | new int[] | |
229 | { 0, length - 1 }, 1, 1); | |
230 | ||
231 | 40 | T newCM = newMappableContactMatrix(newRefSeq, newFromMapList); |
232 | 40 | return newCM; |
233 | } | |
234 | ||
235 | protected abstract T newMappableContactMatrix(SequenceI newRefSeq, | |
236 | MapList newFromMapList); | |
237 | ||
238 | 5 | @Override |
239 | public int[] getMappedPositionsFor(final SequenceI localFrame, | |
240 | final int column) | |
241 | { | |
242 | 5 | return getMappedPositionsFor(localFrame, column, column); |
243 | } | |
244 | ||
245 | 5 | @Override |
246 | public int[] getMappedPositionsFor(final SequenceI localFrame, int from, | |
247 | int to) | |
248 | { | |
249 | 5 | if (localFrame == null) |
250 | { | |
251 | 0 | throw new Error("Unimplemented when no local sequence given."); |
252 | } | |
253 | 5 | SequenceI lf = localFrame, uf = refSeq; |
254 | ||
255 | // check that localFrame is derived from refSeq | |
256 | // just look for dataset sequences and check they are the same. | |
257 | // in future we could use DBRefMappings/whatever. | |
258 | 6 | while (lf.getDatasetSequence() != null |
259 | || uf.getDatasetSequence() != null) | |
260 | { | |
261 | 1 | if (lf.getDatasetSequence() != null) |
262 | { | |
263 | 1 | lf = lf.getDatasetSequence(); |
264 | } | |
265 | 1 | if (uf.getDatasetSequence() != null) |
266 | { | |
267 | 0 | uf = uf.getDatasetSequence(); |
268 | } | |
269 | } | |
270 | 5 | if (lf != uf) |
271 | { | |
272 | // could try harder to find a mapping | |
273 | 0 | throw new Error("This Matrix associated with '" + refSeq.getName() |
274 | + "' is not mappable for the given localFrame sequence. (" | |
275 | + localFrame.getName() + ")"); | |
276 | } | |
277 | ||
278 | // now look up from-to matrix columns in toSeq frame | |
279 | ||
280 | 5 | if (toSeq == null) |
281 | { | |
282 | // no mapping - so we assume 1:1 | |
283 | 0 | return new int[] { from, to }; |
284 | } | |
285 | // from-to are matrix columns | |
286 | // first locate on reference sequence | |
287 | ||
288 | 5 | int[] mappedPositions = toSeq.locateInFrom(from, to); |
289 | 5 | if (mappedPositions == null) |
290 | { | |
291 | 2 | return null; |
292 | } | |
293 | ||
294 | // and now map to localFrame | |
295 | // from-to columns on the associated sequence should be | |
296 | // i. restricted to positions in localFrame | |
297 | // ii. | |
298 | ||
299 | // int s = -1, e = -1; | |
300 | // for (int p = 0; p < mappedPositions.length; p++) | |
301 | // { | |
302 | // if (s == -1 && mappedPositions[p] >= localFrame.getStart()) | |
303 | // { | |
304 | // s = p; // remember first position within local frame | |
305 | // } | |
306 | // if (e == -1 || mappedPositions[p] <= localFrame.getEnd()) | |
307 | // { | |
308 | // // update end pointer | |
309 | // e = p; | |
310 | // // compute local map | |
311 | // mappedPositions[p] = localFrame.findIndex(mappedPositions[p]); | |
312 | // } | |
313 | // } | |
314 | // int[] _trimmed = new int[e - s + 1]; | |
315 | // return _trimmed; | |
316 | 3 | return mappedPositions; |
317 | } | |
318 | ||
319 | 3680 | @Override |
320 | public ContactListI getMappableContactList(final SequenceI localFrame, | |
321 | final int column) | |
322 | { | |
323 | 3680 | final int _column; |
324 | 3680 | final int _lcolumn; |
325 | 3680 | if (localFrame == null) |
326 | { | |
327 | 0 | throw new Error("Unimplemented when no local sequence given."); |
328 | } | |
329 | // return a ContactListI for column | |
330 | // column is index into localFrame | |
331 | // 1. map column to corresponding column in matrix | |
332 | 3680 | final MappableContactMatrix us = this; |
333 | 3680 | _lcolumn = localFrame.findPosition(column); |
334 | ||
335 | 3680 | if (toSeq != null) |
336 | { | |
337 | 3680 | SequenceI lf = localFrame, uf = refSeq; |
338 | ||
339 | // just look for dataset sequences and check they are the same. | |
340 | // in future we could use DBRefMappings/whatever. | |
341 | 7240 | while (lf.getDatasetSequence() != null |
342 | || uf.getDatasetSequence() != null) | |
343 | { | |
344 | 3560 | if (lf.getDatasetSequence() != null) |
345 | { | |
346 | 3560 | lf = lf.getDatasetSequence(); |
347 | } | |
348 | 3560 | if (uf.getDatasetSequence() != null) |
349 | { | |
350 | 2 | uf = uf.getDatasetSequence(); |
351 | } | |
352 | } | |
353 | 3680 | if (lf != uf) |
354 | { | |
355 | // could try harder to find a mapping | |
356 | 0 | throw new Error("This Matrix associated with '" + refSeq.getName() |
357 | + "' is not mappable for the given localFrame sequence. (" | |
358 | + localFrame.getName() + ")"); | |
359 | } | |
360 | // check the mapping to see if localFrame _lcolumn exists | |
361 | 3680 | int[] word = toSeq.locateInTo(_lcolumn, _lcolumn); |
362 | 3680 | if (word == null) |
363 | { | |
364 | 11 | return null; |
365 | } | |
366 | 3669 | _column = word[0]; |
367 | } | |
368 | else | |
369 | { | |
370 | // no mapping | |
371 | 0 | _column = _lcolumn; |
372 | } | |
373 | ||
374 | // TODO - remove ? this may be a redundant check | |
375 | 3669 | if (_column < 0 || ((toSeq != null && _column > toSeq.getToHighest()) |
376 | || (toSeq == null && getHeight() <= _column))) | |
377 | { | |
378 | 0 | return null; |
379 | } | |
380 | ||
381 | // 2. resolve ranges in matrix corresponding to range in localFrame | |
382 | 3669 | final int[] matrixRange = toSeq == null |
383 | ? new int[] | |
384 | { localFrame.getStart(), localFrame.getEnd() } | |
385 | : toSeq.locateInTo(localFrame.getStart(), localFrame.getEnd()); | |
386 | ||
387 | 3669 | int h = 0; |
388 | 7338 | for (int p = 0; p < matrixRange.length; p += 2) |
389 | { | |
390 | 3669 | h += 1 + Math.abs(matrixRange[p + 1] - matrixRange[p]); |
391 | } | |
392 | 3669 | final int rangeHeight = h; |
393 | // 3. Construct ContactListImpl instance for just those segments. | |
394 | ||
395 | 3669 | return new ContactListImpl(new ContactListProviderI() |
396 | { | |
397 | ||
398 | 0 | public int getColumn() |
399 | { | |
400 | 0 | return column; |
401 | } | |
402 | ||
403 | 0 | @Override |
404 | public int getPosition() | |
405 | { | |
406 | 0 | return _column; |
407 | } | |
408 | ||
409 | 287087 | @Override |
410 | public int getContactHeight() | |
411 | { | |
412 | 287087 | return rangeHeight; |
413 | } | |
414 | ||
415 | 435459 | @Override |
416 | public double getContactAt(int mcolumn) | |
417 | { | |
418 | 435468 | if (mcolumn < 0 || mcolumn >= rangeHeight) |
419 | { | |
420 | 0 | return -1; |
421 | } | |
422 | 435475 | return getElementAt(_column, locateInRange(mcolumn)); |
423 | ||
424 | // this code maps from mcolumn to localFrame - but that isn't what's | |
425 | // needed | |
426 | // int loccolumn = localFrame.findPosition(mcolumn); | |
427 | // int[] lcolumn=(toSeq==null) ? new int[] {mcolumn} : | |
428 | // toSeq.locateInTo(loccolumn,loccolumn); | |
429 | // if (lcolumn==null || lcolumn[0] < 0 || lcolumn[0] >= rangeHeight) | |
430 | // { | |
431 | // return -1; | |
432 | // } | |
433 | // return getElementAt(_column,lcolumn[0]); | |
434 | } | |
435 | ||
436 | 142114 | @Override |
437 | public int[] getMappedPositionsFor(int cStart, int cEnd) | |
438 | { | |
439 | 142114 | if (!hasReferenceSeq()) |
440 | { | |
441 | 0 | return ContactListProviderI.super.getMappedPositionsFor(cStart, |
442 | cEnd); | |
443 | } | |
444 | // map into segment of matrix being shown | |
445 | 142113 | int realCstart = locateInRange(cStart); |
446 | 142112 | int realCend = locateInRange(cEnd); |
447 | ||
448 | // TODO account for discontinuities in the mapping | |
449 | ||
450 | 142111 | int[] mappedPositions = toSeq.locateInFrom(realCstart, realCend); |
451 | 142106 | if (mappedPositions != null) |
452 | { | |
453 | 142106 | int s = -1, e = -1; |
454 | 426303 | for (int p = 0; p < mappedPositions.length; p++) |
455 | { | |
456 | 284222 | if (s == -1 && mappedPositions[p] >= localFrame.getStart()) |
457 | { | |
458 | 142103 | s = p; // remember first position within local frame |
459 | } | |
460 | 284166 | if (e == -1 || mappedPositions[p] <= localFrame.getEnd()) |
461 | { | |
462 | // update end pointer | |
463 | 284166 | e = p; |
464 | // compute local map | |
465 | 284167 | mappedPositions[p] = localFrame.findIndex(mappedPositions[p]); |
466 | } | |
467 | } | |
468 | } | |
469 | 142114 | return mappedPositions; |
470 | } | |
471 | ||
472 | /** | |
473 | * @return the mcolumn'th position in the matrixRange window on the matrix | |
474 | */ | |
475 | 719040 | private int locateInRange(int mcolumn) |
476 | { | |
477 | ||
478 | 719040 | int h = 0, p = 0; |
479 | 719048 | while (h < mcolumn && p + 2 < matrixRange.length) |
480 | { | |
481 | 0 | h += 1 + Math.abs(matrixRange[p + 1] - matrixRange[p]); |
482 | 0 | p += 2; |
483 | } | |
484 | 719070 | return matrixRange[p] + mcolumn - h; |
485 | } | |
486 | ||
487 | 3552 | @Override |
488 | public Color getColourForGroup() | |
489 | { | |
490 | 3552 | BitSet gp = us.getGroupsFor(_column); |
491 | 3552 | Color col = us.getColourForGroup(gp); |
492 | 3552 | return col; |
493 | } | |
494 | }); | |
495 | } | |
496 | ||
497 | /** | |
498 | * get a specific element of the underlying contact matrix in its data-local | |
499 | * coordinates rather than the mapped frame. Implementations are allowed to | |
500 | * throw RunTimeExceptions if _column/i are out of bounds | |
501 | * | |
502 | * @param _column | |
503 | * @param i | |
504 | * @return | |
505 | */ | |
506 | 435427 | public double getElementAt(int _column, int i) |
507 | { | |
508 | 435466 | return mappedMatrix.getElementAt(_column, i); |
509 | } | |
510 | ||
511 | 33 | @Override |
512 | public int hashCode() | |
513 | { | |
514 | 33 | return 7 * (refSeq != null ? refSeq.hashCode() : 0) |
515 | 33 | + 11 * (toSeq != null ? toSeq.hashCode() : 0) |
516 | 33 | + 13 * (mappedMatrix != null ? mappedMatrix.hashCode() : 0) |
517 | + length * 3; | |
518 | } | |
519 | ||
520 | 3 | @Override |
521 | public boolean equals(Object obj) | |
522 | { | |
523 | 3 | if (obj == null || !(obj.getClass().equals(getClass()))) |
524 | { | |
525 | 0 | return false; |
526 | } | |
527 | 3 | T them = (T) obj; |
528 | 3 | return mappedMatrix == them.mappedMatrix && length == them.length |
529 | && refSeq == them.refSeq && toSeq.equals(them.toSeq); | |
530 | ||
531 | } | |
532 | } |