import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.function.Consumer;
import jalview.analysis.AlignmentAnnotationUtils;
import jalview.api.AlignViewportI;
ala.graphGroup += graphGroup;
var newAnnot = alignViewport.getAlignment()
.updateFromOrCopyAnnotation(ala);
- if (ala.sequenceRef != null)
+ if (newAnnot.sequenceRef != null)
{
- ala.sequenceRef.addAlignmentAnnotation(newAnnot);
+ newAnnot.sequenceRef.addAlignmentAnnotation(newAnnot);
newAnnot.adjustForAlignment();
AlignmentAnnotationUtils.replaceAnnotationOnAlignmentWith(
newAnnot, newAnnot.label, newAnnot.getCalcId());
AlignCalcWorkerAdapter.this,
new AnnotationResult(
annotations,
- result.transferFeatures,
+ result.hasFeatures,
result.featureColours,
result.featureFilters));
}
}
private WorkerListener listener = WorkerListener.NULL_LISTENER;
-
+
public void setWorkerListener(WorkerListener listener)
{
if (listener == null) listener = WorkerListener.NULL_LISTENER;
import java.util.ArrayList;
import java.util.BitSet;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jalview.analysis.AlignSeq;
import jalview.analysis.SeqsetUtils;
-import jalview.api.FeatureColourI;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AnnotatedCollectionI;
import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceCollectionI;
import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.FeatureMatcherSetI;
import jalview.schemes.ResidueProperties;
import jalview.util.Comparison;
import jalview.ws2.actions.BaseJob;
final Map<String, SequenceI> seqNames;
- final int start, end;
-
- final int minSize;
+ final int regionStart, regionEnd;
- List<AlignmentAnnotation> returnedAnnotations = Collections.emptyList();
-
- Map<String, FeatureColourI> featureColours = Collections.emptyMap();
-
- Map<String, FeatureMatcherSetI> featureFilters = Collections.emptyMap();
-
+ final int minSize;
public AnnotationJob(List<SequenceI> inputSeqs, boolean[] gapMap,
- Map<String, SequenceI> seqNames, int start, int end, int minSize)
+ Map<String, SequenceI> seqNames, int start, int end, int minSize)
{
super(inputSeqs);
this.gapMap = gapMap;
this.seqNames = seqNames;
- this.start = start;
- this.end = end;
+ this.regionStart = start;
+ this.regionEnd = end;
this.minSize = minSize;
}
return nvalid >= minSize;
}
- public static AnnotationJob create(AnnotatedCollectionI inputSeqs,
- boolean bySequence, boolean submitGaps, boolean requireAligned,
- boolean filterNonStandardResidues, int minSize)
+ public static AnnotationJob create(SequenceCollectionI inputSeqs,
+ boolean bySequence, boolean submitGaps, boolean requireAligned,
+ boolean filterNonStandardResidues, int minSize)
{
- List<SequenceI> seqs = new ArrayList<>();
+ List<SequenceI> seqences = new ArrayList<>();
int minlen = 10;
- int ln = -1;
- Map<String, SequenceI> seqNames = bySequence ? new HashMap<>() : null;
- BitSet gapMap = new BitSet();
- int gapMapSize = 0;
+ int width = 0;
+ Map<String, SequenceI> namesMap = bySequence ? new HashMap<>() : null;
+ BitSet residueMap = new BitSet();
int start = inputSeqs.getStartRes();
int end = inputSeqs.getEndRes();
// TODO: URGENT! unify with JPred / MSA code to handle hidden regions
// persisted/restored
for (SequenceI sq : inputSeqs.getSequences())
{
- int sqlen;
- if (bySequence)
- sqlen = sq.findPosition(end + 1) - sq.findPosition(start + 1);
+ int sqLen = (bySequence)
+ ? sq.findPosition(end + 1) - sq.findPosition(start + 1)
+ : sq.getEnd() - sq.getStart();
+ if (sqLen < minlen)
+ continue;
+ String newName = SeqsetUtils.unique_name(seqences.size() + 1);
+ if (namesMap != null)
+ namesMap.put(newName, sq);
+ Sequence seq;
+ if (submitGaps)
+ {
+ seq = new Sequence(newName, sq.getSequenceAsString());
+ updateResidueMap(residueMap, seq, filterNonStandardResidues);
+ }
else
- sqlen = sq.getEnd() - sq.getStart();
- if (sqlen >= minlen)
{
- String newName = SeqsetUtils.unique_name(seqs.size() + 1);
- if (seqNames != null)
- seqNames.put(newName, sq);
- Sequence seq;
- if (submitGaps)
- {
- seq = new Sequence(newName, sq.getSequenceAsString());
- gapMapSize = Math.max(gapMapSize, seq.getLength());
- for (int pos : sq.gapMap())
- {
- char sqchr = sq.getCharAt(pos);
- boolean include = !filterNonStandardResidues;
- include |= sq.isProtein() ? ResidueProperties.aaIndex[sqchr] < 20
- : ResidueProperties.nucleotideIndex[sqchr] < 5;
- if (include)
- gapMap.set(pos);
- }
- }
- else
- {
- // TODO: add ability to exclude hidden regions
- seq = new Sequence(newName, AlignSeq.extractGaps(Comparison.GapChars,
- sq.getSequenceAsString(start, end + 1)));
- // for annotation need to also record map to sequence start/end
- // position in range
- // then transfer back to original sequence on return.
- }
- seqs.add(seq);
- ln = Math.max(ln, seq.getLength());
+ // TODO: add ability to exclude hidden regions
+ seq = new Sequence(newName,
+ AlignSeq.extractGaps(Comparison.GapChars,
+ sq.getSequenceAsString(start, end + 1)));
+ // for annotation need to also record map to sequence start/end
+ // position in range
+ // then transfer back to original sequence on return.
}
+ seqences.add(seq);
+ width = Math.max(width, seq.getLength());
}
if (requireAligned && submitGaps)
{
- int realWidth = gapMap.cardinality();
- for (int i = 0; i < seqs.size(); i++)
+ for (int i = 0; i < seqences.size(); i++)
+ {
+ SequenceI sq = seqences.get(i);
+ char[] padded = fitSequenceToResidueMap(sq.getSequence(),
+ residueMap);
+ seqences.set(i, new Sequence(sq.getName(), padded));
+ }
+ }
+ boolean[] gapMapArray = null;
+ if (submitGaps)
+ {
+ gapMapArray = new boolean[width];
+ for (int i = 0; i < width; i++)
+ gapMapArray[i] = residueMap.get(i);
+ }
+ return new AnnotationJob(seqences, gapMapArray, namesMap, start, end,
+ minSize);
+ }
+
+ private static void updateResidueMap(BitSet residueMap, SequenceI seq,
+ boolean filterNonStandardResidues)
+ {
+ for (int pos : seq.gapMap())
+ {
+ char sqchr = seq.getCharAt(pos);
+ boolean include = !filterNonStandardResidues;
+ include |= seq.isProtein() ? ResidueProperties.aaIndex[sqchr] < 20
+ : ResidueProperties.nucleotideIndex[sqchr] < 5;
+ if (include)
+ residueMap.set(pos);
+ }
+ }
+
+ /**
+ * Fits the sequence to the residue map removing empty columns where residue
+ * map is unset and padding the sequence with gaps at the end if needed.
+ */
+ private static char[] fitSequenceToResidueMap(char[] sequence,
+ BitSet residueMap)
+ {
+ int width = residueMap.cardinality();
+ char[] padded = new char[width];
+ for (int op = 0, pp = 0; pp < width; op++)
+ {
+ if (residueMap.get(op))
{
- SequenceI sq = seqs.get(i);
- char[] padded = new char[realWidth];
- char[] original = sq.getSequence();
- for (int op = 0, pp = 0; pp < realWidth; op++)
- {
- if (gapMap.get(op))
- {
- if (original.length > op)
- padded[pp++] = original[op];
- else
- padded[pp++] = '-';
- }
- }
- seqs.set(i, new Sequence(sq.getName(), padded));
+ if (sequence.length > op)
+ padded[pp++] = sequence[op];
+ else
+ padded[pp++] = '-';
}
}
- boolean[] gapMapArray = new boolean[gapMapSize];
- for (int i = 0; i < gapMapSize; i++)
- gapMapArray[i] = gapMap.get(i);
- return new AnnotationJob(seqs, gapMapArray, seqNames, start, end, minSize);
+ return padded;
}
}
{
final List<AlignmentAnnotation> annotations;
- final boolean transferFeatures;
+ final boolean hasFeatures;
final Map<String, FeatureColourI> featureColours;
final Map<String, FeatureMatcherSetI> featureFilters;
- public AnnotationResult(List<AlignmentAnnotation> annotations, boolean transferFeatures,
+ public AnnotationResult(List<AlignmentAnnotation> annotations, boolean hasFeatures,
Map<String, FeatureColourI> featureColours, Map<String, FeatureMatcherSetI> featureFilters)
{
this.annotations = annotations;
- this.transferFeatures = transferFeatures;
+ this.hasFeatures = hasFeatures;
this.featureColours = featureColours;
this.featureFilters = featureFilters;
}
return annotations;
}
- public boolean getTransferFeatures()
+ public boolean getHasFeatures()
{
- return transferFeatures;
+ return hasFeatures;
}
public Map<String, FeatureColourI> getFeatureColours()
import java.util.List;
import java.util.Map;
-import jalview.analysis.AlignmentAnnotationUtils;
import jalview.api.AlignViewportI;
import jalview.api.FeatureColourI;
import jalview.datamodel.AlignmentAnnotation;
import jalview.ws2.api.JobStatus;
import jalview.ws2.client.api.AnnotationWebServiceClientI;
-public class AnnotationTask extends BaseTask<AnnotationJob, AnnotationResult>
+public class AnnotationTask
+ extends BaseTask<AnnotationJob, AnnotationResult>
{
private AnnotationWebServiceClientI client;
private final AnnotatedCollectionI selectionGroup;
public AnnotationTask(AnnotationWebServiceClientI client,
- AnnotationAction action, List<ArgumentI> args, Credentials credentials,
- AlignViewportI viewport)
+ AnnotationAction action, List<ArgumentI> args,
+ Credentials credentials, AlignViewportI viewport)
{
super(client, args, credentials);
this.client = client;
* Create and return a list of annotation jobs from the current state of the
* viewport. Returned job are not started by this method and should be stored
* in a field and started separately.
- *
+ *
* @return list of annotation jobs
* @throws ServiceInputInvalidException
* input data is not valid
*/
@Override
- public List<AnnotationJob> prepareJobs() throws ServiceInputInvalidException
+ public List<AnnotationJob> prepareJobs()
+ throws ServiceInputInvalidException
{
- if (alignment == null || alignment.getWidth() <= 0 ||
- alignment.getSequences() == null)
- throw new ServiceInputInvalidException("Alignment does not contain sequences");
- if (alignment.isNucleotide() && !action.doAllowNucleotide())
+ if (alignment == null || alignment.getWidth() <= 0
+ || alignment.getSequences() == null)
throw new ServiceInputInvalidException(
- action.getFullName() + " does not allow nucleotide sequences");
+ "Alignment does not contain sequences");
+ if (alignment.isNucleotide() && !action.doAllowNucleotide())
+ throw new ServiceInputInvalidException(action.getFullName()
+ + " does not allow nucleotide sequences");
if (!alignment.isNucleotide() && !action.doAllowProtein())
throw new ServiceInputInvalidException(
- action.getFullName() + " does not allow protein sequences");
+ action.getFullName() + " does not allow protein sequences");
boolean bySequence = !action.isAlignmentAnalysis();
AnnotatedCollectionI inputSeqs = bySequence ? selectionGroup : null;
- if (inputSeqs == null || inputSeqs.getWidth() <= 0 ||
- inputSeqs.getSequences() == null || inputSeqs.getSequences().size() < 1)
+ if (inputSeqs == null || inputSeqs.getWidth() <= 0
+ || inputSeqs.getSequences() == null
+ || inputSeqs.getSequences().size() < 1)
inputSeqs = alignment;
boolean submitGaps = action.isAlignmentAnalysis();
boolean requireAligned = action.getRequireAlignedSequences();
boolean filterSymbols = action.getFilterSymbols();
int minSize = action.getMinSequences();
AnnotationJob job = AnnotationJob.create(inputSeqs, bySequence,
- submitGaps, requireAligned, filterSymbols, minSize);
+ submitGaps, requireAligned, filterSymbols, minSize);
if (!job.isInputValid())
{
job.setStatus(JobStatus.INVALID);
- throw new ServiceInputInvalidException("Annotation job has invalid input");
+ throw new ServiceInputInvalidException(
+ "Annotation job has invalid input");
}
job.setStatus(JobStatus.READY);
return List.of(job);
}
@Override
- protected AnnotationResult collectResult(List<AnnotationJob> jobs) throws IOException
+ protected AnnotationResult collectResult(List<AnnotationJob> jobs)
+ throws IOException
{
final Map<String, FeatureColourI> featureColours = new HashMap<>();
final Map<String, FeatureMatcherSetI> featureFilters = new HashMap<>();
var job = jobs.get(0);
List<AlignmentAnnotation> returnedAnnot = client.attachAnnotations(
- job.getServerJob(), job.getInputSequences(), featureColours,
- featureFilters);
+ job.getServerJob(), job.getInputSequences(), featureColours,
+ featureFilters);
/* TODO
* copy over each annotation row returned and also defined on each
* sequence, excluding regions not annotated due to gapMap/column
udpateCalcId(returnedAnnot);
for (AlignmentAnnotation ala : returnedAnnot)
{
- SequenceI aseq = null;
- if (ala.sequenceRef != null)
+ SequenceI seq = (ala.sequenceRef == null) ? null
+ : job.seqNames.get(ala.sequenceRef.getName());
+ if (job.gapMap != null && job.gapMap.length > 0)
+ ala.annotations = createGappedAnnotations(ala.annotations,
+ job.gapMap);
+ if (seq != null)
{
- SequenceI seq = job.seqNames.get(ala.sequenceRef.getName());
- aseq = seq.getRootDatasetSequence();
+ int startRes = seq.findPosition(job.regionStart);
+ ala.createSequenceMapping(seq, startRes, false);
}
- ala.sequenceRef = aseq;
- Annotation[] gappedAnnots = createGappedAnnotations(ala.annotations, job.start, job.gapMap);
- ala.annotations = gappedAnnots;
}
boolean hasFeatures = false;
for (SequenceI sq : job.getInputSequences())
{
- if (!sq.getFeatures().hasFeatures() && (sq.getDBRefs() == null || sq.getDBRefs().isEmpty()))
+ if (!sq.getFeatures().hasFeatures()
+ && (sq.getDBRefs() == null || sq.getDBRefs().isEmpty()))
continue;
hasFeatures = true;
SequenceI seq = job.seqNames.get(sq.getName());
SequenceI datasetSeq = seq.getRootDatasetSequence();
- List<ContiguousI> sourceRange = findContiguousRanges(datasetSeq, job.gapMap, job.start, job.end);
+ List<ContiguousI> sourceRange = findContiguousRanges(seq,
+ job.gapMap, job.regionStart, job.regionEnd);
int[] sourceStartEnd = ContiguousI.toStartEndArray(sourceRange);
- Mapping mp = new Mapping(new MapList(
- sourceStartEnd, new int[]
- { datasetSeq.getStart(), datasetSeq.getEnd() }, 1, 1));
+ Mapping mp = new Mapping(
+ new MapList(
+ sourceStartEnd,
+ new int[] { datasetSeq.getStart(), datasetSeq.getEnd() },
+ 1, 1));
datasetSeq.transferAnnotation(sq, mp);
}
- return new AnnotationResult(returnedAnnot, hasFeatures, featureColours, featureFilters);
+ return new AnnotationResult(returnedAnnot, hasFeatures, featureColours,
+ featureFilters);
}
/**
{
for (var annotation : annotations)
{
- if (annotation.getCalcId() == null || annotation.getCalcId().isEmpty())
+ if (annotation.getCalcId() == null
+ || annotation.getCalcId().isEmpty())
{
annotation.setCalcId(action.getFullName());
}
- annotation.autoCalculated = action.isAlignmentAnalysis() &&
- action.getWebService().isInteractive();
+ annotation.autoCalculated = action.isAlignmentAnalysis()
+ && action.getWebService().isInteractive();
}
}
- private Annotation[] createGappedAnnotations(Annotation[] annotations, int start, boolean[] gapMap)
+ private Annotation[] createGappedAnnotations(Annotation[] annotations,
+ boolean[] gapMap)
{
- var size = Math.max(alignment.getWidth(), gapMap.length);
+ var size = Math.max(annotations.length, gapMap.length);
Annotation[] gappedAnnotations = new Annotation[size];
- for (int p = 0, ap = start; ap < size; ap++)
+ for (int p = 0, ap = 0; ap < size; ap++)
{
- if (gapMap != null && gapMap.length > ap && !gapMap[ap])
+ if (ap < gapMap.length && !gapMap[ap])
{
gappedAnnotations[ap] = new Annotation("", "", ' ', Float.NaN);
}
return gappedAnnotations;
}
- private List<ContiguousI> findContiguousRanges(SequenceI seq, boolean[] gapMap, int start, int end)
+ // TODO: review ant test!!!
+ private List<ContiguousI> findContiguousRanges(SequenceI seq,
+ boolean[] gapMap, int start, int end)
{
if (gapMap == null || gapMap.length < end)
- return List.of(seq.findPositions(start, end));
+ return List.of(seq.findPositions(start + 1, end + 1));
List<ContiguousI> ranges = new ArrayList<>();
int lastcol = start, col = start;
do
{
if (result == null)
return;
- if (result.getTransferFeatures())
+ if (result.getHasFeatures())
{
alignFrame.getViewport().applyFeaturesStyle(new FeatureSettingsAdapter()
{