001 /** 002 * Copyright (C) 2010 The Roslin Institute <contact andy.law@roslin.ed.ac.uk> 003 * 004 * This file is part of the Ensembl Java API demonstration project developed by the 005 * Bioinformatics Group at The Roslin Institute, The Royal (Dick) School of 006 * Veterinary Studies, University of Edinburgh. 007 * 008 * This is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (version 3) as published by 010 * the Free Software Foundation. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * in this software distribution. If not, see <http://www.gnu.org/licenses/gpl-3.0.html/>. 019 */ 020 021 package uk.ac.roslin.ensembl.model; 022 023 import java.io.Serializable; 024 import java.util.TreeSet; 025 026 /** 027 * 028 * @author paterson 029 */ 030 public class Coordinate implements Comparable<Coordinate>, Serializable { 031 032 Integer start = null; 033 Integer end = null; 034 Strand strand = null; 035 private Integer strandInt = null; 036 037 038 public Coordinate() { 039 040 } 041 042 public Coordinate(Integer start, Integer end) { 043 044 this(start, end, Strand.strand(0)); 045 } 046 047 public Coordinate(Integer start, Integer end, Integer strand) { 048 049 this(start, end, Strand.strand(strand)); 050 } 051 052 public Coordinate(Integer start, Integer end, Coordinate.Strand strand) { 053 054 if (start!= null && end != null) { 055 if (start<=end) { 056 this.setStart(start); 057 this.setEnd(end); 058 } else { 059 this.setStart(end); 060 this.setEnd(start); 061 } 062 } 063 this.setStrand(strand); 064 } 065 066 @Override 067 public String toString() { 068 String out = ""; 069 out += this.start+" - "+this.end+" (" 070 + (( this.strand != null) ? this.strand.toString() : "UNSPECIFIED_STRAND") +")"; 071 return out; 072 } 073 074 public static enum Strand implements Serializable { 075 076 FORWARD_STRAND, 077 REVERSE_STRAND; 078 079 public static Strand strand(int i) { 080 if (i == 1) { 081 return FORWARD_STRAND; 082 } else if (i == -1) { 083 return REVERSE_STRAND; 084 } else { 085 return null; 086 } 087 } 088 public static Strand strand(Integer i) { 089 if (i == 1) { 090 return FORWARD_STRAND; 091 } else if (i == -1) { 092 return REVERSE_STRAND; 093 } else { 094 return null; 095 } 096 } 097 } 098 099 100 public Integer getStart() { 101 return start; 102 } 103 104 public Integer getEnd() { 105 return end; 106 } 107 108 public Strand getStrand() { 109 return strand; 110 } 111 112 public void setStart(Integer b) { 113 start = b; 114 } 115 116 public void setEnd(Integer e) { 117 end = e; 118 } 119 120 public void setStrand(Strand s) { 121 strand = s; 122 } 123 124 public void setStrandInt(Integer s) { 125 strandInt = s; 126 strand = Coordinate.Strand.strand(s); 127 } 128 129 public Integer getStrandInt() { 130 return strandInt; 131 } 132 133 // public Integer getPlusStrandStart() { 134 // if (strand == Strand.FORWARD_STRAND) { 135 // return start; 136 // } else if (strand == Strand.REVERSE_STRAND) { 137 // return end; 138 // } else return null; 139 // } 140 // 141 // public Integer getPlusStrandEnd() { 142 // if (strand == Strand.FORWARD_STRAND) { 143 // return end; 144 // } else if (strand == Strand.REVERSE_STRAND) { 145 // return start; 146 // } else return null; 147 // } 148 149 //this can be used for ordering by the plus strand start, or the plus strand 150 //end if the starts are identical 151 152 //nb careful with Integer comparisons 153 154 //nb 0 != identity , because it doesnt consider the strand, just the order 155 156 public int compareTo(Coordinate o) { 157 158 //dont compare if any nulls 159 if (this.start==null || this.end==null 160 || o.getStart()==null ||o.getEnd()==null) { 161 return 0; 162 } 163 164 165 if (!this.start.equals(o.getStart())) { 166 167 if (this.start.compareTo(o.getStart()) < 0) { 168 return -1; 169 } else { 170 return 1; 171 } 172 173 } else { 174 //put the longest one first 175 //this has consequences for getting the end coordinate of a set 176 if (this.end.compareTo(o.getEnd()) < 0) { 177 return 1; 178 } else if (this.end.compareTo(o.getEnd()) > 0) { 179 return -1; 180 } else { 181 //the coordinates are identical 182 return 0; 183 } 184 } 185 } 186 187 public Boolean liesWithinCoordinate(Coordinate test) { 188 Boolean out = false; 189 190 if (test.getStart()==null || test.getEnd()==null 191 || this.start == null || this.end==null) { 192 return out; 193 } 194 195 if (this.start>=test.getStart() && this.end<=test.getEnd()) { 196 out = true; 197 } 198 199 return out; 200 } 201 202 public Boolean overlaps(Coordinate test) { 203 204 boolean out = false; 205 206 if (this.start==null || this.end==null || test.getEnd()==null || test.getStart()==null) { 207 out = false; 208 // } else if ( (this.start>=test.getStart() && this.start<=test.getEnd()) 209 // || (this.end>=test.getStart() && this.end<=test.getEnd() )) { 210 } else if ( 211 (this.liesWithinCoordinate(test)) || (test.liesWithinCoordinate(this)) 212 || (this.start <= test.getStart() && this.end >= test.getStart()) 213 || (this.end >= test.getEnd() && this.start <= test.getEnd() )) { 214 out = true; 215 } 216 217 return out; 218 } 219 220 public Coordinate getOverlap(Coordinate test) { 221 222 Coordinate out = null; 223 Integer start = test.getStart(); 224 Integer end = test.getEnd(); 225 226 227 if (this.start==null || this.end==null || test.getEnd()==null || test.getStart()==null 228 || !this.overlaps(test)) { 229 out = null; 230 } else { 231 if (this.start>=test.getStart() && this.start<=test.getEnd() ) { 232 start = this.start; 233 } 234 if (this.end>=test.getStart() && this.end<=test.getEnd() ) 235 { 236 end = this.end; 237 } 238 out = new Coordinate(start, end, 1); 239 } 240 241 return out; 242 } 243 244 245 246 // public static Integer getStart(TreeSet<Coordinate> testSet) { 247 // Integer out = null; 248 // 249 // if ( testSet != null && !testSet.isEmpty()) { 250 // out = testSet.first().getStart(); 251 // } 252 // 253 // return out; 254 // } 255 // 256 // public static Integer getEnd(TreeSet<Coordinate> testSet) { 257 // Integer out = null; 258 // 259 // if ( testSet != null && !testSet.isEmpty()) { 260 // 261 // out = testSet.last().getEnd(); 262 // 263 // for (Coordinate c : testSet) { 264 // if (c.getEnd()>out) { 265 // out = c.getEnd(); 266 // } 267 // } 268 // } 269 // return out; 270 // } 271 // 272 // public static Coordinate getRange(TreeSet<Coordinate> testSet) { 273 // Coordinate out = null; 274 // if ( testSet != null && !testSet.isEmpty()) { 275 // 276 // out = new Coordinate(Coordinate.getStart(testSet),Coordinate.getEnd(testSet) ,1); 277 // 278 // } 279 // 280 // return null; 281 // } 282 283 284 285 // 286 // /** 287 // * tests whether this Coordinate represents a region fully covered in the 288 // * parameter Set of Coordinates - without any gaps 289 // * @param testSet TreeSet<Coordinate> 290 // * @return Boolean 291 // */ 292 // public Boolean liesWithinCoordinateSetWithoutGaps(TreeSet<Coordinate> testSet) { 293 // Boolean out = false; 294 // 295 // Integer rangeBegin = Coordinate.getStart(testSet); 296 // Integer rangeEnd = Coordinate.getEnd(testSet); 297 // 298 // if (this.start<rangeBegin || this.end>rangeEnd ) { 299 // out = false; 300 // } else if (this.getGaps(testSet).isEmpty()) { 301 // out = true; 302 // } 303 // 304 // return out; 305 // } 306 307 // /** 308 // * 309 // * @param testSet TreeSet<Coordinate> 310 // * @return TreeSet<Coordinate> 311 // */ 312 // public TreeSet<Coordinate> getGaps(TreeSet<Coordinate> testSet) { 313 // 314 // Integer rangeBegin = Coordinate.getStart(testSet); 315 // Integer rangeEnd = Coordinate.getEnd(testSet); 316 // 317 // TreeSet<Coordinate> gaps = new TreeSet<Coordinate>(); 318 // TreeSet<Coordinate> temp = Coordinate.getCoordinateGaps(testSet); 319 // 320 // 321 // if (this.start<rangeBegin) { 322 // Coordinate newC = new Coordinate(this.getStart(), rangeBegin-1, 1); 323 // gaps.add(newC); 324 // } 325 // 326 // if (this.end>rangeEnd) { 327 // Coordinate newC2 = new Coordinate(rangeEnd+1, this.end, 1); 328 // gaps.add(newC2); 329 // } 330 // 331 // 332 // 333 // for (Coordinate cd : temp) { 334 // 335 // //this gap completely contains the desired range 336 // if (this.liesWithinCoordinate(cd)) { 337 // Coordinate newC = new Coordinate(this.getStart(), this.getEnd(), 1); 338 // gaps.add(newC); 339 // break; 340 // } 341 // 342 // // this gap lies completely within the range 343 // else if (cd.liesWithinCoordinate(this)) { 344 // gaps.add(cd); 345 // } 346 // 347 // //this gap overlaps the beginning of the range 348 // else if (cd.getStart()<=this.getStart() && cd.getEnd()>=this.getStart()) { 349 // Coordinate newC = new Coordinate(this.getStart(), cd.getEnd(), 1); 350 // gaps.add(newC); 351 // } 352 // 353 // //this gap overlies the end of the range 354 // else if (cd.getEnd()>=this.getEnd() && cd.getStart()<=this.getEnd()) { 355 // Coordinate newC = new Coordinate(cd.getStart(), this.getEnd(), 1); 356 // gaps.add(newC); 357 // } 358 // 359 // } 360 // 361 // return gaps; 362 // } 363 364 // public static TreeSet<Coordinate> getCoordinateGaps(TreeSet<Coordinate> testSet){ 365 // TreeSet<Coordinate> gaps = new TreeSet<Coordinate>(); 366 // 367 // Integer rangeBegin = Coordinate.getStart(testSet); 368 // Integer rangeEnd = Coordinate.getEnd(testSet); 369 // Integer currentPosition = Coordinate.getStart(testSet); 370 // 371 // for (Coordinate c : testSet) { 372 // 373 // Integer s = c.getStart(); 374 // Integer e = c.getEnd(); 375 // 376 // if (s>currentPosition) { 377 // 378 // Coordinate newC = new Coordinate(currentPosition, s-1, 1); 379 // gaps.add(newC); 380 // } 381 // 382 // if (e>=rangeEnd) { 383 // break; 384 // } 385 // 386 // 387 // currentPosition = e+1; 388 // 389 // } 390 // 391 // return gaps; 392 // } 393 394 }