/* * 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(); } }