001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2004, Institut de Recherche pour le D�veloppement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.geometry;
018:
019: // J2SE dependencies
020: import java.awt.geom.Point2D;
021: import java.io.IOException;
022: import java.io.ObjectInputStream;
023: import java.io.ObjectOutputStream;
024: import java.io.Serializable;
025:
026: // OpenGIS dependencies
027: import org.opengis.referencing.cs.AxisDirection; // For javadoc
028: import org.opengis.referencing.crs.CoordinateReferenceSystem;
029: import org.opengis.geometry.DirectPosition;
030: import org.opengis.geometry.MismatchedDimensionException;
031:
032: /**
033: * Holds the coordinates for a two-dimensional position within some coordinate reference system.
034: * <p>
035: * <b>Note 1:</b> This class inherits {@linkplain #x x} and {@linkplain #y y} fields. But
036: * despite their names, they don't need to be oriented toward {@linkplain AxisDirection#EAST East}
037: * and {@linkplain AxisDirection#NORTH North} respectively. The (<var>x</var>,<var>y</var>) axis
038: * can have any orientation and should be understood as "ordinate 0" and "ordinate 1" values
039: * instead. This is not specific to this implementation; in Java2D too, the visual axis orientation
040: * depend on the {@linkplain java.awt.Graphics2D#getTransform affine transform in the graphics
041: * context}.
042: * <p>
043: * The rational for avoiding axis orientation restriction is that other {@link DirectPosition}
044: * implementation do not have such restriction, and anyway it would be hard to generalize (what
045: * to do with {@linkplain AxisDirection#NORTH_EAST North-East} direction?).
046: * <p>
047: * <b>Note 2:</b>
048: * <strong>Do not mix instances of this class with ordinary {@link Point2D} instances in a
049: * {@link java.util.HashSet} or as {@link java.util.HashMap} keys.</strong> It is not possible to
050: * meet both {@link Point2D#hashCode} and {@link DirectPosition#hashCode} contract, and this class
051: * choose to implements the later. Concequently, <strong>{@link #hashCode} is inconsistent with
052: * {@link Point2D#equals}</strong> (but is consistent with {@link DirectPosition#equals}).
053: * <p>
054: * In other words, it is safe to add instances of {@code DirectPosition2D} in a
055: * {@code HashSet<DirectPosition>}, but it is unsafe to add them in a {@code HashSet<Point2D>}.
056: * Collections that do not rely on {@link Object#hashCode}, like {@link java.util.ArrayList},
057: * are safe in all cases.
058: *
059: * @since 2.0
060: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/geometry/DirectPosition2D.java $
061: * @version $Id: DirectPosition2D.java 28788 2008-01-16 18:18:22Z desruisseaux $
062: * @author Martin Desruisseaux
063: *
064: * @see DirectPosition1D
065: * @see GeneralPosition
066: * @see java.awt.geom.Point2D
067: */
068: public class DirectPosition2D extends Point2D.Double implements
069: DirectPosition, Serializable {
070: /**
071: * Serial number for interoperability with different versions.
072: */
073: private static final long serialVersionUID = 835130287438466996L;
074:
075: /**
076: * The coordinate reference system for this position;
077: */
078: private CoordinateReferenceSystem crs;
079:
080: /**
081: * Constructs a position initialized to (0,0) with a {@code null}
082: * coordinate reference system.
083: */
084: public DirectPosition2D() {
085: }
086:
087: /**
088: * Constructs a position with the specified coordinate reference system.
089: */
090: public DirectPosition2D(final CoordinateReferenceSystem crs) {
091: setCoordinateReferenceSystem(crs);
092: }
093:
094: /**
095: * Constructs a 2D position from the specified ordinates. Despite their name,
096: * the (<var>x</var>,<var>y</var>) coordinates don't need to be oriented toward
097: * ({@linkplain AxisDirection#EAST East}, {@linkplain AxisDirection#NORTH North}).
098: * Those parameter names simply match the {@linkplain #x x} and {@linkplain #y y}
099: * fields. See the {@linkplain DirectPosition2D class javadoc} for details.
100: */
101: public DirectPosition2D(final double x, final double y) {
102: super (x, y);
103: }
104:
105: /**
106: * Constructs a 2D position from the specified ordinates in the specified CRS. Despite
107: * their name, the (<var>x</var>,<var>y</var>) coordinates don't need to be oriented toward
108: * ({@linkplain AxisDirection#EAST East}, {@linkplain AxisDirection#NORTH North}).
109: * Those parameter names simply match the {@linkplain #x x} and {@linkplain #y y}
110: * fields. The actual axis orientations are determined by the specified CRS.
111: * See the {@linkplain DirectPosition2D class javadoc} for details.
112: */
113: public DirectPosition2D(final CoordinateReferenceSystem crs,
114: final double x, final double y) {
115: super (x, y);
116: setCoordinateReferenceSystem(crs);
117: }
118:
119: /**
120: * Constructs a position from the specified {@link Point2D}.
121: */
122: public DirectPosition2D(final Point2D point) {
123: super (point.getX(), point.getY());
124: if (point instanceof DirectPosition) {
125: setCoordinateReferenceSystem(((DirectPosition) point)
126: .getCoordinateReferenceSystem());
127: }
128: }
129:
130: /**
131: * Constructs a position initialized to the same values than the specified point.
132: */
133: public DirectPosition2D(final DirectPosition point) {
134: setLocation(point);
135: }
136:
137: /**
138: * Returns always <code>this</code>, the direct position for this
139: * {@linkplain org.opengis.geometry.coordinate.Position position}.
140: */
141: public DirectPosition getPosition() {
142: return this ;
143: }
144:
145: /**
146: * Returns the coordinate reference system in which the coordinate is given.
147: * May be {@code null} if this particular {@code DirectPosition} is included
148: * in a larger object with such a reference to a {@linkplain CoordinateReferenceSystem
149: * coordinate reference system}.
150: *
151: * @return The coordinate reference system, or {@code null}.
152: */
153: public final CoordinateReferenceSystem getCoordinateReferenceSystem() {
154: return crs;
155: }
156:
157: /**
158: * Set the coordinate reference system in which the coordinate is given.
159: *
160: * @param crs The new coordinate reference system, or {@code null}.
161: */
162: public void setCoordinateReferenceSystem(
163: final CoordinateReferenceSystem crs) {
164: AbstractDirectPosition.checkCoordinateReferenceSystemDimension(
165: crs, 2);
166: this .crs = crs;
167: }
168:
169: /**
170: * The length of coordinate sequence (the number of entries).
171: * This is always 2 for <code>DirectPosition2D</code> objects.
172: *
173: * @return The dimensionality of this position.
174: */
175: public final int getDimension() {
176: return 2;
177: }
178:
179: /**
180: * Returns a sequence of numbers that hold the coordinate of this position in its
181: * reference system.
182: *
183: * @return The coordinates
184: */
185: public double[] getCoordinates() {
186: return new double[] { x, y };
187: }
188:
189: /**
190: * Returns the ordinate at the specified dimension.
191: *
192: * @param dimension The dimension in the range 0 to 1 inclusive.
193: * @return The coordinate at the specified dimension.
194: * @throws IndexOutOfBoundsException if the specified dimension is out of bounds.
195: *
196: * @todo Provides a more detailled error message.
197: */
198: public final double getOrdinate(final int dimension)
199: throws IndexOutOfBoundsException {
200: switch (dimension) {
201: case 0:
202: return x;
203: case 1:
204: return y;
205: default:
206: throw new IndexOutOfBoundsException(String
207: .valueOf(dimension));
208: }
209: }
210:
211: /**
212: * Sets the ordinate value along the specified dimension.
213: *
214: * @param dimension the dimension for the ordinate of interest.
215: * @param value the ordinate value of interest.
216: * @throws IndexOutOfBoundsException if the specified dimension is out of bounds.
217: *
218: * @todo Provides a more detailled error message.
219: */
220: public final void setOrdinate(int dimension, double value)
221: throws IndexOutOfBoundsException {
222: switch (dimension) {
223: case 0:
224: x = value;
225: break;
226: case 1:
227: y = value;
228: break;
229: default:
230: throw new IndexOutOfBoundsException(String
231: .valueOf(dimension));
232: }
233: }
234:
235: /**
236: * Set this coordinate to the specified direct position. If the specified position
237: * contains a {@linkplain CoordinateReferenceSystem coordinate reference system},
238: * then the CRS for this position will be set to the CRS of the specified position.
239: *
240: * @param position The new position for this point.
241: * @throws MismatchedDimensionException if this point doesn't have the expected dimension.
242: */
243: public void setLocation(final DirectPosition position)
244: throws MismatchedDimensionException {
245: AbstractDirectPosition.ensureDimensionMatch("position",
246: position.getDimension(), 2);
247: setCoordinateReferenceSystem(position
248: .getCoordinateReferenceSystem());
249: x = position.getOrdinate(0);
250: y = position.getOrdinate(1);
251: }
252:
253: /**
254: * Returns a {@link Point2D} with the same coordinate as this direct position.
255: */
256: public Point2D toPoint2D() {
257: return new Point2D.Double(x, y);
258: }
259:
260: /**
261: * Returns a string representation of this coordinate. The default implementation formats
262: * this coordinate using a shared instance of {@link org.geotools.measure.CoordinateFormat}.
263: * This is okay for occasional formatting (for example for debugging purpose). But if there
264: * is a lot of positions to format, users will get better performance and more control by
265: * using their own instance of {@link org.geotools.measure.CoordinateFormat}.
266: */
267: public String toString() {
268: return AbstractDirectPosition.toString(this );
269: }
270:
271: /**
272: * Returns a hash value for this coordinate. This method implements the
273: * {@link DirectPosition#hashCode} contract, not the {@link Point2D#hashCode} contract.
274: */
275: public int hashCode() {
276: return AbstractDirectPosition.hashCode(this );
277: }
278:
279: /**
280: * Compares this point with the specified object for equality. If the given object implements
281: * the {@link DirectPosition} interface, then the comparaison is performed as specified in its
282: * {@link DirectPosition#equals} contract. Otherwise the comparaison is performed as specified
283: * in {@link Point2D#equals}.
284: */
285: public boolean equals(final Object object) {
286: /*
287: * If the other object implements the DirectPosition interface, performs
288: * the comparaison as specified in DirectPosition.equals(Object) contract.
289: */
290: if (object instanceof DirectPosition) {
291: final DirectPosition other = (DirectPosition) object;
292: return other.getDimension() == 2
293: && java.lang.Double.doubleToLongBits(other
294: .getOrdinate(0)) == java.lang.Double
295: .doubleToLongBits(x)
296: && java.lang.Double.doubleToLongBits(other
297: .getOrdinate(1)) == java.lang.Double
298: .doubleToLongBits(y)
299: && org.geotools.resources.Utilities.equals(other
300: .getCoordinateReferenceSystem(), crs);
301: }
302: /*
303: * Otherwise performs the comparaison as in Point2D.equals(Object).
304: * Do NOT check the CRS if the given object is an ordinary Point2D.
305: * This is necessary in order to respect the contract defined in Point2D.
306: */
307: return super .equals(object);
308: }
309:
310: /**
311: * Write this object to the specified stream. This method is necessary
312: * because the super-class is not serializable.
313: */
314: private void writeObject(final ObjectOutputStream out)
315: throws IOException {
316: out.defaultWriteObject();
317: out.writeDouble(x);
318: out.writeDouble(y);
319: }
320:
321: /**
322: * Read this object from the specified stream. This method is necessary
323: * because the super-class is not serializable.
324: */
325: private void readObject(final ObjectInputStream in)
326: throws IOException, ClassNotFoundException {
327: in.defaultReadObject();
328: x = in.readDouble();
329: y = in.readDouble();
330: }
331: }
|