// License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.data;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.text.MessageFormat;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.tools.CheckParameterUtil;
import com.google.gwt.user.client.rpc.IsSerializable;
/**
* GWT
*
* TODO
* some methods missing
*
* changelog
* made class gwt-serializable
* - fields minLat, minLon, maxLat, maxLon: private -> public
* - added no arg constructor
*/
/**
* This is a simple data class for "rectangular" areas of the world, given in
* lat/lon min/max values. The values are rounded to LatLon.OSM_SERVER_PRECISION
*
* @author imi
*/
public class Bounds implements IsSerializable {
/**
* The minimum and maximum coordinates.
*/
public /* private */ double minLat, minLon, maxLat, maxLon;
/* for GWT serialization */
protected Bounds() {
}
public LatLon getMin() {
return new LatLon(minLat, minLon);
}
public LatLon getMax() {
return new LatLon(maxLat, maxLon);
}
/**
* Construct bounds out of two points
*/
public Bounds(LatLon min, LatLon max) {
this(min.lat(), min.lon(), max.lat(), max.lon());
}
public Bounds(LatLon b) {
this(b, b);
}
public Bounds(double minlat, double minlon, double maxlat, double maxlon) {
this.minLat = roundToOsmPrecision(minlat);
this.minLon = roundToOsmPrecision(minlon);
this.maxLat = roundToOsmPrecision(maxlat);
this.maxLon = roundToOsmPrecision(maxlon);
}
public Bounds(double [] coords) {
CheckParameterUtil.ensureParameterNotNull(coords, "coords");
if (coords.length != 4)
throw new IllegalArgumentException(MessageFormat.format("Expected array of length 4, got {0}", coords.length));
this.minLat = roundToOsmPrecision(coords[0]);
this.minLon = roundToOsmPrecision(coords[1]);
this.maxLat = roundToOsmPrecision(coords[2]);
this.maxLon = roundToOsmPrecision(coords[3]);
}
public Bounds(String asString, String separator) throws IllegalArgumentException {
CheckParameterUtil.ensureParameterNotNull(asString, "asString");
String[] components = asString.split(separator);
if (components.length != 4)
throw new IllegalArgumentException(MessageFormat.format("Exactly four doubles excpected in string, got {0}", components.length));
double[] values = new double[4];
for (int i=0; i<4; i++) {
try {
values[i] = Double.parseDouble(components[i]);
} catch(NumberFormatException e) {
throw new IllegalArgumentException(MessageFormat.format("Illegal double value ''{0}''", components[i]));
}
}
if (!LatLon.isValidLat(values[0]))
throw new IllegalArgumentException(tr("Illegal latitude value ''{0}''", values[0]));
if (!LatLon.isValidLon(values[1]))
throw new IllegalArgumentException(tr("Illegal longitude value ''{0}''", values[1]));
if (!LatLon.isValidLat(values[2]))
throw new IllegalArgumentException(tr("Illegal latitude value ''{0}''", values[2]));
if (!LatLon.isValidLon(values[3]))
throw new IllegalArgumentException(tr("Illegal latitude value ''{0}''", values[3]));
this.minLat = roundToOsmPrecision(values[0]);
this.minLon = roundToOsmPrecision(values[1]);
this.maxLat = roundToOsmPrecision(values[2]);
this.maxLon = roundToOsmPrecision(values[3]);
}
public Bounds(Bounds other) {
this(other.getMin(), other.getMax());
}
// public Bounds(Rectangle2D rect) {
// this(rect.getMinY(), rect.getMinX(), rect.getMaxY(), rect.getMaxX());
// }
/**
* Creates new bounds around a coordinate pair <code>center</code>. The
* new bounds shall have an extension in latitude direction of <code>latExtent</code>,
* and in longitude direction of <code>lonExtent</code>.
*
* @param center the center coordinate pair. Must not be null.
* @param latExtent the latitude extent. > 0 required.
* @param lonExtent the longitude extent. > 0 required.
* @throws IllegalArgumentException thrown if center is null
* @throws IllegalArgumentException thrown if latExtent <= 0
* @throws IllegalArgumentException thrown if lonExtent <= 0
*/
public Bounds(LatLon center, double latExtent, double lonExtent) {
CheckParameterUtil.ensureParameterNotNull(center, "center");
if (latExtent <= 0.0)
throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0.0 exptected, got {1}", "latExtent", latExtent));
if (lonExtent <= 0.0)
throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0.0 exptected, got {1}", "lonExtent", lonExtent));
this.minLat = roundToOsmPrecision(center.lat() - latExtent / 2);
this.minLon = roundToOsmPrecision(center.lon() - lonExtent / 2);
this.maxLat = roundToOsmPrecision(center.lat() + latExtent / 2);
this.maxLon = roundToOsmPrecision(center.lon() + lonExtent / 2);
}
@Override public String toString() {
return "Bounds["+minLat+","+minLon+","+maxLat+","+maxLon+"]";
}
// public String toShortString(DecimalFormat format) {
// return
// format.format(minLat) + " "
// + format.format(minLon) + " / "
// + format.format(maxLat) + " "
// + format.format(maxLon);
// }
/**
* @return Center of the bounding box.
*/
public LatLon getCenter()
{
return getMin().getCenter(getMax());
}
/**
* Extend the bounds if necessary to include the given point.
*/
public void extend(LatLon ll) {
if (ll.lat() < minLat) {
minLat = roundToOsmPrecision(ll.lat());
}
if (ll.lon() < minLon) {
minLon = roundToOsmPrecision(ll.lon());
}
if (ll.lat() > maxLat) {
maxLat = roundToOsmPrecision(ll.lat());
}
if (ll.lon() > maxLon) {
maxLon = roundToOsmPrecision(ll.lon());
}
}
public void extend(Bounds b) {
extend(b.getMin());
extend(b.getMax());
}
/**
* Is the given point within this bounds?
*/
public boolean contains(LatLon ll) {
if (ll.lat() < minLat || ll.lon() < minLon)
return false;
if (ll.lat() > maxLat || ll.lon() > maxLon)
return false;
return true;
}
/**
* The two bounds intersect? Compared to java Shape.intersects, if does not use
* the interior but the closure. (">=" instead of ">")
*/
public boolean intersects(Bounds b) {
return b.maxLat >= minLat &&
b.maxLon >= minLon &&
b.minLat <= maxLat &&
b.minLon <= maxLon;
}
// /**
// * Converts the lat/lon bounding box to an object of type Rectangle2D.Double
// * @return the bounding box to Rectangle2D.Double
// */
// public Rectangle2D.Double asRect() {
// return new Rectangle2D.Double(minLon, minLat, maxLon-minLon, maxLat-minLat);
// }
public double getArea() {
return (maxLon - minLon) * (maxLat - minLat);
}
public String encodeAsString(String separator) {
StringBuffer sb = new StringBuffer();
sb.append(minLat).append(separator).append(minLon)
.append(separator).append(maxLat).append(separator)
.append(maxLon);
return sb.toString();
}
/**
* <p>Replies true, if this bounds are <em>collapsed</em>, i.e. if the min
* and the max corner are equal.</p>
*
* @return true, if this bounds are <em>collapsed</em>
*/
public boolean isCollapsed() {
return getMin().equals(getMax());
}
@Override
public int hashCode() {
throw new UnsupportedOperationException("gwt implement me");
// final int prime = 31;
// int result = 1;
// long temp;
// temp = Double.doubleToLongBits(maxLat);
// result = prime * result + (int) (temp ^ (temp >>> 32));
// temp = Double.doubleToLongBits(maxLon);
// result = prime * result + (int) (temp ^ (temp >>> 32));
// temp = Double.doubleToLongBits(minLat);
// result = prime * result + (int) (temp ^ (temp >>> 32));
// temp = Double.doubleToLongBits(minLon);
// result = prime * result + (int) (temp ^ (temp >>> 32));
// return result;
}
@Override
public boolean equals(Object obj) {
throw new UnsupportedOperationException("gwt implement me");
// if (this == obj)
// return true;
// if (obj == null)
// return false;
// if (getClass() != obj.getClass())
// return false;
// Bounds other = (Bounds) obj;
// if (Double.doubleToLongBits(maxLat) != Double.doubleToLongBits(other.maxLat))
// return false;
// if (Double.doubleToLongBits(maxLon) != Double.doubleToLongBits(other.maxLon))
// return false;
// if (Double.doubleToLongBits(minLat) != Double.doubleToLongBits(other.minLat))
// return false;
// if (Double.doubleToLongBits(minLon) != Double.doubleToLongBits(other.minLon))
// return false;
// return true;
}
/**
* Returns the value rounded to OSM precisions, i.e. to
* LatLon.MAX_SERVER_PRECISION
*
* @return rounded value
*/
private double roundToOsmPrecision(double value) {
return Math.round(value / LatLon.MAX_SERVER_PRECISION) * LatLon.MAX_SERVER_PRECISION;
}
}