/* * This is part of Geomajas, a GIS framework, http://www.geomajas.org/. * * Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium. * * The program is available in open source according to the GNU Affero * General Public License. All contributions in this program are covered * by the Geomajas Contributors License Agreement. For full licensing * details, see LICENSE.txt in the project root. */ package org.geomajas.gwt.client.spatial; import org.geomajas.geometry.Coordinate; /** * <p> * Definition of an (axis aligned) Bounding Box. Determined by an x-ordinate an y-ordinate, it's width and it's height. * Notice the the name says "Axis Aligned"! This implies that this type of bounding box cannot rotate! * </p> * * @author Pieter De Graef */ public class Bbox { /** * The min ordinate along the X-axis. */ private double x; /** * The min ordinate along the Y-axis. */ private double y; /** * The bounding box' width. */ private double width; /** * The bounding box' height. */ private double height; // huge bbox, should cover coordinate space of all known crs-es public static final Bbox ALL = new Bbox(org.geomajas.geometry.Bbox.ALL); // ------------------------------------------------------------------------- // Constructors: // ------------------------------------------------------------------------- /** * Default constructor setting the position to (0, 0), and a width and height of 1. */ public Bbox() { this(0, 0, 1, 1); } /** * constructor that immediately applies values to all the fields. * * @param x * The min ordinate along the X-axis. * @param y * The min ordinate along the Y-axis. * @param width * The bounding box' width. * @param height * The bounding box' height. */ public Bbox(double x, double y, double width, double height) { this.x = x; this.y = y; this.width = width; this.height = height; } /** * Construct a bbox by copying another one. * * @param bounds * Another bounding box instance. */ public Bbox(Bbox bounds) { x = bounds.getX(); y = bounds.getY(); width = bounds.getWidth(); height = bounds.getHeight(); } /** * Constructor that transforms the DTO bounding box to the GWT bounding box. * * @param bounds * A DTO bounding box instance. */ public Bbox(org.geomajas.geometry.Bbox bounds) { x = bounds.getX(); y = bounds.getY(); width = bounds.getWidth(); height = bounds.getHeight(); } /** * Convert the GWT bounding box to a DTO bounding box. * * @return the bbox */ public org.geomajas.geometry.Bbox toDtoBbox() { return new org.geomajas.geometry.Bbox(getX(), getY(), getWidth(), getHeight()); } // ------------------------------------------------------------------------- // Class specific functions: // ------------------------------------------------------------------------- /** * Create a clone of the object. * * @return cloned object */ public Object clone() { // NOSONAR super.clone() not supported by GWT return new Bbox(x, y, width, height); } /** * Return the origin (x, y) as a Coordinate. * * @return origin */ public Coordinate getOrigin() { return new Coordinate(x, y); } /** * Get the center of the bounding box as a Coordinate. * * @return center point */ public Coordinate getCenterPoint() { double centerX = (width == 0 ? x : x + width / 2); double centerY = (height == 0 ? y : y + height / 2); return new Coordinate(centerX, centerY); } /** * Get the end-point of the bounding box as a Coordinate. * * @return endpoint or max coordinate */ public Coordinate getEndPoint() { return new Coordinate(x + width, y + height); } /** * Get the coordinates of the bounding box as an array. * * @return Returns 5 coordinates so that the array is closed. This can be useful when using this array to creating a * <code>LinearRing</code>. */ public Coordinate[] getCoordinates() { Coordinate[] result = new Coordinate[5]; result[0] = new Coordinate(x, y); result[1] = new Coordinate(x + width, y); result[2] = new Coordinate(x + width, y + height); result[3] = new Coordinate(x, y + height); result[4] = new Coordinate(x, y); return result; } /** * Does this bounding box contain the given bounding box? * * @param other * Another Bbox. * @return true if the other is completely surrounded by this one, false otherwise. */ public boolean contains(Bbox other) { if (other.getX() < this.getX()) { return false; } if (other.getY() < this.getY()) { return false; } if (other.getEndPoint().getX() > this.getEndPoint().getX()) { return false; } if (other.getEndPoint().getY() > this.getEndPoint().getY()) { return false; } return true; } /** * Does this bounding box contain the given point (coordinate) ? * * @param coord * Coordinate of point to test. * @return true if the point lies in the bounding box. */ public boolean contains(Coordinate coord) { if (coord.getX() < this.getX()) { return false; } if (coord.getY() < this.getY()) { return false; } if (coord.getX() > this.getEndPoint().getX()) { return false; } if (coord.getY() > this.getEndPoint().getY()) { return false; } return true; } /** * Does this bounding box intersect the given bounding box? * * @param other * Another Bbox. * @return true if the other intersects this one, false otherwise. */ public boolean intersects(Bbox other) { if (other.getX() > this.getEndPoint().getX()) { return false; } if (other.getY() > this.getEndPoint().getY()) { return false; } if (other.getEndPoint().getX() < this.getX()) { return false; } if (other.getEndPoint().getY() < this.getY()) { return false; } return true; } /** * Computes the intersection of this bounding box with the specified bounding box. * * @param other * Another Bbox. * @return bounding box of intersection or null if they do not intersect. */ public Bbox intersection(Bbox other) { if (!this.intersects(other)) { return null; } else { double minx = other.getX() > this.getX() ? other.getX() : this.getX(); double maxx = other.getEndPoint().getX() < this.getEndPoint().getX() ? other.getEndPoint().getX() : this .getEndPoint().getX(); double miny = other.getY() > this.getY() ? other.getY() : this.getY(); double maxy = other.getEndPoint().getY() < this.getEndPoint().getY() ? other.getEndPoint().getY() : this .getEndPoint().getY(); return new Bbox(minx, miny, (maxx - minx), (maxy - miny)); } } /** * Calculates the union of 2 bounding boxes. * * @param other The other Bbox. Can be a bounding box with width and height equal to 0. * @return new bbox which is the union of the parameters */ public Bbox union(Bbox other) { if (other.getWidth() == 0 && other.getHeight() == 0 && other.getX() == 0 && other.getY() == 0) { return (Bbox) clone(); } if (width == 0 && height == 0 && x == 0 && y == 0) { return (Bbox) other.clone(); } double minx = other.getX() < this.getX() ? other.getX() : this.getX(); double maxx = other.getEndPoint().getX() > this.getEndPoint().getX() ? other.getEndPoint().getX() : this .getEndPoint().getX(); double miny = other.getY() < this.getY() ? other.getY() : this.getY(); double maxy = other.getEndPoint().getY() > this.getEndPoint().getY() ? other.getEndPoint().getY() : this .getEndPoint().getY(); return new Bbox(minx, miny, (maxx - minx), (maxy - miny)); } /** * Return a new bounding box that has increased in size by adding a range to this bounding box. * * @param range Must be a positive number, otherwise null will be returned. * @return new buffered bounding box */ public Bbox buffer(double range) { if (range > 0) { double r2 = range * 2; return new Bbox(x - range, y - range, width + r2, height + r2); } return null; } /** * Return a new bounding box which has the same center position but has been scaled with the specified factor. * * @param factor * The scale factor (must be > 0). * @return bbox */ public Bbox scale(double factor) { if (factor > 0) { double scaledWidth = width * factor; double scaledHeight = height * factor; Coordinate center = getCenterPoint(); return new Bbox(center.getX() - scaledWidth / 2, center.getY() - scaledHeight / 2, scaledWidth, scaledHeight); } else { return new Bbox(this); } } /** * Creates a bbox that fits exactly in this box but has a different width/height ratio. * * @param ratioWidth width dor ratio * @param ratioHeight height for ratio * @return bbox */ public Bbox createFittingBox(double ratioWidth, double ratioHeight) { if (ratioWidth > 0 && ratioHeight > 0) { double newRatio = ratioWidth / ratioHeight; double oldRatio = width / height; double newWidth = width; double newHeight = height; if (newRatio > oldRatio) { newHeight = width / newRatio; } else { newWidth = height * newRatio; } Bbox result = new Bbox(0, 0, newWidth, newHeight); result.setCenterPoint(getCenterPoint()); return result; } else { return new Bbox(this); } } /** * Translates this bounds with displacement dx and dy. * * @param dx * x displacement * @param dy * y displacement */ public void translate(double dx, double dy) { this.x = this.x + dx; this.y = this.y + dy; } /** * Create a new bounds by transforming this bounds with the specified tranformation matrix. * * @param t * the transformation matrix * @return the transformed bounds */ public Bbox transform(Matrix t) { Coordinate c1 = transform(t, new Coordinate(x, y)); Coordinate c2 = transform(t, new Coordinate(x + width, y + height)); Coordinate origin = new Coordinate(Math.min(c1.getX(), c2.getX()), Math.min(c1.getY(), c2.getY())); Coordinate endPoint = new Coordinate(Math.max(c1.getX(), c2.getX()), Math.max(c1.getY(), c2.getY())); return new Bbox(origin.getX(), origin.getY(), endPoint.getX() - origin.getX(), endPoint.getY() - origin.getY()); } /** * Moves center to the specified coordinate. * * @param coordinate * new center point */ public void setCenterPoint(Coordinate coordinate) { this.x = coordinate.getX() - 0.5 * this.width; this.y = coordinate.getY() - 0.5 * this.height; } /** * Returns whether or not this bounding box is empty. A bounding box is considered empty when either the width or * the height is equal to zero. * * @return true when bbox is empty */ public boolean isEmpty() { return width == 0 || height == 0; } /** * Return a nice print of this bounding box. */ public String toString() { return "Bbox[" + x + " " + y + ", " + width + " " + height + "]"; } // ------------------------------------------------------------------------- // Getters and setters: // ------------------------------------------------------------------------- public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public double getMaxX() { return getX() + getWidth(); } public double getMaxY() { return getY() + getHeight(); } /** * Is this bbox equal to the specified bbox ? * * @param other another bbox * @param delta precision * @return true if equal within the precision, false otherwise */ public boolean equals(Bbox other, double delta) { return null != other && equals(this.x, other.x, delta) && equals(this.y, other.y, delta) && equals(this.width, other.width, delta) && equals(this.height, other.height, delta); } /** * Is this bbox the universal all bbox ? * @return true if all */ public boolean isAll() { return equals(ALL, 0.001 * ALL.getWidth()); } protected boolean equals(double d1, double d2, double delta) { return Math.abs(d1 - d2) <= delta; } private Coordinate transform(Matrix t, Coordinate coordinate) { double x = t.getXx() * coordinate.getX() + t.getXy() * coordinate.getY() + t.getDx(); double y = t.getYx() * coordinate.getY() + t.getYy() * coordinate.getY() + t.getDy(); return new Coordinate(x, y); } }