/** * Add up overlap btw the blocks in this chain and the given interval. * * @return Length of overlap, offsets into first and last ContinuousBlocks, and indices of first * and last ContinuousBlocks. */ private static TargetIntersection targetIntersection(final Chain chain, final Interval interval) { int intersectionLength = 0; // Convert interval to 0-based, half-open int start = interval.getStart() - 1; int end = interval.getEnd(); int firstBlockIndex = -1; int lastBlockIndex = -1; int startOffset = -1; int offsetFromEnd = -1; List<Chain.ContinuousBlock> blockList = chain.getBlocks(); for (int i = 0; i < blockList.size(); ++i) { final Chain.ContinuousBlock block = blockList.get(i); if (block.fromStart >= end) { break; } else if (block.getFromEnd() <= start) { continue; } if (firstBlockIndex == -1) { firstBlockIndex = i; if (start > block.fromStart) { startOffset = start - block.fromStart; } else { startOffset = 0; } } lastBlockIndex = i; if (block.getFromEnd() > end) { offsetFromEnd = block.getFromEnd() - end; } else { offsetFromEnd = 0; } int thisIntersection = Math.min(end, block.getFromEnd()) - Math.max(start, block.fromStart); if (thisIntersection <= 0) { throw new PicardException("Should have been some intersection."); } intersectionLength += thisIntersection; } if (intersectionLength == 0) { return null; } return new TargetIntersection( chain, intersectionLength, startOffset, offsetFromEnd, firstBlockIndex, lastBlockIndex); }
public List<PartialLiftover> diagnosticLiftover(final Interval interval) { final List<PartialLiftover> ret = new ArrayList<PartialLiftover>(); if (interval.length() == 0) { throw new IllegalArgumentException( "Zero-length interval cannot be lifted over. Interval: " + interval.getName()); } for (final Chain chain : chains.getOverlaps(interval)) { Interval intersectingChain = interval.intersect(chain.interval); final TargetIntersection targetIntersection = targetIntersection(chain, intersectingChain); if (targetIntersection == null) { ret.add(new PartialLiftover(intersectingChain, chain.id)); } else { Interval toInterval = createToInterval(interval.getName(), targetIntersection); float percentLiftedOver = targetIntersection.intersectionLength / (float) interval.length(); ret.add( new PartialLiftover( intersectingChain, toInterval, targetIntersection.chain.id, percentLiftedOver)); } } return ret; }
public String toString() { if (toInterval == null) { // Matched a chain, but entirely within a gap. return fromInterval.toString() + " (len " + fromInterval.length() + ")=>null using chain " + chainId; } final String strand = toInterval.isNegativeStrand() ? "-" : "+"; return fromInterval.toString() + " (len " + fromInterval.length() + ")=>" + toInterval + "(" + strand + ") using chain " + chainId + " ; pct matched " + percentLiftedOver; }
/** * Lift over the given interval to the new genome build. * * @param interval Interval to be lifted over. * @param liftOverMinMatch Minimum fraction of bases that must remap. * @return Interval in the output build coordinates, or null if it cannot be lifted over. */ public Interval liftOver(final Interval interval, final double liftOverMinMatch) { if (interval.length() == 0) { throw new IllegalArgumentException( "Zero-length interval cannot be lifted over. Interval: " + interval.getName()); } Chain chainHit = null; TargetIntersection targetIntersection = null; // Number of bases in interval that can be lifted over must be >= this. double minMatchSize = liftOverMinMatch * interval.length(); // Find the appropriate Chain, and the part of the chain corresponding to the interval to be // lifted over. for (final Chain chain : chains.getOverlaps(interval)) { final TargetIntersection candidateIntersection = targetIntersection(chain, interval); if (candidateIntersection != null && candidateIntersection.intersectionLength >= minMatchSize) { if (chainHit != null) { // In basic liftOver, multiple hits are not allowed. return null; } chainHit = chain; targetIntersection = candidateIntersection; } else if (candidateIntersection != null) { LOG.info( "Interval " + interval.getName() + " failed to match chain " + chain.id + " because intersection length " + candidateIntersection.intersectionLength + " < minMatchSize " + minMatchSize + " (" + (candidateIntersection.intersectionLength / (float) interval.length()) + " < " + liftOverMinMatch + ")"); } } if (chainHit == null) { // Can't be lifted over. return null; } return createToInterval(interval.getName(), targetIntersection); }