/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Fred Hutchinson Cancer Research Center and Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.broad.igv.feature;
import org.broad.igv.track.WindowFunction;
import java.util.ArrayList;
import java.util.List;
/**
* A feature class for splice junctions, with depth information for flanking regions if available.
* <p/>
* The interpretation of 'start' and 'end' for these features is the start and end of the flanking regions.
* This is a bit counterintuitive, but it's required for popup text to display correctly in the feature track
*
* @author dhmay
*/
public class SpliceJunctionFeature extends BasicFeature {
//depth of coverage for the splice junction
protected int junctionDepth = 0;
//start and end locations of the junction
protected int junctionStart = 0;
protected int junctionEnd = 0;
int[] startFlankingRegionDepthArray, endFlankingRegionDepthArray;
public SpliceJunctionFeature(String chr, int start, int end) {
super(chr, start, end);
junctionStart = start;
junctionEnd = end;
}
public SpliceJunctionFeature(String chr, int start, int end, Strand strand) {
super(chr, start, end, strand);
junctionStart = start;
junctionEnd = end;
}
/**
* Does this splice junction feature represent the same splice junction as another feature? This is used for
* splice junction feature selection.
*
* @param otherFeature
* @return
*/
public boolean isSameJunction(SpliceJunctionFeature otherFeature) {
if (otherFeature.getJunctionStart() == getJunctionStart() &&
otherFeature.getJunctionEnd() == getJunctionEnd())
return true;
return false;
}
/**
* "Adds a read" to the junction. We don't actually track all the reads associated with a junction
* (though that might be interesting, in the future), but we track depth of coverage information. By
* adding all the reads one by one, we can build up the depth of coverage map for the flanking regions
* and the junction itself.
*
* @param readStart
* @param readEnd
*/
public void addRead(int readStart, int readEnd) {
junctionDepth++;
int newStartFlankingRegionSize = junctionStart - readStart;
if (readStart < start) {
int[] newStartFlankArray = new int[newStartFlankingRegionSize];
if (startFlankingRegionDepthArray != null) {
int offset = newStartFlankingRegionSize - getStartFlankingRegionLength();
System.arraycopy(startFlankingRegionDepthArray, 0, newStartFlankArray,
offset, getStartFlankingRegionLength());
}
startFlankingRegionDepthArray = newStartFlankArray;
start = readStart;
}
for (int i = getStartFlankingRegionLength() - newStartFlankingRegionSize;
i < getStartFlankingRegionLength(); i++)
startFlankingRegionDepthArray[i] = startFlankingRegionDepthArray[i] + 1;
int newEndFlankingRegionSize = readEnd - junctionEnd;
if (readEnd > end) {
int[] newEndFlankArray = new int[newEndFlankingRegionSize];
if (endFlankingRegionDepthArray != null) {
System.arraycopy(endFlankingRegionDepthArray, 0, newEndFlankArray,
0, getEndFlankingRegionLength());
}
endFlankingRegionDepthArray = newEndFlankArray;
end = readEnd;
}
for (int i = 0; i < newEndFlankingRegionSize; i++)
endFlankingRegionDepthArray[i] = endFlankingRegionDepthArray[i] + 1;
}
/**
* The "score" for a SpliceJunctionFeature is the junction depth. This maintains compatibility with Tophat's
* use of the score field in junction bed files.
*/
@Override
public float getScore() {
return junctionDepth;
}
/**
* Splice junction features by definition have 2 "exons", or more precisely "blocks" in bed lingo. This
* follows Tophat's splice junction bed feature convention. They are "lazily" created since these are only
* used to export splice junction features in Tophat compatible format.
* @return
*/
@Override
public List<Exon> getExons() {
if(exons == null) {
exons = new ArrayList<Exon>(2);
exons.add(new Exon(getChr(), start, junctionStart, getStrand()));
exons.add(new Exon(getChr(), junctionEnd, end, getStrand()));
}
return exons;
}
@Override
public int getExonCount() {
return getExons().size();
}
public int getJunctionDepth() {
return junctionDepth;
}
public void setJunctionDepth(int junctionDepth) {
this.junctionDepth = junctionDepth;
}
public int getJunctionStart() {
return junctionStart;
}
public void setJunctionStart(int junctionStart) {
this.junctionStart = junctionStart;
}
public int getJunctionEnd() {
return junctionEnd;
}
public void setJunctionEnd(int junctionEnd) {
this.junctionEnd = junctionEnd;
}
public int getStartFlankingRegionLength() {
return junctionStart - start;
}
public int getEndFlankingRegionLength() {
return end - junctionEnd;
}
public int[] getStartFlankingRegionDepthArray() {
return startFlankingRegionDepthArray;
}
public void setStartFlankingRegionDepthArray(int[] startFlankingRegionDepthArray) {
this.startFlankingRegionDepthArray = startFlankingRegionDepthArray;
}
public int[] getEndFlankingRegionDepthArray() {
return endFlankingRegionDepthArray;
}
public void setEndFlankingRegionDepthArray(int[] endFlankingRegionDepthArray) {
this.endFlankingRegionDepthArray = endFlankingRegionDepthArray;
}
public boolean hasFlankingRegionDepthArrays() {
return ((startFlankingRegionDepthArray != null) && (endFlankingRegionDepthArray != null));
}
/**
* Return a string for popup text, and related uses. The default just
* returns the feature name. Its expected that this method will be
* overriden in subclasses.
*
* @position -- 1 based coordinates
*/
public String getValueString(double position, int mouseX, WindowFunction ignored) {
StringBuffer valueString = new StringBuffer();
String name = getName();
if (name != null) {
valueString.append(name);
}
if ((identifier != null) && ((name == null) || !name.equals(identifier))) {
valueString.append("<br>" + identifier);
}
valueString.append("<br>");
valueString.append(chromosome + ":" + junctionStart + "-" + junctionEnd);
valueString.append("<br>Strand: " + (getStrand().equals(Strand.POSITIVE) ? "+" : "-"));
valueString.append("<br>Depth = " + junctionDepth + ", Flanking Widths: (" +
this.getStartFlankingRegionLength() + "," +
this.getEndFlankingRegionLength() + ")");
if (hasFlankingRegionDepthArrays()) {
if (position >= start && position < junctionStart) {
int index = (int) position - start;
if (index < startFlankingRegionDepthArray.length)
valueString.append("<br>Start Flanking, Depth = " +
startFlankingRegionDepthArray[index]);
} else if (position > junctionEnd && position <= end) {
int index = (int) position - junctionEnd;
if (index < endFlankingRegionDepthArray.length)
valueString.append("<br>End Flanking, Depth = " +
endFlankingRegionDepthArray[index]);
}
}
if (description != null) {
valueString.append("<br>" + description);
}
return valueString.toString();
}
}