import static jalview.io.gff.GffConstants.CLINICAL_SIGNIFICANCE;
-import jalview.api.DBRefEntryI;
import jalview.datamodel.AlignedCodon;
import jalview.datamodel.AlignedCodonFrame;
import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
* Realigns the given dna to match the alignment of the protein, using codon
* mappings to translate aligned peptide positions to codons.
*
+ * Always produces a padded CDS alignment.
+ *
* @param dna
* the alignment whose sequences are realigned by this method
* @param protein
// todo: implement this
List<AlignedCodonFrame> mappings = protein.getCodonFrames();
int alignedCount = 0;
+ int width = 0; // alignment width for padding CDS
for (SequenceI dnaSeq : dna.getSequences())
{
if (alignCdsSequenceAsProtein(dnaSeq, protein, mappings,
{
alignedCount++;
}
+ width = Math.max(dnaSeq.getLength(), width);
+ }
+ int oldwidth, diff;
+ for (SequenceI dnaSeq : dna.getSequences())
+ {
+ oldwidth = dnaSeq.getLength();
+ diff = width - oldwidth;
+ if (diff > 0)
+ {
+ dnaSeq.insertCharAt(oldwidth, diff, dna.getGapCharacter());
+ }
}
return alignedCount;
}
* its dataset sequence to the dataset
*/
cdsSeq = makeCdsSequence(dnaSeq.getDatasetSequence(), aMapping);
+ // cdsSeq has a name constructed as CDS|<dbref>
+ // <dbref> will be either the accession for the coding sequence,
+ // marked in the /via/ dbref to the protein product accession
+ // or it will be the original nucleotide accession.
SequenceI cdsSeqDss = cdsSeq.createDatasetSequence();
cdsSeqs.add(cdsSeq);
if (!dataset.getSequences().contains(cdsSeqDss))
MapList cdsToProteinMap = new MapList(cdsRange, mapList.getToRanges(),
mapList.getFromRatio(), mapList.getToRatio());
AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
- cdsToProteinMapping.addMap(cdsSeq, proteinProduct, cdsToProteinMap);
+ cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
+ cdsToProteinMap);
/*
* guard against duplicating the mapping if repeating this action
mappings.add(cdsToProteinMapping);
}
- /*
- * copy protein's dbrefs to CDS sequence
- * this enables Get Cross-References from CDS alignment
- */
- DBRefEntry[] proteinRefs = DBRefUtils.selectDbRefs(false,
- proteinProduct.getDBRefs());
- if (proteinRefs != null)
- {
- for (DBRefEntry ref : proteinRefs)
- {
- DBRefEntry cdsToProteinRef = new DBRefEntry(ref);
- cdsToProteinRef.setMap(new Mapping(proteinProduct,
- cdsToProteinMap));
- cdsSeqDss.addDBRef(cdsToProteinRef);
- }
- }
-
+ propagateDBRefsToCDS(cdsSeqDss, dnaSeq.getDatasetSequence(),
+ proteinProduct, aMapping);
/*
* add another mapping from original 'from' range to CDS
*/
MapList dnaToCdsMap = new MapList(mapList.getFromRanges(),
cdsRange, 1,
1);
- dnaToCdsMapping.addMap(dnaSeq.getDatasetSequence(), cdsSeq,
+ dnaToCdsMapping.addMap(dnaSeq.getDatasetSequence(), cdsSeqDss,
dnaToCdsMap);
if (!mappings.contains(dnaToCdsMapping))
{
* same source and accession, so need a different accession for
* the CDS from the dna sequence
*/
- DBRefEntryI dnaRef = dnaDss.getSourceDBRef();
- if (dnaRef != null)
+
+ // specific use case:
+ // Genomic contig ENSCHR:1, contains coding regions for ENSG01,
+ // ENSG02, ENSG03, with transcripts and products similarly named.
+ // cannot add distinct dbrefs mapping location on ENSCHR:1 to ENSG01
+
+ // JBPNote: ?? can't actually create an example that demonstrates we
+ // need to
+ // synthesize an xref.
+
+ for (DBRefEntry primRef : dnaDss.getPrimaryDBRefs())
{
+ // creates a complementary cross-reference to the source sequence's
+ // primary reference.
+
+ DBRefEntry cdsCrossRef = new DBRefEntry(primRef.getSource(),
+ primRef.getSource() + ":" + primRef.getVersion(),
+ primRef.getAccessionId());
+ cdsCrossRef
+ .setMap(new Mapping(dnaDss, new MapList(dnaToCdsMap)));
+ cdsSeqDss.addDBRef(cdsCrossRef);
+
+ // problem here is that the cross-reference is synthesized -
+ // cdsSeq.getName() may be like 'CDS|dnaaccession' or
+ // 'CDS|emblcdsacc'
// assuming cds version same as dna ?!?
- DBRefEntry proteinToCdsRef = new DBRefEntry(dnaRef.getSource(),
- dnaRef.getVersion(), cdsSeq.getName());
+
+ DBRefEntry proteinToCdsRef = new DBRefEntry(
+ primRef.getSource(), primRef.getVersion(),
+ cdsSeq.getName());
+ //
proteinToCdsRef.setMap(new Mapping(cdsSeqDss, cdsToProteinMap
.getInverse()));
proteinProduct.addDBRef(proteinToCdsRef);
}
}
}
-
+
/*
* assign 'from id' held in the mapping if set (e.g. EMBL protein_id),
* else generate a sequence name
}
/**
+ * add any DBRefEntrys to cdsSeq from contig that have a Mapping congruent to
+ * the given mapping.
+ *
+ * @param cdsSeq
+ * @param contig
+ * @param mapping
+ * @return list of DBRefEntrys added.
+ */
+ public static List<DBRefEntry> propagateDBRefsToCDS(SequenceI cdsSeq,
+ SequenceI contig, SequenceI proteinProduct, Mapping mapping)
+ {
+
+ // gather direct refs from contig congrent with mapping
+ List<DBRefEntry> direct = new ArrayList<DBRefEntry>();
+ HashSet<String> directSources = new HashSet<String>();
+ if (contig.getDBRefs() != null)
+ {
+ for (DBRefEntry dbr : contig.getDBRefs())
+ {
+ if (dbr.hasMap() && dbr.getMap().getMap().isTripletMap())
+ {
+ MapList map = dbr.getMap().getMap();
+ // check if map is the CDS mapping
+ if (mapping.getMap().equals(map))
+ {
+ direct.add(dbr);
+ directSources.add(dbr.getSource());
+ }
+ }
+ }
+ }
+ DBRefEntry[] onSource = DBRefUtils.selectRefs(
+ proteinProduct.getDBRefs(),
+ directSources.toArray(new String[0]));
+ List<DBRefEntry> propagated = new ArrayList<DBRefEntry>();
+
+ // and generate appropriate mappings
+ for (DBRefEntry cdsref : direct)
+ {
+ // clone maplist and mapping
+ MapList cdsposmap = new MapList(Arrays.asList(new int[][] { new int[]
+ { cdsSeq.getStart(), cdsSeq.getEnd() } }), cdsref.getMap().getMap()
+ .getToRanges(), 3, 1);
+ Mapping cdsmap = new Mapping(cdsref.getMap().getTo(), cdsref.getMap()
+ .getMap());
+
+ // create dbref
+ DBRefEntry newref = new DBRefEntry(cdsref.getSource(),
+ cdsref.getVersion(), cdsref.getAccessionId(), new Mapping(
+ cdsmap.getTo(), cdsposmap));
+
+ // and see if we can map to the protein product for this mapping.
+ // onSource is the filtered set of accessions on protein that we are
+ // tranferring, so we assume accession is the same.
+ if (cdsmap.getTo() == null && onSource != null)
+ {
+ List<DBRefEntry> sourceRefs = DBRefUtils.searchRefs(onSource,
+ cdsref.getAccessionId());
+ if (sourceRefs != null)
+ {
+ for (DBRefEntry srcref : sourceRefs)
+ {
+ if (srcref.getSource().equalsIgnoreCase(cdsref.getSource()))
+ {
+ // we have found a complementary dbref on the protein product, so
+ // update mapping's getTo
+ newref.getMap().setTo(proteinProduct);
+ }
+ }
+ }
+ }
+ cdsSeq.addDBRef(newref);
+ propagated.add(newref);
+ }
+ return propagated;
+ }
+
+ /**
* Transfers co-located features on 'fromSeq' to 'toSeq', adjusting the
* feature start/end ranges, optionally omitting specified feature types.
* Returns the number of features copied.
{
AlignmentI copy = new Alignment(new Alignment(seqs));
copy.setDataset(dataset);
-
+ boolean isProtein = !copy.isNucleotide();
SequenceIdMatcher matcher = new SequenceIdMatcher(seqs);
if (xrefs != null)
{
{
for (DBRefEntry dbref : dbrefs)
{
- if (dbref.getMap() == null || dbref.getMap().getTo() == null)
+ if (dbref.getMap() == null || dbref.getMap().getTo() == null
+ || dbref.getMap().getTo().isProtein() != isProtein)
{
continue;
}
*/
public static int alignAs(AlignmentI unaligned, AlignmentI aligned)
{
+ /*
+ * easy case - aligning a copy of aligned sequences
+ */
+ if (alignAsSameSequences(unaligned, aligned))
+ {
+ return unaligned.getHeight();
+ }
+
+ /*
+ * fancy case - aligning via mappings between sequences
+ */
List<SequenceI> unmapped = new ArrayList<SequenceI>();
Map<Integer, Map<SequenceI, Character>> columnMap = buildMappedColumnsMap(
unaligned, aligned, unmapped);
int width = columnMap.size();
char gap = unaligned.getGapCharacter();
int realignedCount = 0;
+ // TODO: verify this loop scales sensibly for very wide/high alignments
for (SequenceI seq : unaligned.getSequences())
{
if (!unmapped.contains(seq))
{
char[] newSeq = new char[width];
- Arrays.fill(newSeq, gap);
+ Arrays.fill(newSeq, gap); // JBPComment - doubt this is faster than the
+ // Integer iteration below
int newCol = 0;
int lastCol = 0;
System.arraycopy(newSeq, 0, tmp, 0, lastCol + 1);
newSeq = tmp;
}
+ // TODO: optimise SequenceI to avoid char[]->String->char[]
seq.setSequence(String.valueOf(newSeq));
realignedCount++;
}
}
/**
+ * If unaligned and aligned sequences share the same dataset sequences, then
+ * simply copies the aligned sequences to the unaligned sequences and returns
+ * true; else returns false
+ *
+ * @param unaligned
+ * - sequences to be aligned based on aligned
+ * @param aligned
+ * - 'guide' alignment containing sequences derived from same dataset
+ * as unaligned
+ * @return
+ */
+ static boolean alignAsSameSequences(AlignmentI unaligned,
+ AlignmentI aligned)
+ {
+ if (aligned.getDataset() == null || unaligned.getDataset() == null)
+ {
+ return false; // should only pass alignments with datasets here
+ }
+
+ // map from dataset sequence to alignment sequence(s)
+ Map<SequenceI, List<SequenceI>> alignedDatasets = new HashMap<SequenceI, List<SequenceI>>();
+ for (SequenceI seq : aligned.getSequences())
+ {
+ SequenceI ds = seq.getDatasetSequence();
+ if (alignedDatasets.get(ds) == null)
+ {
+ alignedDatasets.put(ds, new ArrayList<SequenceI>());
+ }
+ alignedDatasets.get(ds).add(seq);
+ }
+
+ /*
+ * first pass - check whether all sequences to be aligned share a dataset
+ * sequence with an aligned sequence
+ */
+ for (SequenceI seq : unaligned.getSequences())
+ {
+ if (!alignedDatasets.containsKey(seq.getDatasetSequence()))
+ {
+ return false;
+ }
+ }
+
+ /*
+ * second pass - copy aligned sequences;
+ * heuristic rule: pair off sequences in order for the case where
+ * more than one shares the same dataset sequence
+ */
+ for (SequenceI seq : unaligned.getSequences())
+ {
+ List<SequenceI> alignedSequences = alignedDatasets.get(seq
+ .getDatasetSequence());
+ // TODO: getSequenceAsString() will be deprecated in the future
+ // TODO: need to leave to SequenceI implementor to update gaps
+ seq.setSequence(alignedSequences.get(0).getSequenceAsString());
+ if (alignedSequences.size() > 0)
+ {
+ // pop off aligned sequences (except the last one)
+ alignedSequences.remove(0);
+ }
+ }
+
+ return true;
+ }
+
+ /**
* Returns a map whose key is alignment column number (base 1), and whose
* values are a map of sequence characters in that column.
*