1 /*
2  * Copyright (c) 2018 Vivid Solutions
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
8  * and the Eclipse Distribution License is available at
9  *
10  * http://www.eclipse.org/org/documents/edl-v10.php.
11  */
12 package org.locationtech.jts.geom;
13 
14 import java.io.Serializable;
15 import java.util.Comparator;
16 
17 import org.locationtech.jts.util.Assert;
18 import org.locationtech.jts.util.NumberUtil;
19 
20 
21 /**
22  * A lightweight class used to store coordinates on the 2-dimensional Cartesian plane.
23  * <p>
24  * It is distinct from {@link Point}, which is a subclass of {@link Geometry}.
25  * Unlike objects of type {@link Point} (which contain additional
26  * information such as an envelope, a precision model, and spatial reference
27  * system information), a <code>Coordinate</code> only contains ordinate values
28  * and accessor methods. </p>
29  * <p>
30  * <code>Coordinate</code>s are two-dimensional points, with an additional Z-ordinate.
31  * If an Z-ordinate value is not specified or not defined,
32  * constructed coordinates have a Z-ordinate of <code>NaN</code>
33  * (which is also the value of <code>NULL_ORDINATE</code>).
34  * The standard comparison functions ignore the Z-ordinate.
35  * Apart from the basic accessor functions, JTS supports
36  * only specific operations involving the Z-ordinate.</p>
37  * <p>
38  * Implementations may optionally support Z-ordinate and M-measure values
39  * as appropriate for a {@link CoordinateSequence}.
40  * Use of {@link #getZ()} and {@link #getM()}
41  * accessors, or {@link #getOrdinate(int)} are recommended.</p>
42  *
43  * @version 1.16
44  */
45 public class Coordinate implements Comparable<Coordinate>, Cloneable, Serializable {
46   private static final long serialVersionUID = 6683108902428366910L;
47 
48   /**
49    * The value used to indicate a null or missing ordinate value.
50    * In particular, used for the value of ordinates for dimensions
51    * greater than the defined dimension of a coordinate.
52    */
53   public static final double NULL_ORDINATE = Double.NaN;
54 
55   /** Standard ordinate index value for, where X is 0 */
56   public static final int X = 0;
57 
58   /** Standard ordinate index value for, where Y is 1 */
59   public static final int Y = 1;
60 
61   /**
62    * Standard ordinate index value for, where Z is 2.
63    *
64    * <p>This constant assumes XYZM coordinate sequence definition, please check this assumption
65    * using {@link #getDimension()} and {@link #getMeasures()} before use.
66    */
67   public static final int Z = 2;
68 
69   /**
70    * Standard ordinate index value for, where M is 3.
71    *
72    * <p>This constant assumes XYZM coordinate sequence definition, please check this assumption
73    * using {@link #getDimension()} and {@link #getMeasures()} before use.
74    */
75   public static final int M = 3;
76 
77   /**
78    * The x-ordinate.
79    */
80   public double x;
81 
82   /**
83    * The y-ordinate.
84    */
85   public double y;
86 
87   /**
88    * The z-ordinate.
89    * <p>
90    * Direct access to this field is discouraged; use {@link #getZ()}.
91    */
92   public double z;
93 
94   /**
95    *  Constructs a <code>Coordinate</code> at (x,y,z).
96    *
97    *@param  x  the x-ordinate
98    *@param  y  the y-ordinate
99    *@param  z  the z-ordinate
100    */
Coordinate(double x, double y, double z)101   public Coordinate(double x, double y, double z) {
102     this.x = x;
103     this.y = y;
104     this.z = z;
105   }
106 
107   /**
108    *  Constructs a <code>Coordinate</code> at (0,0,NaN).
109    */
Coordinate()110   public Coordinate() {
111     this(0.0, 0.0);
112   }
113 
114   /**
115    *  Constructs a <code>Coordinate</code> having the same (x,y,z) values as
116    *  <code>other</code>.
117    *
118    *@param  c  the <code>Coordinate</code> to copy.
119    */
Coordinate(Coordinate c)120   public Coordinate(Coordinate c) {
121     this(c.x, c.y, c.getZ());
122   }
123 
124   /**
125    *  Constructs a <code>Coordinate</code> at (x,y,NaN).
126    *
127    *@param  x  the x-value
128    *@param  y  the y-value
129    */
Coordinate(double x, double y)130   public Coordinate(double x, double y) {
131     this(x, y, NULL_ORDINATE);
132   }
133 
134   /**
135    *  Sets this <code>Coordinate</code>s (x,y,z) values to that of <code>other</code>.
136    *
137    *@param  other  the <code>Coordinate</code> to copy
138    */
setCoordinate(Coordinate other)139   public void setCoordinate(Coordinate other) {
140     x = other.x;
141     y = other.y;
142     z = other.getZ();
143   }
144 
145   /**
146    *  Retrieves the value of the X ordinate.
147    *
148    *  @return the value of the X ordinate
149    */
getX()150   public double getX() {
151     return x;
152   }
153 
154   /**
155    * Sets the X ordinate value.
156    *
157    * @param x the value to set as X
158    */
setX(double x)159   public void setX(double x) {
160     this.x = x;
161   }
162 
163   /**
164    *  Retrieves the value of the Y ordinate.
165    *
166    *  @return the value of the Y ordinate
167    */
getY()168   public double getY() {
169       return y;
170   }
171 
172   /**
173    * Sets the Y ordinate value.
174    *
175    * @param y the value to set as Y
176    */
setY(double y)177   public void setY(double y) {
178     this.y = y;
179   }
180 
181   /**
182    *  Retrieves the value of the Z ordinate, if present.
183    *  If no Z value is present returns <tt>NaN</tt>.
184    *
185    *  @return the value of the Z ordinate, or <tt>NaN</tt>
186    */
getZ()187   public double getZ() {
188       return z;
189   }
190 
191   /**
192    * Sets the Z ordinate value.
193    *
194    * @param z the value to set as Z
195    */
setZ(double z)196   public void setZ(double z) {
197     this.z = z;
198   }
199 
200   /**
201    *  Retrieves the value of the measure, if present.
202    *  If no measure value is present returns <tt>NaN</tt>.
203    *
204    *  @return the value of the measure, or <tt>NaN</tt>
205    */
getM()206   public double getM() {
207     return Double.NaN;
208   }
209 
210   /**
211    * Sets the measure value, if supported.
212    *
213    * @param m the value to set as M
214    */
setM(double m)215   public void setM(double m) {
216     throw new IllegalArgumentException("Invalid ordinate index: " + M);
217   }
218 
219   /**
220    * Gets the ordinate value for the given index.
221    *
222    * The base implementation supports values for the index are
223    * {@link X}, {@link Y}, and {@link Z}.
224    *
225    * @param ordinateIndex the ordinate index
226    * @return the value of the ordinate
227    * @throws IllegalArgumentException if the index is not valid
228    */
getOrdinate(int ordinateIndex)229   public double getOrdinate(int ordinateIndex)
230   {
231     switch (ordinateIndex) {
232     case X: return x;
233     case Y: return y;
234     case Z: return getZ(); // sure to delegate to subclass rather than offer direct field access
235     }
236     throw new IllegalArgumentException("Invalid ordinate index: " + ordinateIndex);
237   }
238 
239   /**
240    * Sets the ordinate for the given index
241    * to a given value.
242    *
243    * The base implementation supported values for the index are
244    * {@link X}, {@link Y}, and {@link Z}.
245    *
246    * @param ordinateIndex the ordinate index
247    * @param value the value to set
248    * @throws IllegalArgumentException if the index is not valid
249    */
setOrdinate(int ordinateIndex, double value)250   public void setOrdinate(int ordinateIndex, double value)
251   {
252     switch (ordinateIndex) {
253       case X:
254         x = value;
255         break;
256       case Y:
257         y = value;
258         break;
259       case Z:
260         setZ(value); // delegate to subclass rather than offer direct field access
261         break;
262       default:
263         throw new IllegalArgumentException("Invalid ordinate index: " + ordinateIndex);
264     }
265   }
266 
267   /**
268    *  Returns whether the planar projections of the two <code>Coordinate</code>s
269    *  are equal.
270    *
271    *@param  other  a <code>Coordinate</code> with which to do the 2D comparison.
272    *@return        <code>true</code> if the x- and y-coordinates are equal; the
273    *      z-coordinates do not have to be equal.
274    */
equals2D(Coordinate other)275   public boolean equals2D(Coordinate other) {
276     if (x != other.x) {
277       return false;
278     }
279     if (y != other.y) {
280       return false;
281     }
282     return true;
283   }
284 
285   /**
286    * Tests if another Coordinate has the same values for the X and Y ordinates,
287    * within a specified tolerance value.
288    * The Z ordinate is ignored.
289    *
290    *@param c a <code>Coordinate</code> with which to do the 2D comparison.
291    *@param tolerance the tolerance value to use
292    *@return true if <code>other</code> is a <code>Coordinate</code>
293    *      with the same values for X and Y.
294    */
equals2D(Coordinate c, double tolerance)295   public boolean equals2D(Coordinate c, double tolerance){
296     if (! NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) {
297       return false;
298     }
299     if (! NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) {
300       return false;
301     }
302     return true;
303   }
304 
305   /**
306    * Tests if another coordinate has the same values for the X, Y and Z ordinates.
307    *
308    *@param other a <code>Coordinate</code> with which to do the 3D comparison.
309    *@return true if <code>other</code> is a <code>Coordinate</code>
310    *      with the same values for X, Y and Z.
311    */
equals3D(Coordinate other)312   public boolean equals3D(Coordinate other) {
313     return (x == other.x) && (y == other.y) &&
314                ((getZ() == other.getZ()) ||
315                (Double.isNaN(getZ()) && Double.isNaN(other.getZ())));
316   }
317 
318   /**
319    * Tests if another coordinate has the same value for Z, within a tolerance.
320    *
321    * @param c a coordinate
322    * @param tolerance the tolerance value
323    * @return true if the Z ordinates are within the given tolerance
324    */
equalInZ(Coordinate c, double tolerance)325   public boolean equalInZ(Coordinate c, double tolerance){
326     return NumberUtil.equalsWithTolerance(this.getZ(), c.getZ(), tolerance);
327   }
328 
329   /**
330    *  Returns <code>true</code> if <code>other</code> has the same values for
331    *  the x and y ordinates.
332    *  Since Coordinates are 2.5D, this routine ignores the z value when making the comparison.
333    *
334    *@param  other  a <code>Coordinate</code> with which to do the comparison.
335    *@return        <code>true</code> if <code>other</code> is a <code>Coordinate</code>
336    *      with the same values for the x and y ordinates.
337    */
equals(Object other)338   public boolean equals(Object other) {
339     if (!(other instanceof Coordinate)) {
340       return false;
341     }
342     return equals2D((Coordinate) other);
343   }
344 
345   /**
346    *  Compares this {@link Coordinate} with the specified {@link Coordinate} for order.
347    *  This method ignores the z value when making the comparison.
348    *  Returns:
349    *  <UL>
350    *    <LI> -1 : this.x &lt; other.x || ((this.x == other.x) &amp;&amp; (this.y &lt; other.y))
351    *    <LI> 0 : this.x == other.x &amp;&amp; this.y = other.y
352    *    <LI> 1 : this.x &gt; other.x || ((this.x == other.x) &amp;&amp; (this.y &gt; other.y))
353    *
354    *  </UL>
355    *  Note: This method assumes that ordinate values
356    * are valid numbers.  NaN values are not handled correctly.
357    *
358    *@param  o  the <code>Coordinate</code> with which this <code>Coordinate</code>
359    *      is being compared
360    *@return    -1, zero, or 1 as this <code>Coordinate</code>
361    *      is less than, equal to, or greater than the specified <code>Coordinate</code>
362    */
compareTo(Coordinate o)363   public int compareTo(Coordinate o) {
364     Coordinate other = (Coordinate) o;
365 
366     if (x < other.x) return -1;
367     if (x > other.x) return 1;
368     if (y < other.y) return -1;
369     if (y > other.y) return 1;
370     return 0;
371   }
372 
373   /**
374    *  Returns a <code>String</code> of the form <I>(x,y,z)</I> .
375    *
376    *@return    a <code>String</code> of the form <I>(x,y,z)</I>
377    */
toString()378   public String toString() {
379     return "(" + x + ", " + y + ", " + getZ() + ")";
380   }
381 
clone()382   public Object clone() {
383     try {
384       Coordinate coord = (Coordinate) super.clone();
385 
386       return coord; // return the clone
387     } catch (CloneNotSupportedException e) {
388       Assert.shouldNeverReachHere(
389           "this shouldn't happen because this class is Cloneable");
390 
391       return null;
392     }
393   }
394 
395   /**
396    * Creates a copy of this Coordinate.
397    *
398    * @return a copy of this coordinate.
399    */
copy()400   public Coordinate copy() {
401     return new Coordinate(this);
402   }
403 
404   /**
405    * Create a new Coordinate of the same type as this Coordinate, but with no values.
406    *
407    * @return a new Coordinate
408    */
create()409   public Coordinate create() {
410       return new Coordinate();
411   }
412 
413   /**
414    * Computes the 2-dimensional Euclidean distance to another location.
415    * The Z-ordinate is ignored.
416    *
417    * @param c a point
418    * @return the 2-dimensional Euclidean distance between the locations
419    */
distance(Coordinate c)420   public double distance(Coordinate c) {
421     double dx = x - c.x;
422     double dy = y - c.y;
423     return Math.sqrt(dx * dx + dy * dy);
424   }
425 
426   /**
427    * Computes the 3-dimensional Euclidean distance to another location.
428    *
429    * @param c a coordinate
430    * @return the 3-dimensional Euclidean distance between the locations
431    */
distance3D(Coordinate c)432   public double distance3D(Coordinate c) {
433     double dx = x - c.x;
434     double dy = y - c.y;
435     double dz = getZ() - c.getZ();
436     return Math.sqrt(dx * dx + dy * dy + dz * dz);
437   }
438 
439   /**
440    * Gets a hashcode for this coordinate.
441    *
442    * @return a hashcode for this coordinate
443    */
hashCode()444   public int hashCode() {
445     //Algorithm from Effective Java by Joshua Bloch [Jon Aquino]
446     int result = 17;
447     result = 37 * result + hashCode(x);
448     result = 37 * result + hashCode(y);
449     return result;
450   }
451 
452   /**
453    * Computes a hash code for a double value, using the algorithm from
454    * Joshua Bloch's book <i>Effective Java"</i>
455    *
456    * @param x the value to compute for
457    * @return a hashcode for x
458    */
hashCode(double x)459   public static int hashCode(double x) {
460     long f = Double.doubleToLongBits(x);
461     return (int)(f^(f>>>32));
462   }
463 
464 
465   /**
466    * Compares two {@link Coordinate}s, allowing for either a 2-dimensional
467    * or 3-dimensional comparison, and handling NaN values correctly.
468    */
469   public static class DimensionalComparator
470       implements Comparator<Coordinate>
471   {
472     /**
473      * Compare two <code>double</code>s, allowing for NaN values.
474      * NaN is treated as being less than any valid number.
475      *
476      * @param a a <code>double</code>
477      * @param b a <code>double</code>
478      * @return -1, 0, or 1 depending on whether a is less than, equal to or greater than b
479      */
compare(double a, double b)480     public static int compare(double a, double b)
481     {
482       if (a < b) return -1;
483       if (a > b) return 1;
484 
485       if (Double.isNaN(a)) {
486         if (Double.isNaN(b)) return 0;
487         return -1;
488       }
489 
490       if (Double.isNaN(b)) return 1;
491       return 0;
492     }
493 
494     private int dimensionsToTest = 2;
495 
496     /**
497      * Creates a comparator for 2 dimensional coordinates.
498      */
DimensionalComparator()499     public DimensionalComparator()
500     {
501       this(2);
502     }
503 
504     /**
505      * Creates a comparator for 2 or 3 dimensional coordinates, depending
506      * on the value provided.
507      *
508      * @param dimensionsToTest the number of dimensions to test
509      */
DimensionalComparator(int dimensionsToTest)510     public DimensionalComparator(int dimensionsToTest)
511     {
512       if (dimensionsToTest != 2 && dimensionsToTest != 3)
513         throw new IllegalArgumentException("only 2 or 3 dimensions may be specified");
514       this.dimensionsToTest = dimensionsToTest;
515     }
516 
517     /**
518      * Compares two {@link Coordinate}s along to the number of
519      * dimensions specified.
520      *
521      * @param o1 a {@link Coordinate}
522      * @param o2 a {link Coordinate}
523      * @return -1, 0, or 1 depending on whether o1 is less than,
524      * equal to, or greater than 02
525      *
526      */
compare(Coordinate c1, Coordinate c2)527     public int compare(Coordinate c1, Coordinate c2)
528     {
529       int compX = compare(c1.x, c2.x);
530       if (compX != 0) return compX;
531 
532       int compY = compare(c1.y, c2.y);
533       if (compY != 0) return compY;
534 
535       if (dimensionsToTest <= 2) return 0;
536 
537       int compZ = compare(c1.getZ(), c2.getZ());
538       return compZ;
539     }
540   }
541 
542 }
543