public static int alignProteinAsDna(AlignmentI protein, AlignmentI dna)
{
List<SequenceI> unmappedProtein = new ArrayList<SequenceI>();
+ Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons = buildCodonColumnsMap(
+ protein, dna, unmappedProtein);
+ return alignProteinAs(protein, alignedCodons, unmappedProtein);
+ }
+
+ /**
+ * Builds a map whose key is an aligned codon position (3 alignment column
+ * numbers base 0), and whose value is a map from protein sequence to each
+ * protein's peptide residue for that codon. The map generates an ordering of
+ * the codons, and allows us to read off the peptides at each position in
+ * order to assemble 'aligned' protein sequences.
+ *
+ * @param protein
+ * the protein alignment
+ * @param dna
+ * the coding dna alignment
+ * @param unmappedProtein
+ * any unmapped proteins are added to this list
+ * @return
+ */
+ protected static Map<AlignedCodon, Map<SequenceI, AlignedCodon>> buildCodonColumnsMap(
+ AlignmentI protein, AlignmentI dna,
+ List<SequenceI> unmappedProtein)
+ {
+ /*
+ * maintain a list of any proteins with no mappings - these will be
+ * rendered 'as is' in the protein alignment as we can't align them
+ */
unmappedProtein.addAll(protein.getSequences());
List<AlignedCodonFrame> mappings = protein.getCodonFrames();
* {dnaSequence, {proteinSequence, codonProduct}} at that position. The
* comparator keeps the codon positions ordered.
*/
- Map<AlignedCodon, Map<SequenceI, String>> alignedCodons = new TreeMap<AlignedCodon, Map<SequenceI, String>>(
+ Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons = new TreeMap<AlignedCodon, Map<SequenceI, AlignedCodon>>(
new CodonComparator());
+
for (SequenceI dnaSeq : dna.getSequences())
{
for (AlignedCodonFrame mapping : mappings)
{
- Mapping seqMap = mapping.getMappingForSequence(dnaSeq);
SequenceI prot = mapping.findAlignedSequence(
dnaSeq.getDatasetSequence(), protein);
if (prot != null)
{
+ Mapping seqMap = mapping.getMappingForSequence(dnaSeq);
addCodonPositions(dnaSeq, prot, protein.getGapCharacter(),
seqMap, alignedCodons);
unmappedProtein.remove(prot);
}
}
}
- return alignProteinAs(protein, alignedCodons, unmappedProtein);
+
+ /*
+ * Finally add any unmapped peptide start residues (e.g. for incomplete
+ * codons) as if at the codon position before the second residue
+ */
+ int mappedSequenceCount = protein.getHeight() - unmappedProtein.size();
+ addUnmappedPeptideStarts(alignedCodons, mappedSequenceCount);
+
+ return alignedCodons;
+ }
+
+ /**
+ * Scans for any protein mapped from position 2 (meaning unmapped start
+ * position e.g. an incomplete codon), and synthesizes a 'codon' for it at the
+ * preceding position in the alignment
+ *
+ * @param alignedCodons
+ * the codon-to-peptide map
+ * @param mappedSequenceCount
+ * the number of distinct sequences in the map
+ */
+ protected static void addUnmappedPeptideStarts(
+ Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons,
+ int mappedSequenceCount)
+ {
+ // TODO there must be an easier way! root problem is that our mapping data
+ // model does not include phase so can't map part of a codon to a peptide
+ List<SequenceI> sequencesChecked = new ArrayList<SequenceI>();
+ AlignedCodon lastCodon = null;
+ Map<SequenceI, AlignedCodon> toAdd = new HashMap<SequenceI, AlignedCodon>();
+
+ for (Entry<AlignedCodon, Map<SequenceI, AlignedCodon>> entry : alignedCodons
+ .entrySet())
+ {
+ for (Entry<SequenceI, AlignedCodon> sequenceCodon : entry.getValue()
+ .entrySet())
+ {
+ SequenceI seq = sequenceCodon.getKey();
+ if (sequencesChecked.contains(seq))
+ {
+ continue;
+ }
+ sequencesChecked.add(seq);
+ AlignedCodon codon = sequenceCodon.getValue();
+ if (codon.peptideCol > 1)
+ {
+ System.err
+ .println("Problem mapping protein with >1 unmapped start positions: "
+ + seq.getName());
+ }
+ else if (codon.peptideCol == 1)
+ {
+ /*
+ * first position (peptideCol == 0) was unmapped - add it
+ */
+ if (lastCodon != null)
+ {
+ AlignedCodon firstPeptide = new AlignedCodon(lastCodon.pos1,
+ lastCodon.pos2, lastCodon.pos3, String.valueOf(seq
+ .getCharAt(0)), 0);
+ toAdd.put(seq, firstPeptide);
+ }
+ else
+ {
+ /*
+ * unmapped residue at start of alignment (no prior column) -
+ * 'insert' at nominal codon [0, 0, 0]
+ */
+ AlignedCodon firstPeptide = new AlignedCodon(0, 0, 0,
+ String.valueOf(seq.getCharAt(0)), 0);
+ toAdd.put(seq, firstPeptide);
+ }
+ }
+ if (sequencesChecked.size() == mappedSequenceCount)
+ {
+ // no need to check past first mapped position in all sequences
+ break;
+ }
+ }
+ lastCodon = entry.getKey();
+ }
+
+ /*
+ * add any new codons safely after iterating over the map
+ */
+ for (Entry<SequenceI, AlignedCodon> startCodon : toAdd.entrySet())
+ {
+ addCodonToMap(alignedCodons, startCodon.getValue(),
+ startCodon.getKey());
+ }
}
/**
* @return
*/
protected static int alignProteinAs(AlignmentI protein,
- Map<AlignedCodon, Map<SequenceI, String>> alignedCodons,
+ Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons,
List<SequenceI> unmappedProtein)
{
/*
int column = 0;
for (AlignedCodon codon : alignedCodons.keySet())
{
- final Map<SequenceI, String> columnResidues = alignedCodons
+ final Map<SequenceI, AlignedCodon> columnResidues = alignedCodons
.get(codon);
- for (Entry<SequenceI, String> entry : columnResidues.entrySet())
+ for (Entry<SequenceI, AlignedCodon> entry : columnResidues.entrySet())
{
// place translated codon at its column position in sequence
- entry.getKey().getSequence()[column] = entry.getValue().charAt(0);
+ entry.getKey().getSequence()[column] = entry.getValue().product
+ .charAt(0);
}
column++;
}
*/
static void addCodonPositions(SequenceI dna, SequenceI protein,
char gapChar, Mapping seqMap,
- Map<AlignedCodon, Map<SequenceI, String>> alignedCodons)
+ Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons)
{
Iterator<AlignedCodon> codons = seqMap.getCodonIterator(dna, gapChar);
+
+ /*
+ * add codon positions, and their peptide translations, to the alignment
+ * map, while remembering the first codon mapped
+ */
while (codons.hasNext())
{
try
{
AlignedCodon codon = codons.next();
- Map<SequenceI, String> seqProduct = alignedCodons.get(codon);
- if (seqProduct == null)
- {
- seqProduct = new HashMap<SequenceI, String>();
- alignedCodons.put(codon, seqProduct);
- }
- seqProduct.put(protein, codon.product);
+ addCodonToMap(alignedCodons, codon, protein);
} catch (IncompleteCodonException e)
{
// possible incomplete trailing codon - ignore
}
/**
+ * Helper method to add a codon-to-peptide entry to the aligned codons map
+ *
+ * @param alignedCodons
+ * @param codon
+ * @param protein
+ */
+ protected static void addCodonToMap(
+ Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons,
+ AlignedCodon codon, SequenceI protein)
+ {
+ Map<SequenceI, AlignedCodon> seqProduct = alignedCodons.get(codon);
+ if (seqProduct == null)
+ {
+ seqProduct = new HashMap<SequenceI, AlignedCodon>();
+ alignedCodons.put(codon, seqProduct);
+ }
+ seqProduct.put(protein, codon);
+ }
+
+ /**
* Returns true if a cDNA/Protein mapping either exists, or could be made,
* between at least one pair of sequences in the two alignments. Currently,
* the logic is:
* @return
*/
protected static List<List<int[]>> getMappedColumns(
- List<SequenceI> mappedSequences,
- List<AlignedCodonFrame> mappings)
+ List<SequenceI> mappedSequences, List<AlignedCodonFrame> mappings)
{
List<List<int[]>> result = new ArrayList<List<int[]>>();
for (SequenceI seq : mappedSequences)
* @return
*/
protected static MapList addCdsMappings(SequenceI dnaSeq,
- SequenceI cdsSeq,
- Mapping dnaMapping, AlignedCodonFrame newMappings)
+ SequenceI cdsSeq, Mapping dnaMapping,
+ AlignedCodonFrame newMappings)
{
cdsSeq.createDatasetSequence();
cdsRanges.add(new int[] { 1, cdsDataset.getLength() });
MapList cdsToPeptide = new MapList(cdsRanges, dnaMapping.getMap()
.getToRanges(), 3, 1);
- newMappings.addMap(cdsDataset, dnaMapping.getTo(),
- cdsToPeptide);
+ newMappings.addMap(cdsDataset, dnaMapping.getTo(), cdsToPeptide);
/*
* dna 'from' ranges map 1:1 to the contiguous extracted CDS
*/
- MapList dnaToCds = new MapList(
- dnaMapping.getMap().getFromRanges(), cdsRanges, 1, 1);
+ MapList dnaToCds = new MapList(dnaMapping.getMap().getFromRanges(),
+ cdsRanges, 1, 1);
newMappings.addMap(dnaSeq, cdsDataset, dnaToCds);
return dnaToCds;
}