package org.seqcode.genome.location; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.seqcode.genome.Genome; /** * A <code>Point</code> represents a single base position along some chromosome * in a genome <br> * <i>Note</i>: We assume 0-based, inclusive coordinate. */ public class Point implements Comparable<Point> { protected static final Pattern POINT_PATTERN = Pattern.compile(Region.POINT_REGION_REG_EX); /** * The genome that this point corresponds to */ protected Genome g; /** * The chromosome that this point corresponds to */ protected String chrom; /** * The location that this point corresponds to */ protected int location; public Point(Genome g, String c, int position) { this.g = g; chrom = c; this.location = position; } public Point(Point copied) { this.g = copied.getGenome(); this.chrom = copied.getChrom(); this.location = copied.getLocation(); } public Genome getGenome() { return g; } public String getChrom() { return chrom; } public int getLocation() { return location; } public char getStrand(){ return '.'; } public Point clone() { return new Point(g, chrom, location); } /** * The <code>expand</code> method returns the region which is around * <code>distance</code> genomic coordinates (in bp) from this point.<br> * <i>Note</i>: The expansion happens in both directions.<br> * However, if <code>distance</code> exceeds any of chromosome ends, the * expansion takes place until this end.<br> * <b>In this method, indexing starts from 0</b> * * @param distance * The length of bidirectional expansion * @return The region (as a Region) that corresponds to this expansion */ public Region expand(int distance) { int ns = Math.max(1, location - distance); int ne = Math.min(location + distance, g.getChromLength(chrom)); return new Region(g, chrom, ns, ne); } public String toString() { return getLocationString(); } public String getLocationString() { return "chr"+chrom + ":" + location; } public int hashCode() { int code = 17; code += g.hashCode(); code *= 37; code += chrom.hashCode(); code *= 37; code += location; code *= 37; return code; } public boolean equals(Object o) { if (o instanceof Point) { Point r = (Point) o; return g.equals(r.getGenome()) && chrom.equals(r.getChrom()) && (location == r.getLocation()); } else { return false; } } /** * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(Point p) { if (!chrom.equals(p.chrom)) { return chrom.compareTo(p.chrom); } if (location < p.location) { return -1; } if (location > p.location) { return 1; } return 0; } /* * Absolute distance to another point */ public int distance(Point p) { if (!chrom.equals(p.getChrom())) { throw new IllegalArgumentException(p.getChrom()); } return Math.abs(location - p.getLocation()); } /** * Offset relative to another point (offset = this-p) */ public int offset(Point p) { if (!chrom.equals(p.getChrom())) { throw new IllegalArgumentException(p.getChrom()); } return location - p.getLocation(); } /** * parses the input String into a Point. Understands abbreviates in the * coordinates such as k and m. <br> * The method accepts the form: <blockquote> * * <pre> * chromosome:start * </pre> * * </blockquote> */ public static Point fromString(Genome genome, String input) throws NumberFormatException { String pieces[] = input.split(":"); char strand = ' '; if (pieces.length == 3 && pieces[2].length() == 1) { strand = pieces[2].charAt(0); input = pieces[0] + ":" + pieces[1]; } String trimmed = input.trim(); Matcher pointmatcher = POINT_PATTERN.matcher(trimmed); Point output = null; if (pointmatcher.find()) { if (pointmatcher.groupCount() != 2) { return null; } String chromStr = pointmatcher.group(1); String locStr = pointmatcher.group(2); if (chromStr.startsWith("chr")) { chromStr = chromStr.substring(3, chromStr.length()); } locStr = locStr.replaceAll(",", ""); output = new Point(genome, chromStr, Region.stringToNum(locStr)); } return output; } }