{
distances.setValue(i, i, 0);
ContactListI jth = cm.getContactList(j);
+ if (jth==null)
+ {
+ break;
+ }
double prd = 0;
for (int indx = 0; indx < cm.getHeight(); indx++)
{
*/
SearchResultsI getSearchResults();
+ /**
+ * Retrieve a ContactListI corresponding to column in an annotation row in an alignment.
+ * @param _aa - annotation with associated matrix data
+ * @param column - column in alignment where _aa is associated
+ */
ContactListI getContactList(AlignmentAnnotation _aa, int column);
/**
}
if (cl == null && _aa.sequenceRef != null)
{
- int spos = _aa.sequenceRef.findPosition(column);
- if (spos >= _aa.sequenceRef.getStart()
- && spos <= 1 + _aa.sequenceRef.getEnd())
+ if (_aa.annotations[column] != null)
{
- cl = _aa.sequenceRef.getContactListFor(_aa, spos-_aa.sequenceRef.getStart());
+ // sequence associated
+ cl = _aa.sequenceRef.getContactListFor(_aa, column);
if (cl == null && _aa.sequenceRef.getDatasetSequence() != null)
{
- _aa.sequenceRef.getDatasetSequence().getContactListFor(_aa, spos-_aa.sequenceRef.getStart());
+ int spos = _aa.sequenceRef.findPosition(column);
+ if (spos >= _aa.sequenceRef.getStart()
+ && spos <= 1 + _aa.sequenceRef.getEnd())
+ {
+ cl = _aa.sequenceRef.getDatasetSequence().getContactListFor(_aa,
+ spos-_aa.sequenceRef.getStart());
+ }
}
}
}
package jalview.datamodel;
+import java.awt.Color;
+
+import jalview.renderer.ContactGeometry.contactInterval;
+
public interface ContactListI extends ContactListProviderI
{
*/
ContactRange getRangeFor(int from_column, int to_column);
+ default Color getColourForGroup() {
+ return null;
+ }
}
package jalview.datamodel;
+import jalview.renderer.ContactGeometry.contactInterval;
+
/**
* helper class to compute min/max/mean for a range on a contact list
*
}
return cr;
}
-
}
package jalview.datamodel;
+import jalview.renderer.ContactGeometry.contactInterval;
+
public interface ContactListProviderI
{
*/
double getContactAt(int column);
+
+ /**
+ * Return positions in local reference corresponding to cStart and cEnd in matrix data
+ * @param cStart
+ * @param cEnd
+ * @return int[] { start, end (inclusive) for each contiguous segment}
+ */
+ default int[] getMappedPositionsFor(int cStart, int cEnd) {
+ return new int[] { cStart, cEnd};
+ }
+
}
import java.util.HashMap;
import java.util.Map;
+import jalview.ws.datamodel.MappableContactMatrixI;
+
public class ContactMapHolder implements ContactMapHolderI
{
{
return null;
}
+ if (cm instanceof MappableContactMatrixI)
+ {
+ if (_aa.sequenceRef!=null)
+ {
+ return ((MappableContactMatrixI)cm).getMappableContactList(_aa.sequenceRef, column);
+ }
+ }
// TODO: could resolve sequence position to column position here
// TODO: what about for complexes - where contactMatrix may involve two or
// more sequences
contactmaps.put(aa.annotationId, cm);
// TODO: contact matrices could be intra or inter - more than one refseq
// possible!
- if (cm.hasReferenceSeq())
+ if (cm instanceof MappableContactMatrixI)
{
- aa.setSequenceRef(cm.getReferenceSeq());
+ aa.setSequenceRef(((MappableContactMatrixI) cm).getReferenceSeq());
}
return aa;
}
}
@Override
- public boolean hasReferenceSeq()
- {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public SequenceI getReferenceSeq()
- {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
public String getAnnotLabel()
{
return "Contact Matrix";
float getMax();
- boolean hasReferenceSeq();
-
- SequenceI getReferenceSeq();
-
String getAnnotDescr();
String getAnnotLabel();
void setColorForGroup(BitSet bs, Color color);
- default Color getColourForGroup(BitSet bs) { return Color.white;};
+ default Color getColourForGroup(BitSet bs) { return Color.white;}
+
}
import java.util.HashMap;
import java.util.List;
+import jalview.util.MapList;
+import jalview.ws.datamodel.alphafold.MappableContactMatrix;
+
/**
* Dummy contact matrix based on sequence distance
*
* @author jprocter
*
*/
-public class SeqDistanceContactMatrix implements ContactMatrixI
+public class SeqDistanceContactMatrix extends MappableContactMatrix<SeqDistanceContactMatrix> implements ContactMatrixI
{
private static final String SEQUENCE_DISTANCE = "SEQUENCE_DISTANCE";
private int width = 0;
}
});
}
-
- @Override
- public boolean hasReferenceSeq()
- {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public SequenceI getReferenceSeq()
- {
- // TODO Auto-generated method stub
- return null;
- }
-
@Override
public String getAnnotDescr()
{
{
colorMap.put(bs,color);
}
+ @Override
+ protected double getElementAt(int _column, int i)
+ {
+ return Math.abs(_column-i);
+ }
+
+ @Override
+ protected SeqDistanceContactMatrix newMappableContactMatrix(
+ SequenceI newRefSeq, MapList newFromMapList)
+ {
+
+ return new SeqDistanceContactMatrix(width);
+ }
}
import java.util.Iterator;
+import jalview.datamodel.ColumnSelection;
import jalview.datamodel.ContactListI;
-
+import jalview.datamodel.HiddenColumns;
+import jalview.renderer.ContactGeometry.contactInterval;
+
+/**
+ * encapsulate logic for mapping between positions in a ContactList and their
+ * rendered representation in a given number of pixels.
+ *
+ * @author jprocter
+ *
+ */
public class ContactGeometry
{
+
+ final ContactListI contacts;
+
final int pixels_step;
final double contacts_per_pixel;
final int graphHeight;
- public ContactGeometry(ContactListI contacts, int graphHeight)
+ public ContactGeometry(final ContactListI contacts, int graphHeight)
{
+ this.contacts=contacts;
this.graphHeight = graphHeight;
contact_height = contacts.getContactHeight();
// fractional number of contacts covering each pixel
public final int pStart;
public final int pEnd;
+
+ }
+ /**
+ *
+ * @param columnSelection
+ * @param ci
+ * @param visibleOnly - when true, only test intersection of visible columns given matrix range
+ * @return true if the range on the matrix specified by ci intersects with selected columns in the ContactListI's reference frame.
+ */
+
+ boolean intersects(contactInterval ci,ColumnSelection columnSelection, HiddenColumns hiddenColumns, boolean visibleOnly) {
+ boolean rowsel = false;
+ final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart, ci.cEnd);
+ if (mappedRange==null)
+ {
+ return false;
+ }
+ boolean containsHidden=false;
+ if (visibleOnly && hiddenColumns!=null && hiddenColumns.hasHiddenColumns())
+ {
+ // TODO: turn into function on hiddenColumns and create test !!
+ Iterator<int[]> viscont = hiddenColumns
+ .getVisContigsIterator(mappedRange[0], mappedRange[1], false);
+ containsHidden = !viscont.hasNext();
+ if (!containsHidden)
+ {
+ for (int[] interval=viscont.next();viscont.hasNext();
+ rowsel |= columnSelection.intersects(interval[0],interval[1]))
+ ;
+ }
+ }
+ else
+ {
+ // if containsHidden is true mappedRange is not visible
+ if (containsHidden)
+ {
+ rowsel = columnSelection.intersects(mappedRange[0], mappedRange[1]);
+ }
+ }
+ return rowsel;
+
}
/**
x++;
continue;
}
- Color gpcol = (cm==null) ? Color.white: cm.getColourForGroup(cm.getGroupsFor(column));
+ // ContactListI from viewport can map column -> group
+ Color gpcol = (cm==null) ? Color.white: contacts.getColourForGroup(); // cm.getColourForGroup(cm.getGroupsFor(column));
+
// feature still in development - highlight or omit regions hidden in
// the alignment - currently marks them as red rows
boolean maskHiddenCols = false;
- // TODO: pass visible column mask to the ContactGeometry object so it maps
+ // TODO: optionally pass visible column mask to the ContactGeometry object so it maps
// only visible contacts to geometry
// Bean holding mapping from contact list to pixels
+ // TODO: allow bracketing/limiting of range on contacts to render (like visible column mask but more flexible?)
+
+ // COntactListI provides mapping for column -> cm-groupmapping
final ContactGeometry cgeom = new ContactGeometry(contacts,
_aa.graphHeight);
for (int ht = y2, eht = y2
- _aa.graphHeight; ht >= eht; ht -= cgeom.pixels_step)
{
+
ContactGeometry.contactInterval ci = cgeom.mapFor(y2 - ht,
y2 - ht + cgeom.pixels_step);
// cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
boolean rowsel = false, containsHidden = false;
if (columnSelection != null)
{
- if (_aa.sequenceRef == null)
- {
- rowsel = columnSelection.intersects(ci.cStart, ci.cEnd);
- }
- else
- {
- // TODO check we have correctly mapped cstart to local sequence
- // numbering
- int s = _aa.sequenceRef.findIndex(ci.cStart);
- int e = _aa.sequenceRef.findIndex(ci.cEnd);
- if (maskHiddenCols && hasHiddenColumns)
- {
- // TODO: turn into function and create test !!
- Iterator<int[]> viscont = hiddenColumns
- .getVisContigsIterator(s, e, false);
- containsHidden = !viscont.hasNext();
- }
- if (s > 0 && s < _aa.sequenceRef.getLength())
- {
- rowsel = columnSelection.intersects(s, e);
- }
-
- }
+ rowsel = cgeom.intersects(ci, columnSelection, hiddenColumns, maskHiddenCols);
}
// TODO: show selected region
if (colsel || rowsel)
--- /dev/null
+package jalview.ws.datamodel;
+
+import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactMatrixI;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SequenceI;
+
+public interface MappableContactMatrixI extends ContactMatrixI
+{
+
+ boolean hasReferenceSeq();
+
+ SequenceI getReferenceSeq();
+ /**
+ * remaps the matrix to a new reference sequence
+ * @param dsq
+ * @param sqmpping - mapping from current reference to new reference - 1:1 only
+ * @return new ContactMatrixI instance with updated mapping
+ */
+ MappableContactMatrixI liftOver(SequenceI dsq, Mapping sqmpping);
+
+ /**
+ * like ContactMatrixI.getContactList(int column) but
+ * @param localFrame - sequence or other object that this contact matrix is associated with
+ * @param column - position in localFrame
+ * @return ContactListI that returns contacts w.r.t. localFrame
+ */
+
+ ContactListI getMappableContactList(SequenceI localFrame, int column);
+
+}
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import org.json.simple.JSONObject;
import jalview.analysis.AverageDistanceEngine;
import jalview.bin.Console;
+import jalview.datamodel.Annotation;
import jalview.datamodel.BinaryNode;
import jalview.datamodel.ContactListI;
import jalview.datamodel.ContactListImpl;
import jalview.datamodel.ContactListProviderI;
import jalview.datamodel.ContactMatrixI;
+import jalview.datamodel.Mapping;
import jalview.datamodel.SequenceDummy;
import jalview.datamodel.SequenceI;
import jalview.io.DataSourceType;
import jalview.io.FileFormatException;
import jalview.io.FileParse;
+import jalview.util.MapList;
import jalview.util.MapUtils;
import jalview.ws.dbsources.EBIAlfaFold;
-public class PAEContactMatrix implements ContactMatrixI
+public class PAEContactMatrix extends MappableContactMatrix<PAEContactMatrix> implements ContactMatrixI
{
-
- SequenceI refSeq = null;
-
- /**
- * the length that refSeq is expected to be (excluding gaps, of course)
- */
- int length;
-
int maxrow = 0, maxcol = 0;
- int[] indices1, indices2;
-
float[][] elements;
float maxscore;
- private void setRefSeq(SequenceI _refSeq)
- {
- refSeq = _refSeq;
- while (refSeq.getDatasetSequence() != null)
- {
- refSeq = refSeq.getDatasetSequence();
- }
- length = _refSeq.getEnd() - _refSeq.getStart() + 1;
- }
@SuppressWarnings("unchecked")
public PAEContactMatrix(SequenceI _refSeq, Map<String, Object> pae_obj) throws FileFormatException
}
/**
+ * new matrix with specific mapping to a reference sequence
+ * @param newRefSeq
+ * @param newFromMapList
+ * @param elements2
+ */
+ public PAEContactMatrix(SequenceI newRefSeq,
+ MapList newFromMapList, float[][] elements2)
+ {
+ this(newRefSeq,elements2);
+ toSeq = newFromMapList;
+ }
+
+ /**
* parse a sane JSON representation of the pAE
*
* @param pae_obj
}
@Override
- public ContactListI getContactList(final int _column)
+ public ContactListI getContactList(final int column)
{
+ final int _column;
+ if (toSeq != null)
+ {
+ int[] word = toSeq.locateInTo(column, column);
+ if (word == null)
+ {
+ return null;
+ }
+ _column = word[0];
+ }
+ else
+ {
+ _column = column;
+ }
if (_column < 0 || _column >= elements.length)
{
return null;
}
@Override
- public double getContactAt(int column)
+ public double getContactAt(int mcolumn)
{
- if (column < 0 || column >= elements[_column].length)
+ int[] column=(toSeq==null) ? new int[] {mcolumn} : toSeq.locateInTo(mcolumn,mcolumn);
+ if (column==null || column[0] < 0 || column[0] >= elements[_column].length)
{
return -1;
}
- return elements[_column][column];
+ return elements[_column][column[0]];
}
});
}
@Override
+ protected double getElementAt(int _column, int i)
+ {
+ return elements[_column][i];
+ }
+ @Override
public float getMin()
{
return 0;
}
@Override
- public boolean hasReferenceSeq()
- {
- return (refSeq != null);
- }
-
- @Override
- public SequenceI getReferenceSeq()
- {
- return refSeq;
- }
-
- @Override
public String getAnnotDescr()
{
return "Predicted Alignment Error"+((refSeq==null) ? "" : (" for " + refSeq.getName()));
@Override
public BitSet getGroupsFor(int column)
{
- for (BitSet gp:groups) {
- if (gp.get(column))
+ if (groups != null)
+ {
+ for (BitSet gp : groups)
{
- return gp;
+ if (gp.get(column))
+ {
+ return gp;
+ }
}
}
return ContactMatrixI.super.getGroupsFor(column);
{
throw new FileFormatException("No data in PAE matrix read from '"+fileName+"'");
}
+ }
+
+ @Override
+ protected PAEContactMatrix newMappableContactMatrix(
+ SequenceI newRefSeq, MapList newFromMapList)
+ {
+ return new PAEContactMatrix(newRefSeq, newFromMapList,
+ elements);
}
}
import jalview.structure.StructureImportSettings;
import jalview.structure.StructureMapping;
import jalview.util.Comparison;
+import jalview.ws.datamodel.MappableContactMatrixI;
public class PDBChain
{
ana = new AlignmentAnnotation(ana);
ana.liftOver(dsq, sqmpping);
dsq.addAlignmentAnnotation(ana);
- if (cm != null)
+ if (cm != null && cm instanceof MappableContactMatrixI)
{
- dsq.addContactListFor(ana, cm);
+ dsq.addContactListFor(ana, ((MappableContactMatrixI) cm).liftOver(dsq,sqmpping));
}
}
else
--- /dev/null
+package jalview.datamodel;
+
+import static org.testng.Assert.*;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import jalview.gui.JvOptionPane;
+import jalview.util.MapList;
+import jalview.ws.datamodel.MappableContactMatrixI;
+import jalview.ws.datamodel.alphafold.PAEContactMatrix;
+
+public class PAEContactMatrixTest
+{
+ @BeforeClass(alwaysRun = true)
+ public void setUpJvOptionPane()
+ {
+ JvOptionPane.setInteractiveMode(false);
+ JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+ }
+
+ static float[][] PAEdata = {
+ {1.0f,2.0f,3.0f,4.0f,5.0f},
+ {2.0f,1.0f,2.0f,3.0f,4.0f},
+ {3.0f,2.0f,1.0f,2.0f,3.0f},
+ {4.0f,3.0f,2.0f,1.0f,2.0f},
+ {5.0f,4.0f,3.0f,2.0f,1.0f}};
+
+ /**
+ * test associations for a PAE matrix
+ */
+ @Test(groups = { "Functional" })
+ public void testSeqAssociatedPAEMatrix()
+ {
+ Sequence seq = new Sequence("Seq","ASDQE");
+ AlignmentAnnotation aa = seq.addContactList(new PAEContactMatrix(seq, PAEdata));
+ assertNotNull(seq.getContactListFor(aa, 0));
+ assertEquals(seq.getContactListFor(aa, 0).getContactAt(0),1.0);
+ assertNotNull(seq.getContactListFor(aa, 1));
+ assertEquals(seq.getContactListFor(aa, 1).getContactAt(1),1.0);
+ assertNotNull(seq.getContactListFor(aa, 2));
+ assertEquals(seq.getContactListFor(aa, 2).getContactAt(2),1.0);
+ assertNotNull(seq.getContactListFor(aa, 3));
+ assertEquals(seq.getContactListFor(aa, 3).getContactAt(3),1.0);
+ assertNotNull(seq.getContactListFor(aa, 4));
+ assertEquals(seq.getContactListFor(aa, 4).getContactAt(4),1.0);
+
+ assertNotNull(seq.getContactListFor(aa, seq.getEnd()-1));
+ assertNull(seq.getContactListFor(aa, seq.getEnd()));
+
+ ContactListI cm = seq.getContactListFor(aa, seq.getStart());
+ assertEquals(cm.getContactAt(seq.getStart()),1d);
+ verifyPAEmatrix(seq, aa, 0,0,4);
+
+ // Now associated with sequence not starting at 1
+ seq = new Sequence("Seq/5-9","ASDQE");
+ ContactMatrixI paematrix = new PAEContactMatrix(seq, PAEdata);
+ aa = seq.addContactList(paematrix);
+ cm = seq.getContactListFor(aa, 0);
+ assertEquals(cm.getContactAt(0),1d);
+ verifyPAEmatrix(seq, aa, 0, 0, 4);
+
+ // remap - test the MappableContactMatrix.liftOver method
+ SequenceI newseq = new Sequence("Seq","ASDQEASDQEASDQE");
+ Mapping sqmap = new Mapping(seq, new MapList(new int[] {5,8,10,10},new int[] { 5,9}, 1, 1));
+ assertTrue(paematrix instanceof MappableContactMatrixI);
+
+ MappableContactMatrixI remapped = ((MappableContactMatrixI)paematrix).liftOver(newseq, sqmap);
+ assertTrue(remapped instanceof PAEContactMatrix);
+
+ AlignmentAnnotation newaa = newseq.addContactList(remapped);
+ assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(1)));
+ assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(4)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(5)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(6)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(7)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(8)));
+ // no mapping for position 9
+ assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(9)));
+ // last column
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(10)));
+
+ // remap2 - test with original matrix map from 1-5 remapped to 5-9
+
+ seq = new Sequence("Seq/1-5","ASDQE");
+ paematrix = new PAEContactMatrix(seq, PAEdata);
+ assertTrue(paematrix instanceof MappableContactMatrixI);
+ aa = seq.addContactList(paematrix);
+
+ newseq = new Sequence("Seq","ASDQEASDQEASDQE");
+ sqmap = new Mapping(seq, new MapList(new int[] {5,9},new int[] { 1,5}, 1, 1));
+
+ remapped = ((MappableContactMatrixI)paematrix).liftOver(newseq, sqmap);
+ assertTrue(remapped instanceof PAEContactMatrix);
+
+
+ newaa = newseq.addContactList(remapped);
+ verify_mapping(newseq, newaa);
+
+ // remap3 - remap2 but mapping sense in liftover is reversed
+
+ seq = new Sequence("Seq/1-5","ASDQE");
+ paematrix = new PAEContactMatrix(seq, PAEdata);
+ assertTrue(paematrix instanceof MappableContactMatrixI);
+ aa = seq.addContactList(paematrix);
+
+ newseq = new Sequence("Seq","ASDQEASDQEASDQE");
+ sqmap = new Mapping(newseq, new MapList(new int[] { 1,5},new int[] {5,9},1, 1));
+
+ remapped = ((MappableContactMatrixI)paematrix).liftOver(newseq, sqmap);
+ assertTrue(remapped instanceof PAEContactMatrix);
+
+
+ newaa = newseq.addContactList(remapped);
+ verify_mapping(newseq, newaa);
+
+ }
+ /**
+ * checks that the PAE matrix is located at positions 1-9 in newseq, and columns are not truncated.
+ * @param newseq
+ * @param newaa
+ */
+ private void verify_mapping(SequenceI newseq, AlignmentAnnotation newaa)
+ {
+ assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(1)));
+ assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(4)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(5)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(6)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(7)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(8)));
+ assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(9)));
+ // last column should be null this time
+ assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(10)));
+
+ verifyPAEmatrix(newseq, newaa, 4, 4, 8);
+
+
+ }
+
+ private void verifyPAEmatrix(SequenceI seq, AlignmentAnnotation aa, int topl, int rowl, int rowr)
+ {
+ for (int f=rowl;f<=rowr;f++) {
+ ContactListI clist = seq.getContactListFor(aa, f);
+ assertNotNull(clist,"No ContactListI for position "+(f));
+ assertEquals(clist.getContactAt(0), (double) f-topl+1,"for column "+f+" relative to "+topl);
+ assertEquals(clist.getContactAt(f-topl),1d, "for column and row "+f+" relative to "+topl);
+ }
+ }
+
+}