Location.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 17package android.location; 18 19import android.os.Bundle; 20import android.os.Parcel; 21import android.os.Parcelable; 22 23import java.text.DecimalFormat; 24import java.util.StringTokenizer; 25 26/** 27 * A class representing a geographic location sensed at a particular 28 * time (a "fix"). A location consists of a latitude and longitude, a 29 * UTC timestamp. and optionally information on altitude, speed, and 30 * bearing. 31 * 32 * <p> Information specific to a particular provider or class of 33 * providers may be communicated to the application using getExtras, 34 * which returns a Bundle of key/value pairs. Each provider will only 35 * provide those entries for which information is available. 36 */ 37public class Location implements Parcelable { 38 /** 39 * Constant used to specify formatting of a latitude or longitude 40 * in the form "[+-]DDD.DDDDD where D indicates degrees. 41 */ 42 public static final int FORMAT_DEGREES = 0; 43 44 /** 45 * Constant used to specify formatting of a latitude or longitude 46 * in the form "[+-]DDD:MM.MMMMM" where D indicates degrees and 47 * M indicates minutes of arc (1 minute = 1/60th of a degree). 48 */ 49 public static final int FORMAT_MINUTES = 1; 50 51 /** 52 * Constant used to specify formatting of a latitude or longitude 53 * in the form "DDD:MM:SS.SSSSS" where D indicates degrees, M 54 * indicates minutes of arc, and S indicates seconds of arc (1 55 * minute = 1/60th of a degree, 1 second = 1/3600th of a degree). 56 */ 57 public static final int FORMAT_SECONDS = 2; 58 59 private String mProvider; 60 private long mTime = 0; 61 private double mLatitude = 0.0; 62 private double mLongitude = 0.0; 63 private boolean mHasAltitude = false; 64 private double mAltitude = 0.0f; 65 private boolean mHasSpeed = false; 66 private float mSpeed = 0.0f; 67 private boolean mHasBearing = false; 68 private float mBearing = 0.0f; 69 private boolean mHasAccuracy = false; 70 private float mAccuracy = 0.0f; 71 private Bundle mExtras = null; 72 73 // Cache the inputs and outputs of computeDistanceAndBearing 74 // so calls to distanceTo() and bearingTo() can share work 75 private double mLat1 = 0.0; 76 private double mLon1 = 0.0; 77 private double mLat2 = 0.0; 78 private double mLon2 = 0.0; 79 private float mDistance = 0.0f; 80 private float mInitialBearing = 0.0f; 81 // Scratchpad 82 private float[] mResults = new float[2]; 83 84 /** 85 * Constructs a new Location. By default, time, latitude, 86 * longitude, and numSatellites are 0; hasAltitude, hasSpeed, and 87 * hasBearing are false; and there is no extra information. 88 * 89 * @param provider the name of the location provider that generated this 90 * location fix. 91 */ 92 public Location(String provider) { 93 mProvider = provider; 94 } 95 96 /** 97 * Constructs a new Location object that is a copy of the given 98 * location. 99 */ 100 public Location(Location l) { 101 set(l); 102 } 103 104 /** 105 * Sets the contents of the location to the values from the given location. 106 */ 107 public void set(Location l) { 108 mProvider = l.mProvider; 109 mTime = l.mTime; 110 mLatitude = l.mLatitude; 111 mLongitude = l.mLongitude; 112 mHasAltitude = l.mHasAltitude; 113 mAltitude = l.mAltitude; 114 mHasSpeed = l.mHasSpeed; 115 mSpeed = l.mSpeed; 116 mHasBearing = l.mHasBearing; 117 mBearing = l.mBearing; 118 mHasAccuracy = l.mHasAccuracy; 119 mAccuracy = l.mAccuracy; 120 mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras); 121 } 122 123 /** 124 * Clears the contents of the location. 125 */ 126 public void reset() { 127 mProvider = null; 128 mTime = 0; 129 mLatitude = 0; 130 mLongitude = 0; 131 mHasAltitude = false; 132 mAltitude = 0; 133 mHasSpeed = false; 134 mSpeed = 0; 135 mHasBearing = false; 136 mBearing = 0; 137 mHasAccuracy = false; 138 mAccuracy = 0; 139 mExtras = null; 140 } 141 142 /** 143 * Converts a coordinate to a String representation. The outputType 144 * may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS. 145 * The coordinate must be a valid double between -180.0 and 180.0. 146 * 147 * @throws IllegalArgumentException if coordinate is less than 148 * -180.0, greater than 180.0, or is not a number. 149 * @throws IllegalArgumentException if outputType is not one of 150 * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS. 151 */ 152 public static String convert(double coordinate, int outputType) { 153 if (coordinate < -180.0 || coordinate > 180.0 || 154 Double.isNaN(coordinate)) { 155 throw new IllegalArgumentException("coordinate=" + coordinate); 156 } 157 if ((outputType != FORMAT_DEGREES) && 158 (outputType != FORMAT_MINUTES) && 159 (outputType != FORMAT_SECONDS)) { 160 throw new IllegalArgumentException("outputType=" + outputType); 161 } 162 163 StringBuilder sb = new StringBuilder(); 164 165 // Handle negative values 166 if (coordinate < 0) { 167 sb.append('-'); 168 coordinate = -coordinate; 169 } 170 171 DecimalFormat df = new DecimalFormat("###.#####"); 172 if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) { 173 int degrees = (int) Math.floor(coordinate); 174 sb.append(degrees); 175 sb.append(':'); 176 coordinate -= degrees; 177 coordinate *= 60.0; 178 if (outputType == FORMAT_SECONDS) { 179 int minutes = (int) Math.floor(coordinate); 180 sb.append(minutes); 181 sb.append(':'); 182 coordinate -= minutes; 183 coordinate *= 60.0; 184 } 185 } 186 sb.append(df.format(coordinate)); 187 return sb.toString(); 188 } 189 190 /** 191 * Converts a String in one of the formats described by 192 * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a 193 * double. 194 * 195 * @throws NullPointerException if coordinate is null 196 * @throws IllegalArgumentException if the coordinate is not 197 * in one of the valid formats. 198 */ 199 public static double convert(String coordinate) { 200 // IllegalArgumentException if bad syntax 201 if (coordinate == null) { 202 throw new NullPointerException("coordinate"); 203 } 204 205 boolean negative = false; 206 if (coordinate.charAt(0) == '-') { 207 coordinate = coordinate.substring(1); 208 negative = true; 209 } 210 211 StringTokenizer st = new StringTokenizer(coordinate, ":"); 212 int tokens = st.countTokens(); 213 if (tokens < 1) { 214 throw new IllegalArgumentException("coordinate=" + coordinate); 215 } 216 try { 217 String degrees = st.nextToken(); 218 double val; 219 if (tokens == 1) { 220 val = Double.parseDouble(degrees); 221 return negative ? -val : val; 222 } 223 224 String minutes = st.nextToken(); 225 int deg = Integer.parseInt(degrees); 226 double min; 227 double sec = 0.0; 228 229 if (st.hasMoreTokens()) { 230 min = Integer.parseInt(minutes); 231 String seconds = st.nextToken(); 232 sec = Double.parseDouble(seconds); 233 } else { 234 min = Double.parseDouble(minutes); 235 } 236 237 boolean isNegative180 = negative && (deg == 180) && 238 (min == 0) && (sec == 0); 239 240 // deg must be in [0, 179] except for the case of -180 degrees 241 if ((deg < 0.0) || (deg > 179 && !isNegative180)) { 242 throw new IllegalArgumentException("coordinate=" + coordinate); 243 } 244 if (min < 0 || min > 59) { 245 throw new IllegalArgumentException("coordinate=" + 246 coordinate); 247 } 248 if (sec < 0 || sec > 59) { 249 throw new IllegalArgumentException("coordinate=" + 250 coordinate); 251 } 252 253 val = deg*3600.0 + min*60.0 + sec; 254 val /= 3600.0; 255 return negative ? -val : val; 256 } catch (NumberFormatException nfe) { 257 throw new IllegalArgumentException("coordinate=" + coordinate); 258 } 259 } 260 261 private static void computeDistanceAndBearing(double lat1, double lon1, 262 double lat2, double lon2, float[] results) { 263 // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf 264 // using the "Inverse Formula" (section 4) 265 266 int MAXITERS = 20; 267 // Convert lat/long to radians 268 lat1 *= Math.PI / 180.0; 269 lat2 *= Math.PI / 180.0; 270 lon1 *= Math.PI / 180.0; 271 lon2 *= Math.PI / 180.0; 272 273 double a = 6378137.0; // WGS84 major axis 274 double b = 6356752.3142; // WGS84 semi-major axis 275 double f = (a - b) / a; 276 double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b); 277 278 double L = lon2 - lon1; 279 double A = 0.0; 280 double U1 = Math.atan((1.0 - f) * Math.tan(lat1)); 281 double U2 = Math.atan((1.0 - f) * Math.tan(lat2)); 282 283 double cosU1 = Math.cos(U1); 284 double cosU2 = Math.cos(U2); 285 double sinU1 = Math.sin(U1); 286 double sinU2 = Math.sin(U2); 287 double cosU1cosU2 = cosU1 * cosU2; 288 double sinU1sinU2 = sinU1 * sinU2; 289 290 double sigma = 0.0; 291 double deltaSigma = 0.0; 292 double cosSqAlpha = 0.0; 293 double cos2SM = 0.0; 294 double cosSigma = 0.0; 295 double sinSigma = 0.0; 296 double cosLambda = 0.0; 297 double sinLambda = 0.0; 298 299 double lambda = L; // initial guess 300 for (int iter = 0; iter < MAXITERS; iter++) { 301 double lambdaOrig = lambda; 302 cosLambda = Math.cos(lambda); 303 sinLambda = Math.sin(lambda); 304 double t1 = cosU2 * sinLambda; 305 double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda; 306 double sinSqSigma = t1 * t1 + t2 * t2; // (14) 307 sinSigma = Math.sqrt(sinSqSigma); 308 cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15) 309 sigma = Math.atan2(sinSigma, cosSigma); // (16) 310 double sinAlpha = (sinSigma == 0) ? 0.0 : 311 cosU1cosU2 * sinLambda / sinSigma; // (17) 312 cosSqAlpha = 1.0 - sinAlpha * sinAlpha; 313 cos2SM = (cosSqAlpha == 0) ? 0.0 : 314 cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18) 315 316 double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn 317 A = 1 + (uSquared / 16384.0) * // (3) 318 (4096.0 + uSquared * 319 (-768 + uSquared * (320.0 - 175.0 * uSquared))); 320 double B = (uSquared / 1024.0) * // (4) 321 (256.0 + uSquared * 322 (-128.0 + uSquared * (74.0 - 47.0 * uSquared))); 323 double C = (f / 16.0) * 324 cosSqAlpha * 325 (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10) 326 double cos2SMSq = cos2SM * cos2SM; 327 deltaSigma = B * sinSigma * // (6) 328 (cos2SM + (B / 4.0) * 329 (cosSigma * (-1.0 + 2.0 * cos2SMSq) - 330 (B / 6.0) * cos2SM * 331 (-3.0 + 4.0 * sinSigma * sinSigma) * 332 (-3.0 + 4.0 * cos2SMSq))); 333 334 lambda = L + 335 (1.0 - C) * f * sinAlpha * 336 (sigma + C * sinSigma * 337 (cos2SM + C * cosSigma * 338 (-1.0 + 2.0 * cos2SM * cos2SM))); // (11) 339 340 double delta = (lambda - lambdaOrig) / lambda; 341 if (Math.abs(delta) < 1.0e-12) { 342 break; 343 } 344 } 345 346 float distance = (float) (b * A * (sigma - deltaSigma)); 347 results[0] = distance; 348 if (results.length > 1) { 349 float initialBearing = (float) Math.atan2(cosU2 * sinLambda, 350 cosU1 * sinU2 - sinU1 * cosU2 * cosLambda); 351 initialBearing *= 180.0 / Math.PI; 352 results[1] = initialBearing; 353 if (results.length > 2) { 354 float finalBearing = (float) Math.atan2(cosU1 * sinLambda, 355 -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda); 356 finalBearing *= 180.0 / Math.PI; 357 results[2] = finalBearing; 358 } 359 } 360 } 361 362 /** 363 * Computes the approximate distance in meters between two 364 * locations, and optionally the initial and final bearings of the 365 * shortest path between them. Distance and bearing are defined using the 366 * WGS84 ellipsoid. 367 * 368 * <p> The computed distance is stored in results[0]. If results has length 369 * 2 or greater, the initial bearing is stored in results[1]. If results has 370 * length 3 or greater, the final bearing is stored in results[2]. 371 * 372 * @param startLatitude the starting latitude 373 * @param startLongitude the starting longitude 374 * @param endLatitude the ending latitude 375 * @param endLongitude the ending longitude 376 * @param results an array of floats to hold the results 377 * 378 * @throws IllegalArgumentException if results is null or has length < 1 379 */ 380 public static void distanceBetween(double startLatitude, double startLongitude, 381 double endLatitude, double endLongitude, float[] results) { 382 if (results == null || results.length < 1) { 383 throw new IllegalArgumentException("results is null or has length < 1"); 384 } 385 computeDistanceAndBearing(startLatitude, startLongitude, 386 endLatitude, endLongitude, results); 387 } 388 389 /** 390 * Returns the approximate distance in meters between this 391 * location and the given location. Distance is defined using 392 * the WGS84 ellipsoid. 393 * 394 * @param dest the destination location 395 * @return the approximate distance in meters 396 */ 397 public float distanceTo(Location dest) { 398 // See if we already have the result 399 synchronized (mResults) { 400 if (mLatitude != mLat1 || mLongitude != mLon1 || 401 dest.mLatitude != mLat2 || dest.mLongitude != mLon2) { 402 computeDistanceAndBearing(mLatitude, mLongitude, 403 dest.mLatitude, dest.mLongitude, mResults); 404 mLat1 = mLatitude; 405 mLon1 = mLongitude; 406 mLat2 = dest.mLatitude; 407 mLon2 = dest.mLongitude; 408 mDistance = mResults[0]; 409 mInitialBearing = mResults[1]; 410 } 411 return mDistance; 412 } 413 } 414 415 /** 416 * Returns the approximate initial bearing in degrees East of true 417 * North when traveling along the shortest path between this 418 * location and the given location. The shortest path is defined 419 * using the WGS84 ellipsoid. Locations that are (nearly) 420 * antipodal may produce meaningless results. 421 * 422 * @param dest the destination location 423 * @return the initial bearing in degrees 424 */ 425 public float bearingTo(Location dest) { 426 synchronized (mResults) { 427 // See if we already have the result 428 if (mLatitude != mLat1 || mLongitude != mLon1 || 429 dest.mLatitude != mLat2 || dest.mLongitude != mLon2) { 430 computeDistanceAndBearing(mLatitude, mLongitude, 431 dest.mLatitude, dest.mLongitude, mResults); 432 mLat1 = mLatitude; 433 mLon1 = mLongitude; 434 mLat2 = dest.mLatitude; 435 mLon2 = dest.mLongitude; 436 mDistance = mResults[0]; 437 mInitialBearing = mResults[1]; 438 } 439 return mInitialBearing; 440 } 441 } 442 443 /** 444 * Returns the name of the provider that generated this fix, 445 * or null if it is not associated with a provider. 446 */ 447 public String getProvider() { 448 return mProvider; 449 } 450 451 /** 452 * Sets the name of the provider that generated this fix. 453 */ 454 public void setProvider(String provider) { 455 mProvider = provider; 456 } 457 458 /** 459 * Returns the UTC time of this fix, in milliseconds since January 1, 460 * 1970. 461 */ 462 public long getTime() { 463 return mTime; 464 } 465 466 /** 467 * Sets the UTC time of this fix, in milliseconds since January 1, 468 * 1970. 469 */ 470 public void setTime(long time) { 471 mTime = time; 472 } 473 474 /** 475 * Returns the latitude of this fix. 476 */ 477 public double getLatitude() { 478 return mLatitude; 479 } 480 481 /** 482 * Sets the latitude of this fix. 483 */ 484 public void setLatitude(double latitude) { 485 mLatitude = latitude; 486 } 487 488 /** 489 * Returns the longitude of this fix. 490 */ 491 public double getLongitude() { 492 return mLongitude; 493 } 494 495 /** 496 * Sets the longitude of this fix. 497 */ 498 public void setLongitude(double longitude) { 499 mLongitude = longitude; 500 } 501 502 /** 503 * Returns true if this fix contains altitude information, false 504 * otherwise. 505 */ 506 public boolean hasAltitude() { 507 return mHasAltitude; 508 } 509 510 /** 511 * Returns the altitude of this fix. If {@link #hasAltitude} is false, 512 * 0.0f is returned. 513 */ 514 public double getAltitude() { 515 return mAltitude; 516 } 517 518 /** 519 * Sets the altitude of this fix. Following this call, 520 * hasAltitude() will return true. 521 */ 522 public void setAltitude(double altitude) { 523 mAltitude = altitude; 524 mHasAltitude = true; 525 } 526 527 /** 528 * Clears the altitude of this fix. Following this call, 529 * hasAltitude() will return false. 530 */ 531 public void removeAltitude() { 532 mAltitude = 0.0f; 533 mHasAltitude = false; 534 } 535 536 /** 537 * Returns true if this fix contains speed information, false 538 * otherwise. The default implementation returns false. 539 */ 540 public boolean hasSpeed() { 541 return mHasSpeed; 542 } 543 544 /** 545 * Returns the speed of the device over ground in meters/second. 546 * If hasSpeed() is false, 0.0f is returned. 547 */ 548 public float getSpeed() { 549 return mSpeed; 550 } 551 552 /** 553 * Sets the speed of this fix, in meters/second. Following this 554 * call, hasSpeed() will return true. 555 */ 556 public void setSpeed(float speed) { 557 mSpeed = speed; 558 mHasSpeed = true; 559 } 560 561 /** 562 * Clears the speed of this fix. Following this call, hasSpeed() 563 * will return false. 564 */ 565 public void removeSpeed() { 566 mSpeed = 0.0f; 567 mHasSpeed = false; 568 } 569 570 /** 571 * Returns true if the provider is able to report bearing information, 572 * false otherwise. The default implementation returns false. 573 */ 574 public boolean hasBearing() { 575 return mHasBearing; 576 } 577 578 /** 579 * Returns the direction of travel in degrees East of true 580 * North. If hasBearing() is false, 0.0 is returned. 581 */ 582 public float getBearing() { 583 return mBearing; 584 } 585 586 /** 587 * Sets the bearing of this fix. Following this call, hasBearing() 588 * will return true. 589 */ 590 public void setBearing(float bearing) { 591 while (bearing < 0.0f) { 592 bearing += 360.0f; 593 } 594 while (bearing >= 360.0f) { 595 bearing -= 360.0f; 596 } 597 mBearing = bearing; 598 mHasBearing = true; 599 } 600 601 /** 602 * Clears the bearing of this fix. Following this call, hasBearing() 603 * will return false. 604 */ 605 public void removeBearing() { 606 mBearing = 0.0f; 607 mHasBearing = false; 608 } 609 610 /** 611 * Returns true if the provider is able to report accuracy information, 612 * false otherwise. The default implementation returns false. 613 */ 614 public boolean hasAccuracy() { 615 return mHasAccuracy; 616 } 617 618 /** 619 * Returns the accuracy of the fix in meters. If hasAccuracy() is false, 620 * 0.0 is returned. 621 */ 622 public float getAccuracy() { 623 return mAccuracy; 624 } 625 626 /** 627 * Sets the accuracy of this fix. Following this call, hasAccuracy() 628 * will return true. 629 */ 630 public void setAccuracy(float accuracy) { 631 mAccuracy = accuracy; 632 mHasAccuracy = true; 633 } 634 635 /** 636 * Clears the accuracy of this fix. Following this call, hasAccuracy() 637 * will return false. 638 */ 639 public void removeAccuracy() { 640 mAccuracy = 0.0f; 641 mHasAccuracy = false; 642 } 643 644 /** 645 * Returns additional provider-specific information about the 646 * location fix as a Bundle. The keys and values are determined 647 * by the provider. If no additional information is available, 648 * null is returned. 649 * 650 * <p> A number of common key/value pairs are listed 651 * below. Providers that use any of the keys on this list must 652 * provide the corresponding value as described below. 653 * 654 * <ul> 655 * <li> satellites - the number of satellites used to derive the fix 656 * </ul> 657 */ 658 public Bundle getExtras() { 659 return mExtras; 660 } 661 662 /** 663 * Sets the extra information associated with this fix to the 664 * given Bundle. 665 */ 666 public void setExtras(Bundle extras) { 667 mExtras = (extras == null) ? null : new Bundle(extras); 668 } 669 670 @Override public String toString() { 671 return "Location[mProvider=" + mProvider + 672 ",mTime=" + mTime + 673 ",mLatitude=" + mLatitude + 674 ",mLongitude=" + mLongitude + 675 ",mHasAltitude=" + mHasAltitude + 676 ",mAltitude=" + mAltitude + 677 ",mHasSpeed=" + mHasSpeed + 678 ",mSpeed=" + mSpeed + 679 ",mHasBearing=" + mHasBearing + 680 ",mBearing=" + mBearing + 681 ",mHasAccuracy=" + mHasAccuracy + 682 ",mAccuracy=" + mAccuracy + 683 ",mExtras=" + mExtras + "]"; 684 } 685 686 public static final Parcelable.Creator<Location> CREATOR = 687 new Parcelable.Creator<Location>() { 688 public Location createFromParcel(Parcel in) { 689 String provider = in.readString(); 690 Location l = new Location(provider); 691 l.mTime = in.readLong(); 692 l.mLatitude = in.readDouble(); 693 l.mLongitude = in.readDouble(); 694 l.mHasAltitude = in.readInt() != 0; 695 l.mAltitude = in.readDouble(); 696 l.mHasSpeed = in.readInt() != 0; 697 l.mSpeed = in.readFloat(); 698 l.mHasBearing = in.readInt() != 0; 699 l.mBearing = in.readFloat(); 700 l.mHasAccuracy = in.readInt() != 0; 701 l.mAccuracy = in.readFloat(); 702 l.mExtras = in.readBundle(); 703 return l; 704 } 705 706 public Location[] newArray(int size) { 707 return new Location[size]; 708 } 709 }; 710 711 public int describeContents() { 712 return 0; 713 } 714 715 public void writeToParcel(Parcel parcel, int flags) { 716 parcel.writeString(mProvider); 717 parcel.writeLong(mTime); 718 parcel.writeDouble(mLatitude); 719 parcel.writeDouble(mLongitude); 720 parcel.writeInt(mHasAltitude ? 1 : 0); 721 parcel.writeDouble(mAltitude); 722 parcel.writeInt(mHasSpeed ? 1 : 0); 723 parcel.writeFloat(mSpeed); 724 parcel.writeInt(mHasBearing ? 1 : 0); 725 parcel.writeFloat(mBearing); 726 parcel.writeInt(mHasAccuracy ? 1 : 0); 727 parcel.writeFloat(mAccuracy); 728 parcel.writeBundle(mExtras); 729 } 730} 731