From a27329273cb17fbb12b9c714d2500d8913dee32f Mon Sep 17 00:00:00 2001 From: Renia Correya Date: Fri, 26 Apr 2024 10:27:28 +0100 Subject: [PATCH] JAL-4386 Drop down for secondary structure sources in calculate window --- .../SecondaryStructureDistanceModel.java | 196 +++++++++++++------- src/jalview/gui/CalculationChooser.java | 95 +++++++++- 2 files changed, 214 insertions(+), 77 deletions(-) diff --git a/src/jalview/analysis/scoremodels/SecondaryStructureDistanceModel.java b/src/jalview/analysis/scoremodels/SecondaryStructureDistanceModel.java index 3a719d8..cd09805 100644 --- a/src/jalview/analysis/scoremodels/SecondaryStructureDistanceModel.java +++ b/src/jalview/analysis/scoremodels/SecondaryStructureDistanceModel.java @@ -45,21 +45,35 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel { private static final String NAME = "Secondary Structure Similarity"; + //label in secondary structure annotation data model from 3d structures private static final String SS_ANNOTATION_LABEL = "Secondary Structure"; + //label in secondary structure annotation data model from JPred private static final String SS_ANNOTATION_FROM_JPRED_LABEL = "jnetpred"; + //label in secondary structure annotation data model from JPred + private static final String JPRED_WEBSERVICE = "JPred"; + private String description; - //maximum distance score is defined as 2 as the possible number of unique ss is 2. + //maximum distance score is defined as 2 as the possible number of unique secondary structure is 2. private static final int MAX_SCORE = 2; - //minimum distance score is defined as 2 as the possible number of unique ss is 2. + //minimum distance score is defined as 2 as the possible number of unique secondary structure is 2. private static final int MIN_SCORE = 0; + //character used to represent coil in secondary structure private static final char COIL = 'C'; FeatureRenderer fr; + + /* + * Annotation label for available sources of secondary structure + */ + private static final String[] SS_ANNOTATION_LABELS = { + SS_ANNOTATION_LABEL, + SS_ANNOTATION_FROM_JPRED_LABEL + }; /** * Constructor @@ -137,25 +151,48 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel int noseqs = seqs.length; //no of sequences int cpwidth = 0; // = seqData.getWidth(); double[][] distances = new double[noseqs][noseqs]; //matrix to store distance score - + //secondary structure source parameter selected by the user from the drop down. + String ssSource = params.getSecondaryStructureSource(); + + //defining the default value for secondary structure source as 3d structures + //or JPred if user selected JPred + String selectedSSSource = SS_ANNOTATION_LABEL; + if(ssSource.equals(JPRED_WEBSERVICE)) + selectedSSSource = SS_ANNOTATION_FROM_JPRED_LABEL; + // need to get real position for view position int[] viscont = seqData.getVisibleContigs(); - Map> calcIdMapInAlignmentAnnotation = new HashMap>(); - AlignmentAnnotation[] alignAnnotList = fr.getViewport().getAlignment().getAlignmentAnnotation(); - if(alignAnnotList.length > 0) { - + /* + * Add secondary structure annotations that are added to the annotation track + * to the map + */ + Map> ssAlignmentAnnotationForSequences + = new HashMap>(); + + AlignmentAnnotation[] alignAnnotList = fr.getViewport().getAlignment() + .getAlignmentAnnotation(); + + if(alignAnnotList.length > 0) { for (AlignmentAnnotation aa: alignAnnotList) { - if (SS_ANNOTATION_LABEL.equals(aa.label) || SS_ANNOTATION_FROM_JPRED_LABEL.equals(aa.label)) { - calcIdMapInAlignmentAnnotation.computeIfAbsent(aa.getCalcId(), k -> new HashSet<>()).add(aa.description); - } - + if (selectedSSSource.equals(aa.label)) { + ssAlignmentAnnotationForSequences.computeIfAbsent( + aa.sequenceRef.getName(), k -> new HashSet<>()) + .add(aa.description); + } } } - - Set seqsWithUndefinedSS = findSeqsWithUndefinedSS(seqs, calcIdMapInAlignmentAnnotation); - + /* + * Get the set of sequences which are not considered for the calculation. + * Following sequences are added: + * 1. Sequences without a defined secondary structure from the selected + * source. + * 2. Sequences whose secondary structure annotations are not added to + * the annotation track + */ + Set seqsWithUndefinedSS + = findSeqsWithUndefinedSS(seqs, ssAlignmentAnnotationForSequences); /* * scan each column, compute and add to each distance[i, j] @@ -214,14 +251,14 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel //secondary structure is fetched only if the current column is not //gap for the sequence - if(!gap1 && !undefinedSS1) { - secondaryStructure1.addAll( - findSSAnnotationForGivenSeqAndCol(seqs[i], cpos)); + if(!gap1 && !undefinedSS1) { + secondaryStructure1.addAll( + findSSAnnotationForGivenSeqAndCol(seqs[i], cpos)); } - if(!gap2 && !undefinedSS2) { - secondaryStructure2.addAll( - findSSAnnotationForGivenSeqAndCol(seqs[j], cpos)); + if(!gap2 && !undefinedSS2) { + secondaryStructure2.addAll( + findSSAnnotationForGivenSeqAndCol(seqs[j], cpos)); } /* @@ -232,7 +269,7 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel if ((!gap1 && !gap2) || params.includeGaps()) { int seqDistance = SetUtils.countDisjunction( - secondaryStructure1, secondaryStructure2); + secondaryStructure1, secondaryStructure2); distances[i][j] += seqDistance; } } @@ -278,43 +315,61 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel /* * position is not a gap */ - seqsWithoutGapAtCol.add(seq); + seqsWithoutGapAtCol.add(seq); } } return seqsWithoutGapAtCol; } + /** - * Builds and returns a set containing sequences (SeqCigar) which have - * no secondary structures defined - * + * Builds and returns a set containing sequences (SeqCigar) which + * are not considered for the similarity calculation. + * Following sequences are added: + * 1. Sequences without a defined secondary structure from the selected + * source. + * 2. Sequences whose secondary structure annotations are not added to + * the annotation track * @param seqs - * (0..) + * @param ssAlignmentAnnotationForSequences * @return */ - private static final String[] SS_ANNOTATION_LABELS = { - SS_ANNOTATION_LABEL, - SS_ANNOTATION_FROM_JPRED_LABEL - }; - - protected Set findSeqsWithUndefinedSS(SeqCigar[] seqs, Map> calcIdMapInAlignmentAnnotation) { + protected Set findSeqsWithUndefinedSS(SeqCigar[] seqs, + Map> ssAlignmentAnnotationForSequences) { Set seqsWithUndefinedSS = new HashSet<>(); for (SeqCigar seq : seqs) { - if (isSSUndefinedOrNotAdded(seq, calcIdMapInAlignmentAnnotation)) { + if (isSSUndefinedOrNotAdded(seq, ssAlignmentAnnotationForSequences)) { seqsWithUndefinedSS.add(seq); } } return seqsWithUndefinedSS; } - - private boolean isSSUndefinedOrNotAdded(SeqCigar seq, Map> calcIdMapInAlignmentAnnotation) { + + + /** + * Returns true if a sequence (SeqCigar) should not be + * considered for the similarity calculation. + * Following conditions are checked: + * 1. Sequence without a defined secondary structure from the selected + * source. + * 2. Sequences whose secondary structure annotations are not added to + * the annotation track + * @param seq + * @param ssAlignmentAnnotationForSequences + * @return + */ + private boolean isSSUndefinedOrNotAdded(SeqCigar seq, + Map> ssAlignmentAnnotationForSequences) { for (String label : SS_ANNOTATION_LABELS) { AlignmentAnnotation[] annotations = seq.getRefSeq().getAnnotation(label); if (annotations != null) { for (AlignmentAnnotation annotation : annotations) { - HashSet descriptionList = calcIdMapInAlignmentAnnotation.get(annotation.getCalcId()); - if (descriptionList.contains(annotation.description)) { - // Secondary structure annotation is present and added to the track, no need to add seq + HashSet descriptionSet = ssAlignmentAnnotationForSequences + .get(annotation.sequenceRef.getName()); + if (descriptionSet != null) + if (descriptionSet.contains(annotation.description)) { + // Secondary structure annotation is present and + //added to the track, no need to add seq return false; } } @@ -335,39 +390,39 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel * @return */ private Set findSSAnnotationForGivenSeqAndCol( - SeqCigar seq, int columnPosition) + SeqCigar seq, int columnPosition) { - Set secondaryStructure = new HashSet(); + Set secondaryStructure = new HashSet(); - char ss; - - //fetch the position in sequence for the column and finds the - //corresponding secondary structure annotation - //TO DO - consider based on priority - int seqPosition = seq.findPosition(columnPosition); - AlignmentAnnotation[] aa = seq.getRefSeq().getAnnotation(SS_ANNOTATION_LABEL); - - if(aa == null) { - aa = seq.getRefSeq().getAnnotation(SS_ANNOTATION_FROM_JPRED_LABEL); - } - - if (aa != null) { - if (aa[0].getAnnotationForPosition(seqPosition) != null) { - Annotation a = aa[0].getAnnotationForPosition(seqPosition); - ss = a.secondaryStructure; - - //There is no representation for coil and it can be either ' ' or null. - if (ss == ' ' || ss == '-') { - ss = COIL; - } - } - else { - ss = COIL; - } - secondaryStructure.add(String.valueOf(ss)); - } - - return secondaryStructure; + char ss; + + //fetch the position in sequence for the column and finds the + //corresponding secondary structure annotation + //TO DO - consider based on priority + int seqPosition = seq.findPosition(columnPosition); + AlignmentAnnotation[] aa = seq.getRefSeq().getAnnotation(SS_ANNOTATION_LABEL); + + if(aa == null) { + aa = seq.getRefSeq().getAnnotation(SS_ANNOTATION_FROM_JPRED_LABEL); + } + + if (aa != null) { + if (aa[0].getAnnotationForPosition(seqPosition) != null) { + Annotation a = aa[0].getAnnotationForPosition(seqPosition); + ss = a.secondaryStructure; + + //There is no representation for coil and it can be either ' ' or null. + if (ss == ' ' || ss == '-') { + ss = COIL; + } + } + else { + ss = COIL; + } + secondaryStructure.add(String.valueOf(ss)); + } + + return secondaryStructure; } @@ -404,6 +459,7 @@ public class SecondaryStructureDistanceModel extends DistanceScoreModel @Override public String toString() { - return "Score between sequences based on hamming distance between binary vectors marking secondary structure displayed at each column"; + return "Score between sequences based on hamming distance between binary " + + "vectors marking secondary structure displayed at each column"; } } \ No newline at end of file diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index b7bb58f..d126cc3 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -60,7 +60,6 @@ import jalview.api.analysis.SimilarityParamsI; import jalview.bin.Cache; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.SequenceGroup; -import jalview.datamodel.SequenceI; import jalview.util.MessageManager; /** @@ -81,6 +80,10 @@ public class CalculationChooser extends JPanel private static final int MIN_TREE_SELECTION = 3; private static final int MIN_PCA_SELECTION = 4; + + private static final String SS_ANNOTATION_LABEL = "Secondary Structure"; + + private static final String SS_ANNOTATION_FROM_JPRED_LABEL = "jnetpred"; AlignFrame af; @@ -91,6 +94,8 @@ public class CalculationChooser extends JPanel JRadioButton averageDistance; JComboBox modelNames; + + JComboBox ssSourceDropdown; JButton calculate; @@ -210,16 +215,34 @@ public class CalculationChooser extends JPanel pca.addActionListener(calcChanged); neighbourJoining.addActionListener(calcChanged); averageDistance.addActionListener(calcChanged); + + + //to do + ssSourceDropdown = buildSSSourcesOptionsList(); + ssSourceDropdown.setVisible(false); // Initially hide the dropdown /* * score models drop-down - with added tooltips! */ modelNames = buildModelOptionsList(); + // Step 3: Show or Hide Dropdown Based on Selection + modelNames.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String selectedModel = modelNames.getSelectedItem().toString(); + if (selectedModel.equals("Secondary Structure Similarity")) { + ssSourceDropdown.setVisible(true); + } else { + ssSourceDropdown.setVisible(false); + } + } + }); JPanel scoreModelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); scoreModelPanel.setOpaque(false); scoreModelPanel.add(modelNames); - + scoreModelPanel.add(ssSourceDropdown); + /* * score model parameters */ @@ -234,7 +257,7 @@ public class CalculationChooser extends JPanel paramsPanel.add(matchGaps); paramsPanel.add(includeGappedColumns); paramsPanel.add(shorterSequence); - + /* * OK / Cancel buttons */ @@ -272,7 +295,7 @@ public class CalculationChooser extends JPanel } this.add(actionPanel, BorderLayout.SOUTH); - int width = 350; + int width = 365; int height = includeParams ? 420 : 240; setMinimumSize(new Dimension(325, height - 10)); @@ -420,7 +443,38 @@ public class CalculationChooser extends JPanel return scoreModelsCombo; } + + private JComboBox buildSSSourcesOptionsList() + { + final JComboBox comboBox = new JComboBox<>(); + Object curSel = comboBox.getSelectedItem(); + DefaultComboBoxModel sourcesModel = new DefaultComboBoxModel<>(); + + List ssSources = getApplicableSecondaryStructureSources(); + + boolean selectedIsPresent = false; + for (String source : ssSources) + { + if (curSel != null && source.equals(curSel)) + { + selectedIsPresent = true; + curSel = source; + } + sourcesModel.addElement(source); + + } + + if (selectedIsPresent) + { + sourcesModel.setSelectedItem(curSel); + } + comboBox.setModel(sourcesModel); + + return comboBox; + } + + private void updateScoreModels(JComboBox comboBox, List toolTips) { @@ -531,6 +585,31 @@ public class CalculationChooser extends JPanel return filtered; } + + protected List getApplicableSecondaryStructureSources() + { + List ssSources = new ArrayList<>(); + + AlignmentAnnotation[] annotations = af.getViewport().getAlignment().getAlignmentAnnotation(); + boolean has3DStructure = false, hasJPred = false; + if(annotations.length > 0) { + + for (AlignmentAnnotation annotation : annotations) { + has3DStructure |= SS_ANNOTATION_LABEL.equals(annotation.label); + hasJPred |= SS_ANNOTATION_FROM_JPRED_LABEL.equals(annotation.label); + + if (has3DStructure && hasJPred) + break; + } + } + if(has3DStructure) + ssSources.add("3D Structures"); + if(hasJPred) + ssSources.add("JPred"); + + return ssSources; + } + /** * Open and calculate the selected tree or PCA on 'OK' */ @@ -538,8 +617,10 @@ public class CalculationChooser extends JPanel { boolean doPCA = pca.isSelected(); String modelName = modelNames.getSelectedItem().toString(); - SimilarityParamsI params = getSimilarityParameters(doPCA); - + String ssSource = ssSourceDropdown.getSelectedItem().toString(); + SimilarityParams params = getSimilarityParameters(doPCA); + if(ssSource.length()>0) + params.setSecondaryStructureSource(ssSource); if (doPCA) { openPcaPanel(modelName, params); @@ -640,7 +721,7 @@ public class CalculationChooser extends JPanel * @param doPCA * @return */ - protected SimilarityParamsI getSimilarityParameters(boolean doPCA) + protected SimilarityParams getSimilarityParameters(boolean doPCA) { // commented out: parameter choices read from gui widgets // SimilarityParamsI params = new SimilarityParams( -- 1.7.10.2