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 < other.x || ((this.x == other.x) && (this.y < other.y)) 351 * <LI> 0 : this.x == other.x && this.y = other.y 352 * <LI> 1 : this.x > other.x || ((this.x == other.x) && (this.y > 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