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