X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fstructure%2FStructureSelectionManager.java;h=0f82650c2226e3a7acf0e9313fc5a59fbdcf7c8c;hb=4bc5c1c97bd08007b2ac9ccfd9b3cb29eac10193;hp=53644e94fb3e0150e73fe0528be6dfd3ca7d11f3;hpb=c38d37887ae51d63942ac4321f6769308188ed6b;p=jalview.git diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index 53644e9..0f82650 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -26,14 +26,17 @@ import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.Vector; import jalview.analysis.AlignSeq; import jalview.api.StructureSelectionManagerProvider; -import jalview.bin.Cache; +import jalview.bin.Console; import jalview.commands.CommandI; import jalview.commands.EditCommand; import jalview.commands.OrderCommand; @@ -41,6 +44,7 @@ import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; +import jalview.datamodel.ContiguousI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SearchResults; @@ -51,6 +55,8 @@ import jalview.gui.IProgressIndicator; import jalview.io.AppletFormatAdapter; import jalview.io.DataSourceType; import jalview.io.StructureFile; +import jalview.structure.StructureImportSettings.TFType; +import jalview.util.MapList; import jalview.util.MappingUtils; import jalview.util.MessageManager; import jalview.util.Platform; @@ -152,16 +158,18 @@ public class StructureSelectionManager { if (mappings.isEmpty()) { - System.err.println("reportMapping: No PDB/Sequence mappings."); + jalview.bin.Console + .errPrintln("reportMapping: No PDB/Sequence mappings."); } else { - System.err.println( + jalview.bin.Console.errPrintln( "reportMapping: There are " + mappings.size() + " mappings."); int i = 0; for (StructureMapping sm : mappings) { - System.err.println("mapping " + i++ + " : " + sm.pdbfile); + jalview.bin.Console + .errPrintln("mapping " + i++ + " : " + sm.pdbfile); } } } @@ -320,11 +328,11 @@ public class StructureSelectionManager * @return null or the structure data parsed as a pdb file */ synchronized public StructureFile setMapping(SequenceI[] sequence, - String[] targetChains, String pdbFile, DataSourceType protocol, + String[] targetChains, String pdbFile, DataSourceType protocol, IProgressIndicator progress) { return computeMapping(true, sequence, targetChains, pdbFile, protocol, - progress); + progress, null, null, true); } /** @@ -347,10 +355,50 @@ public class StructureSelectionManager */ synchronized public StructureFile setMapping(boolean forStructureView, SequenceI[] sequenceArray, String[] targetChainIds, - String pdbFile, DataSourceType sourceType) + String pdbFile, DataSourceType sourceType, TFType tft, + String paeFilename) + { + return setMapping(forStructureView, sequenceArray, targetChainIds, + pdbFile, sourceType, tft, paeFilename, true); + } + + /** + * create sequence structure mappings between each sequence and the given + * pdbFile (retrieved via the given protocol). Either constructs a mapping + * using NW alignment or derives one from any available SIFTS mapping data. + * + * @param forStructureView + * when true, record the mapping for use in mouseOvers + * + * @param sequenceArray + * - one or more sequences to be mapped to pdbFile + * @param targetChainIds + * - optional chain specification for mapping each sequence to pdb + * (may be nill, individual elements may be nill) - JBPNote: JAL-2693 + * - this should be List>, empty lists indicate no + * predefined mappings + * @param pdbFile + * - structure data resource + * @param sourceType + * - how to resolve data from resource + * @param tft + * - specify how to interpret the temperature factor column in the + * atom data + * @param paeFilename + * - when not null, specifies a filename containing a matrix + * formatted in JSON using one of the known PAE formats + * @param doXferSettings + * - when true, transfer annotation to mapped sequences in + * sequenceArray + * @return null or the structure data parsed as a pdb file + */ + synchronized public StructureFile setMapping(boolean forStructureView, + SequenceI[] sequenceArray, String[] targetChainIds, + String pdbFile, DataSourceType sourceType, TFType tft, + String paeFilename, boolean doXferSettings) { return computeMapping(forStructureView, sequenceArray, targetChainIds, - pdbFile, sourceType, null); + pdbFile, sourceType, null, tft, paeFilename, doXferSettings); } /** @@ -375,12 +423,22 @@ public class StructureSelectionManager * @param IProgressIndicator * reference to UI component that maintains a progress bar for the * mapping operation + * @param tft + * - specify how to interpret the temperature factor column in the + * atom data + * @param paeFilename + * - when not null, specifies a filename containing a matrix + * formatted in JSON using one of the known PAE formats + * @param doXferSettings + * - when true, transfer annotation to mapped sequences in + * sequenceArray * @return null or the structure data parsed as a pdb file */ - synchronized public StructureFile computeMapping( - boolean forStructureView, SequenceI[] sequenceArray, - String[] targetChainIds, String pdbFile, DataSourceType sourceType, - IProgressIndicator progress) + synchronized public StructureFile computeMapping(boolean forStructureView, + SequenceI[] sequenceArray, String[] targetChainIds, + String pdbFile, DataSourceType sourceType, + IProgressIndicator progress, TFType tft, String paeFilename, + boolean doXferSettings) { long progressSessionId = System.currentTimeMillis() * 3; @@ -390,8 +448,7 @@ public class StructureSelectionManager // FIXME: possibly should just delete boolean parseSecStr = processSecondaryStructure - ? isStructureFileProcessed(pdbFile, sequenceArray) - : false; + && !isStructureFileProcessed(pdbFile, sequenceArray); StructureFile pdb = null; boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts(); @@ -400,17 +457,39 @@ public class StructureSelectionManager // FIXME if sourceType is not null, we've lost data here sourceType = AppletFormatAdapter.checkProtocol(pdbFile); pdb = new JmolParser(false, pdbFile, sourceType); + if (paeFilename != null) + { + pdb.setPAEMatrix(paeFilename); + } + pdb.setTemperatureFactorType(tft); pdb.addSettings(parseSecStr && processSecondaryStructure, parseSecStr && addTempFacAnnot, parseSecStr && secStructServices); + // save doXferSettings and reset after doParse() + boolean temp = pdb.getDoXferSettings(); + pdb.setDoXferSettings(doXferSettings); pdb.doParse(); + pdb.setDoXferSettings(temp); if (pdb.getId() != null && pdb.getId().trim().length() > 0 && DataSourceType.FILE == sourceType) { registerPDBFile(pdb.getId().trim(), pdbFile); } // if PDBId is unavailable then skip SIFTS mapping execution path - isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable(); + // TODO: JAL-3868 need to know if structure is actually from + // PDB (has valid PDB ID and has provenance suggesting it + // actually came from PDB) + boolean isProtein = false; + for (SequenceI s : sequenceArray) + { + if (s.isProtein()) + { + isProtein = true; + break; + } + } + isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() + && !pdb.getId().startsWith("AF-") && isProtein; } catch (Exception ex) { @@ -430,8 +509,8 @@ public class StructureSelectionManager } catch (SiftsException e) { isMapUsingSIFTs = false; - Cache.log.error("SIFTS mapping failed", e); - Cache.log.error("Falling back on Needleman & Wunsch alignment"); + Console.error("SIFTS mapping failed", e); + Console.error("Falling back on Needleman & Wunsch alignment"); siftsClient = null; } @@ -445,32 +524,15 @@ public class StructureSelectionManager { ds = ds.getDatasetSequence(); } - + List putativePDBe = PDBEntryUtils.selectPutativePDBe(seq,ds, pdb); + if (targetChainIds != null && targetChainIds[s] != null) { infChain = false; targetChainId = targetChainIds[s]; } - else if (seq.getName().indexOf("|") > -1) - { - targetChainId = seq.getName() - .substring(seq.getName().lastIndexOf("|") + 1); - if (targetChainId.length() > 1) - { - if (targetChainId.trim().length() == 0) - { - targetChainId = " "; - } - else - { - // not a valid chain identifier - targetChainId = ""; - } - } - } - else - { - targetChainId = ""; + else { + targetChainId = PDBEntryUtils.inferChainId(seq); } /* @@ -482,6 +544,7 @@ public class StructureSelectionManager String maxChainId = " "; PDBChain maxChain = null; boolean first = true; + PDBChain idLengthChain = null; for (PDBChain chain : pdb.getChains()) { if (targetChainId.length() > 0 && !targetChainId.equals(chain.id) @@ -489,15 +552,22 @@ public class StructureSelectionManager { continue; // don't try to map chains don't match. } + PDBEntry putativeChain = null; + if (!putativePDBe.isEmpty() && (putativeChain = PDBEntryUtils + .selectPutativePDBEntry(putativePDBe, chain)) == null) + { + continue; + } // TODO: correctly determine sequence type for mixed na/peptide // structures final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP; AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence, type); - // equivalent to: - // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type); - // as.calcScoreMatrix(); - // as.traceAlignment(); + // TODO: JAL-4366 determinine of a crummy alignment but exact match should make this chain the one to be mapped to a 3di sequence + if (as.s1str.length() == as.s2str.length()) + { + idLengthChain = chain; + } if (first || as.maxscore > max || (as.maxscore == max && chain.id.equals(targetChainId))) @@ -513,18 +583,20 @@ public class StructureSelectionManager { continue; } - if (sourceType == DataSourceType.PASTE) { pdbFile = "INLINE" + pdb.getId(); } - List seqToStrucMapping = new ArrayList<>(); + + List foundSiftsMappings = new ArrayList<>(); if (isMapUsingSIFTs && seq.isProtein()) { - if (progress!=null) { - progress.setProgressBar(MessageManager - .getString("status.obtaining_mapping_with_sifts"), + if (progress != null) + { + progress.setProgressBar( + MessageManager + .getString("status.obtaining_mapping_with_sifts"), progressSessionId); } jalview.datamodel.Mapping sqmpping = maxAlignseq @@ -538,82 +610,90 @@ public class StructureSelectionManager pdb, maxChain, sqmpping, maxAlignseq, siftsClient); seqToStrucMapping.add(siftsMapping); maxChain.makeExactMapping(siftsMapping, seq); - maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS"); + maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS", + pdb.getId().toLowerCase(Locale.ROOT)); maxChain.transferResidueAnnotation(siftsMapping, null); ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + foundSiftsMappings.add(siftsMapping); } catch (SiftsException e) { - // fall back to NW alignment - Cache.log.error(e.getMessage()); - StructureMapping nwMapping = getNWMappings(seq, pdbFile, - targetChainId, maxChain, pdb, maxAlignseq); - seqToStrucMapping.add(nwMapping); - maxChain.makeExactMapping(maxAlignseq, seq); - maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is - // this - // "IEA:Jalview" ? - maxChain.transferResidueAnnotation(nwMapping, sqmpping); - ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + Console.error(e.getMessage()); } } else { - List foundSiftsMappings = new ArrayList<>(); for (PDBChain chain : pdb.getChains()) { StructureMapping siftsMapping = null; try { - siftsMapping = getStructureMapping(seq, - pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq, - siftsClient); + siftsMapping = getStructureMapping(seq, pdbFile, chain.id, + pdb, chain, sqmpping, maxAlignseq, siftsClient); foundSiftsMappings.add(siftsMapping); chain.makeExactMapping(siftsMapping, seq); - chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this + chain.transferRESNUMFeatures(seq, "IEA: SIFTS", + pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this // "IEA:SIFTS" ? chain.transferResidueAnnotation(siftsMapping, null); } catch (SiftsException e) { - System.err.println(e.getMessage()); - } - catch (Exception e) + jalview.bin.Console.errPrintln(e.getMessage()); + } catch (Exception e) { - System.err - .println( - "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair"); - System.err.println(e.getMessage()); + jalview.bin.Console.errPrintln( + "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair"); + jalview.bin.Console.errPrintln(e.getMessage()); } } + // If sifts was successful, add mappings and return if (!foundSiftsMappings.isEmpty()) { - seqToStrucMapping.addAll(foundSiftsMappings); ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0)); } - else - { - StructureMapping nwMapping = getNWMappings(seq, pdbFile, - maxChainId, maxChain, pdb, maxAlignseq); - seqToStrucMapping.add(nwMapping); - maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this - // "IEA:Jalview" ? - maxChain.transferResidueAnnotation(nwMapping, sqmpping); - ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); - } + } + + // If sifts was successful, add mappings and return + if (!foundSiftsMappings.isEmpty()) + { + seqToStrucMapping.addAll(foundSiftsMappings); } } - else + if (foundSiftsMappings.isEmpty()) { - if (progress != null) + // Not doing SIFTS, or SIFTS failed for some reason. + + // first check if we should use an identity mapping + if (idLengthChain != null && maxAlignseq.getS2Coverage() < 0.75) { - progress.setProgressBar(MessageManager - .getString("status.obtaining_mapping_with_nw_alignment"), - progressSessionId); + Console.info( + "Assuming 3Dsi identity mapping between structure and sequence"); + StructureMapping matchMapping = getIdMappings(seq, pdbFile, + idLengthChain.id, idLengthChain, pdb); + seqToStrucMapping.add(matchMapping); + ds.addPDBId(idLengthChain.sequence.getAllPDBEntries().get(0)); + Console.info("Mapping added."); + } + else + { + if (maxAlignseq.getS1Coverage()<0.15 && maxAlignseq.getS2Coverage()<0.15) + { + // skip this - the NW alignment is spurious + continue; + } + // Construct a needleman wunsch mapping instead. + if (progress != null) + { + progress.setProgressBar( + MessageManager.getString( + "status.obtaining_mapping_with_nw_alignment"), + progressSessionId); + } + StructureMapping nwMapping = getNWMappings(seq, pdbFile, + maxChainId, maxChain, pdb, maxAlignseq); + seqToStrucMapping.add(nwMapping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); } - StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId, - maxChain, pdb, maxAlignseq); - seqToStrucMapping.add(nwMapping); - ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); } if (forStructureView) { @@ -641,7 +721,7 @@ public class StructureSelectionManager private boolean isStructureFileProcessed(String pdbFile, SequenceI[] sequenceArray) { - boolean parseSecStr = true; + boolean processed = false; if (isPDBFileRegistered(pdbFile)) { for (SequenceI sq : sequenceArray) @@ -661,13 +741,13 @@ public class StructureSelectionManager // passed, not the structure data ID - if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile))) { - parseSecStr = false; + processed = true; } } } } } - return parseSecStr; + return processed; } public void addStructureMapping(StructureMapping sm) @@ -697,7 +777,8 @@ public class StructureSelectionManager private StructureMapping getStructureMapping(SequenceI seq, String pdbFile, String targetChainId, StructureFile pdb, PDBChain maxChain, jalview.datamodel.Mapping sqmpping, - AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException + AlignSeq maxAlignseq, SiftsClient siftsClient) + throws SiftsException { StructureMapping curChainMapping = siftsClient .getSiftsStructureMapping(seq, pdbFile, targetChainId); @@ -714,6 +795,17 @@ public class StructureSelectionManager } return curChainMapping; } + + /** + * construct a mapping based on a pairwise alignment of the sequence and chain + * @param seq + * @param pdbFile + * @param maxChainId + * @param maxChain + * @param pdb + * @param maxAlignseq + * @return + */ private StructureMapping getNWMappings(SequenceI seq, String pdbFile, String maxChainId, PDBChain maxChain, StructureFile pdb, @@ -763,7 +855,8 @@ public class StructureSelectionManager maxChain.makeExactMapping(maxAlignseq, seq); jalview.datamodel.Mapping sqmpping = maxAlignseq .getMappingFromS1(false); - maxChain.transferRESNUMFeatures(seq, null); + maxChain.transferRESNUMFeatures(seq, null, + pdb.getId().toLowerCase(Locale.ROOT)); HashMap mapping = new HashMap<>(); int resNum = -10000; @@ -794,6 +887,82 @@ public class StructureSelectionManager maxChain.transferResidueAnnotation(nwMapping, sqmpping); return nwMapping; } + + /** + * construct a 1:1 mapping using given residue and sequence numbering + * @param seq + * @param pdbFile + * @param identityChainId + * @param identityChain + * @param pdb + * @return + */ + + private StructureMapping getIdMappings(SequenceI seq, String pdbFile, + String identityChainId, PDBChain identityChain, StructureFile pdb) + { + final StringBuilder mappingDetails = new StringBuilder(128); + mappingDetails.append(NEWLINE) + .append("Sequence \u27f7 Structure mapping details"); + mappingDetails.append(NEWLINE); + mappingDetails.append("Method: Matching length 1:1"); + mappingDetails.append(NEWLINE).append("PDB Sequence is :") + .append(NEWLINE).append("Sequence = ") + .append(identityChain.sequence.getSequenceAsString()); + mappingDetails.append(NEWLINE).append("No of residues = ") + .append(identityChain.residues.size()).append(NEWLINE) + .append(NEWLINE); + + mappingDetails.append(NEWLINE) + .append("Aligned Sequence is: " + seq.getDisplayId(true)); + mappingDetails.append(NEWLINE) + .append("Sequence = " + seq.getSequenceAsString()); + + int from = Math.max(seq.getStart(),identityChain.sequence.getStart()); + int to = Math.min(seq.getEnd(), identityChain.sequence.getEnd()); + jalview.datamodel.Mapping sqmpping = new jalview.datamodel.Mapping(seq, + new MapList(new int[] + { from,to }, + new int[] + { from,to }, + 1, 1)); + identityChain.mapChainWith(sqmpping, seq); + + identityChain.transferRESNUMFeatures(seq, null, + pdb.getId().toLowerCase(Locale.ROOT)); + + // Construct mapping + // TODO REFACTOR TO PDBChain as a builder + HashMap mapping = new HashMap<>(); + int resNum = -10000; + int index = 0; + char insCode = ' '; + + do + { + Atom tmp = identityChain.atoms.elementAt(index); + if ((resNum != tmp.resNumber || insCode != tmp.insCode) + && tmp.alignmentMapping != -1) + { + resNum = tmp.resNumber; + insCode = tmp.insCode; + if (tmp.alignmentMapping >= -1) + { + mapping.put(tmp.alignmentMapping + 1, + new int[] + { tmp.resNumber, tmp.atomIndex }); + } + } + + index++; + } while (index < identityChain.atoms.size()); + + StructureMapping idMapping = new StructureMapping(seq, pdbFile, + pdb.getId(), identityChainId, mapping, + mappingDetails.toString()); + identityChain.transferResidueAnnotation(idMapping, sqmpping); + return idMapping; + } public void removeStructureViewerListener(Object svl, String[] pdbfiles) { @@ -854,6 +1023,52 @@ public class StructureSelectionManager } /** + * hack to highlight a range of positions at once on any structure views + * + * @param sequenceRef + * @param is + * - series of int start-end ranges as positions on sequenceRef + * @param i + * @param object + */ + public void highlightPositionsOn(SequenceI sequenceRef, int[][] is, + Object source) + { + boolean hasSequenceListeners = handlingVamsasMo + || !seqmappings.isEmpty(); + SearchResultsI results = null; + ArrayList listOfPositions = new ArrayList(); + for (int[] s_e : is) + { + for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++)) + ; + } + int seqpos[] = new int[listOfPositions.size()]; + int i = 0; + for (Integer p : listOfPositions) + { + seqpos[i++] = p; + } + + for (i = 0; i < listeners.size(); i++) + { + Object listener = listeners.elementAt(i); + if (listener == source) + { + // TODO listener (e.g. SeqPanel) is never == source (AlignViewport) + // Temporary fudge with SequenceListener.getVamsasSource() + continue; + } + if (listener instanceof StructureListener) + { + highlightStructure((StructureListener) listener, sequenceRef, + seqpos); + } + + } + } + + /** * Propagate mouseover of a single position in a structure * * @param pdbResNum @@ -935,7 +1150,7 @@ public class StructureSelectionManager int indexpos = sm.getSeqPos(atom.getPdbResNum()); if (lastipos != indexpos || lastseq != sm.sequence) { - results.addResult(sm.sequence, indexpos, indexpos); + results.appendResult(sm.sequence, indexpos, indexpos); lastipos = indexpos; lastseq = sm.sequence; // construct highlighted sequence list @@ -1063,6 +1278,62 @@ public class StructureSelectionManager sl.highlightAtoms(atoms); } + public void highlightStructureRegionsFor(StructureListener sl, + SequenceI[] seqs, int... columns) + { + List to_highlight = new ArrayList(); + for (SequenceI seq : seqs) + { + if (sl.isListeningFor(seq)) + { + to_highlight.add(seq); + } + } + if (to_highlight.size() == 0) + { + return; + } + List atoms = new ArrayList<>(); + for (SequenceI seq : to_highlight) + { + int atomNo; + for (StructureMapping sm : mappings) + { + if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence() + || (sm.sequence.getDatasetSequence() != null && sm.sequence + .getDatasetSequence() == seq.getDatasetSequence())) + { + + for (int i = 0; i < columns.length; i += 2) + { + ContiguousI positions = seq.findPositions(columns[i] + 1, + columns[i + 1] + 1); + if (positions == null) + { + continue; + } + for (int index = positions.getBegin(); index <= positions + .getEnd(); index++) + { + + atomNo = sm.getAtomNum(index); + + if (atomNo > 0) + { + atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, + sm.getPDBResNum(index), atomNo)); + } + } + } + } + } + if (atoms.size() > 0) + { + sl.highlightAtoms(atoms); + } + } + } + /** * true if a mouse over event from an external (ie Vamsas) source is being * handled @@ -1108,7 +1379,7 @@ public class StructureSelectionManager * * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid) * && mappings[j].pdbfile.equals(sl.getPdbFile())) { - * System.out.println(pdbid+" "+mappings[j].getPdbId() +" + * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +" * "+mappings[j].pdbfile); * * java.awt.Color col; for(int index=0; index getPdbFileNameIdMap() + { + return pdbFileNameId; + } + + public Map getPdbIdFileNameMap() + { + return pdbIdFileName; + } + }