001: /*
002: * The JTS Topology Suite is a collection of Java classes that
003: * implement the fundamental operations required to validate a given
004: * geo-spatial data set to a known topological specification.
005: *
006: * Copyright (C) 2001 Vivid Solutions
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; either
011: * version 2.1 of the License, or (at your option) any later version.
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: * You should have received a copy of the GNU Lesser General Public
019: * License along with this library; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: *
022: * For more information, contact:
023: *
024: * Vivid Solutions
025: * Suite #1A
026: * 2328 Government Street
027: * Victoria BC V8T 5G5
028: * Canada
029: *
030: * (250)385-6040
031: * www.vividsolutions.com
032: */
033: package com.vividsolutions.jts.geom;
034:
035: import java.io.Serializable;
036: import java.util.Comparator;
037: import com.vividsolutions.jts.util.Assert;
038:
039: /**
040: * A lightweight class used to store coordinates
041: * on the 2-dimensional Cartesian plane.
042: * It is distinct from <code>Point</code>, which is a subclass of <code>Geometry</code>
043: * . Unlike objects of type <code>Point</code> (which contain additional
044: * information such as an envelope, a precision model, and spatial reference
045: * system information), a <code>Coordinate</code> only contains ordinate values
046: * and accessor methods. <P>
047: *
048: * <code>Coordinate</code>s are two-dimensional points, with an additional
049: * z-ordinate. JTS does not support any operations on the z-ordinate except
050: * the basic accessor functions. Constructed coordinates will have a
051: * z-ordinate of <code>NaN</code>. The standard comparison functions will ignore
052: * the z-ordinate.
053: *
054: *@version 1.7
055: */
056: public class Coordinate implements Comparable, Cloneable, Serializable {
057: private static final long serialVersionUID = 6683108902428366910L;
058: /**
059: * The x-coordinate.
060: */
061: public double x;
062: /**
063: * The y-coordinate.
064: */
065: public double y;
066: /**
067: * The z-coordinate.
068: */
069: public double z;
070:
071: /**
072: * Constructs a <code>Coordinate</code> at (x,y,z).
073: *
074: *@param x the x-value
075: *@param y the y-value
076: *@param z the z-value
077: */
078: public Coordinate(double x, double y, double z) {
079: this .x = x;
080: this .y = y;
081: this .z = z;
082: }
083:
084: /**
085: * Constructs a <code>Coordinate</code> at (0,0,NaN).
086: */
087: public Coordinate() {
088: this (0.0, 0.0);
089: }
090:
091: /**
092: * Constructs a <code>Coordinate</code> having the same (x,y,z) values as
093: * <code>other</code>.
094: *
095: *@param c the <code>Coordinate</code> to copy.
096: */
097: public Coordinate(Coordinate c) {
098: this (c.x, c.y, c.z);
099: }
100:
101: /**
102: * Constructs a <code>Coordinate</code> at (x,y,NaN).
103: *
104: *@param x the x-value
105: *@param y the y-value
106: */
107: public Coordinate(double x, double y) {
108: this (x, y, Double.NaN);
109: }
110:
111: /**
112: * Sets this <code>Coordinate</code>s (x,y,z) values to that of <code>other</code>
113: * .
114: *
115: *@param other the <code>Coordinate</code> to copy
116: */
117: public void setCoordinate(Coordinate other) {
118: x = other.x;
119: y = other.y;
120: z = other.z;
121: }
122:
123: /**
124: * Returns whether the planar projections of the two <code>Coordinate</code>s
125: * are equal.
126: *
127: *@param other a <code>Coordinate</code> with which to do the 2D comparison.
128: *@return <code>true</code> if the x- and y-coordinates are equal; the
129: * z-coordinates do not have to be equal.
130: */
131: public boolean equals2D(Coordinate other) {
132: if (x != other.x) {
133: return false;
134: }
135:
136: if (y != other.y) {
137: return false;
138: }
139:
140: return true;
141: }
142:
143: /**
144: * Returns <code>true</code> if <code>other</code> has the same values for
145: * the x and y ordinates.
146: * Since Coordinates are 2.5D, this routine ignores the z value when making the comparison.
147: *
148: *@param other a <code>Coordinate</code> with which to do the comparison.
149: *@return <code>true</code> if <code>other</code> is a <code>Coordinate</code>
150: * with the same values for the x and y ordinates.
151: */
152: public boolean equals(Object other) {
153: if (!(other instanceof Coordinate)) {
154: return false;
155: }
156: return equals2D((Coordinate) other);
157: }
158:
159: /**
160: * Compares this {@link Coordinate} with the specified {@link Coordinate} for order.
161: * This method ignores the z value when making the comparison.
162: * Returns:
163: * <UL>
164: * <LI> -1 : this.x < other.x || ((this.x == other.x) && (this.y <
165: * other.y))
166: * <LI> 0 : this.x == other.x && this.y = other.y
167: * <LI> 1 : this.x > other.x || ((this.x == other.x) && (this.y > other.y))
168: *
169: * </UL>
170: * Note: This method assumes that ordinate values
171: * are valid numbers. NaN values are not handled correctly.
172: *
173: *@param o the <code>Coordinate</code> with which this <code>Coordinate</code>
174: * is being compared
175: *@return -1, zero, or 1 as this <code>Coordinate</code>
176: * is less than, equal to, or greater than the specified <code>Coordinate</code>
177: */
178: public int compareTo(Object o) {
179: Coordinate other = (Coordinate) o;
180:
181: if (x < other.x)
182: return -1;
183: if (x > other.x)
184: return 1;
185: if (y < other.y)
186: return -1;
187: if (y > other.y)
188: return 1;
189: return 0;
190: }
191:
192: /**
193: * Returns <code>true</code> if <code>other</code> has the same values for x,
194: * y and z.
195: *
196: *@param other a <code>Coordinate</code> with which to do the 3D comparison.
197: *@return <code>true</code> if <code>other</code> is a <code>Coordinate</code>
198: * with the same values for x, y and z.
199: */
200: public boolean equals3D(Coordinate other) {
201: return (x == other.x)
202: && (y == other.y)
203: && ((z == other.z) || (Double.isNaN(z) && Double
204: .isNaN(other.z)));
205: }
206:
207: /**
208: * Returns a <code>String</code> of the form <I>(x,y,z)</I> .
209: *
210: *@return a <code>String</code> of the form <I>(x,y,z)</I>
211: */
212: public String toString() {
213: return "(" + x + ", " + y + ", " + z + ")";
214: }
215:
216: public Object clone() {
217: try {
218: Coordinate coord = (Coordinate) super .clone();
219:
220: return coord; // return the clone
221: } catch (CloneNotSupportedException e) {
222: Assert
223: .shouldNeverReachHere("this shouldn't happen because this class is Cloneable");
224:
225: return null;
226: }
227: }
228:
229: public double distance(Coordinate p) {
230: double dx = x - p.x;
231: double dy = y - p.y;
232:
233: return Math.sqrt(dx * dx + dy * dy);
234: }
235:
236: public int hashCode() {
237: //Algorithm from Effective Java by Joshua Bloch [Jon Aquino]
238: int result = 17;
239: result = 37 * result + hashCode(x);
240: result = 37 * result + hashCode(y);
241: return result;
242: }
243:
244: /**
245: * Returns a hash code for a double value, using the algorithm from
246: * Joshua Bloch's book <i>Effective Java"</i>
247: */
248: public static int hashCode(double x) {
249: long f = Double.doubleToLongBits(x);
250: return (int) (f ^ (f >>> 32));
251: }
252:
253: /**
254: * Compares two {@link Coordinate}s, allowing for either a 2-dimensional
255: * or 3-dimensional comparison, and handling NaN values correctly.
256: */
257: public static class DimensionalComparator implements Comparator {
258: /**
259: * Compare two <code>double</code>s, allowing for NaN values.
260: * NaN is treated as being less than any valid number.
261: *
262: * @param a a <code>double</code>
263: * @param b a <code>double</code>
264: * @return -1, 0, or 1 depending on whether a is less than, equal to or greater than b
265: */
266: public static int compare(double a, double b) {
267: if (a < b)
268: return -1;
269: if (a > b)
270: return 1;
271:
272: if (Double.isNaN(a)) {
273: if (Double.isNaN(b))
274: return 0;
275: return -1;
276: }
277:
278: if (Double.isNaN(b))
279: return 1;
280: return 0;
281: }
282:
283: private int dimensionsToTest = 2;
284:
285: /**
286: * Creates a comparator for 2 dimensional coordinates.
287: */
288: public DimensionalComparator() {
289: this (2);
290: }
291:
292: /**
293: * Creates a comparator for 2 or 3 dimensional coordinates, depending
294: * on the value provided.
295: *
296: * @param dimensionLimit the number of dimensions to test
297: */
298: public DimensionalComparator(int dimensionsToTest) {
299: if (dimensionsToTest != 2 && dimensionsToTest != 3)
300: throw new IllegalArgumentException(
301: "only 2 or 3 dimensions may be specified");
302: this .dimensionsToTest = dimensionsToTest;
303: }
304:
305: /**
306: * Compares two {@link Coordinate}s along to the number of
307: * dimensions specified.
308: *
309: * @param o1 a {@link Coordinate}
310: * @param o2 a {link Coordinate}
311: * @return -1, 0, or 1 depending on whether o1 is less than,
312: * equal to, or greater than 02
313: *
314: */
315: public int compare(Object o1, Object o2) {
316: Coordinate c1 = (Coordinate) o1;
317: Coordinate c2 = (Coordinate) o2;
318:
319: int compX = compare(c1.x, c2.x);
320: if (compX != 0)
321: return compX;
322:
323: int compY = compare(c1.y, c2.y);
324: if (compY != 0)
325: return compY;
326:
327: if (dimensionsToTest <= 2)
328: return 0;
329:
330: int compZ = compare(c1.z, c2.z);
331: return compZ;
332: }
333: }
334:
335: }
|