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.gui.Desktop;
import jalview.renderer.seqfeatures.FeatureColourFinder;
import jalview.structure.StructureMapping;
import jalview.structure.StructureMappingcommandSet;
* delimited). If length limit issues arise, refactor to return one color
* command per colour.
*/
- List<String> commands = new ArrayList<String>();
+ List<String> commands = new ArrayList<>();
StringBuilder sb = new StringBuilder(256);
boolean firstColour = true;
for (Object key : colourMap.keySet())
AlignViewportI viewport = viewPanel.getAlignViewport();
HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
AlignmentI al = viewport.getAlignment();
- Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<Object, AtomSpecModel>();
+ Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<>();
Color lastColour = null;
for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
{
if (startPos != -1)
{
- addColourRange(colourMap, lastColour, pdbfnum, startPos,
+ addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos,
lastPos, lastChain);
}
startPos = pos;
// final colour range
if (lastColour != null)
{
- addColourRange(colourMap, lastColour, pdbfnum, startPos,
+ addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos,
lastPos, lastChain);
}
// break;
}
/**
- * Helper method to add one contiguous colour range to the colour map.
+ * Helper method to add one contiguous range to the AtomSpec model for the given
+ * value (creating the model if necessary). As used by Jalview, {@code value} is
+ * <ul>
+ * <li>a colour, when building a 'colour structure by sequence' command</li>
+ * <li>a feature value, when building a 'set Chimera attributes from features'
+ * command</li>
+ * </ul>
*
* @param map
- * @param key
+ * @param value
* @param model
* @param startPos
* @param endPos
* @param chain
*/
- protected static void addColourRange(Map<Object, AtomSpecModel> map,
- Object key, int model, int startPos, int endPos, String chain)
+ protected static void addAtomSpecRange(Map<Object, AtomSpecModel> map,
+ Object value, int model, int startPos, int endPos, String chain)
{
/*
* Get/initialize map of data for the colour
*/
- AtomSpecModel atomSpec = map.get(key);
+ AtomSpecModel atomSpec = map.get(value);
if (atomSpec == null)
{
atomSpec = new AtomSpecModel();
- map.put(key, atomSpec);
+ map.put(value, atomSpec);
}
atomSpec.addRange(model, startPos, endPos, chain);
StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
AlignmentViewPanel viewPanel)
{
- Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
+ Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<>();
FeatureRenderer fr = viewPanel.getFeatureRenderer();
if (fr == null)
return theMap;
}
+ AlignViewportI viewport = viewPanel.getAlignViewport();
List<String> 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<String> 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;
}
{
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);
+ }
}
}
}
}
/**
- * 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<String, Map<Object, AtomSpecModel>> 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<int[]> 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<Object, AtomSpecModel> 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
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<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
sf.getEnd());
Map<Object, AtomSpecModel> featureValues = theMap.get(type);
if (featureValues == null)
{
- featureValues = new HashMap<Object, AtomSpecModel>();
+ featureValues = new HashMap<>();
theMap.put(type, featureValues);
}
for (int[] range : mappedRanges)
{
- addColourRange(featureValues, value, modelNumber, range[0],
+ addAtomSpecRange(featureValues, value, modelNumber, range[0],
range[1], mapping.getChain());
}
}
protected static List<String> buildSetAttributeCommands(
Map<String, Map<Object, AtomSpecModel>> featureMap)
{
- List<String> commands = new ArrayList<String>();
+ List<String> commands = new ArrayList<>();
for (String featureType : featureMap.keySet())
{
String attributeName = makeAttributeName(featureType);
{
Map<Object, AtomSpecModel> map = new LinkedHashMap<Object, AtomSpecModel>();
- ChimeraCommands.addColourRange(map, Color.blue, 0, 2, 5, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 0, 7, 7, "B");
- ChimeraCommands.addColourRange(map, Color.blue, 0, 9, 23, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 1, 1, 1, "A");
- ChimeraCommands.addColourRange(map, Color.blue, 1, 4, 7, "B");
- ChimeraCommands.addColourRange(map, Color.yellow, 1, 8, 8, "A");
- ChimeraCommands.addColourRange(map, Color.yellow, 1, 3, 5, "A");
- ChimeraCommands.addColourRange(map, Color.red, 0, 3, 5, "A");
- ChimeraCommands.addColourRange(map, Color.red, 0, 6, 9, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 2, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 7, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 9, 23, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 1, 1, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 4, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 8, 8, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 6, 9, "A");
// Colours should appear in the Chimera command in the order in which
// they were added; within colour, by model, by chain, ranges in start order
* start with just one feature/value...
*/
featuresMap.put("chain", featureValues);
- ChimeraCommands.addColourRange(featureValues, "X", 0, 8, 20, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 8, 20, "A");
List<String> commands = ChimeraCommands
.buildSetAttributeCommands(featuresMap);
assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:8-20.A");
// add same feature value, overlapping range
- ChimeraCommands.addColourRange(featureValues, "X", 0, 3, 9, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 3, 9, "A");
// same feature value, contiguous range
- ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "A");
commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
assertEquals(1, commands.size());
assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A");
// same feature value and model, different chain
- ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "B");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "B");
// same feature value and chain, different model
- ChimeraCommands.addColourRange(featureValues, "X", 1, 26, 30, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", 1, 26, 30, "A");
commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
assertEquals(1, commands.size());
assertEquals(commands.get(0),
"setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A");
// same feature, different value
- ChimeraCommands.addColourRange(featureValues, "Y", 0, 40, 50, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "Y", 0, 40, 50, "A");
commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
assertEquals(2, commands.size());
// commands are ordered by feature type but not by value
featuresMap.clear();
featureValues.clear();
featuresMap.put("side-chain binding!", featureValues);
- ChimeraCommands.addColourRange(featureValues,
+ ChimeraCommands.addAtomSpecRange(featureValues,
"<html>metal <a href=\"http:a.b.c/x\"> 'ion!", 0, 7, 15,
"A");
// feature names are sanitised to change non-alphanumeric to underscore