import java.util.Arrays;
import java.util.List;
+import jalview.bin.Cache;
+
/**
* A simple way of bijectively mapping a non-contiguous linear range to another
* non-contiguous linear range.
if (range.length != 2)
{
// throw new IllegalArgumentException(range);
- System.err.println("Invalid format for fromRange "
+ Cache.log.error("Invalid format for fromRange "
+ Arrays.toString(range) + " may cause errors");
}
fromLowest = Math.min(fromLowest, Math.min(range[0], range[1]));
if (range.length != 2)
{
// throw new IllegalArgumentException(range);
- System.err.println("Invalid format for toRange "
+ Cache.log.error("Invalid format for toRange "
+ Arrays.toString(range) + " may cause errors");
}
toLowest = Math.min(toLowest, Math.min(range[0], range[1]));
{
return fromShifts.size() == 1 && toShifts.size() == 1;
}
+
+ /**
+ * Returns the [start, end...] positions in the range mapped from, that are
+ * mapped to by part or all of the given begin-end of the range mapped to.
+ * Returns null if begin-end does not overlap any position mapped to.
+ *
+ * @param begin
+ * @param end
+ * @return
+ */
+ public int[] getOverlapsInFrom(final int begin, final int end)
+ {
+ int[] overlaps = MappingUtils.findOverlap(toShifts, begin, end);
+
+ return overlaps == null ? null : locateInFrom(overlaps[0], overlaps[1]);
+ }
+
+ /**
+ * Returns the [start, end...] positions in the range mapped to, that are
+ * mapped to by part or all of the given begin-end of the range mapped from.
+ * Returns null if begin-end does not overlap any position mapped from.
+ *
+ * @param begin
+ * @param end
+ * @return
+ */
+ public int[] getOverlapsInTo(final int begin, final int end)
+ {
+ int[] overlaps = MappingUtils.findOverlap(fromShifts, begin, end);
+
+ return overlaps == null ? null : locateInTo(overlaps[0], overlaps[1]);
+ }
}
*/
package jalview.util;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
import jalview.analysis.AlignmentSorter;
import jalview.api.AlignViewportI;
import jalview.bin.Cache;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
/**
* Helper methods for manipulations involving sequence mappings.
*
{
if (range.length % 2 != 0)
{
- System.err.println(
+ Cache.log.error(
"Error unbalance start/end ranges: " + ranges.toString());
return 0;
}
/*
* not coded for [start1, end1, start2, end2, ...]
*/
- System.err.println(
+ Cache.log.error(
"MappingUtils.removeEndPositions doesn't handle multiple ranges");
return;
}
/*
* not coded for a reverse strand range (end < start)
*/
- System.err.println(
+ Cache.log.error(
"MappingUtils.removeEndPositions doesn't handle reverse strand");
return;
}
}
return result;
}
+
+ /**
+ * Returns the maximal start-end positions in the given (ordered) list of
+ * ranges which is overlapped by the given begin-end range, or null if there
+ * is no overlap.
+ *
+ * <pre>
+ * Examples:
+ * if ranges is {[4, 8], [10, 12], [16, 19]}
+ * then
+ * findOverlap(ranges, 1, 20) == [4, 19]
+ * findOverlap(ranges, 6, 11) == [6, 11]
+ * findOverlap(ranges, 9, 15) == [10, 12]
+ * findOverlap(ranges, 13, 15) == null
+ * </pre>
+ *
+ * @param ranges
+ * @param begin
+ * @param end
+ * @return
+ */
+ protected static int[] findOverlap(List<int[]> ranges, final int begin,
+ final int end)
+ {
+ boolean foundStart = false;
+ int from = 0;
+ int to = 0;
+
+ /*
+ * traverse the ranges to find the first position (if any) >= begin,
+ * and the last position (if any) <= end
+ */
+ for (int[] range : ranges)
+ {
+ if (!foundStart)
+ {
+ if (range[0] >= begin)
+ {
+ /*
+ * first range that starts with, or follows, begin
+ */
+ foundStart = true;
+ from = Math.max(range[0], begin);
+ }
+ else if (range[1] >= begin)
+ {
+ /*
+ * first range that contains begin
+ */
+ foundStart = true;
+ from = begin;
+ }
+ }
+
+ if (range[0] <= end)
+ {
+ to = Math.min(end, range[1]);
+ }
+ }
+
+ return foundStart && to >= from ? new int[] { from, to } : null;
+ }
}
{
Cache.initLogger();
}
-
+
@BeforeClass(alwaysRun = true)
public void setUpJvOptionPane()
{
}
/**
+ * Tests for method that locates the overlap of the ranges in the 'from' map
+ * for given range in the 'to' map
+ */
+ @Test(groups = { "Functional" })
+ public void testGetOverlapsInFrom_withIntrons()
+ {
+ /*
+ * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [16, 17, 18] i.e.
+ * 2-3, 5-7, 9-10, 12-12, 14-14, 16-18
+ */
+ int[] codons = { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
+ int[] protein = { 11, 14 };
+ MapList ml = new MapList(codons, protein, 3, 1);
+
+ assertEquals("[2, 3, 5, 5]",
+ Arrays.toString(ml.getOverlapsInFrom(11, 11)));
+ assertEquals("[2, 3, 5, 7, 9, 9]",
+ Arrays.toString(ml.getOverlapsInFrom(11, 12)));
+ // out of range 5' :
+ assertEquals("[2, 3, 5, 7, 9, 9]",
+ Arrays.toString(ml.getOverlapsInFrom(8, 12)));
+ // out of range 3' :
+ assertEquals("[10, 10, 12, 12, 14, 14, 16, 18]",
+ Arrays.toString(ml.getOverlapsInFrom(13, 16)));
+ // out of range both :
+ assertEquals("[2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18]",
+ Arrays.toString(ml.getOverlapsInFrom(1, 16)));
+ // no overlap:
+ assertNull(ml.getOverlapsInFrom(20, 25));
+ }
+
+ /**
+ * Tests for method that locates the overlap of the ranges in the 'to' map
+ * for given range in the 'from' map
+ */
+ @Test(groups = { "Functional" })
+ public void testGetOverlapsInTo_withIntrons()
+ {
+ /*
+ * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [17, 18, 19] i.e.
+ * 2-3, 5-7, 9-10, 12-12, 14-14, 17-19
+ */
+ int[] codons = { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 17, 19 };
+ /*
+ * Mapped proteins at positions 1, 3, 4, 6 in the sequence
+ */
+ int[] protein = { 1, 1, 3, 4, 6, 6 };
+ MapList ml = new MapList(codons, protein, 3, 1);
+
+ /*
+ * Can't map from an unmapped position
+ */
+ assertNull(ml.getOverlapsInTo(1, 1));
+ assertNull(ml.getOverlapsInTo(4, 4));
+ assertNull(ml.getOverlapsInTo(15, 16));
+
+ /*
+ * nor from a range that includes no mapped position (exon)
+ */
+ assertNull(ml.getOverlapsInTo(15, 16));
+
+ // end of codon 1 maps to first peptide
+ assertEquals("[1, 1]", Arrays.toString(ml.getOverlapsInTo(2, 2)));
+ // end of codon 1 and start of codon 2 maps to first 2 peptides
+ assertEquals("[1, 1, 3, 3]", Arrays.toString(ml.getOverlapsInTo(3, 7)));
+
+ // range overlaps 5' end of dna:
+ assertEquals("[1, 1, 3, 3]", Arrays.toString(ml.getOverlapsInTo(1, 6)));
+ assertEquals("[1, 1, 3, 3]", Arrays.toString(ml.getOverlapsInTo(1, 8)));
+
+ // range overlaps 3' end of dna:
+ assertEquals("[6, 6]", Arrays.toString(ml.getOverlapsInTo(17, 24)));
+ assertEquals("[6, 6]", Arrays.toString(ml.getOverlapsInTo(16, 24)));
+
+ // dna positions 8, 11 are intron but include end of exon 2 and start of exon 3
+ assertEquals("[3, 4]", Arrays.toString(ml.getOverlapsInTo(8, 11)));
+ }
+
+ /**
* Tests for method that locates ranges in the 'to' map for given range in the
* 'from' map.
*/
public void testAddMapList_sameMap()
{
MapList ml = new MapList(new int[] { 11, 15, 20, 25, 35, 30 },
- new int[] { 72, 22 }, 1, 3);
+ new int[]
+ { 72, 22 }, 1, 3);
String before = ml.toString();
ml.addMapList(ml);
assertEquals(before, ml.toString());
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertSame;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
+import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
+
import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
Cache.initLogger();
}
-
@BeforeClass(alwaysRun = true)
public void setUpJvOptionPane()
{
assertEquals(0, mappedGroup.getStartRes());
assertEquals(1, mappedGroup.getEndRes()); // two columns
}
+
+ @Test(groups = "Functional")
+ public void testFindOverlap()
+ {
+ List<int[]> ranges = new ArrayList<>();
+ ranges.add(new int[] {4, 8});
+ ranges.add(new int[] {10, 12});
+ ranges.add(new int[] {16, 19});
+
+ int[] overlap = MappingUtils.findOverlap(ranges, 5, 13);
+ assertArrayEquals(overlap, new int[] {5, 12});
+ overlap = MappingUtils.findOverlap(ranges, -100, 100);
+ assertArrayEquals(overlap, new int[] {4, 19});
+ overlap = MappingUtils.findOverlap(ranges, 7, 17);
+ assertArrayEquals(overlap, new int[] {7, 17});
+ overlap = MappingUtils.findOverlap(ranges, 13, 15);
+ assertNull(overlap);
+ }
}