diff --git a/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java b/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java index f8921b367..f31539bee 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/ChartActivity.java @@ -22,7 +22,7 @@ import com.google.android.apps.mytracks.content.TrackPointsColumns; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.WaypointsColumns; import com.google.android.apps.mytracks.stats.DoubleBuffer; -import com.google.android.apps.mytracks.stats.TripStatistics; +import com.google.android.apps.mytracks.stats.TripStatisticsBuilder; import com.google.android.apps.mytracks.util.MyTracksUtils; import com.google.android.apps.mytracks.util.UnitConversions; import com.google.android.maps.mytracks.R; @@ -488,10 +488,10 @@ public class ChartActivity extends Activity implements if (Math.abs(location.getSpeed() - 128) > 1) { speedBuffer.setNext(location.getSpeed()); } - } else if (TripStatistics.isValidSpeed( + } else if (TripStatisticsBuilder.isValidSpeed( location.getTime(), location.getSpeed(), lastLocation.getTime(), lastLocation.getSpeed(), speedBuffer) - && (location.getSpeed() <= track.getMaxSpeed())) { + && (location.getSpeed() <= track.getStatistics().getMaxSpeed())) { speedBuffer.setNext(location.getSpeed()); } result[2] = speedBuffer.getAverage() * 3.6; diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java old mode 100755 new mode 100644 diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java old mode 100755 new mode 100644 index 9a8c57b43..c7b73f7d7 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java @@ -21,6 +21,7 @@ import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.TrackPointsColumns; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.WaypointsColumns; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.util.MyTracksUtils; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapActivity; @@ -657,14 +658,15 @@ public class MyTracksMap extends MapActivity if (track == null || mapView == null || track.getNumberOfPoints() < 2) { return; } - int latSpanE6 = track.getTop() - track.getBottom(); - int lonSpanE6 = track.getRight() - track.getLeft(); + TripStatistics stats = track.getStatistics(); + int latSpanE6 = stats.getTop() - stats.getBottom(); + int lonSpanE6 = stats.getRight() - stats.getLeft(); if (latSpanE6 > 0 && latSpanE6 < 180E6 && lonSpanE6 > 0 && lonSpanE6 < 180E6) { keepMyLocationVisible = false; GeoPoint center = new GeoPoint( - track.getBottom() + latSpanE6 / 2, - track.getLeft() + lonSpanE6 / 2); + stats.getBottom() + latSpanE6 / 2, + stats.getLeft() + lonSpanE6 / 2); if (MyTracksUtils.isValidGeoPoint(center)) { mapView.getController().setCenter(center); mapView.getController().zoomToSpan(latSpanE6, lonSpanE6); diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java old mode 100755 new mode 100644 diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java index 41f058c03..33b911762 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointDetails.java @@ -18,6 +18,7 @@ package com.google.android.apps.mytracks; import com.google.android.apps.mytracks.content.MyTracksProviderUtils; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.WaypointsColumns; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.maps.mytracks.R; import android.app.Activity; @@ -132,8 +133,9 @@ public class MyTracksWaypointDetails extends Activity detailsView.setVisibility(View.GONE); statsView.setVisibility(View.VISIBLE); iconId = R.drawable.ylw_pushpin; - utils.setAllStats(waypoint); - utils.setTime(R.id.total_time_register, waypoint.getTotalTime()); + TripStatistics waypointStats = waypoint.getStatistics(); + utils.setAllStats(waypointStats); + utils.setTime(R.id.total_time_register, waypointStats.getTotalTime()); utils.setAltitude( R.id.elevation_register, waypoint.getLocation().getAltitude()); break; diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java index 7a523060d..0e9d6040d 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksWaypointsList.java @@ -168,16 +168,20 @@ public class MyTracksWaypointsList extends ListActivity switch (v.getId()) { case R.id.waypointslist_btn_insert_waypoint: { long id = MyTracks.getInstance().insertWaypointMarker(); - Intent intent = new Intent(this, MyTracksWaypointDetails.class); - intent.putExtra("waypointid", id); - startActivity(intent); + if (id >= 0) { + Intent intent = new Intent(this, MyTracksWaypointDetails.class); + intent.putExtra("waypointid", id); + startActivity(intent); + } break; } case R.id.waypointslist_btn_insert_statistics: { long id = MyTracks.getInstance().insertStatisticsMarker(); - Intent intent = new Intent(this, MyTracksWaypointDetails.class); - intent.putExtra("waypointid", id); - startActivity(intent); + if (id >= 0) { + Intent intent = new Intent(this, MyTracksWaypointDetails.class); + intent.putExtra("waypointid", id); + startActivity(intent); + } break; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/StatsUtilities.java b/MyTracks/src/com/google/android/apps/mytracks/StatsUtilities.java index 74a512f97..b0c3c8b14 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/StatsUtilities.java +++ b/MyTracks/src/com/google/android/apps/mytracks/StatsUtilities.java @@ -15,8 +15,7 @@ */ package com.google.android.apps.mytracks; -import com.google.android.apps.mytracks.content.Track; -import com.google.android.apps.mytracks.content.Waypoint; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.UnitConversions; import com.google.android.maps.mytracks.R; @@ -219,32 +218,18 @@ public class StatsUtilities { setGrade(R.id.max_grade_register, maxGrade); } - public void setAllStats(Waypoint wpt) { - setTime(R.id.moving_time_register, wpt.getMovingTime()); - setDistance(R.id.total_distance_register, wpt.getTotalDistance() / 1000); - setSpeed(R.id.average_speed_register, wpt.getAverageSpeed() * 3.6); + public void setAllStats(TripStatistics stats) { + setTime(R.id.moving_time_register, stats.getMovingTime()); + setDistance(R.id.total_distance_register, stats.getTotalDistance() / 1000); + setSpeed(R.id.average_speed_register, stats.getAverageSpeed() * 3.6); setSpeed(R.id.average_moving_speed_register, - wpt.getAverageMovingSpeed() * 3.6); - setSpeed(R.id.max_speed_register, wpt.getMaxSpeed() * 3.6); - setAltitude(R.id.min_elevation_register, wpt.getMinElevation()); - setAltitude(R.id.max_elevation_register, wpt.getMaxElevation()); - setAltitude(R.id.elevation_gain_register, wpt.getTotalElevationGain()); - setGrade(R.id.min_grade_register, wpt.getMinGrade()); - setGrade(R.id.max_grade_register, wpt.getMaxGrade()); - } - - public void setAllStats(Track track) { - setTime(R.id.moving_time_register, track.getMovingTime()); - setDistance(R.id.total_distance_register, track.getTotalDistance() / 1000); - setSpeed(R.id.average_speed_register, track.getAverageSpeed() * 3.6); - setSpeed(R.id.average_moving_speed_register, - track.getAverageMovingSpeed() * 3.6); - setSpeed(R.id.max_speed_register, track.getMaxSpeed() * 3.6); - setAltitude(R.id.min_elevation_register, track.getMinElevation()); - setAltitude(R.id.max_elevation_register, track.getMaxElevation()); - setAltitude(R.id.elevation_gain_register, track.getTotalElevationGain()); - setGrade(R.id.min_grade_register, track.getMinGrade()); - setGrade(R.id.max_grade_register, track.getMaxGrade()); + stats.getAverageMovingSpeed() * 3.6); + setSpeed(R.id.max_speed_register, stats.getMaxSpeed() * 3.6); + setAltitude(R.id.min_elevation_register, stats.getMinElevation()); + setAltitude(R.id.max_elevation_register, stats.getMaxElevation()); + setAltitude(R.id.elevation_gain_register, stats.getTotalElevationGain()); + setGrade(R.id.min_grade_register, stats.getMinGrade()); + setGrade(R.id.max_grade_register, stats.getMaxGrade()); } public void setSpeedLabel(int id, int speedString, int paceString) { diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java index 5c0db42c5..42595ee16 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java @@ -15,6 +15,8 @@ */ package com.google.android.apps.mytracks.content; +import com.google.android.apps.mytracks.stats.TripStatistics; + import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -82,6 +84,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { */ private static ContentValues createContentValues(Track track) { ContentValues values = new ContentValues(); + TripStatistics stats = track.getStatistics(); + // Values id < 0 indicate no id is available: if (track.getId() >= 0) { values.put(TracksColumns._ID, track.getId()); @@ -92,29 +96,31 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { values.put(TracksColumns.CATEGORY, track.getCategory()); values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints()); values.put(TracksColumns.STARTID, track.getStartId()); - values.put(TracksColumns.STARTTIME, track.getStartTime()); - values.put(TracksColumns.STOPTIME, track.getStopTime()); + values.put(TracksColumns.STARTTIME, stats.getStartTime()); + values.put(TracksColumns.STOPTIME, stats.getStopTime()); values.put(TracksColumns.STOPID, track.getStopId()); - values.put(TracksColumns.TOTALDISTANCE, track.getTotalDistance()); - values.put(TracksColumns.TOTALTIME, track.getTotalTime()); - values.put(TracksColumns.MOVINGTIME, track.getMovingTime()); - values.put(TracksColumns.MAXLAT, track.getTop()); - values.put(TracksColumns.MINLAT, track.getBottom()); - values.put(TracksColumns.MAXLON, track.getRight()); - values.put(TracksColumns.MINLON, track.getLeft()); - values.put(TracksColumns.AVGSPEED, track.getAverageSpeed()); - values.put(TracksColumns.AVGMOVINGSPEED, track.getAverageMovingSpeed()); - values.put(TracksColumns.MAXSPEED, track.getMaxSpeed()); - values.put(TracksColumns.MINELEVATION, track.getMinElevation()); - values.put(TracksColumns.MAXELEVATION, track.getMaxElevation()); - values.put(TracksColumns.ELEVATIONGAIN, track.getTotalElevationGain()); - values.put(TracksColumns.MINGRADE, track.getMinGrade()); - values.put(TracksColumns.MAXGRADE, track.getMaxGrade()); + values.put(TracksColumns.TOTALDISTANCE, stats.getTotalDistance()); + values.put(TracksColumns.TOTALTIME, stats.getTotalTime()); + values.put(TracksColumns.MOVINGTIME, stats.getMovingTime()); + values.put(TracksColumns.MAXLAT, stats.getTop()); + values.put(TracksColumns.MINLAT, stats.getBottom()); + values.put(TracksColumns.MAXLON, stats.getRight()); + values.put(TracksColumns.MINLON, stats.getLeft()); + values.put(TracksColumns.AVGSPEED, stats.getAverageSpeed()); + values.put(TracksColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed()); + values.put(TracksColumns.MAXSPEED, stats.getMaxSpeed()); + values.put(TracksColumns.MINELEVATION, stats.getMinElevation()); + values.put(TracksColumns.MAXELEVATION, stats.getMaxElevation()); + values.put(TracksColumns.ELEVATIONGAIN, stats.getTotalElevationGain()); + values.put(TracksColumns.MINGRADE, stats.getMinGrade()); + values.put(TracksColumns.MAXGRADE, stats.getMaxGrade()); return values; } private static ContentValues createContentValues(Waypoint waypoint) { ContentValues values = new ContentValues(); + TripStatistics stats = waypoint.getStatistics(); + // Values id < 0 indicate no id is available: if (waypoint.getId() >= 0) { values.put(WaypointsColumns._ID, waypoint.getId()); @@ -127,23 +133,21 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { values.put(WaypointsColumns.TYPE, waypoint.getType()); values.put(WaypointsColumns.LENGTH, waypoint.getLength()); values.put(WaypointsColumns.DURATION, waypoint.getDuration()); - values.put(WaypointsColumns.STARTTIME, waypoint.getStartTime()); + values.put(WaypointsColumns.STARTTIME, stats.getStartTime()); values.put(WaypointsColumns.STARTID, waypoint.getStartId()); values.put(WaypointsColumns.STOPID, waypoint.getStopId()); - values.put(WaypointsColumns.TOTALDISTANCE, waypoint.getTotalDistance()); - values.put(WaypointsColumns.TOTALTIME, waypoint.getTotalTime()); - values.put(WaypointsColumns.MOVINGTIME, waypoint.getMovingTime()); - values.put(WaypointsColumns.AVGSPEED, waypoint.getAverageSpeed()); - values.put(WaypointsColumns.AVGMOVINGSPEED, - waypoint.getAverageMovingSpeed()); - values.put(WaypointsColumns.MAXSPEED, waypoint.getMaxSpeed()); - values.put(WaypointsColumns.MINELEVATION, waypoint.getMinElevation()); - values.put(WaypointsColumns.MAXELEVATION, waypoint.getMaxElevation()); - values.put(WaypointsColumns.ELEVATIONGAIN, - waypoint.getTotalElevationGain()); - values.put(WaypointsColumns.MINGRADE, waypoint.getMinGrade()); - values.put(WaypointsColumns.MAXGRADE, waypoint.getMaxGrade()); + values.put(WaypointsColumns.TOTALDISTANCE, stats.getTotalDistance()); + values.put(WaypointsColumns.TOTALTIME, stats.getTotalTime()); + values.put(WaypointsColumns.MOVINGTIME, stats.getMovingTime()); + values.put(WaypointsColumns.AVGSPEED, stats.getAverageSpeed()); + values.put(WaypointsColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed()); + values.put(WaypointsColumns.MAXSPEED, stats.getMaxSpeed()); + values.put(WaypointsColumns.MINELEVATION, stats.getMinElevation()); + values.put(WaypointsColumns.MAXELEVATION, stats.getMaxElevation()); + values.put(WaypointsColumns.ELEVATIONGAIN, stats.getTotalElevationGain()); + values.put(WaypointsColumns.MINGRADE, stats.getMinGrade()); + values.put(WaypointsColumns.MAXGRADE, stats.getMaxGrade()); Location location = waypoint.getLocation(); if (location != null) { @@ -232,9 +236,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE); int idxTotalTime = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME); int idxMovingTime = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME); - int idxAverageSpeed = cursor.getColumnIndexOrThrow(TracksColumns.AVGSPEED); - int idxAverageMovingSpeed = - cursor.getColumnIndexOrThrow(TracksColumns.AVGMOVINGSPEED); int idxMaxSpeed = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED); int idxMinElevation = cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION); @@ -246,6 +247,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { int idxMaxGrade = cursor.getColumnIndexOrThrow(TracksColumns.MAXGRADE); Track track = new Track(); + TripStatistics stats = track.getStatistics(); if (!cursor.isNull(idxId)) { track.setId(cursor.getLong(idxId)); } @@ -265,10 +267,10 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { track.setStartId(cursor.getInt(idxStartId)); } if (!cursor.isNull(idxStartTime)) { - track.setStartTime(cursor.getLong(idxStartTime)); + stats.setStartTime(cursor.getLong(idxStartTime)); } if (!cursor.isNull(idxStopTime)) { - track.setStopTime(cursor.getLong(idxStopTime)); + stats.setStopTime(cursor.getLong(idxStopTime)); } if (!cursor.isNull(idxStopId)) { track.setStopId(cursor.getInt(idxStopId)); @@ -277,13 +279,13 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { track.setNumberOfPoints(cursor.getInt(idxNumPoints)); } if (!cursor.isNull(idxTotalDistance)) { - track.setTotalDistance(cursor.getFloat(idxTotalDistance)); + stats.setTotalDistance(cursor.getFloat(idxTotalDistance)); } if (!cursor.isNull(idxTotalTime)) { - track.setTotalTime(cursor.getLong(idxTotalTime)); + stats.setTotalTime(cursor.getLong(idxTotalTime)); } if (!cursor.isNull(idxMovingTime)) { - track.setMovingTime(cursor.getLong(idxMovingTime)); + stats.setMovingTime(cursor.getLong(idxMovingTime)); } if (!cursor.isNull(idxMaxlat) && !cursor.isNull(idxMinlat) @@ -293,31 +295,25 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { int bottom = cursor.getInt(idxMinlat); int right = cursor.getInt(idxMaxlon); int left = cursor.getInt(idxMinlon); - track.setBounds(left, top, right, bottom); - } - if (!cursor.isNull(idxAverageSpeed)) { - track.setAverageSpeed(cursor.getFloat(idxAverageSpeed)); - } - if (!cursor.isNull(idxAverageMovingSpeed)) { - track.setAverageMovingSpeed(cursor.getFloat(idxAverageMovingSpeed)); + stats.setBounds(left, top, right, bottom); } if (!cursor.isNull(idxMaxSpeed)) { - track.setMaxSpeed(cursor.getFloat(idxMaxSpeed)); + stats.setMaxSpeed(cursor.getFloat(idxMaxSpeed)); } if (!cursor.isNull(idxMinElevation)) { - track.setMinElevation(cursor.getFloat(idxMinElevation)); + stats.setMinElevation(cursor.getFloat(idxMinElevation)); } if (!cursor.isNull(idxMaxElevation)) { - track.setMaxElevation(cursor.getFloat(idxMaxElevation)); + stats.setMaxElevation(cursor.getFloat(idxMaxElevation)); } if (!cursor.isNull(idxElevationGain)) { - track.setTotalElevationGain(cursor.getFloat(idxElevationGain)); + stats.setTotalElevationGain(cursor.getFloat(idxElevationGain)); } if (!cursor.isNull(idxMinGrade)) { - track.setMinGrade(cursor.getFloat(idxMinGrade)); + stats.setMinGrade(cursor.getFloat(idxMinGrade)); } if (!cursor.isNull(idxMaxGrade)) { - track.setMaxGrade(cursor.getFloat(idxMaxGrade)); + stats.setMaxGrade(cursor.getFloat(idxMaxGrade)); } return track; } @@ -343,10 +339,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { int idxTotalTime = cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALTIME); int idxMovingTime = cursor.getColumnIndexOrThrow(WaypointsColumns.MOVINGTIME); - int idxAverageSpeed = - cursor.getColumnIndexOrThrow(WaypointsColumns.AVGSPEED); - int idxAverageMovingSpeed = - cursor.getColumnIndexOrThrow(WaypointsColumns.AVGMOVINGSPEED); int idxMaxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXSPEED); int idxMinElevation = cursor.getColumnIndexOrThrow(WaypointsColumns.MINELEVATION); @@ -366,6 +358,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { int idxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.SPEED); Waypoint waypoint = new Waypoint(); + TripStatistics stats = waypoint.getStatistics(); + if (!cursor.isNull(idxId)) { waypoint.setId(cursor.getLong(idxId)); } @@ -394,7 +388,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { waypoint.setDuration(cursor.getLong(idxDuration)); } if (!cursor.isNull(idxStartTime)) { - waypoint.setStartTime(cursor.getLong(idxStartTime)); + stats.setStartTime(cursor.getLong(idxStartTime)); } if (!cursor.isNull(idxStartId)) { waypoint.setStartId(cursor.getLong(idxStartId)); @@ -403,37 +397,31 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { waypoint.setStopId(cursor.getLong(idxStopId)); } if (!cursor.isNull(idxTotalDistance)) { - waypoint.setTotalDistance(cursor.getFloat(idxTotalDistance)); + stats.setTotalDistance(cursor.getFloat(idxTotalDistance)); } if (!cursor.isNull(idxTotalTime)) { - waypoint.setTotalTime(cursor.getLong(idxTotalTime)); + stats.setTotalTime(cursor.getLong(idxTotalTime)); } if (!cursor.isNull(idxMovingTime)) { - waypoint.setMovingTime(cursor.getLong(idxMovingTime)); - } - if (!cursor.isNull(idxAverageSpeed)) { - waypoint.setAverageSpeed(cursor.getFloat(idxAverageSpeed)); - } - if (!cursor.isNull(idxAverageMovingSpeed)) { - waypoint.setAverageMovingSpeed(cursor.getFloat(idxAverageMovingSpeed)); + stats.setMovingTime(cursor.getLong(idxMovingTime)); } if (!cursor.isNull(idxMaxSpeed)) { - waypoint.setMaxSpeed(cursor.getFloat(idxMaxSpeed)); + stats.setMaxSpeed(cursor.getFloat(idxMaxSpeed)); } if (!cursor.isNull(idxMinElevation)) { - waypoint.setMinElevation(cursor.getFloat(idxMinElevation)); + stats.setMinElevation(cursor.getFloat(idxMinElevation)); } if (!cursor.isNull(idxMaxElevation)) { - waypoint.setMaxElevation(cursor.getFloat(idxMaxElevation)); + stats.setMaxElevation(cursor.getFloat(idxMaxElevation)); } if (!cursor.isNull(idxElevationGain)) { - waypoint.setTotalElevationGain(cursor.getFloat(idxElevationGain)); + stats.setTotalElevationGain(cursor.getFloat(idxElevationGain)); } if (!cursor.isNull(idxMinGrade)) { - waypoint.setMinGrade(cursor.getFloat(idxMinGrade)); + stats.setMinGrade(cursor.getFloat(idxMinGrade)); } if (!cursor.isNull(idxMaxGrade)) { - waypoint.setMaxGrade(cursor.getFloat(idxMaxGrade)); + stats.setMaxGrade(cursor.getFloat(idxMaxGrade)); } Location location = new Location(""); @@ -494,7 +482,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { if (nextWaypoint != null) { Log.d(MyTracksProvider.TAG, "Correcting marker " + nextWaypoint.getId() + " after deleted marker " + deletedWaypoint.getId()); - nextWaypoint.combine(deletedWaypoint); + nextWaypoint.getStatistics().merge(deletedWaypoint.getStatistics()); nextWaypoint.setDescription( descriptionGenerator.generateWaypointDescription(nextWaypoint)); if (!updateWaypoint(nextWaypoint)) { diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/Track.java b/MyTracks/src/com/google/android/apps/mytracks/content/Track.java index 866cca8af..f881c966b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/Track.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/Track.java @@ -15,6 +15,8 @@ */ package com.google.android.apps.mytracks.content; +import com.google.android.apps.mytracks.stats.TripStatistics; + import android.location.Location; import android.os.Parcel; import android.os.Parcelable; @@ -24,7 +26,10 @@ import java.util.ArrayList; /** * A class representing a (GPS) Track. * + * TODO: hashCode and equals + * * @author Leif Hendrik Wilden + * @author Rodrigo Damazio */ public class Track implements Parcelable { @@ -34,6 +39,7 @@ public class Track implements Parcelable { public static class Creator implements Parcelable.Creator { public Track createFromParcel(Parcel source) { + ClassLoader classLoader = getClass().getClassLoader(); Track track = new Track(); track.id = source.readLong(); track.name = source.readString(); @@ -42,28 +48,14 @@ public class Track implements Parcelable { track.category = source.readString(); track.startId = source.readLong(); track.stopId = source.readLong(); - track.startTime = source.readLong(); - track.stopTime = source.readLong(); - track.totalDistance = source.readDouble(); - track.totalTime = source.readLong(); - track.movingTime = source.readLong(); - track.averageSpeed = source.readDouble(); - track.averageMovingSpeed = source.readDouble(); - track.maxSpeed = source.readDouble(); - track.minElevation = source.readDouble(); - track.maxElevation = source.readDouble(); - track.totalElevationGain = source.readDouble(); - track.minGrade = source.readDouble(); - track.maxGrade = source.readDouble(); - track.left = source.readInt(); - track.top = source.readInt(); - track.right = source.readInt(); - track.bottom = source.readInt(); + track.stats = source.readParcelable(classLoader); + track.numberOfPoints = source.readInt(); for (int i = 0; i < track.numberOfPoints; ++i) { - Location loc = source.readParcelable(null); + Location loc = source.readParcelable(classLoader); track.locations.add(loc); } + return track; } @@ -75,10 +67,16 @@ public class Track implements Parcelable { public static final Creator CREATOR = new Creator(); /** - * The track points. + * The track points (which may not have been loaded). */ private ArrayList locations = new ArrayList(); + /** + * The number of location points (present even if the points themselves were + * not loaded). + */ + private int numberOfPoints = 0; + private long id = -1; private String name = ""; private String description = ""; @@ -87,34 +85,9 @@ public class Track implements Parcelable { private long stopId = -1; private String category = ""; - // Fields derived from locations. Use setDerivedFields() to set them: - //------------------------------------------------------------------- - - private long startTime = -1; - private long stopTime = -1; - private int numberOfPoints = 0; - private double totalDistance = 0; - private long totalTime = 0; - private long movingTime = 0; - private int left; - private int top; - private int right; - private int bottom; - - private double averageSpeed = 0; - private double averageMovingSpeed = 0; - private double maxSpeed = 0; - private double minElevation = 0; - private double maxElevation = 0; - private double totalElevationGain = 0; - private double minGrade = 0; - private double maxGrade = 0; + private TripStatistics stats = new TripStatistics(); public Track() { - left = Integer.MAX_VALUE; - top = Integer.MIN_VALUE; - right = Integer.MIN_VALUE; - bottom = Integer.MAX_VALUE; } public void writeToParcel(Parcel dest, int flags) { @@ -125,23 +98,8 @@ public class Track implements Parcelable { dest.writeString(category); dest.writeLong(startId); dest.writeLong(stopId); - dest.writeLong(startTime); - dest.writeLong(stopTime); - dest.writeDouble(totalDistance); - dest.writeLong(totalTime); - dest.writeLong(movingTime); - dest.writeDouble(averageSpeed); - dest.writeDouble(averageMovingSpeed); - dest.writeDouble(maxSpeed); - dest.writeDouble(minElevation); - dest.writeDouble(maxElevation); - dest.writeDouble(totalElevationGain); - dest.writeDouble(minGrade); - dest.writeDouble(maxGrade); - dest.writeInt(left); - dest.writeInt(top); - dest.writeInt(right); - dest.writeInt(bottom); + dest.writeParcelable(stats, 0); + dest.writeInt(numberOfPoints); for (int i = 0; i < numberOfPoints; ++i) { dest.writeParcelable(locations.get(i), 0); @@ -151,29 +109,6 @@ public class Track implements Parcelable { // Getters and setters: //--------------------- - public int getLeft() { - return left; - } - - public int getTop() { - return top; - } - - public int getRight() { - return right; - } - - public int getBottom() { - return bottom; - } - - public void setBounds(int aLeft, int aTop, int aRight, int aBottom) { - left = aLeft; - top = aTop; - right = aRight; - bottom = aBottom; - } - public int describeContents() { return 0; } @@ -186,46 +121,6 @@ public class Track implements Parcelable { this.id = id; } - public double getTotalDistance() { - return totalDistance; - } - - public void setTotalDistance(double totalDistance) { - this.totalDistance = totalDistance; - } - - public long getTotalTime() { - return totalTime; - } - - public void setTotalTime(long totalTime) { - this.totalTime = totalTime; - } - - public long getMovingTime() { - return movingTime; - } - - public void setMovingTime(long movingTime) { - this.movingTime = movingTime; - } - - public long getStartTime() { - return startTime; - } - - public void setStartTime(long startTime) { - this.startTime = startTime; - } - - public long getStopTime() { - return stopTime; - } - - public void setStopTime(long stopTime) { - this.stopTime = stopTime; - } - public String getName() { return name; } @@ -274,70 +169,6 @@ public class Track implements Parcelable { this.category = category; } - public double getAverageSpeed() { - return averageSpeed; - } - - public void setAverageSpeed(double averageSpeed) { - this.averageSpeed = averageSpeed; - } - - public double getAverageMovingSpeed() { - return averageMovingSpeed; - } - - public void setAverageMovingSpeed(double averageMovingSpeed) { - this.averageMovingSpeed = averageMovingSpeed; - } - - public double getMaxSpeed() { - return maxSpeed; - } - - public void setMaxSpeed(double maxSpeed) { - this.maxSpeed = maxSpeed; - } - - public double getMinElevation() { - return minElevation; - } - - public void setMinElevation(double minElevation) { - this.minElevation = minElevation; - } - - public double getMaxElevation() { - return maxElevation; - } - - public void setMaxElevation(double maxElevation) { - this.maxElevation = maxElevation; - } - - public double getTotalElevationGain() { - return totalElevationGain; - } - - public void setTotalElevationGain(double totalElevationGain) { - this.totalElevationGain = totalElevationGain; - } - - public double getMinGrade() { - return minGrade; - } - - public void setMinGrade(double minGrade) { - this.minGrade = minGrade; - } - - public double getMaxGrade() { - return maxGrade; - } - - public void setMaxGrade(double maxGrade) { - this.maxGrade = maxGrade; - } - public int getNumberOfPoints() { return numberOfPoints; } @@ -357,4 +188,12 @@ public class Track implements Parcelable { public void setLocations(ArrayList locations) { this.locations = locations; } + + public TripStatistics getStatistics() { + return stats; + } + + public void setStatistics(TripStatistics stats) { + this.stats = stats; + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/Waypoint.java b/MyTracks/src/com/google/android/apps/mytracks/content/Waypoint.java index c77223745..4507a4276 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/Waypoint.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/Waypoint.java @@ -15,6 +15,8 @@ */ package com.google.android.apps.mytracks.content; +import com.google.android.apps.mytracks.stats.TripStatistics; + import android.location.Location; import android.os.Parcel; import android.os.Parcelable; @@ -23,16 +25,20 @@ import android.os.Parcelable; * A way point. It has a location, meta data such as name, description, * category, and icon, plus it can store track statistics for a "sub-track". * + * TODO: hashCode and equals + * * @author Leif Hendrik Wilden + * @author Rodrigo Damazio */ public final class Waypoint implements Parcelable { /** - * Creator for a Waypoint object + * Creator for a Waypoint object */ public static class Creator implements Parcelable.Creator { public Waypoint createFromParcel(Parcel source) { + ClassLoader classLoader = getClass().getClassLoader(); Waypoint waypoint = new Waypoint(); waypoint.id = source.readLong(); waypoint.name = source.readString(); @@ -41,26 +47,13 @@ public final class Waypoint implements Parcelable { waypoint.icon = source.readString(); waypoint.trackId = source.readLong(); waypoint.type = source.readInt(); - waypoint.length = source.readDouble(); - waypoint.duration = source.readLong(); - waypoint.startTime = source.readLong(); waypoint.startId = source.readLong(); waypoint.stopId = source.readLong(); + waypoint.stats = source.readParcelable(classLoader); byte hasLocation = source.readByte(); if (hasLocation > 0) { - waypoint.location = source.readParcelable(null); + waypoint.location = source.readParcelable(classLoader); } - waypoint.totalDistance = source.readDouble(); - waypoint.totalTime = source.readLong(); - waypoint.movingTime = source.readLong(); - waypoint.averageSpeed = source.readDouble(); - waypoint.averageMovingSpeed = source.readDouble(); - waypoint.maxSpeed = source.readDouble(); - waypoint.minElevation = source.readDouble(); - waypoint.maxElevation = source.readDouble(); - waypoint.totalElevationGain = source.readDouble(); - waypoint.minGrade = source.readDouble(); - waypoint.maxGrade = source.readDouble(); return waypoint; } @@ -84,28 +77,18 @@ public final class Waypoint implements Parcelable { private Location location; - /** Length along the track in meters. */ - private double length = 0; - /** Time along the track in seconds. */ - private long duration = 0; - /** Start time of a segment */ - private long startTime = -1; /** Start track point id */ private long startId = -1; /** Stop track point id */ private long stopId = -1; - private double totalDistance = 0; - private long totalTime = 0; - private long movingTime = 0; - private double averageSpeed = 0; - private double averageMovingSpeed = 0; - private double maxSpeed = 0; - private double minElevation = 0; - private double maxElevation = 0; - private double totalElevationGain = 0; - private double minGrade = 0; - private double maxGrade = 0; + private TripStatistics stats = new TripStatistics(); + + /** The length of the track, without smoothing. */ + private double length; + + /** The total duration of the track (not from the last waypoint) */ + private long duration; public void writeToParcel(Parcel dest, int flags) { dest.writeLong(id); @@ -115,46 +98,13 @@ public final class Waypoint implements Parcelable { dest.writeString(icon); dest.writeLong(trackId); dest.writeInt(type); - dest.writeDouble(length); - dest.writeLong(duration); - dest.writeLong(startTime); dest.writeLong(startId); dest.writeLong(stopId); + dest.writeParcelable(stats, 0); dest.writeByte(location == null ? (byte) 0 : (byte) 1); if (location != null) { dest.writeParcelable(location, 0); } - dest.writeDouble(totalDistance); - dest.writeLong(totalTime); - dest.writeLong(movingTime); - dest.writeDouble(averageSpeed); - dest.writeDouble(averageMovingSpeed); - dest.writeDouble(maxSpeed); - dest.writeDouble(minElevation); - dest.writeDouble(maxElevation); - dest.writeDouble(totalElevationGain); - dest.writeDouble(minGrade); - dest.writeDouble(maxGrade); - } - - /** - * Combines the statistics from another (earlier) waypoint with this one. - * This is needed when the user deletes a statistic waypoint. - * - * @param other the other waypoint - */ - public void combine(Waypoint other) { - totalTime += other.totalTime; - movingTime += other.movingTime; - totalDistance += other.totalDistance; - totalElevationGain += other.totalElevationGain; - averageSpeed = totalDistance / totalTime; - averageMovingSpeed = totalDistance / movingTime; - maxSpeed = Math.max(maxSpeed, other.maxSpeed); - minElevation = Math.min(minElevation, other.minElevation); - maxElevation = Math.max(maxElevation, other.maxElevation); - minGrade = Math.min(minGrade, other.minGrade); - maxGrade = Math.max(maxGrade, other.maxGrade); } // Getters and setters: @@ -188,30 +138,6 @@ public final class Waypoint implements Parcelable { this.id = id; } - public double getTotalDistance() { - return totalDistance; - } - - public void setTotalDistance(double totalDistance) { - this.totalDistance = totalDistance; - } - - public long getTotalTime() { - return totalTime; - } - - public void setTotalTime(long totalTime) { - this.totalTime = totalTime; - } - - public long getMovingTime() { - return movingTime; - } - - public void setMovingTime(long movingTime) { - this.movingTime = movingTime; - } - public String getName() { return name; } @@ -232,30 +158,6 @@ public final class Waypoint implements Parcelable { this.type = type; } - public double getLength() { - return length; - } - - public void setLength(double length) { - this.length = length; - } - - public long getDuration() { - return duration; - } - - public void setDuration(long duration) { - this.duration = duration; - } - - public long getStartTime() { - return startTime; - } - - public void setStartTime(long startTime) { - this.startTime = startTime; - } - public long getStartId() { return startId; } @@ -288,71 +190,34 @@ public final class Waypoint implements Parcelable { this.category = category; } - public double getAverageSpeed() { - return averageSpeed; - } - - public void setAverageSpeed(double averageSpeed) { - this.averageSpeed = averageSpeed; - } - - public double getAverageMovingSpeed() { - return averageMovingSpeed; - } - - public void setAverageMovingSpeed(double averageMovingSpeed) { - this.averageMovingSpeed = averageMovingSpeed; - } - - public double getMaxSpeed() { - return maxSpeed; - } - - public void setMaxSpeed(double maxSpeed) { - this.maxSpeed = maxSpeed; - } - - public double getMinElevation() { - return minElevation; - } - - public void setMinElevation(double minElevation) { - this.minElevation = minElevation; - } - - public double getMaxElevation() { - return maxElevation; - } - - public void setMaxElevation(double maxElevation) { - this.maxElevation = maxElevation; - } - - public double getTotalElevationGain() { - return totalElevationGain; - } - - public void setTotalElevationGain(double totalElevationGain) { - this.totalElevationGain = totalElevationGain; - } - - public double getMinGrade() { - return minGrade; - } - - public void setMinGrade(double minGrade) { - this.minGrade = minGrade; - } - - public double getMaxGrade() { - return maxGrade; - } - - public void setMaxGrade(double maxGrade) { - this.maxGrade = maxGrade; - } - public void setLocation(Location location) { this.location = location; } + + public TripStatistics getStatistics() { + return stats; + } + + public void setStatistics(TripStatistics stats) { + this.stats = stats; + } + + // WARNING: These fields are used for internal state keeping. You probably + // want to look at getStatistics instead. + + public double getLength() { + return length; + } + + public void setLength(double length) { + this.length = length; + } + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/GpxImport.java b/MyTracks/src/com/google/android/apps/mytracks/io/GpxImport.java index 968f3138a..2bb46ab57 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/GpxImport.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/GpxImport.java @@ -1,12 +1,12 @@ /* * Copyright 2008 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -16,7 +16,7 @@ package com.google.android.apps.mytracks.io; import com.google.android.apps.mytracks.content.Track; -import com.google.android.apps.mytracks.stats.TripStatistics; +import com.google.android.apps.mytracks.stats.TripStatisticsBuilder; import com.google.android.apps.mytracks.util.MyTracksUtils; import android.location.Location; @@ -214,21 +214,21 @@ public class GpxImport { if (locations.size() > 0) { long startTime = locations.get(0).getTime(); - track.setStartTime(startTime); // Calculate statistics for the imported track - TripStatistics stats = new TripStatistics(startTime); + TripStatisticsBuilder statsBuilder = new TripStatisticsBuilder(); + statsBuilder.resumeAt(startTime); for (Location location : locations) { if (MyTracksUtils.isValidLocation(location)) { /* Any time works here. The totalTime will be set by "pauseAt" later: */ - stats.addLocation(location, location.getTime()); + statsBuilder.addLocation(location, location.getTime()); } } - long lastPointTime = locations.get(locations.size() - 1).getTime(); - stats.pauseAt(lastPointTime); - track.setStopTime(lastPointTime); - stats.fillStatisticsForTrack(track); + long lastPointTime = locations.get(locations.size() - 1).getTime(); + statsBuilder.pauseAt(lastPointTime); + + track.setStatistics(statsBuilder.getStatistics()); } } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java index 6fab274c6..163e29b03 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToDocs.java @@ -24,6 +24,7 @@ import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.io.gdata.GDataClientFactory; import com.google.android.apps.mytracks.io.gdata.GDataWrapper; import com.google.android.apps.mytracks.io.gdata.GDataWrapper.QueryFunction; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.util.ResourceUtils; import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.UnitConversions; @@ -429,6 +430,7 @@ public class SendToDocs { String worksheetId) throws IOException { String worksheetUri = String.format(DOCS_SPREADSHEET_URL_FORMAT, spreadsheetId, worksheetId); + TripStatistics stats = track.getStatistics(); /* Prepare the Post-Text we are going to send. */ StringBuilder sb = new StringBuilder(); @@ -437,29 +439,29 @@ public class SendToDocs { + "2006/extended'>"); appendTag("name", track.getName(), sb); appendTag("description", track.getDescription(), sb); - appendTag("date", String.format("%tc", track.getStartTime()), sb); + appendTag("date", String.format("%tc", stats.getStartTime()), sb); appendTag("totaltime", StringUtils.formatTimeAlwaysShowingHours( - track.getTotalTime()), sb); + stats.getTotalTime()), sb); appendTag("movingtime", StringUtils.formatTimeAlwaysShowingHours( - track.getMovingTime()), sb); - appendLargeUnitsTag("distance", track.getTotalDistance() / 1000, sb); + stats.getMovingTime()), sb); + appendLargeUnitsTag("distance", stats.getTotalDistance() / 1000, sb); appendTag("distanceunit", metricUnits ? activity.getString(R.string.kilometer) : activity.getString(R.string.mile), sb); - appendLargeUnitsTag("averagespeed", track.getAverageSpeed() * 3.6, sb); + appendLargeUnitsTag("averagespeed", stats.getAverageSpeed() * 3.6, sb); appendLargeUnitsTag("averagemovingspeed", - track.getAverageMovingSpeed() * 3.6, sb); - appendLargeUnitsTag("maxspeed", track.getMaxSpeed() * 3.6, sb); + stats.getAverageMovingSpeed() * 3.6, sb); + appendLargeUnitsTag("maxspeed", stats.getMaxSpeed() * 3.6, sb); appendTag("speedunit", metricUnits ? activity.getString(R.string.kilometer_per_hour) : activity.getString(R.string.mile_per_hour), sb); - appendSmallUnitsTag("elevationgain", track.getTotalElevationGain(), sb); - appendSmallUnitsTag("minelevation", track.getMinElevation(), sb); - appendSmallUnitsTag("maxelevation", track.getMaxElevation(), sb); + appendSmallUnitsTag("elevationgain", stats.getTotalElevationGain(), sb); + appendSmallUnitsTag("minelevation", stats.getMinElevation(), sb); + appendSmallUnitsTag("maxelevation", stats.getMaxElevation(), sb); appendTag("elevationunit", metricUnits ? activity.getString(R.string.meter) diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java index 4c5897fca..cfebe4539 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java @@ -28,6 +28,7 @@ import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.TrackBuffer; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.stats.DoubleBuffer; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.util.MyTracksUtils; import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.UnitConversions; @@ -105,12 +106,14 @@ public class SendToMyMaps { // Create segments from each full track: Track segment = new Track(); + TripStatistics segmentStats = segment.getStatistics(); + TripStatistics trackStats = track.getStatistics(); segment.setId(track.getId()); segment.setName(track.getName()); segment.setDescription(/* track.getDescription() */ ""); segment.setCategory(track.getCategory()); - segment.setStartTime(track.getStartTime()); - segment.setStopTime(track.getStopTime()); + segmentStats.setStartTime(trackStats.getStartTime()); + segmentStats.setStopTime(trackStats.getStopTime()); boolean startNewTrackSegment = false; for (int i = 0; i < buffer.getLocationsLoaded(); ++i) { Location loc = buffer.get(i); @@ -134,8 +137,8 @@ public class SendToMyMaps { if (loc.getLatitude() <= 90) { segment.addLocation(loc); - if (segment.getStartTime() < 0) { - segment.setStartTime(loc.getTime()); + if (segmentStats.getStartTime() < 0) { + segmentStats.setStartTime(loc.getTime()); } } } @@ -159,8 +162,10 @@ public class SendToMyMaps { */ private void prepareTrackSegment( Track segment, ArrayList splitTracks) { - if (segment.getStopTime() < 0 && segment.getLocations().size() > 0) { - segment.setStopTime(segment.getLocations().size() - 1); + TripStatistics segmentStats = segment.getStatistics(); + if (segmentStats.getStopTime() < 0 + && segment.getLocations().size() > 0) { + segmentStats.setStopTime(segment.getLocations().size() - 1); } /* diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/ITrackRecordingService.aidl b/MyTracks/src/com/google/android/apps/mytracks/services/ITrackRecordingService.aidl index 6e692311b..c13e04d1e 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/ITrackRecordingService.aidl +++ b/MyTracks/src/com/google/android/apps/mytracks/services/ITrackRecordingService.aidl @@ -16,7 +16,6 @@ package com.google.android.apps.mytracks.services; import com.google.android.apps.mytracks.content.Waypoint; -import com.google.android.apps.mytracks.stats.TripStatisticsData; /** * MyTracks service. diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index e415aa5ef..fc952387d 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -24,6 +24,7 @@ import com.google.android.apps.mytracks.content.TracksColumns; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.WaypointsColumns; import com.google.android.apps.mytracks.stats.TripStatistics; +import com.google.android.apps.mytracks.stats.TripStatisticsBuilder; import com.google.android.apps.mytracks.util.MyTracksUtils; import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.maps.mytracks.R; @@ -92,8 +93,8 @@ public class TrackRecordingService extends Service implements LocationListener { */ private MyTracksProviderUtils providerUtils; - private TripStatistics stats = new TripStatistics(); - private TripStatistics waypointStats = new TripStatistics(); + private TripStatisticsBuilder statsBuilder = new TripStatisticsBuilder(); + private TripStatisticsBuilder waypointStatsBuilder = new TripStatisticsBuilder(); /** * Current length of the recorded track. This length is calculated from the @@ -229,6 +230,7 @@ public class TrackRecordingService extends Service implements LocationListener { if (lastRecordedLocation != null && lastRecordedLocation.getLatitude() < 90) { ContentValues values = new ContentValues(); + TripStatistics stats = statsBuilder.getStatistics(); if (recordingTrack.getStartId() < 0) { values.put(TracksColumns.STARTID, pointId); recordingTrack.setStartId(pointId); @@ -271,10 +273,11 @@ public class TrackRecordingService extends Service implements LocationListener { private void updateCurrentWaypoint() { if (currentWaypointId >= 0) { ContentValues values = new ContentValues(); + TripStatistics waypointStats = waypointStatsBuilder.getStatistics(); values.put(WaypointsColumns.STARTTIME, waypointStats.getStartTime()); values.put(WaypointsColumns.LENGTH, length); - values.put(WaypointsColumns.DURATION, - System.currentTimeMillis() - stats.getStartTime()); + values.put(WaypointsColumns.DURATION, System.currentTimeMillis() + - statsBuilder.getStatistics().getStartTime()); values.put(WaypointsColumns.TOTALDISTANCE, waypointStats.getTotalDistance()); values.put(WaypointsColumns.TOTALTIME, waypointStats.getTotalTime()); @@ -397,7 +400,9 @@ public class TrackRecordingService extends Service implements LocationListener { return; } - stats = new TripStatistics(track.getStartTime()); + TripStatistics stats = track.getStatistics(); + statsBuilder = new TripStatisticsBuilder(); + statsBuilder.resumeAt(stats.getStartTime()); if (executer != null) { executer.scheduleTask(announcementFrequency * 60000); } @@ -410,10 +415,11 @@ public class TrackRecordingService extends Service implements LocationListener { Waypoint waypoint = providerUtils.getFirstWaypoint(recordingTrackId); if (waypoint != null) { currentWaypointId = waypoint.getId(); - waypointStats = new TripStatistics(waypoint); + waypointStatsBuilder = new TripStatisticsBuilder(waypoint.getStatistics()); } else { // This should never happen, but we got to do something so life goes on: - waypointStats = new TripStatistics(track.getStartTime()); + waypointStatsBuilder = new TripStatisticsBuilder(); + waypointStatsBuilder.resumeAt(stats.getStartTime()); currentWaypointId = -1; } @@ -427,7 +433,7 @@ public class TrackRecordingService extends Service implements LocationListener { do { Location location = providerUtils.createLocation(cursor); if (MyTracksUtils.isValidLocation(location)) { - stats.addLocation(location, location.getTime()); + statsBuilder.addLocation(location, location.getTime()); if (lastValidLocation != null) { length += location.distanceTo(lastValidLocation); } @@ -435,9 +441,9 @@ public class TrackRecordingService extends Service implements LocationListener { } } while (cursor.moveToPrevious()); } - stats.setMovingTime(track.getMovingTime()); - stats.pauseAt(track.getStopTime()); - stats.resume(); + statsBuilder.getStatistics().setMovingTime(stats.getMovingTime()); + statsBuilder.pauseAt(stats.getStopTime()); + statsBuilder.resume(); } else { Log.e(MyTracksConstants.TAG, "Could not get track points cursor."); } @@ -494,12 +500,12 @@ public class TrackRecordingService extends Service implements LocationListener { if (MyTracksUtils.isValidLocation(location)) { long now = System.currentTimeMillis(); - stats.addLocation(location, now); - waypointStats.addLocation(location, now); + statsBuilder.addLocation(location, now); + waypointStatsBuilder.addLocation(location, now); } // Update the idle time if needed. - locationListenerPolicy.updateIdleTime(stats.getIdleTime()); + locationListenerPolicy.updateIdleTime(statsBuilder.getIdleTime()); if (currentRecordingInterval != locationListenerPolicy.getDesiredPollingInterval()) { registerLocationListener(); @@ -798,8 +804,8 @@ public class TrackRecordingService extends Service implements LocationListener { public long insertWaypointMarker(Waypoint waypoint) { if (waypoint.getLocation() != null) { waypoint.setLength(length); - waypoint.setDuration( - waypoint.getLocation().getTime() - stats.getStartTime()); + waypoint.setDuration(waypoint.getLocation().getTime() + - statsBuilder.getStatistics().getStartTime()); Uri uri = providerUtils.insertWaypoint(waypoint); return Long.parseLong(uri.getLastPathSegment()); } @@ -814,22 +820,35 @@ public class TrackRecordingService extends Service implements LocationListener { * @return the unique id of the inserted marker */ public long insertStatisticsMarker(Location location) { + StringUtils utils = new StringUtils(TrackRecordingService.this); + + // Create a new waypoint to save Waypoint waypoint = new Waypoint(); + + // Set stop and total time in the stats data + final long time = System.currentTimeMillis(); + waypointStatsBuilder.pauseAt(time); + + // Override the duration - it's not the duration from the last waypoint, but + // the duration from the beginning of the whole track + waypoint.setDuration(time - statsBuilder.getStatistics().getStartTime()); + + // Set the rest of the waypoint data waypoint.setTrackId(recordingTrackId); waypoint.setType(Waypoint.TYPE_STATISTICS); waypoint.setName(TrackRecordingService.this.getString(R.string.statistics)); - waypointStats.fillStatisticsForWaypoint(waypoint); - StringUtils utils = new StringUtils(TrackRecordingService.this); + waypoint.setStatistics(waypointStatsBuilder.getStatistics()); waypoint.setDescription(utils.generateWaypointDescription(waypoint)); waypoint.setLocation(location); waypoint.setIcon(STATISTICS_ICON_URL); waypoint.setLength(length); - final long time = System.currentTimeMillis(); - waypoint.setDuration(time - stats.getStartTime()); - waypoint.setStartTime(waypointStats.getStartTime()); + waypoint.setStartId(providerUtils.getLastLocationId(recordingTrackId)); Uri uri = providerUtils.insertWaypoint(waypoint); - waypointStats = new TripStatistics(time); + + // Create a new stats keeper for the next marker + waypointStatsBuilder = new TripStatisticsBuilder(); + waypointStatsBuilder.resumeAt(time); updateCurrentWaypoint(); return Long.parseLong(uri.getLastPathSegment()); } @@ -858,8 +877,10 @@ public class TrackRecordingService extends Service implements LocationListener { public long startNewTrack() { Log.d(MyTracksConstants.TAG, "TrackRecordingService.startNewTrack"); Track track = new Track(); + TripStatistics trackStats = track.getStatistics(); track.setName("new"); - track.setStartTime(System.currentTimeMillis()); + long startTime = System.currentTimeMillis(); + trackStats.setStartTime(startTime); track.setStartId(-1); Uri trackUri = providerUtils.insertTrack(track); long trackId = Long.parseLong(trackUri.getLastPathSegment()); @@ -870,7 +891,8 @@ public class TrackRecordingService extends Service implements LocationListener { currentWaypointId = insertStatisticsMarker(null); isRecording = true; isMoving = true; - stats = new TripStatistics(track.getStartTime()); + statsBuilder = new TripStatisticsBuilder(); + statsBuilder.resumeAt(startTime); if (announcementFrequency != -1 && executer != null) { executer.scheduleTask(announcementFrequency * 60000); } @@ -912,9 +934,9 @@ public class TrackRecordingService extends Service implements LocationListener { isRecording = false; Track recordingTrack = providerUtils.getTrack(recordingTrackId); if (recordingTrack != null) { - recordingTrack.setStopTime(System.currentTimeMillis()); - recordingTrack.setTotalTime( - recordingTrack.getStopTime() - recordingTrack.getStartTime()); + TripStatistics stats = recordingTrack.getStatistics(); + stats.setStopTime(System.currentTimeMillis()); + stats.setTotalTime(stats.getStopTime() - stats.getStartTime()); long lastRecordedLocationId = providerUtils.getLastLocationId(recordingTrackId); ContentValues values = new ContentValues(); @@ -922,8 +944,8 @@ public class TrackRecordingService extends Service implements LocationListener { && recordingTrack.getStopId() >= 0) { values.put(TracksColumns.STOPID, lastRecordedLocationId); } - values.put(TracksColumns.STOPTIME, recordingTrack.getStopTime()); - values.put(TracksColumns.TOTALTIME, recordingTrack.getTotalTime()); + values.put(TracksColumns.STOPTIME, stats.getStopTime()); + values.put(TracksColumns.TOTALTIME, stats.getTotalTime()); getContentResolver().update(TracksColumns.CONTENT_URI, values, "_id=" + recordingTrack.getId(), null); } @@ -951,7 +973,7 @@ public class TrackRecordingService extends Service implements LocationListener { }; TripStatistics getTripStatistics() { - return stats; + return statsBuilder.getStatistics(); } Location getLastLocation() { diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/DoubleBuffer.java b/MyTracks/src/com/google/android/apps/mytracks/stats/DoubleBuffer.java old mode 100755 new mode 100644 diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/ExtremityMonitor.java b/MyTracks/src/com/google/android/apps/mytracks/stats/ExtremityMonitor.java index 8fdfeead4..5b7aa855d 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/stats/ExtremityMonitor.java +++ b/MyTracks/src/com/google/android/apps/mytracks/stats/ExtremityMonitor.java @@ -84,10 +84,23 @@ public class ExtremityMonitor { /** * Sets the minimum and maximum values. - * Useful when restoring state. */ public void set(double min, double max) { this.min = min; this.max = max; } + + /** + * Sets the minimum value. + */ + public void setMin(double min) { + this.min = min; + } + + /** + * Sets the maximum value. + */ + public void setMax(double max) { + this.max = max; + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsData.aidl b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatistics.aidl similarity index 61% rename from MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsData.aidl rename to MyTracks/src/com/google/android/apps/mytracks/stats/TripStatistics.aidl index 94e71fd63..b411f6ccd 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsData.aidl +++ b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatistics.aidl @@ -1,2 +1,2 @@ package com.google.android.apps.mytracks.stats; -parcelable TripStatisticsData; \ No newline at end of file +parcelable TripStatistics; \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatistics.java b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatistics.java index a07d7b77e..85c7f421c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatistics.java +++ b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright 2009 Google Inc. + * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -13,438 +13,133 @@ * License for the specific language governing permissions and limitations under * the License. */ - package com.google.android.apps.mytracks.stats; -import com.google.android.apps.mytracks.MyTracksConstants; -import com.google.android.apps.mytracks.content.Track; -import com.google.android.apps.mytracks.content.Waypoint; - -import android.location.Location; -import android.util.Log; +import android.os.Parcel; +import android.os.Parcelable; /** - * Statistics keeper for a trip. - * - * @author Sandor Dornbush + * Statistical data about a trip. + * The data in this class should be filled out by {@link TripStatisticsBuilder}. + * + * TODO: hashCode and equals + * * @author Rodrigo Damazio */ -public class TripStatistics { - /** - * Statistical data about the trip, which can be displayed to the user. - */ - private final TripStatisticsData data = new TripStatisticsData(); +public class TripStatistics implements Parcelable { /** - * The last location that the gps reported. + * The start time for the trip. This is system time which might not match gps + * time. */ - private Location lastLocation = null; + private long startTime = -1; /** - * The last location that contributed to the stats. It is also the last - * location the user was found to be moving. + * The stop time for the trip. This is the system time which might not match + * gps time. */ - private Location lastMovingLocation = null; + private long stopTime = -1; /** - * Is the trip currently paused? - * All trips start paused. + * The total time that we believe the user was traveling in milliseconds. */ - private boolean paused = true; + private long movingTime; /** - * A buffer of the last speed readings in meters/second. + * The total time of the trip in milliseconds. + * This is only updated when new points are received, so it may be stale. */ - private final DoubleBuffer speedBuffer = - new DoubleBuffer(MyTracksConstants.SPEED_SMOOTHING_FACTOR); + private long totalTime; /** - * A buffer of the recent elevation readings in meters. + * The total distance in meters that the user traveled on this trip. */ - private final DoubleBuffer elevationBuffer = - new DoubleBuffer(MyTracksConstants.ELEVATION_SMOOTHING_FACTOR); + private double totalDistance; /** - * A buffer of the distance between recent gps readings in meters. + * The total elevation gained on this trip in meters. */ - private final DoubleBuffer distanceBuffer = - new DoubleBuffer(MyTracksConstants.DISTANCE_SMOOTHING_FACTOR); + private double totalElevationGain; /** - * A buffer of the recent grade calculations. + * The maximum speed in meters/second reported that we believe to be a valid + * speed. */ - private final DoubleBuffer gradeBuffer = - new DoubleBuffer(MyTracksConstants.GRADE_SMOOTHING_FACTOR); + private double maxSpeed; /** - * The total number of locations in this trip. + * The min and max latitude values seen in this trip. */ - private long totalLocations = 0; + private final ExtremityMonitor latitudeExtremities = new ExtremityMonitor(); /** - * Creates a new trip starting at the current system time. + * The min and max longitude values seen in this trip. + */ + private final ExtremityMonitor longitudeExtremities = new ExtremityMonitor(); + + /** + * The min and max elevation seen on this trip in meters. + */ + private final ExtremityMonitor elevationExtremities = new ExtremityMonitor(); + + /** + * The minimum and maximum grade calculations on this trip. + */ + private final ExtremityMonitor gradeExtremities = new ExtremityMonitor(); + + /** + * Default constructor. */ public TripStatistics() { - data.startTime = System.currentTimeMillis(); } /** - * Creates a new trip at the given start time. - * - * @param startTime the time that the trip started + * Copy constructor. + * + * @param other another statistics data object to copy from */ - public TripStatistics(long startTime) { - data.startTime = startTime; + public TripStatistics(TripStatistics other) { + this.maxSpeed = other.maxSpeed; + this.movingTime = other.movingTime; + this.startTime = other.startTime; + this.stopTime = other.stopTime; + this.totalDistance = other.totalDistance; + this.totalElevationGain = other.totalElevationGain; + this.totalTime = other.totalTime; + + this.latitudeExtremities.set(other.latitudeExtremities.getMin(), + other.latitudeExtremities.getMax()); + this.longitudeExtremities.set(other.longitudeExtremities.getMin(), + other.longitudeExtremities.getMax()); + this.elevationExtremities.set(other.elevationExtremities.getMin(), + other.elevationExtremities.getMax()); + this.gradeExtremities.set(other.gradeExtremities.getMin(), + other.gradeExtremities.getMax()); } /** - * Creates a new trip using the waypoint for start time and other information. - * - * @param waypoint the waypoint to get starting information from + * Combines these statistics with those from another object. + * This assumes that the time periods covered by each do not intersect. + * + * @param other the other waypoint */ - public TripStatistics(Waypoint waypoint) { - data.startTime = waypoint.getStartTime(); - data.totalTime = waypoint.getTotalTime(); - data.movingTime = waypoint.getMovingTime(); - data.totalDistance = waypoint.getTotalDistance(); - data.totalElevationGain = waypoint.getTotalElevationGain(); - data.maxSpeed = waypoint.getMaxSpeed(); - } + public void merge(TripStatistics other) { + startTime = Math.min(startTime, other.startTime); + stopTime = Math.max(stopTime, other.stopTime); + totalTime += other.totalTime; + movingTime += other.movingTime; + totalDistance += other.totalDistance; + totalElevationGain += other.totalElevationGain; + maxSpeed = Math.max(maxSpeed, other.maxSpeed); - /** - * Adds a location to the current trip. This will update all of the internal - * variables with this new location. - * - * @param currentLocation the current gps location - * @param systemTime the time used for calculation of totalTime. This should - * be the phone's time (not GPS time) - * @return true if the person is moving - */ - public boolean addLocation(Location currentLocation, long systemTime) { - totalLocations++; - - double elevationDifference = updateElevation(currentLocation.getAltitude()); - - // Update the "instant" values: - data.totalTime = systemTime - data.startTime; - data.currentSpeed = currentLocation.getSpeed(); - - // This was the 1st location added, remember it and do nothing else: - if (lastLocation == null) { - lastLocation = currentLocation; - lastMovingLocation = currentLocation; - return false; - } - - updateBounds(currentLocation); - - // Don't do anything if we didn't move since last fix: - double distance = lastLocation.distanceTo(currentLocation); - if (distance < MyTracksConstants.MAX_NO_MOVEMENT_DISTANCE - && data.currentSpeed < MyTracksConstants.MAX_NO_MOVEMENT_SPEED) { - lastLocation = currentLocation; - return false; - } - - data.totalDistance += lastMovingLocation.distanceTo(currentLocation); - updateSpeed(currentLocation.getTime(), data.currentSpeed, - lastLocation.getTime(), lastLocation.getSpeed()); - - updateGrade(distance, elevationDifference); - lastLocation = currentLocation; - lastMovingLocation = currentLocation; - return true; - } - - /** - * Updates the track's bounding box to include the given location. - */ - private void updateBounds(Location location) { - data.latitudeExtremities.update(location.getLatitude()); - data.longitudeExtremities.update(location.getLongitude()); - } - - /** - * Updates the elevation measurements. - * - * @param elevation the current elevation - */ - // @VisibleForTesting - double updateElevation(double elevation) { - double oldSmoothedElevation = getSmoothedElevation(); - elevationBuffer.setNext(elevation); - double smoothedElevation = getSmoothedElevation(); - data.elevationExtremities.update(smoothedElevation); - double elevationDifference = elevationBuffer.isFull() - ? smoothedElevation - oldSmoothedElevation - : 0.0; - if (elevationDifference > 0) { - data.totalElevationGain += elevationDifference; - } - return elevationDifference; - } - - /** - * Updates the speed measurements. - * - * @param updateTime the time of the speed update - * @param speed the current speed - * @param lastLocationTime the time of the last speed update - * @param lastLocationSpeed the speed of the last update - */ - // @VisibleForTesting - void updateSpeed(long updateTime, double speed, long lastLocationTime, - double lastLocationSpeed) { - // We are now sure the user is moving. - long timeDifference = updateTime - lastLocationTime; - if (timeDifference < 0) { - Log.e(MyTracksConstants.TAG, - "Found negative time change: " + timeDifference); - } - data.movingTime += timeDifference; - - if (isValidSpeed(updateTime, speed, lastLocationTime, lastLocationSpeed, - speedBuffer)) { - speedBuffer.setNext(speed); - if (speed > data.maxSpeed) { - data.maxSpeed = speed; - } - double movingSpeed = getAverageMovingSpeed(); - if (speedBuffer.isFull() && (movingSpeed > data.maxSpeed)) { - data.maxSpeed = movingSpeed; - } - } else { - Log.d(MyTracksConstants.TAG, - "TripStatistics ignoring big change: Raw Speed: " + speed - + " old: " + lastLocationSpeed + " [" + toString() + "]"); - } - } - - /** - * Checks to see if this is a valid speed. - * - * @param updateTime The time at the current reading - * @param speed The current speed - * @param lastLocationTime The time at the last location - * @param lastLocationSpeed Speed at the last location - * @param speedBuffer A buffer of recent readings - * @return True if this is likely a valid speed - */ - public static boolean isValidSpeed(long updateTime, double speed, - long lastLocationTime, double lastLocationSpeed, - DoubleBuffer speedBuffer) { - - // We don't want to count 0 towards the speed. - if (speed == 0) { - return false; - } - // We are now sure the user is moving. - long timeDifference = updateTime - lastLocationTime; - - // There are a lot of noisy speed readings. - // Do the cheapest checks first, most expensive last. - // The following code will ignore unlikely to be real readings. - // - 128 m/s seems to be an internal android error code. - if (Math.abs(speed - 128) < 1) { - return false; - } - - // Another check for a spurious reading. See if the path seems physically - // likely. Ignore any speeds that imply accelaration greater than 2g's - // Really who can accelerate faster? - double speedDifference = Math.abs(lastLocationSpeed - speed); - if (speedDifference > MyTracksConstants.MAX_ACCELERATION * timeDifference) { - return false; - } - - // There are three additional checks if the reading gets this far: - // - Only use the speed if the buffer is full - // - Check that the current speed is less than 10x the recent smoothed speed - // - Double check that the current speed does not imply crazy acceleration - double smoothedSpeed = speedBuffer.getAverage(); - double smoothedDiff = Math.abs(smoothedSpeed - speed); - return !speedBuffer.isFull() || - (speed < smoothedSpeed * 10 - && smoothedDiff < MyTracksConstants.MAX_ACCELERATION * timeDifference); - } - - /** - * Updates the grade measurements. - * - * @param distance the distance the user just traveled - * @param elevationDifference the elevation difference between the current - * reading and the previous reading - */ - // @VisibleForTesting - void updateGrade(double distance, double elevationDifference) { - distanceBuffer.setNext(distance); - double smoothedDistance = distanceBuffer.getAverage(); - - // With the error in the altitude measurement it is dangerous to divide - // by anything less than 5. - if (!elevationBuffer.isFull() || !distanceBuffer.isFull() - || smoothedDistance < 5.0) { - return; - } - data.currentGrade = elevationDifference / smoothedDistance; - gradeBuffer.setNext(data.currentGrade); - data.gradeExtremities.update(gradeBuffer.getAverage()); - } - - - /** - * Gets the current elevation smoothed over several readings. The elevation - * data is very noisy so it is better to use the smoothed elevation than the - * raw elevation for many tasks. - * - * @return The elevation smoothed over several readings - */ - public double getSmoothedElevation() { - return elevationBuffer.getAverage(); - } - - /** - * Pauses the track at the current time. - */ - public void pause() { - pauseAt(System.currentTimeMillis()); - } - - /** - * Pauses the track at the give time. - * - * @param time the time to pause at - */ - public void pauseAt(long time) { - data.totalTime = time - data.startTime; - lastLocation = null; // Make sure the counter restarts. - paused = true; - } - - /** - * Resumes the current track. - */ - public void resume() { - // TODO: The total time is bogus after this, it will include the paused time - paused = false; - } - - /** - * Gets the total time that this track has been active. - * - * @return The total number of milliseconds the track was active - */ - public long getTotalTime() { - return paused - ? data.totalTime - : System.currentTimeMillis() - data.startTime; - } - - /** - * Gets the moving time. - * - * @return The total number of milliseconds the user was moving - */ - public long getMovingTime() { - return data.movingTime; - } - - /** - * Sets the moving time. - * - * @parm movingTime The total number of milliseconds the user was moving - */ - public void setMovingTime(long movingTime) { - data.movingTime = movingTime; - } - - /** - * Gets the total distance the user traveled. - * - * @return The total distance traveled in meters - */ - public double getTotalDistance() { - return data.totalDistance; - } - - /** - * Gets the the average speed the user traveled. - * - * @return The average speed in m/s - */ - public double getAverageSpeed() { - return getTotalDistance() / ((double) getTotalTime() / 1000); - } - - /** - * Gets the the average speed the user traveled when they were actively - * moving. - * - * @return The average moving speed in m/s - */ - public double getAverageMovingSpeed() { - return getTotalDistance() / ((double) getMovingTime() / 1000); - } - - /** - * Gets the the maximum speed for this track. - * - * @return The maximum speed in m/s. - */ - public double getMaxSpeed() { - return data.maxSpeed; - } - - /** - * Gets the the current speed for this track. - * - * @return The current speed in m/s - */ - public double getCurrentSpeed() { - return data.currentSpeed; - } - - @Override - public String toString() { - return "TripStatistics:" + " Start Time: " + data.startTime - + " Total Time: " + data.totalTime + " Moving Time: " + data.movingTime - + " Total Distance: " + data.totalDistance - + " Elevation Gain: " + data.totalElevationGain - + " Min Elevation: " + getMinElevation() - + " Max Elevation: " + getMaxElevation() - + " Average Speed: " + getAverageMovingSpeed() - + " Min Grade: " + getMinGrade() + " Max Grade: " + getMaxGrade() - + " Total Locations: " + totalLocations; - } - - /** - * Gets the minimum elevation seen on this trip. This is calculated from the - * smoothed elevation so this can actually be more than the current elevation. - * - * @return The smallest elevation reading for this trip - */ - public double getMinElevation() { - return data.elevationExtremities.getMin(); - } - - /** - * Gets the maximum elevation seen on this trip. This is calculated from the - * smoothed elevation so this can actually be less than the current elevation. - * - * @return The largest elevation reading for this trip - */ - public double getMaxElevation() { - return data.elevationExtremities.getMax(); - } - - /** - * Gets the total elevation gain for this trip. This is calculated as the sum - * of all positive differences in the smoothed elevation. - * - * @return The elevation gain in meters for this trip - */ - public double getTotalElevationGain() { - return data.totalElevationGain; + latitudeExtremities.update(other.latitudeExtremities.getMax()); + latitudeExtremities.update(other.latitudeExtremities.getMin()); + longitudeExtremities.update(other.longitudeExtremities.getMax()); + longitudeExtremities.update(other.longitudeExtremities.getMin()); + elevationExtremities.update(other.elevationExtremities.getMax()); + elevationExtremities.update(other.elevationExtremities.getMin()); + gradeExtremities.update(other.gradeExtremities.getMax()); + gradeExtremities.update(other.gradeExtremities.getMin()); } /** @@ -454,112 +149,423 @@ public class TripStatistics { * started */ public long getStartTime() { - return data.startTime; + return startTime; } /** - * Gets the current grade. + * Gets the time that this track stopped. * - * @return The current grade + * @return The number of milliseconds since epoch to the time when this track + * stopped */ - public double getGrade() { - return data.currentGrade; + public long getStopTime() { + return startTime; + } + + /** + * Gets the total time that this track has been active. + * This statistic is only updated when a new point is added to the statistics, + * so it may be off. If you need to calculate the proper total time, use + * {@link #getStartTime} with the current time. + * + * @return The total number of milliseconds the track was active + */ + public long getTotalTime() { + return totalTime; + } + + /** + * Gets the total distance the user traveled. + * + * @return The total distance traveled in meters + */ + public double getTotalDistance() { + return totalDistance; + } + + /** + * Gets the the average speed the user traveled. + * This calculation only takes into account the displacement until the last + * point that was accounted for in statistics. + * + * @return The average speed in m/s + */ + public double getAverageSpeed() { + return totalDistance / ((double) totalTime / 1000); + } + + /** + * Gets the the average speed the user traveled when they were actively + * moving. + * + * @return The average moving speed in m/s + */ + public double getAverageMovingSpeed() { + return totalDistance / ((double) movingTime / 1000); + } + + /** + * Gets the the maximum speed for this track. + * + * @return The maximum speed in m/s + */ + public double getMaxSpeed() { + return maxSpeed; + } + + /** + * Gets the moving time. + * + * @return The total number of milliseconds the user was moving + */ + public long getMovingTime() { + return movingTime; + } + + /** + * Gets the total elevation gain for this trip. This is calculated as the sum + * of all positive differences in the smoothed elevation. + * + * @return The elevation gain in meters for this trip + */ + public double getTotalElevationGain() { + return totalElevationGain; + } + + /** + * Returns the leftmost position (lowest longitude) of the track, in signed + * decimal degrees. + */ + public int getLeft() { + return (int) (longitudeExtremities.getMin() * 1E6); + } + + /** + * Returns the rightmost position (highest longitude) of the track, in signed + * decimal degrees. + */ + public int getRight() { + return (int) (longitudeExtremities.getMax() * 1E6); + } + + /** + * Returns the bottommost position (lowest latitude) of the track, in meters. + */ + public int getBottom() { + return (int) (latitudeExtremities.getMin() * 1E6); + } + + /** + * Returns the topmost position (highest latitude) of the track, in meters. + */ + public int getTop() { + return (int) (latitudeExtremities.getMax() * 1E6); + } + + /** + * Gets the minimum elevation seen on this trip. This is calculated from the + * smoothed elevation so this can actually be more than the current elevation. + * + * @return The smallest elevation reading for this trip in meters + */ + public double getMinElevation() { + return elevationExtremities.getMin(); + } + + /** + * Gets the maximum elevation seen on this trip. This is calculated from the + * smoothed elevation so this can actually be less than the current elevation. + * + * @return The largest elevation reading for this trip in meters + */ + public double getMaxElevation() { + return elevationExtremities.getMax(); } /** * Gets the maximum grade for this trip. * - * @return The maximum grade for this trip + * @return The maximum grade for this trip as a fraction */ public double getMaxGrade() { - return data.gradeExtremities.getMax(); + return gradeExtremities.getMax(); } /** * Gets the minimum grade for this trip. * - * @return The minimum grade for this trip + * @return The minimum grade for this trip as a fraction */ public double getMinGrade() { - return data.gradeExtremities.getMin(); + return gradeExtremities.getMin(); + } + + // Setters - to be used when restoring state or loading from the DB + + /** + * Sets the start time for this trip. + * + * @param startTime the start time, in milliseconds since the epoch + */ + public void setStartTime(long startTime) { + this.startTime = startTime; } /** - * Returns the amount of time the user has been idle or 0 if they are moving. + * Sets the stop time for this trip. + * + * @param stopTime the stop time, in milliseconds since the epoch */ - public long getIdleTime() { - return lastLocation.getTime() - lastMovingLocation.getTime(); + public void setStopTime(long stopTime) { + this.stopTime = stopTime; } /** - - * Returns the leftmost position (lowest longitude) of the track. + * Sets the total moving time. + * + * @param movingTime the moving time in milliseconds */ - public int getLeft() { - return (int) (data.longitudeExtremities.getMin() * 1E6); + public void setMovingTime(long movingTime) { + this.movingTime = movingTime; } /** - * Returns the rightmost position (highest longitude) of the track. + * Sets the total trip time. + * + * @param totalTime the total trip time in milliseconds */ - public int getRight() { - return (int) (data.longitudeExtremities.getMax() * 1E6); + public void setTotalTime(long totalTime) { + this.totalTime = totalTime; } /** - * Returns the bottommost position (lowest latitude) of the track. + * Sets the total trip distance. + * + * @param totalDistance the trip distance in meters */ - public int getBottom() { - return (int) (data.latitudeExtremities.getMin() * 1E6); + public void setTotalDistance(double totalDistance) { + this.totalDistance = totalDistance; } /** - * Returns the topmost position (highest latitude) of the track. + * Sets the total elevation variation during the trip. + * + * @param totalElevationGain the elevation variation in meters */ - public int getTop() { - return (int) (data.latitudeExtremities.getMax() * 1E6); - } - - public TripStatisticsData getData() { - return data; + public void setTotalElevationGain(double totalElevationGain) { + this.totalElevationGain = totalElevationGain; } /** - * Fills the given track with statistics about itself, calculated by this - * statitics class. + * Sets the maximum speed reached during the trip. + * + * @param maxSpeed the maximum speed in meters per second */ - public void fillStatisticsForTrack(Track track) { - track.setTotalDistance(getTotalDistance()); - track.setTotalTime(getTotalTime()); - track.setMovingTime(getMovingTime()); - track.setAverageSpeed(getAverageSpeed()); - track.setAverageMovingSpeed(getAverageMovingSpeed()); - track.setMaxSpeed(getMaxSpeed()); - track.setMinElevation(getMinElevation()); - track.setMaxElevation(getMaxElevation()); - track.setTotalElevationGain(getTotalElevationGain()); - track.setMinGrade(getMinGrade()); - track.setMaxGrade(getMaxGrade()); - - track.setBounds(getLeft(), getTop(), getRight(), getBottom()); + public void setMaxSpeed(double maxSpeed) { + this.maxSpeed = maxSpeed; } /** - * Write all of the statistics fields to the waypoint. - * @param waypoint The waypoint to write the trip statistics to + * Sets the minimum elevation reached during the trip. + * + * @param elevation the minimum elevation in meters */ - public void fillStatisticsForWaypoint(Waypoint waypoint) { - waypoint.setTotalDistance(getTotalDistance()); - waypoint.setTotalTime(getTotalTime()); - waypoint.setMovingTime(getMovingTime()); - waypoint.setAverageSpeed(getAverageSpeed()); - waypoint.setAverageMovingSpeed(getAverageMovingSpeed()); - waypoint.setMaxSpeed(getMaxSpeed()); - waypoint.setMinElevation(getMinElevation()); - waypoint.setMaxElevation(getMaxElevation()); - waypoint.setTotalElevationGain(getTotalElevationGain()); - waypoint.setMinGrade(getMinGrade()); - waypoint.setMaxGrade(getMaxGrade()); + public void setMinElevation(double elevation) { + elevationExtremities.setMin(elevation); } -} + /** + * Sets the maximum elevation reached during the trip. + * + * @param elevation the maximum elevation in meters + */ + public void setMaxElevation(double elevation) { + elevationExtremities.setMax(elevation); + } + + /** + * Sets the minimum grade obtained during the trip. + * + * @param grade the grade as a fraction (-1.0 would mean vertical downwards) + */ + public void setMinGrade(double grade) { + gradeExtremities.setMin(grade); + } + + /** + * Sets the maximum grade obtained during the trip). + * + * @param grade the grade as a fraction (1.0 would mean vertical upwards) + */ + public void setMaxGrade(double grade) { + gradeExtremities.setMax(grade); + } + + /** + * Sets the bounding box for this trip. + * + * @param left the westmost longitude reached + * @param top the northmost latitude reached + * @param right the eastmost longitude reached + * @param bottom the southmost latitude reached + */ + public void setBounds(int left, int top, int right, int bottom) { + latitudeExtremities.set(bottom, top); + longitudeExtremities.set(left, right); + } + + // Data manipulation methods + + /** + * Adds to the current total distance. + * + * @param distance the distance to add in meters + */ + void addTotalDistance(double distance) { + totalDistance += distance; + } + + /** + * Adds to the total elevation variation. + * + * @param gain the elevation variation in meters + */ + void addTotalElevationGain(double gain) { + totalElevationGain += gain; + } + + /** + * Adds to the total moving time of the trip. + * + * @param time the time in milliseconds + */ + void addMovingTime(long time) { + movingTime += time; + } + + /** + * Accounts for a new latitude value for the bounding box. + * + * @param latitude the latitude value in signed decimal degrees + */ + void updateLatitudeExtremities(double latitude) { + latitudeExtremities.update(latitude); + } + + /** + * Accounts for a new longitude value for the bounding box. + * + * @param longitude the longitude value in signed decimal degrees + */ + void updateLongitudeExtremities(double longitude) { + longitudeExtremities.update(longitude); + } + + /** + * Accounts for a new elevation value for the bounding box. + * + * @param elevation the elevation value in meters + */ + void updateElevationExtremities(double elevation) { + elevationExtremities.update(elevation); + } + + /** + * Accounts for a new grade value. + * + * @param grade the grade value as a fraction + */ + void updateGradeExtremities(double grade) { + gradeExtremities.update(grade); + } + + // String conversion + + @Override + public String toString() { + return "TripStatistics { Start Time: " + getStartTime() + + "; Total Time: " + getTotalTime() + + "; Moving Time: " + getMovingTime() + + "; Total Distance: " + getTotalDistance() + + "; Elevation Gain: " + getTotalElevationGain() + + "; Min Elevation: " + getMinElevation() + + "; Max Elevation: " + getMaxElevation() + + "; Average Speed: " + getAverageMovingSpeed() + + "; Min Grade: " + getMinGrade() + + "; Max Grade: " + getMaxGrade() + + "}"; + } + + // Parcelable interface and creator + + /** + * Creator of statistics data from parcels. + */ + public static class Creator + implements Parcelable.Creator { + + @Override + public TripStatistics createFromParcel(Parcel source) { + TripStatistics data = new TripStatistics(); + + data.startTime = source.readLong(); + data.movingTime = source.readLong(); + data.totalTime = source.readLong(); + data.totalDistance = source.readDouble(); + data.totalElevationGain = source.readDouble(); + data.maxSpeed = source.readDouble(); + + double minLat = source.readDouble(); + double maxLat = source.readDouble(); + data.latitudeExtremities.set(minLat, maxLat); + + double minLong = source.readDouble(); + double maxLong = source.readDouble(); + data.longitudeExtremities.set(minLong, maxLong); + + double minElev = source.readDouble(); + double maxElev = source.readDouble(); + data.elevationExtremities.set(minElev, maxElev); + + double minGrade = source.readDouble(); + double maxGrade = source.readDouble(); + data.gradeExtremities.set(minGrade, maxGrade); + + return data; + } + + @Override + public TripStatistics[] newArray(int size) { + return new TripStatistics[size]; + } + } + + /** + * Creator of {@link TripStatistics} from parcels. + */ + public static final Creator CREATOR = new Creator(); + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(startTime); + dest.writeLong(movingTime); + dest.writeLong(totalTime); + dest.writeDouble(totalDistance); + dest.writeDouble(totalElevationGain); + dest.writeDouble(maxSpeed); + + dest.writeDouble(latitudeExtremities.getMin()); + dest.writeDouble(latitudeExtremities.getMax()); + dest.writeDouble(longitudeExtremities.getMin()); + dest.writeDouble(longitudeExtremities.getMax()); + dest.writeDouble(elevationExtremities.getMin()); + dest.writeDouble(elevationExtremities.getMax()); + dest.writeDouble(gradeExtremities.getMin()); + dest.writeDouble(gradeExtremities.getMax()); + } +} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java new file mode 100644 index 000000000..9724ba727 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java @@ -0,0 +1,369 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.android.apps.mytracks.stats; + +import com.google.android.apps.mytracks.MyTracksConstants; + +import android.location.Location; +import android.util.Log; + +/** + * Statistics keeper for a trip. + * + * @author Sandor Dornbush + * @author Rodrigo Damazio + */ +public class TripStatisticsBuilder { + /** + * Statistical data about the trip, which can be displayed to the user. + */ + private final TripStatistics data; + + /** + * The last location that the gps reported. + */ + private Location lastLocation = null; + + /** + * The last location that contributed to the stats. It is also the last + * location the user was found to be moving. + */ + private Location lastMovingLocation = null; + + /** + * The current speed in meters/second as reported by the gps. + */ + private double currentSpeed; + + /** + * The current grade. This value is very noisy and not reported to the user. + */ + private double currentGrade; + + /** + * Is the trip currently paused? + * All trips start paused. + */ + private boolean paused = true; + + /** + * A buffer of the last speed readings in meters/second. + */ + private final DoubleBuffer speedBuffer = + new DoubleBuffer(MyTracksConstants.SPEED_SMOOTHING_FACTOR); + + /** + * A buffer of the recent elevation readings in meters. + */ + private final DoubleBuffer elevationBuffer = + new DoubleBuffer(MyTracksConstants.ELEVATION_SMOOTHING_FACTOR); + + /** + * A buffer of the distance between recent gps readings in meters. + */ + private final DoubleBuffer distanceBuffer = + new DoubleBuffer(MyTracksConstants.DISTANCE_SMOOTHING_FACTOR); + + /** + * A buffer of the recent grade calculations. + */ + private final DoubleBuffer gradeBuffer = + new DoubleBuffer(MyTracksConstants.GRADE_SMOOTHING_FACTOR); + + /** + * The total number of locations in this trip. + */ + private long totalLocations = 0; + + /** + * Creates a new trip starting at the current system time. + */ + public TripStatisticsBuilder() { + data = new TripStatistics(); + } + + /** + * Creates a new trip, starting with existing statistics data. + * + * @param statsData the statistics data to copy and start from + */ + public TripStatisticsBuilder(TripStatistics statsData) { + data = new TripStatistics(statsData); + } + + /** + * Adds a location to the current trip. This will update all of the internal + * variables with this new location. + * + * @param currentLocation the current gps location + * @param systemTime the time used for calculation of totalTime. This should + * be the phone's time (not GPS time) + * @return true if the person is moving + */ + public boolean addLocation(Location currentLocation, long systemTime) { + if (paused) { + Log.w(MyTracksConstants.TAG, + "Tried to account for location while track is paused"); + return false; + } + + totalLocations++; + + double elevationDifference = updateElevation(currentLocation.getAltitude()); + + // Update the "instant" values: + data.setTotalTime(systemTime - data.getStartTime()); + currentSpeed = currentLocation.getSpeed(); + + // This was the 1st location added, remember it and do nothing else: + if (lastLocation == null) { + lastLocation = currentLocation; + lastMovingLocation = currentLocation; + return false; + } + + updateBounds(currentLocation); + + // Don't do anything if we didn't move since last fix: + double distance = lastLocation.distanceTo(currentLocation); + if (distance < MyTracksConstants.MAX_NO_MOVEMENT_DISTANCE + && currentSpeed < MyTracksConstants.MAX_NO_MOVEMENT_SPEED) { + lastLocation = currentLocation; + return false; + } + + data.addTotalDistance(lastMovingLocation.distanceTo(currentLocation)); + updateSpeed(currentLocation.getTime(), currentSpeed, + lastLocation.getTime(), lastLocation.getSpeed()); + + updateGrade(distance, elevationDifference); + lastLocation = currentLocation; + lastMovingLocation = currentLocation; + return true; + } + + /** + * Updates the track's bounding box to include the given location. + */ + private void updateBounds(Location location) { + data.updateLatitudeExtremities(location.getLatitude()); + data.updateLongitudeExtremities(location.getLongitude()); + } + + /** + * Updates the elevation measurements. + * + * @param elevation the current elevation + */ + // @VisibleForTesting + double updateElevation(double elevation) { + double oldSmoothedElevation = getSmoothedElevation(); + elevationBuffer.setNext(elevation); + double smoothedElevation = getSmoothedElevation(); + data.updateElevationExtremities(smoothedElevation); + double elevationDifference = elevationBuffer.isFull() + ? smoothedElevation - oldSmoothedElevation + : 0.0; + if (elevationDifference > 0) { + data.addTotalElevationGain(elevationDifference); + } + return elevationDifference; + } + + /** + * Updates the speed measurements. + * + * @param updateTime the time of the speed update + * @param speed the current speed + * @param lastLocationTime the time of the last speed update + * @param lastLocationSpeed the speed of the last update + */ + // @VisibleForTesting + void updateSpeed(long updateTime, double speed, long lastLocationTime, + double lastLocationSpeed) { + // We are now sure the user is moving. + long timeDifference = updateTime - lastLocationTime; + if (timeDifference < 0) { + Log.e(MyTracksConstants.TAG, + "Found negative time change: " + timeDifference); + } + data.addMovingTime(timeDifference); + + if (isValidSpeed(updateTime, speed, lastLocationTime, lastLocationSpeed, + speedBuffer)) { + speedBuffer.setNext(speed); + if (speed > data.getMaxSpeed()) { + data.setMaxSpeed(speed); + } + double movingSpeed = data.getAverageMovingSpeed(); + if (speedBuffer.isFull() && (movingSpeed > data.getMaxSpeed())) { + data.setMaxSpeed(movingSpeed); + } + } else { + Log.d(MyTracksConstants.TAG, + "TripStatistics ignoring big change: Raw Speed: " + speed + + " old: " + lastLocationSpeed + " [" + toString() + "]"); + } + } + + /** + * Checks to see if this is a valid speed. + * + * @param updateTime The time at the current reading + * @param speed The current speed + * @param lastLocationTime The time at the last location + * @param lastLocationSpeed Speed at the last location + * @param speedBuffer A buffer of recent readings + * @return True if this is likely a valid speed + */ + public static boolean isValidSpeed(long updateTime, double speed, + long lastLocationTime, double lastLocationSpeed, + DoubleBuffer speedBuffer) { + + // We don't want to count 0 towards the speed. + if (speed == 0) { + return false; + } + // We are now sure the user is moving. + long timeDifference = updateTime - lastLocationTime; + + // There are a lot of noisy speed readings. + // Do the cheapest checks first, most expensive last. + // The following code will ignore unlikely to be real readings. + // - 128 m/s seems to be an internal android error code. + if (Math.abs(speed - 128) < 1) { + return false; + } + + // Another check for a spurious reading. See if the path seems physically + // likely. Ignore any speeds that imply accelaration greater than 2g's + // Really who can accelerate faster? + double speedDifference = Math.abs(lastLocationSpeed - speed); + if (speedDifference > MyTracksConstants.MAX_ACCELERATION * timeDifference) { + return false; + } + + // There are three additional checks if the reading gets this far: + // - Only use the speed if the buffer is full + // - Check that the current speed is less than 10x the recent smoothed speed + // - Double check that the current speed does not imply crazy acceleration + double smoothedSpeed = speedBuffer.getAverage(); + double smoothedDiff = Math.abs(smoothedSpeed - speed); + return !speedBuffer.isFull() || + (speed < smoothedSpeed * 10 + && smoothedDiff < MyTracksConstants.MAX_ACCELERATION * timeDifference); + } + + /** + * Updates the grade measurements. + * + * @param distance the distance the user just traveled + * @param elevationDifference the elevation difference between the current + * reading and the previous reading + */ + // @VisibleForTesting + void updateGrade(double distance, double elevationDifference) { + distanceBuffer.setNext(distance); + double smoothedDistance = distanceBuffer.getAverage(); + + // With the error in the altitude measurement it is dangerous to divide + // by anything less than 5. + if (!elevationBuffer.isFull() || !distanceBuffer.isFull() + || smoothedDistance < 5.0) { + return; + } + currentGrade = elevationDifference / smoothedDistance; + gradeBuffer.setNext(currentGrade); + data.updateGradeExtremities(gradeBuffer.getAverage()); + } + + /** + * Pauses the track at the current time. + */ + public void pause() { + pauseAt(System.currentTimeMillis()); + } + + /** + * Pauses the track at the given time. + * + * @param time the time to pause at + */ + public void pauseAt(long time) { + if (paused) { return; } + + data.setStopTime(time); + data.setTotalTime(time - data.getStartTime()); + lastLocation = null; // Make sure the counter restarts. + paused = true; + } + + /** + * Resumes the current track at the current time. + */ + public void resume() { + resumeAt(System.currentTimeMillis()); + } + + /** + * Resumes the current track at the given time. + * + * @param time the time to resume at + */ + public void resumeAt(long time) { + if (!paused) { return; } + + // TODO: The times are bogus if the track is paused then resumed again + data.setStartTime(time); + data.setStopTime(-1); + paused = false; + } + + @Override + public String toString() { + return "TripStatistics { Data: " + data.toString() + + "; Total Locations: " + totalLocations + + "; Paused: " + paused + + "; Current speed: " + currentSpeed + + "; Current grade: " + currentGrade + + "}"; + } + + /** + * Returns the amount of time the user has been idle or 0 if they are moving. + */ + public long getIdleTime() { + return lastLocation.getTime() - lastMovingLocation.getTime(); + } + + /** + * Gets the current elevation smoothed over several readings. The elevation + * data is very noisy so it is better to use the smoothed elevation than the + * raw elevation for many tasks. + * + * @return The elevation smoothed over several readings + */ + public double getSmoothedElevation() { + return elevationBuffer.getAverage(); + } + + public TripStatistics getStatistics() { + // Take a snapshot - we do't want anyone messing with our internals + return new TripStatistics(data); + } +} + diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsData.java b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsData.java deleted file mode 100644 index f795e6689..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsData.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.android.apps.mytracks.stats; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Statistical data about a trip. - * The data in this class should be filled out by {@link TripStatistics}. - * - * TODO: Remove delegate methods from TripStatistics - * (detail is getTotalTime) - * TODO: Make Waypoint and Track use this instead of having the same fields - * - * @author Rodrigo Damazio - */ -public class TripStatisticsData implements Parcelable { - - /** - * The start time for the trip. This is system time which might not match gps - * time. - */ - long startTime; - - /** - * The total time that we believe the user was traveling in milliseconds. - */ - long movingTime; - - /** - * The total time of the trip in milliseconds. - * This is only updated when new points are received, so it may be stale. - */ - long totalTime; - - /** - * The total distance in meters that the user traveled on this trip. - */ - double totalDistance; - - /** - * The total elevation gained on this trip in meters. - */ - double totalElevationGain; - - /** - * The maximum speed in meters/second reported that we believe to be a valid - * speed. - */ - double maxSpeed; - - /** - * The current speed in meters/second as reported by the gps. - */ - double currentSpeed; - - /** - * The current grade. This value is very noisy and not reported to the user. - */ - double currentGrade; - - /** - * The min and max latitude values seen in this trip. - */ - final ExtremityMonitor latitudeExtremities = new ExtremityMonitor(); - - /** - * The min and max longitude values seen in this trip. - */ - final ExtremityMonitor longitudeExtremities = new ExtremityMonitor(); - - /** - * The min and max elevation seen on this trip in meters. - */ - final ExtremityMonitor elevationExtremities = new ExtremityMonitor(); - - /** - * The minimum and maximum grade calculations on this trip. - */ - final ExtremityMonitor gradeExtremities = new ExtremityMonitor(); - - /** - * Gets the time that this track started. - * - * @return The number of milliseconds since epoch to the time when this track - * started - */ - public long getStartTime() { - return startTime; - } - - /** - * Gets the total time that this track has been active. - * - * @return The total number of milliseconds the track was active - */ - public long getTotalTime() { - return totalTime; - } - - /** - * Gets the total distance the user traveled. - * - * @return The total distance traveled in meters - */ - public double getTotalDistance() { - return totalDistance; - } - - /** - * Gets the the maximum speed for this track. - * - * @return The maximum speed in m/s. - */ - public double getMaxSpeed() { - return maxSpeed; - } - - /** - * Gets the the current speed for this track. - * - * @return The current speed in m/s - */ - public double getCurrentSpeed() { - return currentSpeed; - } - - /** - * Gets the moving time. - * - * @return The total number of milliseconds the user was moving - */ - public long getMovingTime() { - return movingTime; - } - - /** - * Gets the total elevation gain for this trip. This is calculated as the sum - * of all positive differences in the smoothed elevation. - * - * @return The elevation gain in meters for this trip - */ - public double getTotalElevationGain() { - return totalElevationGain; - } - - /** - * Gets the current grade. - * - * @return The current grade - */ - public double getCurrentGrade() { - return currentGrade; - } - - /** - * Returns the leftmost position (lowest longitude) of the track. - */ - public int getLeft() { - return (int) (longitudeExtremities.getMin() * 1E6); - } - - /** - * Returns the rightmost position (highest longitude) of the track. - */ - public int getRight() { - return (int) (longitudeExtremities.getMax() * 1E6); - } - - /** - * Returns the bottommost position (lowest latitude) of the track. - */ - public int getBottom() { - return (int) (latitudeExtremities.getMin() * 1E6); - } - - /** - * Returns the topmost position (highest latitude) of the track. - */ - public int getTop() { - return (int) (latitudeExtremities.getMax() * 1E6); - } - - /** - * Gets the minimum elevation seen on this trip. This is calculated from the - * smoothed elevation so this can actually be more than the current elevation. - * - * @return The smallest elevation reading for this trip - */ - public double getMinElevation() { - return elevationExtremities.getMin(); - } - - /** - * Gets the maximum elevation seen on this trip. This is calculated from the - * smoothed elevation so this can actually be less than the current elevation. - * - * @return The largest elevation reading for this trip - */ - public double getMaxElevation() { - return elevationExtremities.getMax(); - } - - /** - * Gets the maximum grade for this trip. - * - * @return The maximum grade for this trip - */ - public double getMaxGrade() { - return gradeExtremities.getMax(); - } - - /** - * Gets the minimum grade for this trip. - * - * @return The minimum grade for this trip - */ - public double getMinGrade() { - return gradeExtremities.getMin(); - } - - // Parcelable interface and creator - - /** - * Creator of statistics data from parcels. - */ - public static class Creator - implements Parcelable.Creator { - - @Override - public TripStatisticsData createFromParcel(Parcel source) { - TripStatisticsData data = new TripStatisticsData(); - - data.startTime = source.readLong(); - data.movingTime = source.readLong(); - data.totalTime = source.readLong(); - data.totalDistance = source.readDouble(); - data.totalElevationGain = source.readDouble(); - data.maxSpeed = source.readDouble(); - data.currentSpeed = source.readDouble(); - data.currentGrade = source.readDouble(); - - double minLat = source.readDouble(); - double maxLat = source.readDouble(); - data.latitudeExtremities.set(minLat, maxLat); - - double minLong = source.readDouble(); - double maxLong = source.readDouble(); - data.longitudeExtremities.set(minLong, maxLong); - - double minElev = source.readDouble(); - double maxElev = source.readDouble(); - data.elevationExtremities.set(minElev, maxElev); - - double minGrade = source.readDouble(); - double maxGrade = source.readDouble(); - data.gradeExtremities.set(minGrade, maxGrade); - - return data; - } - - @Override - public TripStatisticsData[] newArray(int size) { - return new TripStatisticsData[size]; - } - } - - public static final Creator CREATOR = new Creator(); - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeLong(startTime); - dest.writeLong(movingTime); - dest.writeLong(totalTime); - dest.writeDouble(totalDistance); - dest.writeDouble(totalElevationGain); - dest.writeDouble(maxSpeed); - dest.writeDouble(currentSpeed); - dest.writeDouble(currentGrade); - - dest.writeDouble(latitudeExtremities.getMin()); - dest.writeDouble(latitudeExtremities.getMax()); - dest.writeDouble(longitudeExtremities.getMin()); - dest.writeDouble(longitudeExtremities.getMax()); - dest.writeDouble(elevationExtremities.getMin()); - dest.writeDouble(elevationExtremities.getMax()); - dest.writeDouble(gradeExtremities.getMin()); - dest.writeDouble(gradeExtremities.getMax()); - } -} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java b/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java index 649ba57d0..f232dbf51 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/ChartURLGenerator.java @@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.util; import com.google.android.apps.mytracks.MyTracksSettings; import com.google.android.apps.mytracks.content.Track; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.maps.mytracks.R; import android.content.Context; @@ -82,18 +83,19 @@ public class ChartURLGenerator { } // Round it up. + TripStatistics stats = track.getStatistics(); double effectiveMaxY = metricUnits - ? track.getMaxElevation() - : track.getMaxElevation() * UnitConversions.M_TO_FT; + ? stats.getMaxElevation() + : stats.getMaxElevation() * UnitConversions.M_TO_FT; effectiveMaxY = ((int) (effectiveMaxY / 100)) * 100 + 100; // Round it down. double effectiveMinY = 0; double minElevation = metricUnits - ? track.getMinElevation() - : track.getMinElevation() * UnitConversions.M_TO_FT; + ? stats.getMinElevation() + : stats.getMinElevation() * UnitConversions.M_TO_FT; effectiveMinY = ((int) (minElevation / 100)) * 100; - if (track.getMinElevation() < 0) { + if (stats.getMinElevation() < 0) { effectiveMinY -= 100; } double ySpread = effectiveMaxY - effectiveMinY; @@ -108,7 +110,7 @@ public class ChartURLGenerator { // Labels sb.append("&chxt=x,y"); - double distKM = track.getTotalDistance() / 1000.0; + double distKM = stats.getTotalDistance() / 1000.0; double distDisplay = metricUnits ? distKM : (distKM * UnitConversions.KM_TO_MI); int xInterval = ((int) (distDisplay / 6)); @@ -162,11 +164,11 @@ public class ChartURLGenerator { return sb.toString(); } - protected static double getNormalizedDistance(double d, Track track) { - return d / track.getTotalDistance(); + private static double getNormalizedDistance(double d, Track track) { + return d / track.getStatistics().getTotalDistance(); } - protected static double getNormalizedElevation( + private static double getNormalizedElevation( double d, double effectiveMinY, double ySpread) { return (d - effectiveMinY) / ySpread; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java index 8f21bb72a..71caa003c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java @@ -17,6 +17,7 @@ package com.google.android.apps.mytracks.util; import com.google.android.apps.mytracks.MyTracksConstants; import com.google.android.apps.mytracks.content.Track; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.maps.GeoPoint; @@ -181,6 +182,7 @@ public class MyTracksUtils { Track piece = null; do { piece = new Track(); + TripStatistics pieceStats = piece.getStatistics(); piece.setId(track.getId()); piece.setName(track.getName()); piece.setDescription(track.getDescription()); @@ -191,8 +193,8 @@ public class MyTracksUtils { } int nPointsPiece = pieceLocations.size(); if (nPointsPiece >= 2) { - piece.setStartTime(pieceLocations.get(0).getTime()); - piece.setStopTime(pieceLocations.get(nPointsPiece - 1).getTime()); + pieceStats.setStartTime(pieceLocations.get(0).getTime()); + pieceStats.setStopTime(pieceLocations.get(nPointsPiece - 1).getTime()); result.add(piece); } n += (pieceLocations.size() - 1); diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java index 47c3b4e62..d5a8ed34b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java @@ -19,6 +19,7 @@ import com.google.android.apps.mytracks.MyTracksSettings; import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.DescriptionGenerator; +import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.maps.mytracks.R; import android.content.Context; @@ -175,23 +176,24 @@ public class StringUtils implements DescriptionGenerator { preferences.getBoolean(MyTracksSettings.REPORT_SPEED, true); } - final double distanceInKm = track.getTotalDistance() / 1000; + TripStatistics trackStats = track.getStatistics(); + final double distanceInKm = trackStats.getTotalDistance() / 1000; final double distanceInMiles = distanceInKm * UnitConversions.KM_TO_MI; - final long minElevationInMeters = Math.round(track.getMinElevation()); + final long minElevationInMeters = Math.round(trackStats.getMinElevation()); final long minElevationInFeet = - Math.round(track.getMinElevation() * UnitConversions.M_TO_FT); - final long maxElevationInMeters = Math.round(track.getMaxElevation()); + Math.round(trackStats.getMinElevation() * UnitConversions.M_TO_FT); + final long maxElevationInMeters = Math.round(trackStats.getMaxElevation()); final long maxElevationInFeet = - Math.round(track.getMaxElevation() * UnitConversions.M_TO_FT); + Math.round(trackStats.getMaxElevation() * UnitConversions.M_TO_FT); final long elevationGainInMeters = - Math.round(track.getTotalElevationGain()); - final long elevationGainInFeet = - Math.round(track.getTotalElevationGain() * UnitConversions.M_TO_FT); + Math.round(trackStats.getTotalElevationGain()); + final long elevationGainInFeet = Math.round( + trackStats.getTotalElevationGain() * UnitConversions.M_TO_FT); long minGrade = 0; long maxGrade = 0; - double trackMaxGrade = track.getMaxGrade(); - double trackMinGrade = track.getMinGrade(); + double trackMaxGrade = trackStats.getMaxGrade(); + double trackMinGrade = trackStats.getMinGrade(); if (!Double.isNaN(trackMaxGrade) && !Double.isInfinite(trackMaxGrade)) { maxGrade = Math.round(trackMaxGrade * 100); @@ -207,19 +209,19 @@ public class StringUtils implements DescriptionGenerator { } String averageSpeed = - getSpeedString(track.getAverageSpeed(), + getSpeedString(trackStats.getAverageSpeed(), R.string.average_speed_label, R.string.average_pace_label, displaySpeed); String averageMovingSpeed = - getSpeedString(track.getAverageMovingSpeed(), + getSpeedString(trackStats.getAverageMovingSpeed(), R.string.average_moving_speed_label, R.string.average_moving_pace_label, displaySpeed); String maxSpeed = - getSpeedString(track.getMaxSpeed(), + getSpeedString(trackStats.getMaxSpeed(), R.string.max_speed_label, R.string.min_pace_label, displaySpeed); @@ -248,11 +250,11 @@ public class StringUtils implements DescriptionGenerator { // Line 3 context.getString(R.string.total_time_label), - StringUtils.formatTime(track.getTotalTime()), + StringUtils.formatTime(trackStats.getTotalTime()), // Line 4 context.getString(R.string.moving_time_label), - StringUtils.formatTime(track.getMovingTime()), + StringUtils.formatTime(trackStats.getMovingTime()), // Line 5 averageSpeed, averageMovingSpeed, maxSpeed, @@ -280,7 +282,7 @@ public class StringUtils implements DescriptionGenerator { // Line 11 context.getString(R.string.recorded_date), - new Date(track.getStartTime()), + new Date(trackStats.getStartTime()), // Line 12 context.getString(R.string.category), category, @@ -322,35 +324,39 @@ public class StringUtils implements DescriptionGenerator { * @return a track description */ public String generateWaypointDescription(Waypoint waypoint) { - final double distanceInKm = waypoint.getTotalDistance() / 1000; + TripStatistics stats = waypoint.getStatistics(); + + final double distanceInKm = stats.getTotalDistance() / 1000; final double distanceInMiles = distanceInKm * UnitConversions.KM_TO_MI; - final double averageSpeedInKmh = waypoint.getAverageSpeed() * 3.6; + final double averageSpeedInKmh = stats.getAverageSpeed() * 3.6; final double averageSpeedInMph = averageSpeedInKmh * UnitConversions.KMH_TO_MPH; - final double movingSpeedInKmh = waypoint.getAverageMovingSpeed() * 3.6; + final double movingSpeedInKmh = stats.getAverageMovingSpeed() * 3.6; final double movingSpeedInMph = movingSpeedInKmh * UnitConversions.KMH_TO_MPH; - final double maxSpeedInKmh = waypoint.getMaxSpeed() * 3.6; + final double maxSpeedInKmh = stats.getMaxSpeed() * 3.6; final double maxSpeedInMph = maxSpeedInKmh * UnitConversions.KMH_TO_MPH; - final long minElevationInMeters = Math.round(waypoint.getMinElevation()); + final long minElevationInMeters = Math.round(stats.getMinElevation()); final long minElevationInFeet = - Math.round(waypoint.getMinElevation() * UnitConversions.M_TO_FT); - final long maxElevationInMeters = Math.round(waypoint.getMaxElevation()); + Math.round(stats.getMinElevation() * UnitConversions.M_TO_FT); + final long maxElevationInMeters = Math.round(stats.getMaxElevation()); final long maxElevationInFeet = - Math.round(waypoint.getMaxElevation() * UnitConversions.M_TO_FT); + Math.round(stats.getMaxElevation() * UnitConversions.M_TO_FT); final long elevationGainInMeters = - Math.round(waypoint.getTotalElevationGain()); - final long elevationGainInFeet = Math.round(waypoint.getTotalElevationGain() - * UnitConversions.M_TO_FT); + Math.round(stats.getTotalElevationGain()); + final long elevationGainInFeet = Math.round( + stats.getTotalElevationGain() * UnitConversions.M_TO_FT); long theMinGrade = 0; long theMaxGrade = 0; - if (!Double.isNaN(waypoint.getMaxGrade()) && - !Double.isInfinite(waypoint.getMaxGrade())) { - theMaxGrade = Math.round(waypoint.getMaxGrade() * 100); + double maxGrade = stats.getMaxGrade(); + double minGrade = stats.getMinGrade(); + if (!Double.isNaN(maxGrade) && + !Double.isInfinite(maxGrade)) { + theMaxGrade = Math.round(maxGrade * 100); } - if (!Double.isNaN(waypoint.getMinGrade()) && - !Double.isInfinite(waypoint.getMinGrade())) { - theMinGrade = Math.round(waypoint.getMinGrade() * 100); + if (!Double.isNaN(minGrade) && + !Double.isInfinite(minGrade)) { + theMinGrade = Math.round(minGrade * 100); } final String percent = "%"; @@ -370,9 +376,9 @@ public class StringUtils implements DescriptionGenerator { distanceInKm, context.getString(R.string.kilometer), distanceInMiles, context.getString(R.string.mile), context.getString(R.string.time_label), - StringUtils.formatTime(waypoint.getTotalTime()), + StringUtils.formatTime(stats.getTotalTime()), context.getString(R.string.moving_time_label), - StringUtils.formatTime(waypoint.getMovingTime()), + StringUtils.formatTime(stats.getMovingTime()), context.getString(R.string.average_speed_label), averageSpeedInKmh, context.getString(R.string.kilometer_per_hour), averageSpeedInMph, context.getString(R.string.mile_per_hour), diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java similarity index 61% rename from MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsTest.java rename to MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java index f8bc1b64a..f19b2da90 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java @@ -9,24 +9,27 @@ import android.location.Location; import junit.framework.TestCase; /** - * Test the the function of the TripStatistics class. + * Test the the function of the TripStatisticsBuilder class. * * @author Sandor Dornbush */ -public class TripStatisticsTest extends TestCase { +public class TripStatisticsBuilderTest extends TestCase { - private TripStatistics stats = null; + private TripStatisticsBuilder builder = null; @Override protected void setUp() throws Exception { super.setUp(); - stats = new TripStatistics(); + builder = new TripStatisticsBuilder(); + builder.resume(); } public void testAddLocationSimple() throws Exception { - stats = new TripStatistics(1000); + builder = new TripStatisticsBuilder(); + builder.resumeAt(1000); + TripStatistics stats = builder.getStatistics(); - assertEquals(0.0, stats.getSmoothedElevation()); + assertEquals(0.0, builder.getSmoothedElevation()); assertEquals(Double.POSITIVE_INFINITY, stats.getMinElevation()); assertEquals(Double.NEGATIVE_INFINITY, stats.getMaxElevation()); assertEquals(0.0, stats.getMaxSpeed()); @@ -48,19 +51,20 @@ public class TripStatisticsTest extends TestCase { // Each time slice is 10 seconds. long time = 1000 + 10000 * i; l.setTime(time); - boolean moving = stats.addLocation(l, time); + boolean moving = builder.addLocation(l, time); assertEquals((i != 0), moving); + stats = builder.getStatistics(); assertEquals(10000 * i, stats.getTotalTime()); - System.out.println("i: " + i + "\nLocation: " + l + "\nStats: " + stats); assertEquals(10000 * i, stats.getMovingTime()); - assertEquals(i, stats.getSmoothedElevation(), + assertEquals(i, builder.getSmoothedElevation(), MyTracksConstants.ELEVATION_SMOOTHING_FACTOR / 2); assertEquals(0.0, stats.getMinElevation()); - assertEquals(i , stats.getMaxElevation(), + assertEquals(i, stats.getMaxElevation(), MyTracksConstants.ELEVATION_SMOOTHING_FACTOR / 2); assertEquals(i, stats.getTotalElevationGain(), MyTracksConstants.ELEVATION_SMOOTHING_FACTOR); + if (i > MyTracksConstants.SPEED_SMOOTHING_FACTOR) { assertEquals(11.1f, stats.getMaxSpeed(), 0.1); } @@ -80,13 +84,15 @@ public class TripStatisticsTest extends TestCase { */ public void testElevationSimple() throws Exception { for (double elevation = 0; elevation < 1000; elevation += 10) { - stats = new TripStatistics(); + builder = new TripStatisticsBuilder(); + builder.resume(); for (int j = 0; j < 100; j++) { - assertEquals(0.0, stats.updateElevation(elevation)); - assertEquals(elevation, stats.getSmoothedElevation()); - assertEquals(elevation, stats.getMinElevation()); - assertEquals(elevation, stats.getMaxElevation()); - assertEquals(0.0, stats.getTotalElevationGain()); + assertEquals(0.0, builder.updateElevation(elevation)); + assertEquals(elevation, builder.getSmoothedElevation()); + TripStatistics data = builder.getStatistics(); + assertEquals(elevation, data.getMinElevation()); + assertEquals(elevation, data.getMaxElevation()); + assertEquals(0.0, data.getTotalElevationGain()); } } } @@ -102,12 +108,13 @@ public class TripStatisticsTest extends TestCase { expectedGain = 1.0; } assertEquals(expectedGain, - stats.updateElevation(i)); - assertEquals(i, stats.getSmoothedElevation(), 20); - assertEquals(0.0, stats.getMinElevation(), 0.0); - assertEquals(i, stats.getMaxElevation(), + builder.updateElevation(i)); + assertEquals(i, builder.getSmoothedElevation(), 20); + TripStatistics data = builder.getStatistics(); + assertEquals(0.0, data.getMinElevation(), 0.0); + assertEquals(i, data.getMaxElevation(), MyTracksConstants.ELEVATION_SMOOTHING_FACTOR); - assertEquals(i, stats.getTotalElevationGain(), + assertEquals(i, data.getTotalElevationGain(), MyTracksConstants.ELEVATION_SMOOTHING_FACTOR); } } @@ -116,23 +123,23 @@ public class TripStatisticsTest extends TestCase { for (double i = 0; i < 1000; i++) { // The value of the elevation does not matter. This is just to fill the // buffer. - stats.updateElevation(i); - stats.updateGrade(100, 100); + builder.updateElevation(i); + builder.updateGrade(100, 100); if ((i > MyTracksConstants.GRADE_SMOOTHING_FACTOR) && (i > MyTracksConstants.ELEVATION_SMOOTHING_FACTOR)) { - assertEquals(1.0, stats.getMaxGrade()); - assertEquals(1.0, stats.getMinGrade()); + assertEquals(1.0, builder.getStatistics().getMaxGrade()); + assertEquals(1.0, builder.getStatistics().getMinGrade()); } } for (double i = 0; i < 1000; i++) { // The value of the elevation does not matter. This is just to fill the // buffer. - stats.updateElevation(i); - stats.updateGrade(100, -100); + builder.updateElevation(i); + builder.updateGrade(100, -100); if ((i > MyTracksConstants.GRADE_SMOOTHING_FACTOR) && (i > MyTracksConstants.ELEVATION_SMOOTHING_FACTOR)) { - assertEquals(1.0, stats.getMaxGrade()); - assertEquals(-1.0, stats.getMinGrade()); + assertEquals(1.0, builder.getStatistics().getMaxGrade()); + assertEquals(-1.0, builder.getStatistics().getMinGrade()); } } } @@ -141,39 +148,39 @@ public class TripStatisticsTest extends TestCase { for (double i = 0; i < 100; i++) { // The value of the elevation does not matter. This is just to fill the // buffer. - stats.updateElevation(i); - stats.updateGrade(1, 100); - assertEquals(Double.NEGATIVE_INFINITY, stats.getMaxGrade()); - assertEquals(Double.POSITIVE_INFINITY, stats.getMinGrade()); + builder.updateElevation(i); + builder.updateGrade(1, 100); + assertEquals(Double.NEGATIVE_INFINITY, builder.getStatistics().getMaxGrade()); + assertEquals(Double.POSITIVE_INFINITY, builder.getStatistics().getMinGrade()); } } public void testUpdateSpeedIncludeZero() { for (int i = 0; i < 1000; i++) { - stats.updateSpeed(i + 1000, 0.0, i, 4.0); - assertEquals(0.0, stats.getMaxSpeed()); - assertEquals((i + 1) * 1000, stats.getMovingTime()); + builder.updateSpeed(i + 1000, 0.0, i, 4.0); + assertEquals(0.0, builder.getStatistics().getMaxSpeed()); + assertEquals((i + 1) * 1000, builder.getStatistics().getMovingTime()); } } public void testUpdateSpeedIngoreErrorCode() { - stats.updateSpeed(12345000, 128.0, 12344000, 0.0); - assertEquals(0.0, stats.getMaxSpeed()); - assertEquals(1000, stats.getMovingTime()); + builder.updateSpeed(12345000, 128.0, 12344000, 0.0); + assertEquals(0.0, builder.getStatistics().getMaxSpeed()); + assertEquals(1000, builder.getStatistics().getMovingTime()); } public void testUpdateSpeedIngoreLargeAcceleration() { - stats.updateSpeed(12345000, 100.0, 12344000, 1.0); - assertEquals(0.0, stats.getMaxSpeed()); - assertEquals(1000, stats.getMovingTime()); + builder.updateSpeed(12345000, 100.0, 12344000, 1.0); + assertEquals(0.0, builder.getStatistics().getMaxSpeed()); + assertEquals(1000, builder.getStatistics().getMovingTime()); } public void testUpdateSpeed() { for (int i = 0; i < 1000; i++) { - stats.updateSpeed(i + 1000, 4.0, i, 4.0); - assertEquals((i + 1) * 1000, stats.getMovingTime()); + builder.updateSpeed(i + 1000, 4.0, i, 4.0); + assertEquals((i + 1) * 1000, builder.getStatistics().getMovingTime()); if (i > MyTracksConstants.SPEED_SMOOTHING_FACTOR) { - assertEquals(4.0, stats.getMaxSpeed()); + assertEquals(4.0, builder.getStatistics().getMaxSpeed()); } } } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/util/ChartURLGeneratorTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/util/ChartURLGeneratorTest.java index ceb714783..5fba76b2f 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/util/ChartURLGeneratorTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/util/ChartURLGeneratorTest.java @@ -3,6 +3,7 @@ package com.google.android.apps.mytracks.util; import com.google.android.apps.mytracks.content.Track; +import com.google.android.apps.mytracks.stats.TripStatistics; import junit.framework.TestCase; @@ -19,9 +20,10 @@ public class ChartURLGeneratorTest extends TestCase { Vector distances = new Vector(); Vector elevations = new Vector(); Track t = new Track(); - t.setMinElevation(0); - t.setMaxElevation(2000); - t.setTotalDistance(100); + TripStatistics stats = t.getStatistics(); + stats.setMinElevation(0); + stats.setMaxElevation(2000); + stats.setTotalDistance(100); distances.add(0.0); elevations.add(10.0);