1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.location; 18 19 import static java.util.concurrent.TimeUnit.NANOSECONDS; 20 21 import android.annotation.FloatRange; 22 import android.annotation.IntDef; 23 import android.annotation.IntRange; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.SystemApi; 27 import android.os.Bundle; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.SystemClock; 31 import android.util.Printer; 32 import android.util.TimeUtils; 33 34 import com.android.internal.util.Preconditions; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.text.DecimalFormat; 39 import java.util.Locale; 40 import java.util.Objects; 41 import java.util.StringTokenizer; 42 43 /** 44 * A data class representing a geographic location. A location consists of a latitude, longitude, 45 * timestamp, accuracy, and other information such as bearing, altitude and velocity. 46 * 47 * <p>All locations generated through {@link LocationManager} are guaranteed to have a valid 48 * latitude, longitude, timestamp (both Unix epoch time and elapsed realtime since boot), and 49 * accuracy. All other parameters are optional. 50 * 51 * <p class="note">Note that Android provides the ability for applications to submit "mock" or faked 52 * locations through {@link LocationManager}, and that these locations can then be received by 53 * applications using LocationManager to obtain location information. These locations can be 54 * identified via the {@link #isMock()} API. Applications that wish to determine if a given location 55 * represents the best estimate of the real position of the device as opposed to a fake location 56 * coming from another application or the user should use this API. Keep in mind that the user may 57 * have a good reason for mocking their location, and thus apps should generally reject mock 58 * locations only when it is essential to their use case that only real locations are accepted. 59 */ 60 public class Location implements Parcelable { 61 62 /** 63 * Constant used to specify formatting of a latitude or longitude in the form "[+-]DDD.DDDDD 64 * where D indicates degrees. 65 */ 66 public static final int FORMAT_DEGREES = 0; 67 68 /** 69 * Constant used to specify formatting of a latitude or longitude in the form "[+-]DDD:MM.MMMMM" 70 * where D indicates degrees and M indicates minutes of arc (1 minute = 1/60th of a degree). 71 */ 72 public static final int FORMAT_MINUTES = 1; 73 74 /** 75 * Constant used to specify formatting of a latitude or longitude in the form "DDD:MM:SS.SSSSS" 76 * where D indicates degrees, M indicates minutes of arc, and S indicates seconds of arc (1 77 * minute = 1/60th of a degree, 1 second = 1/3600th of a degree). 78 */ 79 public static final int FORMAT_SECONDS = 2; 80 81 /** @hide */ 82 @Retention(RetentionPolicy.SOURCE) 83 @IntDef({FORMAT_DEGREES, FORMAT_MINUTES, FORMAT_SECONDS}) 84 public @interface Format {} 85 86 /** 87 * Bundle key for a version of the location containing no GPS data. 88 * 89 * @hide 90 * @deprecated As of Android R, this extra is longer in use, since it is not necessary to keep 91 * gps locations separate from other locations for coarsening. Providers that do not need to 92 * support platforms below Android R should not use this constant. 93 */ 94 @SystemApi 95 @Deprecated 96 public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; 97 98 private static final int HAS_ALTITUDE_MASK = 1 << 0; 99 private static final int HAS_SPEED_MASK = 1 << 1; 100 private static final int HAS_BEARING_MASK = 1 << 2; 101 private static final int HAS_HORIZONTAL_ACCURACY_MASK = 1 << 3; 102 private static final int HAS_MOCK_PROVIDER_MASK = 1 << 4; 103 private static final int HAS_ALTITUDE_ACCURACY_MASK = 1 << 5; 104 private static final int HAS_SPEED_ACCURACY_MASK = 1 << 6; 105 private static final int HAS_BEARING_ACCURACY_MASK = 1 << 7; 106 private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK = 1 << 8; 107 private static final int HAS_MSL_ALTITUDE_MASK = 1 << 9; 108 private static final int HAS_MSL_ALTITUDE_ACCURACY_MASK = 1 << 10; 109 110 // Cached data to make bearing/distance computations more efficient for the case 111 // where distanceTo and bearingTo are called in sequence. Assume this typically happens 112 // on the same thread for caching purposes. 113 private static final ThreadLocal<BearingDistanceCache> sBearingDistanceCache = 114 ThreadLocal.withInitial(BearingDistanceCache::new); 115 116 // A bitmask of fields present in this object (see HAS_* constants defined above). 117 private int mFieldsMask = 0; 118 119 private @Nullable String mProvider; 120 private long mTimeMs; 121 private long mElapsedRealtimeNs; 122 private double mElapsedRealtimeUncertaintyNs; 123 private double mLatitudeDegrees; 124 private double mLongitudeDegrees; 125 private float mHorizontalAccuracyMeters; 126 private double mAltitudeMeters; 127 private float mAltitudeAccuracyMeters; 128 private float mSpeedMetersPerSecond; 129 private float mSpeedAccuracyMetersPerSecond; 130 private float mBearingDegrees; 131 private float mBearingAccuracyDegrees; 132 private double mMslAltitudeMeters; 133 private float mMslAltitudeAccuracyMeters; 134 135 private Bundle mExtras = null; 136 137 /** 138 * Constructs a new location with a named provider. By default all values are zero, and no 139 * optional values are present. 140 * 141 * @param provider the location provider name associated with this location 142 */ Location(@ullable String provider)143 public Location(@Nullable String provider) { 144 mProvider = provider; 145 } 146 147 /** 148 * Constructs a new location copied from the given location. 149 */ Location(@onNull Location location)150 public Location(@NonNull Location location) { 151 set(location); 152 } 153 154 /** 155 * Turns this location into a copy of the given location. 156 */ set(@onNull Location location)157 public void set(@NonNull Location location) { 158 mFieldsMask = location.mFieldsMask; 159 mProvider = location.mProvider; 160 mTimeMs = location.mTimeMs; 161 mElapsedRealtimeNs = location.mElapsedRealtimeNs; 162 mElapsedRealtimeUncertaintyNs = location.mElapsedRealtimeUncertaintyNs; 163 mLatitudeDegrees = location.mLatitudeDegrees; 164 mLongitudeDegrees = location.mLongitudeDegrees; 165 mHorizontalAccuracyMeters = location.mHorizontalAccuracyMeters; 166 mAltitudeMeters = location.mAltitudeMeters; 167 mAltitudeAccuracyMeters = location.mAltitudeAccuracyMeters; 168 mSpeedMetersPerSecond = location.mSpeedMetersPerSecond; 169 mSpeedAccuracyMetersPerSecond = location.mSpeedAccuracyMetersPerSecond; 170 mBearingDegrees = location.mBearingDegrees; 171 mBearingAccuracyDegrees = location.mBearingAccuracyDegrees; 172 mMslAltitudeMeters = location.mMslAltitudeMeters; 173 mMslAltitudeAccuracyMeters = location.mMslAltitudeAccuracyMeters; 174 mExtras = (location.mExtras == null) ? null : new Bundle(location.mExtras); 175 } 176 177 /** 178 * Sets the provider to null, removes all optional fields, and sets the values of all other 179 * fields to zero. 180 */ reset()181 public void reset() { 182 mProvider = null; 183 mTimeMs = 0; 184 mElapsedRealtimeNs = 0; 185 mElapsedRealtimeUncertaintyNs = 0.0; 186 mFieldsMask = 0; 187 mLatitudeDegrees = 0; 188 mLongitudeDegrees = 0; 189 mAltitudeMeters = 0; 190 mSpeedMetersPerSecond = 0; 191 mBearingDegrees = 0; 192 mHorizontalAccuracyMeters = 0; 193 mAltitudeAccuracyMeters = 0; 194 mSpeedAccuracyMetersPerSecond = 0; 195 mBearingAccuracyDegrees = 0; 196 mMslAltitudeMeters = 0; 197 mMslAltitudeAccuracyMeters = 0; 198 mExtras = null; 199 } 200 201 /** 202 * Returns the approximate distance in meters between this location and the given location. 203 * Distance is defined using the WGS84 ellipsoid. 204 * 205 * @param dest the destination location 206 * @return the approximate distance in meters 207 */ distanceTo(@onNull Location dest)208 public @FloatRange(from = 0.0) float distanceTo(@NonNull Location dest) { 209 BearingDistanceCache cache = sBearingDistanceCache.get(); 210 // See if we already have the result 211 if (mLatitudeDegrees != cache.mLat1 || mLongitudeDegrees != cache.mLon1 212 || dest.mLatitudeDegrees != cache.mLat2 || dest.mLongitudeDegrees != cache.mLon2) { 213 computeDistanceAndBearing(mLatitudeDegrees, mLongitudeDegrees, 214 dest.mLatitudeDegrees, dest.mLongitudeDegrees, cache); 215 } 216 return cache.mDistance; 217 } 218 219 /** 220 * Returns the approximate initial bearing in degrees east of true north when traveling along 221 * the shortest path between this location and the given location. The shortest path is defined 222 * using the WGS84 ellipsoid. Locations that are (nearly) antipodal may produce meaningless 223 * results. 224 * 225 * @param dest the destination location 226 * @return the initial bearing in degrees 227 */ bearingTo(@onNull Location dest)228 public float bearingTo(@NonNull Location dest) { 229 BearingDistanceCache cache = sBearingDistanceCache.get(); 230 // See if we already have the result 231 if (mLatitudeDegrees != cache.mLat1 || mLongitudeDegrees != cache.mLon1 232 || dest.mLatitudeDegrees != cache.mLat2 || dest.mLongitudeDegrees != cache.mLon2) { 233 computeDistanceAndBearing(mLatitudeDegrees, mLongitudeDegrees, 234 dest.mLatitudeDegrees, dest.mLongitudeDegrees, cache); 235 } 236 return cache.mInitialBearing; 237 } 238 239 /** 240 * Returns the name of the provider associated with this location. 241 * 242 * @return the name of the provider 243 */ getProvider()244 public @Nullable String getProvider() { 245 return mProvider; 246 } 247 248 /** 249 * Sets the name of the provider associated with this location 250 * 251 * @param provider the name of the provider 252 */ setProvider(@ullable String provider)253 public void setProvider(@Nullable String provider) { 254 mProvider = provider; 255 } 256 257 /** 258 * Returns the Unix epoch time of this location fix, in milliseconds since the start of the Unix 259 * epoch (00:00:00 January 1, 1970 UTC). 260 * 261 * <p>There is no guarantee that different locations have times set from the same clock. 262 * Locations derived from the {@link LocationManager#GPS_PROVIDER} are guaranteed to have their 263 * time originate from the clock in use by the satellite constellation that provided the fix. 264 * Locations derived from other providers may use any clock to set their time, though it is most 265 * common to use the device's Unix epoch time system clock (which may be incorrect). 266 * 267 * <p>Note that the device's Unix epoch time system clock is not monotonic; it can jump forwards 268 * or backwards unpredictably and may be changed at any time by the user, so this time should 269 * not be used to order or compare locations. Prefer {@link #getElapsedRealtimeNanos} for that 270 * purpose, as the elapsed realtime clock is guaranteed to be monotonic. 271 * 272 * <p>On the other hand, this method may be useful for presenting a human-readable time to the 273 * user, or as a heuristic for comparing location fixes across reboot or across devices. 274 * 275 * <p>All locations generated by the {@link LocationManager} are guaranteed to have this time 276 * set, however remember that the device's system clock may have changed since the location was 277 * generated. 278 * 279 * @return the Unix epoch time of this location 280 */ getTime()281 public @IntRange(from = 0) long getTime() { 282 return mTimeMs; 283 } 284 285 /** 286 * Sets the Unix epoch time of this location fix, in milliseconds since the start of the Unix 287 * epoch (00:00:00 January 1 1970 UTC). 288 * 289 * @param timeMs the Unix epoch time of this location 290 */ setTime(@ntRangefrom = 0) long timeMs)291 public void setTime(@IntRange(from = 0) long timeMs) { 292 mTimeMs = timeMs; 293 } 294 295 /** 296 * Return the time of this fix in nanoseconds of elapsed realtime since system boot. 297 * 298 * <p>This value can be compared with {@link android.os.SystemClock#elapsedRealtimeNanos} to 299 * reliably order or compare locations. This is reliable because elapsed realtime is guaranteed 300 * to be monotonic and continues to increment even when the system is in deep sleep (unlike 301 * {@link #getTime}). However, since elapsed realtime is with reference to system boot, it does 302 * not make sense to use this value to order or compare locations across boot cycles or devices. 303 * 304 * <p>All locations generated by the {@link LocationManager} are guaranteed to have a valid 305 * elapsed realtime set. 306 * 307 * @return elapsed realtime of this location in nanoseconds 308 */ getElapsedRealtimeNanos()309 public @IntRange(from = 0) long getElapsedRealtimeNanos() { 310 return mElapsedRealtimeNs; 311 } 312 313 /** 314 * Return the time of this fix in milliseconds of elapsed realtime since system boot. 315 * 316 * @return elapsed realtime of this location in milliseconds 317 * @see #getElapsedRealtimeNanos() 318 */ getElapsedRealtimeMillis()319 public @IntRange(from = 0) long getElapsedRealtimeMillis() { 320 return NANOSECONDS.toMillis(mElapsedRealtimeNs); 321 } 322 323 /** 324 * A convenience methods that returns the age of this location in milliseconds with respect to 325 * the current elapsed realtime. 326 * 327 * @return age of this location in milliseconds 328 */ getElapsedRealtimeAgeMillis()329 public @IntRange(from = 0) long getElapsedRealtimeAgeMillis() { 330 return getElapsedRealtimeAgeMillis(SystemClock.elapsedRealtime()); 331 } 332 333 /** 334 * A convenience method that returns the age of this location with respect to the given 335 * reference elapsed realtime. 336 * 337 * @param referenceRealtimeMs reference realtime in milliseconds 338 * @return age of this location in milliseconds 339 */ getElapsedRealtimeAgeMillis( @ntRangefrom = 0) long referenceRealtimeMs)340 public long getElapsedRealtimeAgeMillis( 341 @IntRange(from = 0) long referenceRealtimeMs) { 342 return referenceRealtimeMs - getElapsedRealtimeMillis(); 343 } 344 345 /** 346 * Set the time of this location in nanoseconds of elapsed realtime since system boot. 347 * 348 * @param elapsedRealtimeNs elapsed realtime in nanoseconds 349 */ setElapsedRealtimeNanos(@ntRangefrom = 0) long elapsedRealtimeNs)350 public void setElapsedRealtimeNanos(@IntRange(from = 0) long elapsedRealtimeNs) { 351 mElapsedRealtimeNs = elapsedRealtimeNs; 352 } 353 354 /** 355 * Get the uncertainty in nanoseconds of the precision of {@link #getElapsedRealtimeNanos()} at 356 * the 68th percentile confidence level. This means that there is 68% chance that the true 357 * elapsed realtime of this location is within {@link #getElapsedRealtimeNanos()} +/- this 358 * uncertainty. 359 * 360 * <p>This is only valid if {@link #hasElapsedRealtimeUncertaintyNanos()} is true. 361 * 362 * @return uncertainty in nanoseconds of the elapsed realtime of this location 363 */ getElapsedRealtimeUncertaintyNanos()364 public @FloatRange(from = 0.0) double getElapsedRealtimeUncertaintyNanos() { 365 return mElapsedRealtimeUncertaintyNs; 366 } 367 368 /** 369 * Sets the uncertainty in nanoseconds of the precision of the elapsed realtime timestamp at a 370 * 68% confidence level. 371 * 372 * @param elapsedRealtimeUncertaintyNs uncertainty in nanoseconds of the elapsed realtime of 373 * this location 374 */ setElapsedRealtimeUncertaintyNanos( @loatRangefrom = 0.0) double elapsedRealtimeUncertaintyNs)375 public void setElapsedRealtimeUncertaintyNanos( 376 @FloatRange(from = 0.0) double elapsedRealtimeUncertaintyNs) { 377 mElapsedRealtimeUncertaintyNs = elapsedRealtimeUncertaintyNs; 378 mFieldsMask |= HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK; 379 } 380 381 /** 382 * True if this location has an elapsed realtime uncertainty, false otherwise. 383 */ hasElapsedRealtimeUncertaintyNanos()384 public boolean hasElapsedRealtimeUncertaintyNanos() { 385 return (mFieldsMask & HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK) != 0; 386 } 387 388 /** 389 * Removes the elapsed realtime uncertainty from this location. 390 */ removeElapsedRealtimeUncertaintyNanos()391 public void removeElapsedRealtimeUncertaintyNanos() { 392 mFieldsMask &= ~HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK; 393 } 394 395 /** 396 * Get the latitude in degrees. All locations generated by the {@link LocationManager} will have 397 * a valid latitude. 398 * 399 * @return latitude of this location 400 */ getLatitude()401 public @FloatRange(from = -90.0, to = 90.0) double getLatitude() { 402 return mLatitudeDegrees; 403 } 404 405 /** 406 * Set the latitude of this location. 407 * 408 * @param latitudeDegrees latitude in degrees 409 */ setLatitude(@loatRangefrom = -90.0, to = 90.0) double latitudeDegrees)410 public void setLatitude(@FloatRange(from = -90.0, to = 90.0) double latitudeDegrees) { 411 mLatitudeDegrees = latitudeDegrees; 412 } 413 414 /** 415 * Get the longitude in degrees. All locations generated by the {@link LocationManager} will 416 * have a valid longitude. 417 * 418 * @return longitude of this location 419 */ getLongitude()420 public @FloatRange(from = -180.0, to = 180.0) double getLongitude() { 421 return mLongitudeDegrees; 422 } 423 424 /** 425 * Set the longitude of this location. 426 * 427 * @param longitudeDegrees longitude in degrees 428 */ setLongitude(@loatRangefrom = -180.0, to = 180.0) double longitudeDegrees)429 public void setLongitude(@FloatRange(from = -180.0, to = 180.0) double longitudeDegrees) { 430 mLongitudeDegrees = longitudeDegrees; 431 } 432 433 /** 434 * Returns the estimated horizontal accuracy radius in meters of this location at the 68th 435 * percentile confidence level. This means that there is a 68% chance that the true location of 436 * the device is within a distance of this uncertainty of the reported location. Another way of 437 * putting this is that if a circle with a radius equal to this accuracy is drawn around the 438 * reported location, there is a 68% chance that the true location falls within this circle. 439 * This accuracy value is only valid for horizontal positioning, and not vertical positioning. 440 * 441 * <p>This is only valid if {@link #hasAccuracy()} is true. All locations generated by the 442 * {@link LocationManager} include horizontal accuracy. 443 * 444 * @return horizontal accuracy of this location 445 */ getAccuracy()446 public @FloatRange(from = 0.0) float getAccuracy() { 447 return mHorizontalAccuracyMeters; 448 } 449 450 /** 451 * Set the horizontal accuracy in meters of this location. 452 * 453 * @param horizontalAccuracyMeters horizontal altitude in meters 454 */ setAccuracy(@loatRangefrom = 0.0) float horizontalAccuracyMeters)455 public void setAccuracy(@FloatRange(from = 0.0) float horizontalAccuracyMeters) { 456 mHorizontalAccuracyMeters = horizontalAccuracyMeters; 457 mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK; 458 } 459 460 /** 461 * Returns true if this location has a horizontal accuracy, false otherwise. 462 */ hasAccuracy()463 public boolean hasAccuracy() { 464 return (mFieldsMask & HAS_HORIZONTAL_ACCURACY_MASK) != 0; 465 } 466 467 /** 468 * Remove the horizontal accuracy from this location. 469 */ removeAccuracy()470 public void removeAccuracy() { 471 mFieldsMask &= ~HAS_HORIZONTAL_ACCURACY_MASK; 472 } 473 474 /** 475 * The altitude of this location in meters above the WGS84 reference ellipsoid. 476 * 477 * <p>This is only valid if {@link #hasAltitude()} is true. 478 * 479 * @return altitude of this location 480 */ getAltitude()481 public @FloatRange double getAltitude() { 482 return mAltitudeMeters; 483 } 484 485 /** 486 * Set the altitude of this location in meters above the WGS84 reference ellipsoid. 487 * 488 * @param altitudeMeters altitude in meters 489 */ setAltitude(@loatRange double altitudeMeters)490 public void setAltitude(@FloatRange double altitudeMeters) { 491 mAltitudeMeters = altitudeMeters; 492 mFieldsMask |= HAS_ALTITUDE_MASK; 493 } 494 495 /** 496 * Returns true if this location has an altitude, false otherwise. 497 */ hasAltitude()498 public boolean hasAltitude() { 499 return (mFieldsMask & HAS_ALTITUDE_MASK) != 0; 500 } 501 502 /** 503 * Removes the altitude from this location. 504 */ removeAltitude()505 public void removeAltitude() { 506 mFieldsMask &= ~HAS_ALTITUDE_MASK; 507 } 508 509 /** 510 * Returns the estimated altitude accuracy in meters of this location at the 68th percentile 511 * confidence level. This means that there is 68% chance that the true altitude of this location 512 * falls within {@link #getAltitude()} ()} +/- this uncertainty. 513 * 514 * <p>This is only valid if {@link #hasVerticalAccuracy()} is true. 515 * 516 * @return vertical accuracy of this location 517 */ getVerticalAccuracyMeters()518 public @FloatRange(from = 0.0) float getVerticalAccuracyMeters() { 519 return mAltitudeAccuracyMeters; 520 } 521 522 /** 523 * Set the altitude accuracy of this location in meters. 524 * 525 * @param altitudeAccuracyMeters altitude accuracy in meters 526 */ setVerticalAccuracyMeters(@loatRangefrom = 0.0) float altitudeAccuracyMeters)527 public void setVerticalAccuracyMeters(@FloatRange(from = 0.0) float altitudeAccuracyMeters) { 528 mAltitudeAccuracyMeters = altitudeAccuracyMeters; 529 mFieldsMask |= HAS_ALTITUDE_ACCURACY_MASK; 530 } 531 532 /** 533 * Returns true if this location has a vertical accuracy, false otherwise. 534 */ hasVerticalAccuracy()535 public boolean hasVerticalAccuracy() { 536 return (mFieldsMask & HAS_ALTITUDE_ACCURACY_MASK) != 0; 537 } 538 539 /** 540 * Remove the vertical accuracy from this location. 541 */ removeVerticalAccuracy()542 public void removeVerticalAccuracy() { 543 mFieldsMask &= ~HAS_ALTITUDE_ACCURACY_MASK; 544 } 545 546 /** 547 * Returns the speed at the time of this location in meters per second. Note that the speed 548 * returned here may be more accurate than would be obtained simply by calculating 549 * {@code distance / time} for sequential positions, such as if the Doppler measurements from 550 * GNSS satellites are taken into account. 551 * 552 * <p>This is only valid if {@link #hasSpeed()} is true. 553 * 554 * @return speed at the time of this location 555 */ getSpeed()556 public @FloatRange(from = 0.0) float getSpeed() { 557 return mSpeedMetersPerSecond; 558 } 559 560 /** 561 * Set the speed at the time of this location, in meters per second. 562 * 563 * @param speedMetersPerSecond speed in meters per second 564 */ setSpeed(@loatRangefrom = 0.0) float speedMetersPerSecond)565 public void setSpeed(@FloatRange(from = 0.0) float speedMetersPerSecond) { 566 mSpeedMetersPerSecond = speedMetersPerSecond; 567 mFieldsMask |= HAS_SPEED_MASK; 568 } 569 570 /** 571 * True if this location has a speed, false otherwise. 572 */ hasSpeed()573 public boolean hasSpeed() { 574 return (mFieldsMask & HAS_SPEED_MASK) != 0; 575 } 576 577 /** 578 * Remove the speed from this location. 579 */ removeSpeed()580 public void removeSpeed() { 581 mFieldsMask &= ~HAS_SPEED_MASK; 582 } 583 584 /** 585 * Returns the estimated speed accuracy in meters per second of this location at the 68th 586 * percentile confidence level. This means that there is 68% chance that the true speed at the 587 * time of this location falls within {@link #getSpeed()} ()} +/- this uncertainty. 588 * 589 * <p>This is only valid if {@link #hasSpeedAccuracy()} is true. 590 * 591 * @return vertical accuracy of this location 592 */ getSpeedAccuracyMetersPerSecond()593 public @FloatRange(from = 0.0) float getSpeedAccuracyMetersPerSecond() { 594 return mSpeedAccuracyMetersPerSecond; 595 } 596 597 /** 598 * Set the speed accuracy of this location in meters per second. 599 * 600 * @param speedAccuracyMeterPerSecond speed accuracy in meters per second 601 */ setSpeedAccuracyMetersPerSecond( @loatRangefrom = 0.0) float speedAccuracyMeterPerSecond)602 public void setSpeedAccuracyMetersPerSecond( 603 @FloatRange(from = 0.0) float speedAccuracyMeterPerSecond) { 604 mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond; 605 mFieldsMask |= HAS_SPEED_ACCURACY_MASK; 606 } 607 608 /** 609 * Returns true if this location has a speed accuracy, false otherwise. 610 */ hasSpeedAccuracy()611 public boolean hasSpeedAccuracy() { 612 return (mFieldsMask & HAS_SPEED_ACCURACY_MASK) != 0; 613 } 614 615 /** 616 * Remove the speed accuracy from this location. 617 */ removeSpeedAccuracy()618 public void removeSpeedAccuracy() { 619 mFieldsMask &= ~HAS_SPEED_ACCURACY_MASK; 620 } 621 622 /** 623 * Returns the bearing at the time of this location in degrees. Bearing is the horizontal 624 * direction of travel of this device and is unrelated to the device orientation. The bearing 625 * is guaranteed to be in the range [0, 360). 626 * 627 * <p>This is only valid if {@link #hasBearing()} is true. 628 * 629 * @return bearing at the time of this location 630 */ getBearing()631 public @FloatRange(from = 0.0, to = 360.0, toInclusive = false) float getBearing() { 632 return mBearingDegrees; 633 } 634 635 /** 636 * Set the bearing at the time of this location, in degrees. The given bearing will be converted 637 * into the range [0, 360). 638 * 639 * <p class="note">Note: passing in extremely high or low floating point values to this function 640 * may produce strange results due to the intricacies of floating point math. 641 * 642 * @param bearingDegrees bearing in degrees 643 */ setBearing( @loatRangefromInclusive = false, toInclusive = false) float bearingDegrees)644 public void setBearing( 645 @FloatRange(fromInclusive = false, toInclusive = false) float bearingDegrees) { 646 Preconditions.checkArgument(Float.isFinite(bearingDegrees)); 647 648 // final addition of zero is to remove -0 results. while these are technically within the 649 // range [0, 360) according to IEEE semantics, this eliminates possible user confusion. 650 float modBearing = bearingDegrees % 360f + 0f; 651 if (modBearing < 0) { 652 modBearing += 360f; 653 } 654 mBearingDegrees = modBearing; 655 mFieldsMask |= HAS_BEARING_MASK; 656 } 657 658 /** 659 * True if this location has a bearing, false otherwise. 660 */ hasBearing()661 public boolean hasBearing() { 662 return (mFieldsMask & HAS_BEARING_MASK) != 0; 663 } 664 665 /** 666 * Remove the bearing from this location. 667 */ removeBearing()668 public void removeBearing() { 669 mFieldsMask &= ~HAS_BEARING_MASK; 670 } 671 672 /** 673 * Returns the estimated bearing accuracy in degrees of this location at the 68th percentile 674 * confidence level. This means that there is 68% chance that the true bearing at the 675 * time of this location falls within {@link #getBearing()} ()} +/- this uncertainty. 676 * 677 * <p>This is only valid if {@link #hasBearingAccuracy()} ()} is true. 678 * 679 * @return bearing accuracy in degrees of this location 680 */ getBearingAccuracyDegrees()681 public @FloatRange(from = 0.0) float getBearingAccuracyDegrees() { 682 return mBearingAccuracyDegrees; 683 } 684 685 /** 686 * Set the bearing accuracy in degrees of this location. 687 * 688 * @param bearingAccuracyDegrees bearing accuracy in degrees 689 */ setBearingAccuracyDegrees(@loatRangefrom = 0.0) float bearingAccuracyDegrees)690 public void setBearingAccuracyDegrees(@FloatRange(from = 0.0) float bearingAccuracyDegrees) { 691 mBearingAccuracyDegrees = bearingAccuracyDegrees; 692 mFieldsMask |= HAS_BEARING_ACCURACY_MASK; 693 } 694 695 /** 696 * Returns true if this location has a bearing accuracy, false otherwise. 697 */ hasBearingAccuracy()698 public boolean hasBearingAccuracy() { 699 return (mFieldsMask & HAS_BEARING_ACCURACY_MASK) != 0; 700 } 701 702 /** 703 * Remove the bearing accuracy from this location. 704 */ removeBearingAccuracy()705 public void removeBearingAccuracy() { 706 mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK; 707 } 708 709 /** 710 * Returns the Mean Sea Level altitude of this location in meters. 711 * 712 * <p>This is only valid if {@link #hasMslAltitude()} is true. 713 */ getMslAltitudeMeters()714 public @FloatRange double getMslAltitudeMeters() { 715 return mMslAltitudeMeters; 716 } 717 718 /** 719 * Sets the Mean Sea Level altitude of this location in meters. 720 */ setMslAltitudeMeters(@loatRange double mslAltitudeMeters)721 public void setMslAltitudeMeters(@FloatRange double mslAltitudeMeters) { 722 mMslAltitudeMeters = mslAltitudeMeters; 723 mFieldsMask |= HAS_MSL_ALTITUDE_MASK; 724 } 725 726 /** 727 * Returns true if this location has a Mean Sea Level altitude, false otherwise. 728 */ hasMslAltitude()729 public boolean hasMslAltitude() { 730 return (mFieldsMask & HAS_MSL_ALTITUDE_MASK) != 0; 731 } 732 733 /** 734 * Removes the Mean Sea Level altitude from this location. 735 */ removeMslAltitude()736 public void removeMslAltitude() { 737 mFieldsMask &= ~HAS_MSL_ALTITUDE_MASK; 738 } 739 740 /** 741 * Returns the estimated Mean Sea Level altitude accuracy in meters of this location at the 68th 742 * percentile confidence level. This means that there is 68% chance that the true Mean Sea Level 743 * altitude of this location falls within {@link #getMslAltitudeMeters()} +/- this uncertainty. 744 * 745 * <p>This is only valid if {@link #hasMslAltitudeAccuracy()} is true. 746 */ getMslAltitudeAccuracyMeters()747 public @FloatRange(from = 0.0) float getMslAltitudeAccuracyMeters() { 748 return mMslAltitudeAccuracyMeters; 749 } 750 751 /** 752 * Sets the Mean Sea Level altitude accuracy of this location in meters. 753 */ setMslAltitudeAccuracyMeters( @loatRangefrom = 0.0) float mslAltitudeAccuracyMeters)754 public void setMslAltitudeAccuracyMeters( 755 @FloatRange(from = 0.0) float mslAltitudeAccuracyMeters) { 756 mMslAltitudeAccuracyMeters = mslAltitudeAccuracyMeters; 757 mFieldsMask |= HAS_MSL_ALTITUDE_ACCURACY_MASK; 758 } 759 760 /** 761 * Returns true if this location has a Mean Sea Level altitude accuracy, false otherwise. 762 */ hasMslAltitudeAccuracy()763 public boolean hasMslAltitudeAccuracy() { 764 return (mFieldsMask & HAS_MSL_ALTITUDE_ACCURACY_MASK) != 0; 765 } 766 767 /** 768 * Removes the Mean Sea Level altitude accuracy from this location. 769 */ removeMslAltitudeAccuracy()770 public void removeMslAltitudeAccuracy() { 771 mFieldsMask &= ~HAS_MSL_ALTITUDE_ACCURACY_MASK; 772 } 773 774 /** 775 * Returns true if this is a mock location. If this location comes from the Android framework, 776 * this indicates that the location was provided by a test location provider, and thus may not 777 * be related to the actual location of the device. 778 * 779 * @return true if this location came from a mock provider, false otherwise 780 * @deprecated Prefer {@link #isMock()} instead. 781 */ 782 @Deprecated isFromMockProvider()783 public boolean isFromMockProvider() { 784 return isMock(); 785 } 786 787 /** 788 * Flag this location as having come from a mock provider or not. 789 * 790 * @param isFromMockProvider true if this location came from a mock provider, false otherwise 791 * @deprecated Prefer {@link #setMock(boolean)} instead. 792 * @hide 793 */ 794 @Deprecated 795 @SystemApi setIsFromMockProvider(boolean isFromMockProvider)796 public void setIsFromMockProvider(boolean isFromMockProvider) { 797 setMock(isFromMockProvider); 798 } 799 800 /** 801 * Returns true if this location is marked as a mock location. If this location comes from the 802 * Android framework, this indicates that the location was provided by a test location provider, 803 * and thus may not be related to the actual location of the device. 804 * 805 * @see LocationManager#addTestProvider 806 */ isMock()807 public boolean isMock() { 808 return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0; 809 } 810 811 /** 812 * Sets whether this location is marked as a mock location. 813 */ setMock(boolean mock)814 public void setMock(boolean mock) { 815 if (mock) { 816 mFieldsMask |= HAS_MOCK_PROVIDER_MASK; 817 } else { 818 mFieldsMask &= ~HAS_MOCK_PROVIDER_MASK; 819 } 820 } 821 822 /** 823 * Returns an optional bundle of additional information associated with this location. The keys 824 * and values within the bundle are determined by the location provider. 825 * 826 * <p> Common key/value pairs are listed below. There is no guarantee that these key/value pairs 827 * will be present for any location. 828 * 829 * <ul> 830 * <li> satellites - the number of satellites used to derive a GNSS fix. This key was deprecated 831 * in API 34 because the information can be obtained through more accurate means, such as by 832 * referencing {@link GnssStatus#usedInFix}. 833 * </ul> 834 */ getExtras()835 public @Nullable Bundle getExtras() { 836 return mExtras; 837 } 838 839 /** 840 * Sets the extra information associated with this fix to the given Bundle. 841 * 842 * <p>Note this stores a copy of the given extras, so any changes to extras after calling this 843 * method won't be reflected in the location bundle. 844 */ setExtras(@ullable Bundle extras)845 public void setExtras(@Nullable Bundle extras) { 846 mExtras = (extras == null) ? null : new Bundle(extras); 847 } 848 849 /** 850 * Return true if this location is considered complete. A location is considered complete if it 851 * has a non-null provider, accuracy, and non-zero time and elapsed realtime. The exact 852 * definition of completeness may change over time. 853 * 854 * <p>All locations supplied by the {@link LocationManager} are guaranteed to be complete. 855 */ isComplete()856 public boolean isComplete() { 857 return mProvider != null && hasAccuracy() && mTimeMs != 0 && mElapsedRealtimeNs != 0; 858 } 859 860 /** 861 * Helper to fill incomplete fields with valid (but likely nonsensical) values. 862 * 863 * @hide 864 */ 865 @SystemApi makeComplete()866 public void makeComplete() { 867 if (mProvider == null) { 868 mProvider = ""; 869 } 870 if (!hasAccuracy()) { 871 mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK; 872 mHorizontalAccuracyMeters = 100.0f; 873 } 874 if (mTimeMs == 0) { 875 mTimeMs = System.currentTimeMillis(); 876 } 877 if (mElapsedRealtimeNs == 0) { 878 mElapsedRealtimeNs = SystemClock.elapsedRealtimeNanos(); 879 } 880 } 881 882 /** 883 * Location equality is provided primarily for test purposes. Comparing locations for equality 884 * in production may indicate incorrect assumptions, and should be avoided whenever possible. 885 * 886 * <p>{@inheritDoc} 887 */ 888 @Override equals(@ullable Object o)889 public boolean equals(@Nullable Object o) { 890 if (this == o) { 891 return true; 892 } 893 if (!(o instanceof Location)) { 894 return false; 895 } 896 897 Location location = (Location) o; 898 return mTimeMs == location.mTimeMs 899 && mElapsedRealtimeNs == location.mElapsedRealtimeNs 900 && hasElapsedRealtimeUncertaintyNanos() 901 == location.hasElapsedRealtimeUncertaintyNanos() 902 && (!hasElapsedRealtimeUncertaintyNanos() || Double.compare( 903 location.mElapsedRealtimeUncertaintyNs, mElapsedRealtimeUncertaintyNs) == 0) 904 && Double.compare(location.mLatitudeDegrees, mLatitudeDegrees) == 0 905 && Double.compare(location.mLongitudeDegrees, mLongitudeDegrees) == 0 906 && hasAltitude() == location.hasAltitude() 907 && (!hasAltitude() || Double.compare(location.mAltitudeMeters, mAltitudeMeters) 908 == 0) 909 && hasSpeed() == location.hasSpeed() 910 && (!hasSpeed() || Float.compare(location.mSpeedMetersPerSecond, 911 mSpeedMetersPerSecond) == 0) 912 && hasBearing() == location.hasBearing() 913 && (!hasBearing() || Float.compare(location.mBearingDegrees, mBearingDegrees) == 0) 914 && hasAccuracy() == location.hasAccuracy() 915 && (!hasAccuracy() || Float.compare(location.mHorizontalAccuracyMeters, 916 mHorizontalAccuracyMeters) == 0) 917 && hasVerticalAccuracy() == location.hasVerticalAccuracy() 918 && (!hasVerticalAccuracy() || Float.compare(location.mAltitudeAccuracyMeters, 919 mAltitudeAccuracyMeters) == 0) 920 && hasSpeedAccuracy() == location.hasSpeedAccuracy() 921 && (!hasSpeedAccuracy() || Float.compare(location.mSpeedAccuracyMetersPerSecond, 922 mSpeedAccuracyMetersPerSecond) == 0) 923 && hasBearingAccuracy() == location.hasBearingAccuracy() 924 && (!hasBearingAccuracy() || Float.compare(location.mBearingAccuracyDegrees, 925 mBearingAccuracyDegrees) == 0) 926 && hasMslAltitude() == location.hasMslAltitude() 927 && (!hasMslAltitude() || Double.compare(location.mMslAltitudeMeters, 928 mMslAltitudeMeters) 929 == 0) 930 && hasMslAltitudeAccuracy() == location.hasMslAltitudeAccuracy() 931 && (!hasMslAltitudeAccuracy() || Float.compare( 932 location.mMslAltitudeAccuracyMeters, 933 mMslAltitudeAccuracyMeters) == 0) 934 && Objects.equals(mProvider, location.mProvider) 935 && areExtrasEqual(mExtras, location.mExtras); 936 } 937 areExtrasEqual(@ullable Bundle extras1, @Nullable Bundle extras2)938 private static boolean areExtrasEqual(@Nullable Bundle extras1, @Nullable Bundle extras2) { 939 if ((extras1 == null || extras1.isEmpty()) && (extras2 == null || extras2.isEmpty())) { 940 return true; 941 } else if (extras1 == null || extras2 == null) { 942 return false; 943 } else { 944 return extras1.kindofEquals(extras2); 945 } 946 } 947 948 @Override hashCode()949 public int hashCode() { 950 return Objects.hash(mProvider, mElapsedRealtimeNs, mLatitudeDegrees, mLongitudeDegrees); 951 } 952 953 @Override toString()954 public @NonNull String toString() { 955 StringBuilder s = new StringBuilder(); 956 s.append("Location["); 957 s.append(mProvider); 958 s.append(" ").append(String.format(Locale.ROOT, "%.6f,%.6f", mLatitudeDegrees, 959 mLongitudeDegrees)); 960 if (hasAccuracy()) { 961 s.append(" hAcc=").append(mHorizontalAccuracyMeters); 962 } 963 s.append(" et="); 964 TimeUtils.formatDuration(getElapsedRealtimeMillis(), s); 965 if (hasAltitude()) { 966 s.append(" alt=").append(mAltitudeMeters); 967 if (hasVerticalAccuracy()) { 968 s.append(" vAcc=").append(mAltitudeAccuracyMeters); 969 } 970 } 971 if (hasMslAltitude()) { 972 s.append(" mslAlt=").append(mMslAltitudeMeters); 973 if (hasMslAltitudeAccuracy()) { 974 s.append(" mslAltAcc=").append(mMslAltitudeAccuracyMeters); 975 } 976 } 977 if (hasSpeed()) { 978 s.append(" vel=").append(mSpeedMetersPerSecond); 979 if (hasSpeedAccuracy()) { 980 s.append(" sAcc=").append(mSpeedAccuracyMetersPerSecond); 981 } 982 } 983 if (hasBearing()) { 984 s.append(" bear=").append(mBearingDegrees); 985 if (hasBearingAccuracy()) { 986 s.append(" bAcc=").append(mBearingAccuracyDegrees); 987 } 988 } 989 if (isMock()) { 990 s.append(" mock"); 991 } 992 993 if (mExtras != null && !mExtras.isEmpty()) { 994 s.append(" {").append(mExtras).append('}'); 995 } 996 s.append(']'); 997 return s.toString(); 998 } 999 1000 /** 1001 * Dumps location information to the given Printer. 1002 * 1003 * @deprecated Prefer to use {@link #toString()} along with whatever custom formatting is 1004 * required instead of this method. It is not this class's job to manage print representations. 1005 */ 1006 @Deprecated dump(@onNull Printer pw, @Nullable String prefix)1007 public void dump(@NonNull Printer pw, @Nullable String prefix) { 1008 pw.println(prefix + this); 1009 } 1010 1011 public static final @NonNull Parcelable.Creator<Location> CREATOR = 1012 new Parcelable.Creator<Location>() { 1013 @Override 1014 public Location createFromParcel(Parcel in) { 1015 Location l = new Location(in.readString8()); 1016 l.mFieldsMask = in.readInt(); 1017 l.mTimeMs = in.readLong(); 1018 l.mElapsedRealtimeNs = in.readLong(); 1019 if (l.hasElapsedRealtimeUncertaintyNanos()) { 1020 l.mElapsedRealtimeUncertaintyNs = in.readDouble(); 1021 } 1022 l.mLatitudeDegrees = in.readDouble(); 1023 l.mLongitudeDegrees = in.readDouble(); 1024 if (l.hasAltitude()) { 1025 l.mAltitudeMeters = in.readDouble(); 1026 } 1027 if (l.hasSpeed()) { 1028 l.mSpeedMetersPerSecond = in.readFloat(); 1029 } 1030 if (l.hasBearing()) { 1031 l.mBearingDegrees = in.readFloat(); 1032 } 1033 if (l.hasAccuracy()) { 1034 l.mHorizontalAccuracyMeters = in.readFloat(); 1035 } 1036 if (l.hasVerticalAccuracy()) { 1037 l.mAltitudeAccuracyMeters = in.readFloat(); 1038 } 1039 if (l.hasSpeedAccuracy()) { 1040 l.mSpeedAccuracyMetersPerSecond = in.readFloat(); 1041 } 1042 if (l.hasBearingAccuracy()) { 1043 l.mBearingAccuracyDegrees = in.readFloat(); 1044 } 1045 if (l.hasMslAltitude()) { 1046 l.mMslAltitudeMeters = in.readDouble(); 1047 } 1048 if (l.hasMslAltitudeAccuracy()) { 1049 l.mMslAltitudeAccuracyMeters = in.readFloat(); 1050 } 1051 l.mExtras = Bundle.setDefusable(in.readBundle(), true); 1052 return l; 1053 } 1054 1055 @Override 1056 public Location[] newArray(int size) { 1057 return new Location[size]; 1058 } 1059 }; 1060 1061 @Override describeContents()1062 public int describeContents() { 1063 return 0; 1064 } 1065 1066 @Override writeToParcel(@onNull Parcel parcel, int flags)1067 public void writeToParcel(@NonNull Parcel parcel, int flags) { 1068 parcel.writeString8(mProvider); 1069 parcel.writeInt(mFieldsMask); 1070 parcel.writeLong(mTimeMs); 1071 parcel.writeLong(mElapsedRealtimeNs); 1072 if (hasElapsedRealtimeUncertaintyNanos()) { 1073 parcel.writeDouble(mElapsedRealtimeUncertaintyNs); 1074 } 1075 parcel.writeDouble(mLatitudeDegrees); 1076 parcel.writeDouble(mLongitudeDegrees); 1077 if (hasAltitude()) { 1078 parcel.writeDouble(mAltitudeMeters); 1079 } 1080 if (hasSpeed()) { 1081 parcel.writeFloat(mSpeedMetersPerSecond); 1082 } 1083 if (hasBearing()) { 1084 parcel.writeFloat(mBearingDegrees); 1085 } 1086 if (hasAccuracy()) { 1087 parcel.writeFloat(mHorizontalAccuracyMeters); 1088 } 1089 if (hasVerticalAccuracy()) { 1090 parcel.writeFloat(mAltitudeAccuracyMeters); 1091 } 1092 if (hasSpeedAccuracy()) { 1093 parcel.writeFloat(mSpeedAccuracyMetersPerSecond); 1094 } 1095 if (hasBearingAccuracy()) { 1096 parcel.writeFloat(mBearingAccuracyDegrees); 1097 } 1098 if (hasMslAltitude()) { 1099 parcel.writeDouble(mMslAltitudeMeters); 1100 } 1101 if (hasMslAltitudeAccuracy()) { 1102 parcel.writeFloat(mMslAltitudeAccuracyMeters); 1103 } 1104 parcel.writeBundle(mExtras); 1105 } 1106 1107 /** 1108 * Converts a latitude/longitude coordinate to a String representation. The outputType must be 1109 * one of {@link #FORMAT_DEGREES}, {@link #FORMAT_MINUTES}, or {@link #FORMAT_SECONDS}. The 1110 * coordinate must be a number between -180.0 and 180.0, inclusive. This conversion is performed 1111 * in a method that is dependent on the default locale, and so is not guaranteed to round-trip 1112 * with {@link #convert(String)}. 1113 * 1114 * @throws IllegalArgumentException if coordinate is less than -180.0, greater than 180.0, or is 1115 * not a number. 1116 * @throws IllegalArgumentException if outputType is not a recognized value. 1117 */ convert(@loatRange double coordinate, @Format int outputType)1118 public static @NonNull String convert(@FloatRange double coordinate, @Format int outputType) { 1119 Preconditions.checkArgumentInRange(coordinate, -180D, 180D, "coordinate"); 1120 Preconditions.checkArgument(outputType == FORMAT_DEGREES || outputType == FORMAT_MINUTES 1121 || outputType == FORMAT_SECONDS, "%d is an unrecognized format", outputType); 1122 1123 StringBuilder sb = new StringBuilder(); 1124 1125 if (coordinate < 0) { 1126 sb.append('-'); 1127 coordinate = -coordinate; 1128 } 1129 1130 DecimalFormat df = new DecimalFormat("###.#####"); 1131 if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) { 1132 int degrees = (int) Math.floor(coordinate); 1133 sb.append(degrees); 1134 sb.append(':'); 1135 coordinate -= degrees; 1136 coordinate *= 60.0; 1137 if (outputType == FORMAT_SECONDS) { 1138 int minutes = (int) Math.floor(coordinate); 1139 sb.append(minutes); 1140 sb.append(':'); 1141 coordinate -= minutes; 1142 coordinate *= 60.0; 1143 } 1144 } 1145 sb.append(df.format(coordinate)); 1146 return sb.toString(); 1147 } 1148 1149 /** 1150 * Converts a String in one of the formats described by {@link #FORMAT_DEGREES}, 1151 * {@link #FORMAT_MINUTES}, or {@link #FORMAT_SECONDS} into a double. This conversion is 1152 * performed in a locale agnostic method, and so is not guaranteed to round-trip with 1153 * {@link #convert(double, int)}. 1154 * 1155 * @throws NullPointerException if coordinate is null 1156 * @throws IllegalArgumentException if the coordinate is not 1157 * in one of the valid formats. 1158 */ convert(@onNull String coordinate)1159 public static @FloatRange double convert(@NonNull String coordinate) { 1160 Objects.requireNonNull(coordinate); 1161 1162 boolean negative = false; 1163 if (coordinate.charAt(0) == '-') { 1164 coordinate = coordinate.substring(1); 1165 negative = true; 1166 } 1167 1168 StringTokenizer st = new StringTokenizer(coordinate, ":"); 1169 int tokens = st.countTokens(); 1170 if (tokens < 1) { 1171 throw new IllegalArgumentException("coordinate=" + coordinate); 1172 } 1173 try { 1174 String degrees = st.nextToken(); 1175 double val; 1176 if (tokens == 1) { 1177 val = Double.parseDouble(degrees); 1178 return negative ? -val : val; 1179 } 1180 1181 String minutes = st.nextToken(); 1182 int deg = Integer.parseInt(degrees); 1183 double min; 1184 double sec = 0.0; 1185 boolean secPresent = false; 1186 1187 if (st.hasMoreTokens()) { 1188 min = Integer.parseInt(minutes); 1189 String seconds = st.nextToken(); 1190 sec = Double.parseDouble(seconds); 1191 secPresent = true; 1192 } else { 1193 min = Double.parseDouble(minutes); 1194 } 1195 1196 boolean isNegative180 = negative && deg == 180 && min == 0 && sec == 0; 1197 1198 // deg must be in [0, 179] except for the case of -180 degrees 1199 if (deg < 0.0 || (deg > 179 && !isNegative180)) { 1200 throw new IllegalArgumentException("coordinate=" + coordinate); 1201 } 1202 1203 // min must be in [0, 59] if seconds are present, otherwise [0.0, 60.0) 1204 if (min < 0 || min >= 60 || (secPresent && min > 59)) { 1205 throw new IllegalArgumentException("coordinate=" + coordinate); 1206 } 1207 1208 // sec must be in [0.0, 60.0) 1209 if (sec < 0 || sec >= 60) { 1210 throw new IllegalArgumentException("coordinate=" + coordinate); 1211 } 1212 1213 val = deg * 3600.0 + min * 60.0 + sec; 1214 val /= 3600.0; 1215 return negative ? -val : val; 1216 } catch (NumberFormatException e) { 1217 throw new IllegalArgumentException("coordinate=" + coordinate, e); 1218 } 1219 } 1220 computeDistanceAndBearing(double lat1, double lon1, double lat2, double lon2, BearingDistanceCache results)1221 private static void computeDistanceAndBearing(double lat1, double lon1, 1222 double lat2, double lon2, BearingDistanceCache results) { 1223 // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf 1224 // using the "Inverse Formula" (section 4) 1225 1226 // Convert lat/long to radians 1227 lat1 *= Math.PI / 180.0; 1228 lat2 *= Math.PI / 180.0; 1229 lon1 *= Math.PI / 180.0; 1230 lon2 *= Math.PI / 180.0; 1231 1232 double a = 6378137.0; // WGS84 major axis 1233 double b = 6356752.3142; // WGS84 semi-major axis 1234 double f = (a - b) / a; 1235 double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b); 1236 1237 double l = lon2 - lon1; 1238 double aA = 0.0; 1239 double u1 = Math.atan((1.0 - f) * Math.tan(lat1)); 1240 double u2 = Math.atan((1.0 - f) * Math.tan(lat2)); 1241 1242 double cosU1 = Math.cos(u1); 1243 double cosU2 = Math.cos(u2); 1244 double sinU1 = Math.sin(u1); 1245 double sinU2 = Math.sin(u2); 1246 double cosU1cosU2 = cosU1 * cosU2; 1247 double sinU1sinU2 = sinU1 * sinU2; 1248 1249 double sigma = 0.0; 1250 double deltaSigma = 0.0; 1251 double cosSqAlpha; 1252 double cos2SM; 1253 double cosSigma; 1254 double sinSigma; 1255 double cosLambda = 0.0; 1256 double sinLambda = 0.0; 1257 1258 double lambda = l; // initial guess 1259 for (int iter = 0; iter < 20; iter++) { 1260 double lambdaOrig = lambda; 1261 cosLambda = Math.cos(lambda); 1262 sinLambda = Math.sin(lambda); 1263 double t1 = cosU2 * sinLambda; 1264 double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda; 1265 double sinSqSigma = t1 * t1 + t2 * t2; 1266 sinSigma = Math.sqrt(sinSqSigma); 1267 cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; 1268 sigma = Math.atan2(sinSigma, cosSigma); 1269 double sinAlpha = (sinSigma == 0) ? 0.0 : 1270 cosU1cosU2 * sinLambda / sinSigma; 1271 cosSqAlpha = 1.0 - sinAlpha * sinAlpha; 1272 cos2SM = (cosSqAlpha == 0) ? 0.0 : cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; 1273 1274 double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; 1275 aA = 1 + (uSquared / 16384.0) * (4096.0 + uSquared * (-768 + uSquared * (320.0 1276 - 175.0 * uSquared))); 1277 double bB = (uSquared / 1024.0) * (256.0 + uSquared * (-128.0 + uSquared * (74.0 1278 - 47.0 * uSquared))); 1279 double cC = (f / 16.0) * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); 1280 double cos2SMSq = cos2SM * cos2SM; 1281 deltaSigma = bB * sinSigma * (cos2SM + (bB / 4.0) * (cosSigma * (-1.0 + 2.0 * cos2SMSq) 1282 - (bB / 6.0) * cos2SM * (-3.0 + 4.0 * sinSigma * sinSigma) * (-3.0 1283 + 4.0 * cos2SMSq))); 1284 1285 lambda = l + (1.0 - cC) * f * sinAlpha * (sigma + cC * sinSigma * (cos2SM 1286 + cC * cosSigma * (-1.0 + 2.0 * cos2SM * cos2SM))); 1287 1288 double delta = (lambda - lambdaOrig) / lambda; 1289 if (Math.abs(delta) < 1.0e-12) { 1290 break; 1291 } 1292 } 1293 1294 results.mDistance = (float) (b * aA * (sigma - deltaSigma)); 1295 float initialBearing = (float) Math.atan2(cosU2 * sinLambda, 1296 cosU1 * sinU2 - sinU1 * cosU2 * cosLambda); 1297 initialBearing = (float) (initialBearing * (180.0 / Math.PI)); 1298 results.mInitialBearing = initialBearing; 1299 float finalBearing = (float) Math.atan2(cosU1 * sinLambda, 1300 -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda); 1301 finalBearing = (float) (finalBearing * (180.0 / Math.PI)); 1302 results.mFinalBearing = finalBearing; 1303 results.mLat1 = lat1; 1304 results.mLat2 = lat2; 1305 results.mLon1 = lon1; 1306 results.mLon2 = lon2; 1307 } 1308 1309 /** 1310 * Computes the approximate distance in meters between two 1311 * locations, and optionally the initial and final bearings of the 1312 * shortest path between them. Distance and bearing are defined using the 1313 * WGS84 ellipsoid. 1314 * 1315 * <p> The computed distance is stored in results[0]. If results has length 1316 * 2 or greater, the initial bearing is stored in results[1]. If results has 1317 * length 3 or greater, the final bearing is stored in results[2]. 1318 * 1319 * @param startLatitude the starting latitude 1320 * @param startLongitude the starting longitude 1321 * @param endLatitude the ending latitude 1322 * @param endLongitude the ending longitude 1323 * @param results an array of floats to hold the results 1324 * 1325 * @throws IllegalArgumentException if results is null or has length < 1 1326 */ distanceBetween( @loatRangefrom = -90.0, to = 90.0) double startLatitude, @FloatRange(from = -180.0, to = 180.0) double startLongitude, @FloatRange(from = -90.0, to = 90.0) double endLatitude, @FloatRange(from = -180.0, to = 180.0) double endLongitude, float[] results)1327 public static void distanceBetween( 1328 @FloatRange(from = -90.0, to = 90.0) double startLatitude, 1329 @FloatRange(from = -180.0, to = 180.0) double startLongitude, 1330 @FloatRange(from = -90.0, to = 90.0) double endLatitude, 1331 @FloatRange(from = -180.0, to = 180.0) double endLongitude, 1332 float[] results) { 1333 if (results == null || results.length < 1) { 1334 throw new IllegalArgumentException("results is null or has length < 1"); 1335 } 1336 BearingDistanceCache cache = sBearingDistanceCache.get(); 1337 computeDistanceAndBearing(startLatitude, startLongitude, 1338 endLatitude, endLongitude, cache); 1339 results[0] = cache.mDistance; 1340 if (results.length > 1) { 1341 results[1] = cache.mInitialBearing; 1342 if (results.length > 2) { 1343 results[2] = cache.mFinalBearing; 1344 } 1345 } 1346 } 1347 1348 private static class BearingDistanceCache { 1349 double mLat1 = 0.0; 1350 double mLon1 = 0.0; 1351 double mLat2 = 0.0; 1352 double mLon2 = 0.0; 1353 float mDistance = 0.0f; 1354 float mInitialBearing = 0.0f; 1355 float mFinalBearing = 0.0f; 1356 } 1357 } 1358