org.geolatte.geom.Box Maven / Gradle / Ivy
Show all versions of geolatte-geom Show documentation
package org.geolatte.geom;
import org.geolatte.geom.crs.CoordinateReferenceSystem;
import static java.lang.String.format;
/**
* An multi-dimensional axis-aligned bounding box.
*
* A Box
determines a sub characterised by a lower-left and an upper-right coordinate.
* An Box
is empty if the set of enclosed points are emptym, and has Double.NaN for its lowerleft/upperright coordinates.
*
* @author Karel Maesen, Geovise BVBA, 2011
*/
public class Box {
private final CoordinateReferenceSystem
crs;
private final P lowerLeft;
private final P upperRight;
/**
* Creates an empty Box
*/
public Box(CoordinateReferenceSystem
crs) {
this.crs = crs;
lowerLeft = Positions.mkPosition(crs);
upperRight = lowerLeft;
}
/**
* Creates an instance from specified lower-left and upper-right Point
s.
*
*
* @param lowerLeft the Point
designating the lower-left coordinates
* @param upperRight the Point
designating the upper-right coordinates
* of the envelope.
*/
public Box(P lowerLeft, P upperRight, CoordinateReferenceSystem
crs) {
if (crs == null) {
throw new IllegalArgumentException("Null CRS argument not allowed");
}
for (int i = 0; i < lowerLeft.getCoordinateDimension(); i++) {
if (lowerLeft.getCoordinate(i) > upperRight.getCoordinate(i)) {
throw new IllegalArgumentException("Lowerleft needs to be smaller than upperRight");
}
}
this.crs = crs;
this.lowerLeft = lowerLeft;
this.upperRight = upperRight;
}
/**
* Returns the CoordinateReferenceSystem
for this Box
*
* @return
*/
public CoordinateReferenceSystem
getCoordinateReferenceSystem() {
return this.crs;
}
/**
* Returns the lower-left point of this Box
.
*
* @return the lower-left point
*/
public P lowerLeft() {
return this.lowerLeft;
}
/**
* Returns the upper-right point of this Box
.
*
* @return the upper-right point
*/
public P upperRight() {
return this.upperRight;
}
public String toString() {
StringBuilder builder = new StringBuilder(getCoordinateReferenceSystem().getCrsId().toString());
builder.append("Box LL: ")
.append(this.lowerLeft.toString())
.append(" - UR: ")
.append(this.upperRight.toString());
return builder.toString();
}
/**
* Creates an Box
that is the set-theoretic union of this {@code Box} with the specified {@code Box}
*
* @param other other Box
* @return an Box
that encompasses both operands.
* @throws IllegalArgumentException when the operand Box
s don't have the same coordinate reference system.
*/
public Box
union(Box
other) {
if (other == null || other.isEmpty()) return this;
if (!this.getCoordinateReferenceSystem().equals(other.getCoordinateReferenceSystem()))
throw new IllegalArgumentException("Boxs have different CRS.");
double[] lowerLeft = new double[getCoordinateDimension()];
double[] upperRight = new double[getCoordinateDimension()];
for (int i = 0; i < getCoordinateDimension(); i++){
lowerLeft[i] = Math.min(this.lowerLeft.getCoordinate(i), other.lowerLeft.getCoordinate(i));
upperRight[i] = Math.max(this.upperRight.getCoordinate(i), other.upperRight.getCoordinate(i));
}
return new Box
(Positions.mkPosition(crs, lowerLeft), Positions.mkPosition(crs, upperRight) , crs);
}
/**
* Intersects the specified Box
with this Box
and returns the result.
*
* @param other the Box to intersect with this instance
* @return the set-theoretic intersection of this Box and the specified Box.
* @throws IllegalArgumentException when the specified Box
doesn't have the same coordinate reference system as this instance.
*/
public Box
intersect(Box
other) {
if (other == null || other.isEmpty()) return this;
if (!this.getCoordinateReferenceSystem().equals(other.getCoordinateReferenceSystem()))
throw new IllegalArgumentException("Boxs have different CRS.");
double[] lowerLeft = new double[getCoordinateDimension()];
double[] upperRight = new double[getCoordinateDimension()];
for (int i = 0; i < getCoordinateDimension(); i++){
if (this.lowerLeft.getCoordinate(i) > other.upperRight.getCoordinate(i) || this.upperRight.getCoordinate(i) <
other.lowerLeft.getCoordinate(i)) {
return mkEmpty();
}
lowerLeft[i] = Math.max(this.lowerLeft.getCoordinate(i), other.lowerLeft.getCoordinate(i));
upperRight[i] = Math.min(this.upperRight.getCoordinate(i), other.upperRight.getCoordinate(i));
}
return new Box
(Positions.mkPosition(crs, lowerLeft), Positions.mkPosition(crs, upperRight) , crs);
}
private Box
mkEmpty() {
P pos = Positions.mkPosition(crs);
return new Box
(pos, pos, crs);
}
static public
Box
mkEmpty(CoordinateReferenceSystem
crs) {
P pos = Positions.mkPosition(crs);
return new Box
(pos, pos, crs);
}
public int getCoordinateDimension(){
return crs.getCoordinateDimension();
}
/**
* Checks whether this Box
is empty.
*
* @return true iff this instance is empty (the empty set).
*/
public boolean isEmpty() {
return this.lowerLeft.isEmpty() && this.upperRight.isEmpty() ;
}
/**
* Checks whether this Box
is contained within the specified Box
*
* @param other the other Box
* @return true iff this instance is contained within the specified Box
* @throws IllegalArgumentException when the specified Box
doesn't have the same coordinate reference system as this instance.
*/
public boolean within(Box
other) {
if (other == null || other.isEmpty()) return false;
if (!this.getCoordinateReferenceSystem().equals(other.getCoordinateReferenceSystem()))
throw new IllegalArgumentException("Boxs have different CRS.");
for (int i = 0; i < getCoordinateDimension(); i++){
if (this.lowerLeft.getCoordinate(i) < other.lowerLeft.getCoordinate(i) || this.upperRight.getCoordinate(i) >
other.upperRight.getCoordinate(i)) {
return false;
}
}
return true;
}
/**
* Checks whether this Box
contains the specifies Box
.
*
* @param other the other Box
* @return true iff this instance contains the specified Box
* @throws IllegalArgumentException when the specified Box
doesn't have the same coordinate reference system as this instance.
*/
public boolean contains(Box other) {
return other.within(this);
}
/**
* Checks whether this Box
contains the specifies Position
.
*
* @param p the Position
* @return true iff this instance contains the specified Position
* @throws IllegalArgumentException when the specified Point
doesn't have the same coordinate reference system as this instance.
*/
public boolean contains(P p) {
if (!p.getClass().equals(this.getCoordinateReferenceSystem().getPositionClass()))
throw new IllegalArgumentException("Position and envelope of different types");
if (isEmpty()) return false;
for(int i = 0; i < getCoordinateDimension(); i++) {
if (p.getCoordinate(i) < lowerLeft.getCoordinate(i) || p.getCoordinate(i) > upperRight.getCoordinate(i)) {
return false;
}
}
return true;
}
/**
* Checks whether this Box
intersects the specifies Box
.
*
*
Two instances intersect when their set-theoretic intersection is non-empty.
*
* @param other the other Box
* @return true iff this instance intersects with the other Box
* @throws IllegalArgumentException when the specified Box
doesn't have the same coordinate reference system as this instance.
*/
public boolean intersects(Box other) {
if (isEmpty() || other.isEmpty()) return false;
for(int i = 0; i < getCoordinateDimension(); i++){
if (this.lowerLeft.getCoordinate(i) > other.upperRight.getCoordinate(i) || this.upperRight.getCoordinate(i) <
other.lowerLeft.getCoordinate(i)) {
return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
public Box as(Class castToType){
if (! castToType.isAssignableFrom(this.crs.getPositionClass()) ) {
throw new ClassCastException(format("Can't cast a %s to a %s", this.crs.getPositionClass().getName(),
castToType.getName()));
}
return (Box)this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Box> envelope = (Box>) o;
if (crs != null ? !crs.equals(envelope.crs) : envelope.crs != null) return false;
if (this.isEmpty() && envelope.isEmpty()) return true;
if (lowerLeft != null ? !lowerLeft.equals(envelope.lowerLeft) : envelope.lowerLeft != null) return false;
if (upperRight != null ? !upperRight.equals(envelope.upperRight) : envelope.upperRight != null) return false;
return true;
}
@Override
public int hashCode() {
int result = crs != null ? crs.hashCode() : 0;
result = 31 * result + (lowerLeft != null ? lowerLeft.hashCode() : 0);
result = 31 * result + (upperRight != null ? upperRight.hashCode() : 0);
return result;
}
}