/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.ext.rbvi.chimera;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
import jalview.api.FeatureRenderer;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.MappedFeatures;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
import jalview.gui.Desktop;
import jalview.structure.AtomSpecModel;
import jalview.structure.StructureCommand;
import jalview.structure.StructureCommandI;
import jalview.structure.StructureCommandsBase;
import jalview.structure.StructureMapping;
import jalview.structure.StructureSelectionManager;
import jalview.util.ColorUtils;
/**
* Routines for generating Chimera commands for Jalview/Chimera binding
*
* @author JimP
*
*/
public class ChimeraCommands extends StructureCommandsBase
{
private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
"~display all;~ribbon;chain @CA|P");
public static final String NAMESPACE_PREFIX = "jv_";
private static final StructureCommandI COLOUR_BY_CHARGE = new StructureCommand(
"color white;color red ::ASP,GLU;color blue ::LYS,ARG;color yellow ::CYS");
private static final StructureCommandI COLOUR_BY_CHAIN = new StructureCommand(
"rainbow chain");
// Chimera clause to exclude alternate locations in atom selection
private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
@Override
public StructureCommandI getColourCommand(String atomSpec, Color colour)
{
// https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/color.html
String colourCode = getColourString(colour);
return new StructureCommand("color " + colourCode + " " + atomSpec);
}
/**
* Returns a colour formatted suitable for use in viewer command syntax
*
* @param colour
* @return
*/
protected String getColourString(Color colour)
{
return ColorUtils.toTkCode(colour);
}
/**
* 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.
*
* @param ssm
* @param files
* @param seqs
* @param viewPanel
* @return
*/
@Override
public List setAttributesForFeatures(
StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
AlignmentViewPanel viewPanel)
{
Map> featureMap = buildFeaturesMap(
ssm, files, seqs, viewPanel);
return setAttributes(featureMap);
}
/**
*
* Helper method to build a map of
* { featureType, { feature value, AtomSpecModel } }
*
*
* @param ssm
* @param files
* @param seqs
* @param viewPanel
* @return
*/
protected Map> buildFeaturesMap(
StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
AlignmentViewPanel viewPanel)
{
Map> theMap = new LinkedHashMap<>();
FeatureRenderer fr = viewPanel.getFeatureRenderer();
if (fr == null)
{
return theMap;
}
AlignViewportI viewport = viewPanel.getAlignViewport();
List visibleFeatures = fr.getDisplayedFeatureTypes();
/*
* 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;
}
AlignmentI alignment = viewPanel.getAlignment();
for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
{
final int modelNumber = pdbfnum + getModelStartNo();
String modelId = String.valueOf(modelNumber);
StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
if (mapping == null || mapping.length < 1)
{
continue;
}
for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++)
{
for (int m = 0; m < mapping.length; m++)
{
final SequenceI seq = seqs[pdbfnum][seqNo];
int sp = alignment.findIndex(seq);
StructureMapping structureMapping = mapping[m];
if (structureMapping.getSequence() == seq && sp > -1)
{
/*
* found a sequence with a mapping to a structure;
* now scan its features
*/
if (!visibleFeatures.isEmpty())
{
scanSequenceFeatures(visibleFeatures, structureMapping, seq,
theMap, modelId);
}
if (showLinkedFeatures)
{
scanComplementFeatures(complementRenderer, structureMapping,
seq, theMap, modelId);
}
}
}
}
}
return theMap;
}
/**
* 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,
String 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