diff --git a/MyTracks/res/values/keys.xml b/MyTracks/res/values/keys.xml index 30a83a613..8cc20c02b 100644 --- a/MyTracks/res/values/keys.xml +++ b/MyTracks/res/values/keys.xml @@ -37,6 +37,7 @@ limitations under the License. shareUrlOnly signalSamplingFrequency splitFrequency + debugDrawBounds recordingTrack diff --git a/MyTracks/res/xml/preferences.xml b/MyTracks/res/xml/preferences.xml index ddc6b2ff9..f9cc821a7 100644 --- a/MyTracks/res/xml/preferences.xml +++ b/MyTracks/res/xml/preferences.xml @@ -125,4 +125,13 @@ limitations under the License. android:persistent="false" /> + + + diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java index 7966975b4..fcf9c9308 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksMap.java @@ -372,6 +372,8 @@ public class MyTracksMap extends MapActivity setSelectedTrack(selectedTrackId); } updateOptionsButton(); + mapOverlay.setDrawBounds(preferences.getBoolean( + getString(R.string.debug_draw_bounds_key), false)); preferences.registerOnSharedPreferenceChangeListener(this); } } @@ -916,6 +918,10 @@ public class MyTracksMap extends MapActivity mapOverlay.setShowEndMarker(!isRecordingSelected()); mapView.postInvalidate(); } + } else if (key.equals(getString(R.string.debug_draw_bounds_key))) { + mapOverlay.setDrawBounds( + sharedPreferences.getBoolean( + getString(R.string.debug_draw_bounds_key), false)); } } }); diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java index dfc235827..47faf8fad 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksOverlay.java @@ -29,6 +29,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.graphics.drawable.Drawable; import android.location.Location; @@ -59,6 +60,7 @@ public class MyTracksOverlay extends Overlay { private int lastHeading = 0; private Location myLocation; private boolean showEndMarker = true; + private boolean drawBounds; public MyTracksOverlay(Context context) { this.context = context; @@ -127,7 +129,11 @@ public class MyTracksOverlay extends Overlay { * @param l the location to add */ public void addLocation(Location l) { - points.add(l); + if (l != null) { + synchronized (points) { + points.add(l); + } + } } public void addWaypoint(Waypoint wpt) { @@ -135,7 +141,9 @@ public class MyTracksOverlay extends Overlay { } public int getNumLocations() { - return points.size(); + synchronized (points) { + return points.size(); + } } public void clearWaypoints() { @@ -143,7 +151,9 @@ public class MyTracksOverlay extends Overlay { } public void clearPoints() { - points.clear(); + synchronized (points) { + points.clear(); + } } public void setTrackDrawingEnabled(boolean trackDrawingEnabled) { @@ -220,73 +230,118 @@ public class MyTracksOverlay extends Overlay { } private void drawTrack(Canvas canvas, MapView mapView) { - if (points.size() < 2) { - return; - } + Path path; + Point pt = new Point(); + Location lastValidLocation; + int locLon = 0, locLat = 0; + GeoPoint firstGeoPoint = null; + boolean lastLocValid; // Get the current viewing window: int w = mapView.getLongitudeSpan(); int h = mapView.getLatitudeSpan(); int cx = mapView.getMapCenter().getLongitudeE6(); int cy = mapView.getMapCenter().getLatitudeE6(); - Rect rect = new Rect(cx - w, cy - h, cx + w, cy + h); + Rect viewRect = new Rect(cx - w, cy - h, cx + w, cy + h); - Point pt = new Point(); - GeoPoint geoPoint; - Location loc; - Location lastValidLocation = null; - Path path; - boolean wasInside; - - // Do as much allocation and preparation outside the loop over track - // points: - wasInside = false; - int locLon = 0, locLat = 0, minLon, maxLon, minLat, maxLat; - int lastLocLon = (int) (points.get(0).getLongitude() * 1E6); - int lastLocLat = (int) (points.get(0).getLatitude() * 1E6); - - // Loop over track points: - path = new Path(); - for (int i = 1; i < points.size(); i++) { - - loc = points.get(i); - if (loc == null) { - continue; + // Global bounding box, including points not visible + int allMinLat, allMinLon, allMaxLat, allMaxLon; + + synchronized (points) { + int numPoints = points.size(); + if (numPoints < 2) { + return; } - locLon = (int) (loc.getLongitude() * 1E6); - locLat = (int) (loc.getLatitude() * 1E6); - // Draw a line segment if it's inside the viewing window: - if (locLat < 90E6 && lastLocLat < 90E6) { - lastValidLocation = loc; - minLon = Math.min(locLon, lastLocLon); - maxLon = Math.max(locLon, lastLocLon); - minLat = Math.min(locLat, lastLocLat); - maxLat = Math.max(locLat, lastLocLat); - if (rect.intersects(minLon, minLat, maxLon, maxLat)) { - if (!wasInside) { - geoPoint = new GeoPoint(lastLocLat, lastLocLon); - mapView.getProjection().toPixels(geoPoint, pt); - path.moveTo(pt.x, pt.y); + GeoPoint geoPoint; + Location loc; + int minLon, maxLon, minLat, maxLat; + lastValidLocation = points.get(0); + int lastLocLon = allMinLon = allMaxLon = (int) (lastValidLocation.getLongitude() * 1E6); + int lastLocLat = allMinLat = allMaxLat = (int) (lastValidLocation.getLatitude() * 1E6); + lastLocValid = MyTracksUtils.isValidLocation(lastValidLocation); + boolean lastLocVisible = false; + path = new Path(); + + // Loop over track points: + path.incReserve(numPoints); + for (int i = 1; i < numPoints; i++) { + loc = points.get(i); + + boolean locValid = MyTracksUtils.isValidLocation(loc); + boolean locVisible = false; + if (locValid) { + locLon = (int) (loc.getLongitude() * 1E6); + locLat = (int) (loc.getLatitude() * 1E6); + + if (firstGeoPoint == null) { + // Found the starting point + firstGeoPoint = new GeoPoint(locLat, locLon); + } + + // If both the current and previous locations were valid + if (lastLocValid) { + lastValidLocation = loc; + + // Get the bounding box of the segment about to be drawn + if (locLon > lastLocLon) { + minLon = lastLocLon; + maxLon = locLon; + } else { + minLon = locLon; + maxLon = lastLocLon; + } + if (locLat > lastLocLat) { + minLat = lastLocLat; + maxLat = locLat; + } else { + minLat = locLat; + maxLat = lastLocLat; + } + + if (drawBounds) { + allMaxLat = Math.max(allMaxLat, maxLat); + allMinLat = Math.min(allMinLat, minLat); + allMaxLon = Math.max(allMaxLon, maxLon); + allMinLon = Math.min(allMinLon, minLon); + } + + // See if that bounding box intersects the viewable bounding box + // Assume that if it does, the location is visible + locVisible = viewRect.intersects(minLon, minLat, maxLon, maxLat); + if (locVisible) { + // If the previous point wasn't drawn, start at its position + if (!lastLocVisible) { + geoPoint = new GeoPoint(lastLocLat, lastLocLon); + mapView.getProjection().toPixels(geoPoint, pt); + path.moveTo(pt.x, pt.y); + } + + // Draw a line to the new point + geoPoint = new GeoPoint(locLat, locLon); + mapView.getProjection().toPixels(geoPoint, pt); + path.lineTo(pt.x, pt.y); + } } - geoPoint = new GeoPoint(locLat, locLon); - mapView.getProjection().toPixels(geoPoint, pt); - path.lineTo(pt.x, pt.y); - wasInside = rect.contains(locLon, locLat); } - } else { - wasInside = false; + lastLocLon = locLon; + lastLocLat = locLat; + lastLocValid = locValid; + lastLocVisible = locVisible; } - lastLocLon = locLon; - lastLocLat = locLat; } + canvas.drawPath(path, selectedTrackPaint); + if (drawBounds) { + drawBoundingBox(canvas, mapView, + allMinLat, allMinLon, allMaxLat, allMaxLon); + } + // Draw the "End" marker: - Location currentLastValidLocation = lastValidLocation; - if (showEndMarker && currentLastValidLocation != null) { + if (showEndMarker && lastValidLocation != null) { canvas.save(); - geoPoint = MyTracksUtils.getGeoPoint(currentLastValidLocation); + GeoPoint geoPoint = MyTracksUtils.getGeoPoint(lastValidLocation); mapView.getProjection().toPixels(geoPoint, pt); canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight); endMarker.draw(canvas); @@ -294,22 +349,46 @@ public class MyTracksOverlay extends Overlay { } // Draw the "Start" marker: - for (int i = 0; i < points.size(); i++) { - loc = points.get(i); - if (loc.getLatitude() < 90) { - locLon = (int) (loc.getLongitude() * 1E6); - locLat = (int) (loc.getLatitude() * 1E6); - geoPoint = new GeoPoint(locLat, locLon); - mapView.getProjection().toPixels(geoPoint, pt); - canvas.save(); - canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight); - startMarker.draw(canvas); - canvas.restore(); - break; - } + if (firstGeoPoint != null) { + mapView.getProjection().toPixels(firstGeoPoint, pt); + canvas.save(); + canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight); + startMarker.draw(canvas); + canvas.restore(); } } + private void drawBoundingBox(Canvas canvas, MapView mapView, int allMinLat, + int allMinLon, int allMaxLat, int allMaxLon) { + // Transform coordinates + GeoPoint maxPoint = new GeoPoint(allMaxLat, allMaxLon); + GeoPoint minPoint = new GeoPoint(allMinLat, allMinLon); + Point minPt = new Point(); + Point maxPt = new Point(); + mapView.getProjection().toPixels(minPoint, minPt); + mapView.getProjection().toPixels(maxPoint, maxPt); + Rect allBounds = new Rect(minPt.x, minPt.y, maxPt.x, maxPt.y); + + // Prepare a green paint + Paint boundingBoxPaint = new Paint(); + boundingBoxPaint.setColor(context.getResources().getColor(R.color.green)); + boundingBoxPaint.setStrokeWidth(2); + boundingBoxPaint.setStyle(Paint.Style.STROKE); + boundingBoxPaint.setAntiAlias(true); + + // Disable clipping + canvas.save(); + canvas.clipRect(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, + Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, + Region.Op.REPLACE); + + // Draw the bounding box + canvas.drawRect(allBounds, boundingBoxPaint); + + // Re-enable clipping + canvas.restore(); + } + @Override public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) { @@ -378,4 +457,8 @@ public class MyTracksOverlay extends Overlay { } return super.onTap(p, mapView); } + + public void setDrawBounds(boolean drawBounds) { + this.drawBounds = drawBounds; + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java b/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java index 1432888f5..9eed646a2 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/StatsActivity.java @@ -49,14 +49,12 @@ import android.widget.Toast; * @author Sandor Dornbush */ public class StatsActivity extends Activity - implements OnSharedPreferenceChangeListener, LocationListener { - + implements OnSharedPreferenceChangeListener { private final StatsUtilities utils; private UIUpdateThread thread; private ContentObserver observer; - private LocationManager locationManager; /** * The id of the currently selected track. @@ -96,7 +94,6 @@ public class StatsActivity extends Activity private boolean showCurrentSegment = false; private final MyTracksProviderUtils providerUtils; - private Track track = null; /** * A runnable for posting to the UI thread. Will update the total time field. @@ -179,11 +176,9 @@ public class StatsActivity extends Activity preferences.getBoolean(getString(R.string.report_speed_key), true); checkLiveTrack(); restoreStats(); - updateLocation(null); + showUnknownLocation(); preferences.registerOnSharedPreferenceChangeListener(this); } - locationManager = - (LocationManager) getSystemService(Context.LOCATION_SERVICE); utils.setMetricUnits(metricUnits); utils.setReportSpeed(displaySpeed); utils.updateUnits(); @@ -214,7 +209,7 @@ public class StatsActivity extends Activity activityOnTop = true; checkLiveTrack(); restoreStats(); - updateLocation(null); + showUnknownLocation(); super.onResume(); } @@ -234,7 +229,7 @@ public class StatsActivity extends Activity -1); checkLiveTrack(); restoreStats(); - updateLocation(null); + showUnknownLocation(); } else if (key.equals(getString(R.string.recording_track_key))) { recordingTrackId = sharedPreferences.getLong( @@ -242,7 +237,7 @@ public class StatsActivity extends Activity -1); checkLiveTrack(); restoreStats(); - updateLocation(null); + showUnknownLocation(); } else if (key.equals(getString(R.string.metric_units_key))) { metricUnits = sharedPreferences.getBoolean( @@ -299,29 +294,36 @@ public class StatsActivity extends Activity return super.onOptionsItemSelected(item); } - @Override - public void onLocationChanged(Location l) { - if (selectedTrackIsRecording()) { - updateLocation(l); + private final LocationListener locationListener = new LocationListener() { + @Override + public void onLocationChanged(Location l) { + if (selectedTrackIsRecording()) { + showLocation(l); + } } - } - - @Override - public void onProviderDisabled(String provider) { - } - - @Override - public void onProviderEnabled(String provider) { - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - } + + @Override + public void onProviderDisabled(String provider) { + // Do nothing + } + + @Override + public void onProviderEnabled(String provider) { + // Do nothing + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + // Do nothing + } + }; /** * Registers to receive location updates from the GPS location provider. */ private void registerLocationListener() { + LocationManager locationManager = + (LocationManager) getSystemService(Context.LOCATION_SERVICE); if (locationManager != null) { LocationProvider gpsProvider = locationManager.getProvider(MyTracksConstants.GPS_PROVIDER); @@ -334,7 +336,7 @@ public class StatsActivity extends Activity + gpsProvider.getName()); } locationManager.requestLocationUpdates(gpsProvider.getName(), - 0/*minTime*/, 0/*minDist*/, this); + 0/*minTime*/, 0/*minDist*/, locationListener); } } @@ -342,8 +344,10 @@ public class StatsActivity extends Activity * Unregisters all location listener. */ private void unregisterLocationListener() { + LocationManager locationManager = + (LocationManager) getSystemService(Context.LOCATION_SERVICE); if (locationManager != null) { - locationManager.removeUpdates(this); + locationManager.removeUpdates(locationListener); } } @@ -364,7 +368,7 @@ public class StatsActivity extends Activity return; } - track = providerUtils.getTrack(selectedTrackId); + Track track = providerUtils.getTrack(selectedTrackId); if (track == null || track.getStatistics() == null) { utils.setAllToUnknown(); return; @@ -421,17 +425,17 @@ public class StatsActivity extends Activity * * @param l may be null (will set location fields to unknown) */ - private void updateLocation(Location l) { - if (l == null) { - utils.setUnknown(R.id.elevation_register); - utils.setUnknown(R.id.latitude_register); - utils.setUnknown(R.id.longitude_register); - utils.setUnknown(R.id.speed_register); - } else { - utils.setAltitude(R.id.elevation_register, l.getAltitude()); - utils.setLatLong(R.id.latitude_register, l.getLatitude()); - utils.setLatLong(R.id.longitude_register, l.getLongitude()); - utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6); - } + private void showLocation(Location l) { + utils.setAltitude(R.id.elevation_register, l.getAltitude()); + utils.setLatLong(R.id.latitude_register, l.getLatitude()); + utils.setLatLong(R.id.longitude_register, l.getLongitude()); + utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6); + } + + private void showUnknownLocation() { + utils.setUnknown(R.id.elevation_register); + utils.setUnknown(R.id.latitude_register); + utils.setUnknown(R.id.longitude_register); + utils.setUnknown(R.id.speed_register); } }