atomSpecs = new ArrayList<>();
for (String atomSpec : structureSelection)
{
try
{
AtomSpec spec = parseAtomSpec(atomSpec);
String pdbfilename = getPdbFileForModel(spec.getModelNumber());
spec.setPdbFile(pdbfilename);
atomSpecs.add(spec);
} catch (IllegalArgumentException e)
{
Cache.log.error("Failed to parse atomspec: " + atomSpec);
}
}
return atomSpecs;
}
/**
* @param modelId
* @return
*/
protected String getPdbFileForModel(int modelId)
{
/*
* Work out the pdbfilename from the model number
*/
String pdbfilename = modelFileNames[0];
findfileloop: for (String pdbfile : this.chimeraMaps.keySet())
{
for (ChimeraModel cm : chimeraMaps.get(pdbfile))
{
if (cm.getModelNumber() == modelId)
{
pdbfilename = pdbfile;
break findfileloop;
}
}
}
return pdbfilename;
}
private void log(String message)
{
System.err.println("## Chimera log: " + message);
}
/**
* Constructs and send commands to Chimera to set attributes on residues for
* features visible in Jalview.
*
* The syntax is: setattr r <attName> <attValue> <atomSpec>
*
* For example: setattr r jv_chain "Ferredoxin-1, Chloroplastic" #0:94.A
*
* @param avp
* @return
*/
public int sendFeaturesToViewer(AlignmentViewPanel avp)
{
// TODO refactor as required to pull up to an interface
Map> featureValues = buildFeaturesMap(
avp);
List commands = getCommandGenerator()
.setAttributes(featureValues);
if (commands.size() > 10)
{
sendCommandsByFile(commands);
}
else
{
for (StructureCommandI command : commands)
{
sendAsynchronousCommand(command, null);
}
}
return commands.size();
}
/**
* Write commands to a temporary file, and send a command to Chimera to open the
* file as a commands script. For use when sending a large number of separate
* commands would overload the REST interface mechanism.
*
* @param commands
*/
protected void sendCommandsByFile(List commands)
{
try
{
File tmp = File.createTempFile("chim", getCommandFileExtension());
tmp.deleteOnExit();
PrintWriter out = new PrintWriter(new FileOutputStream(tmp));
for (StructureCommandI command : commands)
{
out.println(command.getCommand());
}
out.flush();
out.close();
String path = tmp.getAbsolutePath();
StructureCommandI command = getCommandGenerator()
.openCommandFile(path);
sendAsynchronousCommand(command, null);
} catch (IOException e)
{
System.err.println("Sending commands to Chimera via file failed with "
+ e.getMessage());
}
}
/**
* Returns the file extension required for a file of commands to be read by
* the structure viewer
* @return
*/
protected String getCommandFileExtension()
{
return ".com";
}
/**
* Create features in Jalview for the given attribute name and structure
* residues.
*
*
* The residue list should be 0, 1 or more reply lines of the format:
* residue id #0:5.A isHelix -155.000836316 index 5
* or
* residue id #0:6.A isHelix None
*
*
* @param attName
* @param residues
* @return the number of features added
*/
protected int createFeaturesForAttributes(String attName,
List residues)
{
int featuresAdded = 0;
String featureGroup = getViewerFeatureGroup();
for (String residue : residues)
{
AtomSpec spec = null;
String[] tokens = residue.split(" ");
if (tokens.length < 5)
{
continue;
}
String atomSpec = tokens[2];
String attValue = tokens[4];
/*
* ignore 'None' (e.g. for phi) or 'False' (e.g. for isHelix)
*/
if ("None".equalsIgnoreCase(attValue)
|| "False".equalsIgnoreCase(attValue))
{
continue;
}
try
{
spec = parseAtomSpec(atomSpec);
} catch (IllegalArgumentException e)
{
Cache.log.error("Problem parsing atomspec " + atomSpec);
continue;
}
String chainId = spec.getChain();
String description = attValue;
float score = Float.NaN;
try
{
score = Float.valueOf(attValue);
description = chainId;
} catch (NumberFormatException e)
{
// was not a float value
}
String pdbFile = getPdbFileForModel(spec.getModelNumber());
spec.setPdbFile(pdbFile);
List atoms = Collections.singletonList(spec);
/*
* locate the mapped position in the alignment (if any)
*/
SearchResultsI sr = getSsm()
.findAlignmentPositionsForStructurePositions(atoms);
/*
* expect one matched alignment position, or none
* (if the structure position is not mapped)
*/
for (SearchResultMatchI m : sr.getResults())
{
SequenceI seq = m.getSequence();
int start = m.getStart();
int end = m.getEnd();
SequenceFeature sf = new SequenceFeature(attName, description,
start, end, score, featureGroup);
// todo: should SequenceFeature have an explicit property for chain?
// note: repeating the action shouldn't duplicate features
if (seq.addSequenceFeature(sf))
{
featuresAdded++;
}
}
}
return featuresAdded;
}
/**
* Answers the feature group name to apply to features created in Jalview from
* Chimera attributes
*
* @return
*/
protected String getViewerFeatureGroup()
{
// todo pull up to interface
return CHIMERA_FEATURE_GROUP;
}
@Override
public String getModelIdForFile(String pdbFile)
{
List foundModels = chimeraMaps.get(pdbFile);
if (foundModels != null && !foundModels.isEmpty())
{
return String.valueOf(foundModels.get(0).getModelNumber());
}
return "";
}
/**
* Answers a (possibly empty) list of attribute names in Chimera[X], excluding
* any which were added from Jalview
*
* @return
*/
public List getChimeraAttributes()
{
List attributes = new ArrayList<>();
StructureCommandI command = getCommandGenerator().listResidueAttributes();
final List reply = executeCommand(command, true);
if (reply != null)
{
for (String inputLine : reply)
{
String[] lineParts = inputLine.split("\\s");
if (lineParts.length == 2 && lineParts[0].equals("resattr"))
{
String attName = lineParts[1];
/*
* exclude attributes added from Jalview
*/
if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX))
{
attributes.add(attName);
}
}
}
}
return attributes;
}
/**
* Returns the file extension to use for a saved viewer session file (.py)
*
* @return
*/
@Override
public String getSessionFileExtension()
{
return CHIMERA_SESSION_EXTENSION;
}
@Override
public String getHelpURL()
{
return "https://www.cgl.ucsf.edu/chimera/docs/UsersGuide";
}
}