/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 1999-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.measure;
import java.io.Serializable;
import java.text.Format;
import java.text.ParseException;
import java.util.Locale;
import org.geotools.resources.ClassChanger;
/**
* An angle in degrees. An angle is the amount of rotation needed to bring one line or plane
* into coincidence with another, generally measured in degrees, sexagesimal degrees or grads.
*
* @since 2.0
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (PMO, IRD)
*
* @see Latitude
* @see Longitude
* @see AngleFormat
*/
public class Angle implements Comparable<Angle>, Serializable {
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = 1158747349433104534L;
/**
* A shared instance of {@link AngleFormat}.
*/
private static Format format;
/**
* Define how angle can be converted to {@link Number} objects.
*/
static {
ClassChanger.register(new ClassChanger<Angle,Double>(Angle.class, Double.class) {
protected Double convert(final Angle o) {
return o.theta;
}
protected Angle inverseConvert(final Double value) {
return new Angle(value);
}
});
}
/**
* Angle value in degres.
*/
private final double theta;
/**
* Contructs a new angle with the specified value.
*
* @param theta Angle in degrees.
*/
public Angle(final double theta) {
this.theta = theta;
}
/**
* Constructs a newly allocated {@code Angle} object that represents the angle value
* represented by the string. The string should represents an angle in either fractional
* degrees (e.g. 45.5°) or degrees with minutes and seconds (e.g. 45°30').
*
* @param string A string to be converted to an {@code Angle}.
* @throws NumberFormatException if the string does not contain a parsable angle.
*/
public Angle(final String string) throws NumberFormatException {
final Format format = getAngleFormat();
final Angle theta;
try {
synchronized (Angle.class) {
theta = (Angle) format.parseObject(string);
}
} catch (ParseException exception) {
NumberFormatException e = new NumberFormatException(exception.getLocalizedMessage());
e.initCause(exception);
throw e;
}
if (getClass().isAssignableFrom(theta.getClass())) {
this.theta = theta.theta;
} else {
throw new NumberFormatException(string);
}
}
/**
* Returns the angle value in degrees.
*/
public double degrees() {
return theta;
}
/**
* Returns the angle value in radians.
*/
public double radians() {
return Math.toRadians(theta);
}
/**
* Returns a hash code for this {@code Angle} object.
*/
@Override
public int hashCode() {
final long code = Double.doubleToLongBits(theta);
return (int) code ^ (int) (code >>> 32);
}
/**
* Compares the specified object with this angle for equality.
*/
@Override
public boolean equals(final Object object) {
if (object == this) {
return true;
}
if (object!=null && getClass().equals(object.getClass())) {
final Angle that = (Angle) object;
return Double.doubleToLongBits(this.theta) ==
Double.doubleToLongBits(that.theta);
} else {
return false;
}
}
/**
* Compares two {@code Angle} objects numerically. The comparaison
* is done as if by the {@link Double#compare(double,double)} method.
*/
public int compareTo(final Angle that) {
return Double.compare(this.theta, that.theta);
}
/**
* Returns a string representation of this {@code Angle} object.
*/
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
synchronized (Angle.class) {
final Format format = getAngleFormat();
buffer = format.format(this, buffer, null);
}
return buffer.toString();
}
/**
* Returns a shared instance of {@link AngleFormat}. The return type is
* {@link Format} in order to avoid class loading before necessary.
*/
private static Format getAngleFormat() {
assert Thread.holdsLock(Angle.class);
if (format == null) {
format = new AngleFormat("D°MM.m'", Locale.US);
}
return format;
}
}