X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fext%2Frbvi%2Fchimera%2FChimeraCommands.java;h=bfec5b274ae824ceb7cedeaaf60417453737e8af;hb=ec1c914a99011e20634fbff19df71e2627766dfd;hp=c52b9a217412d522dfa1cf8b641789bd51bfc033;hpb=2d62933ef95beb94c1ec2444bcced4a3a7ec42c0;p=jalview.git diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index c52b9a2..bfec5b2 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -23,25 +23,21 @@ package jalview.ext.rbvi.chimera; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; -import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; -import jalview.datamodel.HiddenColumns; +import jalview.datamodel.MappedFeatures; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; -import jalview.renderer.seqfeatures.FeatureColourFinder; +import jalview.gui.Desktop; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; import jalview.util.ColorUtils; -import jalview.util.Comparison; -import jalview.util.IntRangeComparator; +import jalview.util.StructureCommands; import java.awt.Color; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -52,7 +48,7 @@ import java.util.Map; * @author JimP * */ -public class ChimeraCommands +public class ChimeraCommands extends StructureCommands { public static final String NAMESPACE_PREFIX = "jv_"; @@ -65,31 +61,17 @@ public class ChimeraCommands /** * Constructs Chimera commands to colour residues as per the Jalview alignment * - * @param files - * @param viewPanel + * @param colourMap * @param binding * @return */ - public static StructureMappingcommandSet[] getColourBySequenceCommand( - String[] files, AlignmentViewPanel viewPanel, + public static String[] getColourBySequenceCommand( + Map colourMap, AAStructureBindingModel binding) { - StructureSelectionManager ssm = binding.getSsm(); - SequenceRenderer sr = binding.getSequenceRenderer(viewPanel); - SequenceI[][] sequence = binding.getSequence(); - boolean hideHiddenRegions = binding.isShowAlignmentOnly() - && binding.isHideHiddenRegions(); - - Map colourMap = buildColoursMap(ssm, files, - sequence, sr, hideHiddenRegions, viewPanel); - List colourCommands = buildColourCommands(colourMap, binding); - StructureMappingcommandSet cs = new StructureMappingcommandSet( - ChimeraCommands.class, null, - colourCommands.toArray(new String[colourCommands.size()])); - - return new StructureMappingcommandSet[] { cs }; + return colourCommands.toArray(new String[colourCommands.size()]); } /** @@ -135,155 +117,6 @@ public class ChimeraCommands } /** - * Build a data structure which records contiguous subsequences for each colour. - * From this we can easily generate the Chimera command for colour by sequence. - * - *
-   * Color
-   *     Model number
-   *         Chain
-   *             list of start/end ranges
-   * 
- * - * Ordering is by order of addition (for colours and positions), natural - * ordering (for models and chains) - * - * @param ssm - * @param files - * @param sequence - * @param sr - * @param hideHiddenRegions - * @param viewPanel - * @return - */ - protected static Map buildColoursMap( - StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, - boolean hideHiddenRegions, AlignmentViewPanel viewPanel) - { - FeatureRenderer fr = viewPanel.getFeatureRenderer(); - FeatureColourFinder finder = new FeatureColourFinder(fr); - AlignViewportI viewport = viewPanel.getAlignViewport(); - HiddenColumns cs = viewport.getAlignment().getHiddenColumns(); - AlignmentI al = viewport.getAlignment(); - Map colourMap = new LinkedHashMap<>(); - Color lastColour = null; - - for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) - { - StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); - - if (mapping == null || mapping.length < 1) - { - continue; - } - - int startPos = -1, lastPos = -1; - String lastChain = ""; - for (int s = 0; s < sequence[pdbfnum].length; s++) - { - for (int sp, m = 0; m < mapping.length; m++) - { - final SequenceI seq = sequence[pdbfnum][s]; - if (mapping[m].getSequence() == seq - && (sp = al.findIndex(seq)) > -1) - { - SequenceI asp = al.getSequenceAt(sp); - for (int r = 0; r < asp.getLength(); r++) - { - // no mapping to gaps in sequence - if (Comparison.isGap(asp.getCharAt(r))) - { - continue; - } - int pos = mapping[m].getPDBResNum(asp.findPosition(r)); - - if (pos < 1 || pos == lastPos) - { - continue; - } - - Color colour = sr.getResidueColour(seq, r, finder); - - /* - * hidden regions are shown gray or, optionally, ignored - */ - if (!cs.isVisible(r)) - { - if (hideHiddenRegions) - { - continue; - } - else - { - colour = Color.GRAY; - } - } - - final String chain = mapping[m].getChain(); - - /* - * Just keep incrementing the end position for this colour range - * _unless_ colour, PDB model or chain has changed, or there is a - * gap in the mapped residue sequence - */ - final boolean newColour = !colour.equals(lastColour); - final boolean nonContig = lastPos + 1 != pos; - final boolean newChain = !chain.equals(lastChain); - if (newColour || nonContig || newChain) - { - if (startPos != -1) - { - addColourRange(colourMap, lastColour, pdbfnum, startPos, - lastPos, lastChain); - } - startPos = pos; - } - lastColour = colour; - lastPos = pos; - lastChain = chain; - } - // final colour range - if (lastColour != null) - { - addColourRange(colourMap, lastColour, pdbfnum, startPos, - lastPos, lastChain); - } - // break; - } - } - } - } - return colourMap; - } - - /** - * Helper method to add one contiguous colour range to the colour map. - * - * @param map - * @param key - * @param model - * @param startPos - * @param endPos - * @param chain - */ - protected static void addColourRange(Map map, - Object key, int model, int startPos, int endPos, String chain) - { - /* - * Get/initialize map of data for the colour - */ - AtomSpecModel atomSpec = map.get(key); - if (atomSpec == null) - { - atomSpec = new AtomSpecModel(); - map.put(key, atomSpec); - } - - atomSpec.addRange(model, startPos, endPos, chain); - } - - /** * Constructs and returns Chimera commands to set attributes on residues * corresponding to features in Jalview. Attribute names are the Jalview feature * type, with a "jv_" prefix. @@ -338,8 +171,27 @@ public class ChimeraCommands return theMap; } + AlignViewportI viewport = viewPanel.getAlignViewport(); List visibleFeatures = fr.getDisplayedFeatureTypes(); - if (visibleFeatures.isEmpty()) + + /* + * if alignment is showing features from complement, we also transfer + * these features to the corresponding mapped structure residues + */ + boolean showLinkedFeatures = viewport.isShowComplementFeatures(); + List complementFeatures = new ArrayList<>(); + FeatureRenderer complementRenderer = null; + if (showLinkedFeatures) + { + AlignViewportI comp = fr.getViewport().getCodingComplement(); + if (comp != null) + { + complementRenderer = Desktop.getAlignFrameFor(comp) + .getFeatureRenderer(); + complementFeatures = complementRenderer.getDisplayedFeatureTypes(); + } + } + if (visibleFeatures.isEmpty() && complementFeatures.isEmpty()) { return theMap; } @@ -360,16 +212,23 @@ public class ChimeraCommands { final SequenceI seq = seqs[pdbfnum][seqNo]; int sp = alignment.findIndex(seq); - if (mapping[m].getSequence() == seq && sp > -1) + StructureMapping structureMapping = mapping[m]; + if (structureMapping.getSequence() == seq && sp > -1) { /* * found a sequence with a mapping to a structure; * now scan its features */ - SequenceI asp = alignment.getSequenceAt(sp); - - scanSequenceFeatures(visibleFeatures, mapping[m], asp, theMap, - pdbfnum); + if (!visibleFeatures.isEmpty()) + { + scanSequenceFeatures(visibleFeatures, structureMapping, seq, + theMap, pdbfnum); + } + if (showLinkedFeatures) + { + scanComplementFeatures(complementRenderer, structureMapping, + seq, theMap, pdbfnum); + } } } } @@ -378,9 +237,85 @@ public class ChimeraCommands } /** - * Inspect features on the sequence; for each feature that is visible, - * determine its mapped ranges in the structure (if any) according to the - * given mapping, and add them to the map + * Scans visible features in mapped positions of the CDS/peptide complement, and + * adds any found to the map of attribute values/structure positions + * + * @param complementRenderer + * @param structureMapping + * @param seq + * @param theMap + * @param modelNumber + */ + protected static void scanComplementFeatures( + FeatureRenderer complementRenderer, + StructureMapping structureMapping, SequenceI seq, + Map> theMap, int modelNumber) + { + /* + * for each sequence residue mapped to a structure position... + */ + for (int seqPos : structureMapping.getMapping().keySet()) + { + /* + * find visible complementary features at mapped position(s) + */ + MappedFeatures mf = complementRenderer + .findComplementFeaturesAtResidue(seq, seqPos); + if (mf != null) + { + for (SequenceFeature sf : mf.features) + { + String type = sf.getType(); + + /* + * Don't copy features which originated from Chimera + */ + if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP + .equals(sf.getFeatureGroup())) + { + continue; + } + + /* + * record feature 'value' (score/description/type) as at the + * corresponding structure position + */ + List mappedRanges = structureMapping + .getPDBResNumRanges(seqPos, seqPos); + + if (!mappedRanges.isEmpty()) + { + String value = sf.getDescription(); + if (value == null || value.length() == 0) + { + value = type; + } + float score = sf.getScore(); + if (score != 0f && !Float.isNaN(score)) + { + value = Float.toString(score); + } + Map featureValues = theMap.get(type); + if (featureValues == null) + { + featureValues = new HashMap<>(); + theMap.put(type, featureValues); + } + for (int[] range : mappedRanges) + { + addAtomSpecRange(featureValues, value, modelNumber, range[0], + range[1], structureMapping.getChain()); + } + } + } + } + } + } + + /** + * Inspect features on the sequence; for each feature that is visible, determine + * its mapped ranges in the structure (if any) according to the given mapping, + * and add them to the map. * * @param visibleFeatures * @param mapping @@ -399,15 +334,14 @@ public class ChimeraCommands String type = sf.getType(); /* - * Only copy visible features, don't copy any which originated - * from Chimera, and suppress uninteresting ones (e.g. RESNUM) + * Don't copy features which originated from Chimera */ - boolean isFromViewer = JalviewChimeraBinding.CHIMERA_FEATURE_GROUP - .equals(sf.getFeatureGroup()); - if (isFromViewer) + if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP + .equals(sf.getFeatureGroup())) { continue; } + List mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(), sf.getEnd()); @@ -431,7 +365,7 @@ public class ChimeraCommands } for (int[] range : mappedRanges) { - addColourRange(featureValues, value, modelNumber, range[0], + addAtomSpecRange(featureValues, value, modelNumber, range[0], range[1], mapping.getChain()); } } @@ -543,8 +477,6 @@ public class ChimeraCommands sb.append("|"); } firstModel = false; - // todo use JalviewChimeraBinding.getModelSpec(model) - // which means this cannot be static sb.append(binding.getModelSpec(model)).append(":"); boolean firstPositionForModel = true; @@ -555,77 +487,24 @@ public class ChimeraCommands List rangeList = atomSpec.getRanges(model, chain); - /* - * sort ranges into ascending start position order - */ - Collections.sort(rangeList, IntRangeComparator.ASCENDING); - - int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0]; - int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1]; - - Iterator iterator = rangeList.iterator(); - while (iterator.hasNext()) - { - int[] range = iterator.next(); - if (range[0] <= end + 1) - { - /* - * range overlaps or is contiguous with the last one - * - so just extend the end position, and carry on - * (unless this is the last in the list) - */ - end = Math.max(end, range[1]); - } - else - { - /* - * we have a break so append the last range - */ - appendRange(sb, start, end, chain, firstPositionForModel); - firstPositionForModel = false; - start = range[0]; - end = range[1]; - } - } - - /* - * and append the last range - */ - if (!rangeList.isEmpty()) - { - appendRange(sb, start, end, chain, firstPositionForModel); - firstPositionForModel = false; - } + String chainToken = " ".equals(chain) ? "." : "." + chain; + appendResidueRange(sb, rangeList, chainToken, + firstPositionForModel); + firstPositionForModel = false; } } return sb.toString(); } /** - * A helper method that appends one start-end range to a Chimera atomspec + * Chimera atomspec requires chain to be specified for each start-end residue + * range, otherwise it will apply to all chains * * @param sb - * @param start - * @param end * @param chain - * @param firstPositionForModel */ - static void appendRange(StringBuilder sb, int start, int end, - String chain, boolean firstPositionForModel) + protected static void appendChainToRange(StringBuilder sb, String chain) { - if (!firstPositionForModel) - { - sb.append(","); - } - if (end == start) - { - sb.append(start); - } - else - { - sb.append(start).append("-").append(end); - } - sb.append("."); if (!" ".equals(chain)) {