001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2001, Institut de Recherche pour le Développement
006: * (C) 1999, Fisheries and Oceans Canada
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation;
011: * version 2.1 of the License.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: */
018: package org.geotools.measure;
019:
020: // J2SE dependencies
021: import java.io.Serializable;
022: import java.lang.ref.Reference;
023: import java.lang.ref.SoftReference;
024: import java.text.Format;
025: import java.text.ParseException;
026: import java.util.Locale;
027:
028: // Geotools dependencies
029: import org.geotools.resources.ClassChanger;
030:
031: /**
032: * An angle in degrees. An angle is the amount of rotation needed to bring one line or plane
033: * into coincidence with another, generally measured in degrees, sexagesimal degrees or grads.
034: *
035: * @since 2.0
036: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/measure/Angle.java $
037: * @version $Id: Angle.java 20874 2006-08-07 10:00:01Z jgarnett $
038: * @author Martin Desruisseaux
039: *
040: * @see Latitude
041: * @see Longitude
042: * @see AngleFormat
043: */
044: public class Angle implements Comparable, Serializable {
045: /**
046: * Serial number for interoperability with different versions.
047: */
048: private static final long serialVersionUID = 1158747349433104534L;
049:
050: /**
051: * A shared instance of {@link AngleFormat}.
052: */
053: private static Reference format;
054:
055: /**
056: * Define how angle can be converted to {@link Number} objects.
057: */
058: static {
059: ClassChanger.register(new ClassChanger(Angle.class,
060: Double.class) {
061: protected Number convert(final Comparable o) {
062: return new Double(((Angle) o).theta);
063: }
064:
065: protected Comparable inverseConvert(final Number value) {
066: return new Angle(value.doubleValue());
067: }
068: });
069: }
070:
071: /**
072: * Angle value in degres.
073: */
074: private final double theta;
075:
076: /**
077: * Contruct a new angle with the specified value.
078: *
079: * @param theta Angle in degrees.
080: */
081: public Angle(final double theta) {
082: this .theta = theta;
083: }
084:
085: /**
086: * Constructs a newly allocated {@code Angle} object that represents the angle value
087: * represented by the string. The string should represents an angle in either fractional
088: * degrees (e.g. 45.5°) or degrees with minutes and seconds (e.g. 45°30').
089: *
090: * @param string A string to be converted to an {@code Angle}.
091: * @throws NumberFormatException if the string does not contain a parsable angle.
092: */
093: public Angle(final String string) throws NumberFormatException {
094: try {
095: final Angle theta = (Angle) getAngleFormat().parseObject(
096: string);
097: if (getClass().isAssignableFrom(theta.getClass())) {
098: this .theta = theta.theta;
099: } else {
100: throw new NumberFormatException();
101: }
102: } catch (ParseException exception) {
103: NumberFormatException e = new NumberFormatException(
104: exception.getLocalizedMessage());
105: e.initCause(exception);
106: throw e;
107: }
108: }
109:
110: /**
111: * Returns the angle value in degrees.
112: */
113: public double degrees() {
114: return theta;
115: }
116:
117: /**
118: * Returns the angle value in radians.
119: */
120: public double radians() {
121: return Math.toRadians(theta);
122: }
123:
124: /**
125: * Returns a hash code for this {@code Angle} object.
126: */
127: public int hashCode() {
128: final long code = Double.doubleToLongBits(theta);
129: return (int) code ^ (int) (code >>> 32);
130: }
131:
132: /**
133: * Compares the specified object with this angle for equality.
134: */
135: public boolean equals(final Object object) {
136: if (object == this ) {
137: return true;
138: }
139: if (object != null && getClass().equals(object.getClass())) {
140: final Angle that = (Angle) object;
141: return Double.doubleToLongBits(this .theta) == Double
142: .doubleToLongBits(that.theta);
143: } else {
144: return false;
145: }
146: }
147:
148: /**
149: * Compares two {@code Angle} objects numerically. The comparaison
150: * is done as if by the {@link Double#compare(double,double)} method.
151: */
152: public int compareTo(final Object that) {
153: return Double.compare(this .theta, ((Angle) that).theta);
154: }
155:
156: /**
157: * Returns a string representation of this {@code Angle} object.
158: */
159: public String toString() {
160: return getAngleFormat().format(this , new StringBuffer(), null)
161: .toString();
162: }
163:
164: /**
165: * Returns a shared instance of {@link AngleFormat}. The return type is
166: * {@link Format} in order to avoid class loading before necessary.
167: */
168: private static synchronized Format getAngleFormat() {
169: if (format != null) {
170: final Format angleFormat = (Format) format.get();
171: if (angleFormat != null) {
172: return angleFormat;
173: }
174: }
175: final Format newFormat = new AngleFormat("D°MM.m'", Locale.US);
176: format = new SoftReference(newFormat);
177: return newFormat;
178: }
179: }
|