Addressed Rodrigo's comments.

This commit is contained in:
Bartlomiej Niechwiej
2010-10-13 21:58:26 -07:00
parent 09203ffb95
commit b70e001206
2 changed files with 104 additions and 75 deletions
@@ -67,20 +67,18 @@ public class MyTracksOverlay extends Overlay {
private boolean showEndMarker = true; private boolean showEndMarker = true;
// TODO: Remove it completely after completing performance tests. // TODO: Remove it completely after completing performance tests.
private boolean alwaysVisible = true; private boolean alwaysVisible = true;
private GeoPoint lastReferencePoint;
private Rect lastViewRect; private Rect lastViewRect;
private Path lastPath; private Path lastPath;
/** /**
* Represents a pre-processed {@code Location} to speed up drawing. * Represents a pre-processed {@code Location} to speed up drawing.
* This class is more like a data object and doesn't provide getters/setters. * This class is more like a data object and doesn't provide accessors.
*/ */
private static class CachedLocation { private static class CachedLocation {
public final boolean valid; public final boolean valid;
public final GeoPoint geoPoint; public final GeoPoint geoPoint;
// Cached point in the screen's coordinate system.
// TODO: Use it in drawTrack.
public Point point;
public CachedLocation(Location location) { public CachedLocation(Location location) {
this.valid = MyTracksUtils.isValidLocation(location); this.valid = MyTracksUtils.isValidLocation(location);
@@ -285,76 +283,91 @@ public class MyTracksOverlay extends Overlay {
private void drawTrack(Canvas canvas, Projection projection, Rect viewRect) { private void drawTrack(Canvas canvas, Projection projection, Rect viewRect) {
Path path; Path path;
final Point pt = new Point();
GeoPoint firstGeoPoint = null;
GeoPoint lastGeoPoint = null;
synchronized (points) { synchronized (points) {
// Merge the pending points with the list of cached locations. // Merge the pending points with the list of cached locations.
if (pendingPoints.drainTo(points) == 0 && lastPath != null && final GeoPoint referencePoint = projection.fromPixels(0, 0);
viewRect.equals(lastViewRect)) { int newPoints = pendingPoints.drainTo(points);
boolean newProjection = !viewRect.equals(lastViewRect) ||
!referencePoint.equals(lastReferencePoint);
if (newPoints == 0 && lastPath != null && !newProjection) {
// No need to recreate path (same points and viewing area). // No need to recreate path (same points and viewing area).
path = lastPath; path = lastPath;
} else { } else {
// Regenerate the whole path.
int numPoints = points.size(); int numPoints = points.size();
if (numPoints < 2) { if (numPoints < 2) {
return; path = null;
} } else if (lastPath != null && !newProjection) {
path = newPath(); path = lastPath;
path.incReserve(numPoints); updatePath(projection, viewRect, path, numPoints - newPoints);
} else {
// Whether to start a new segment on new valid and visible point. path = newPath();
boolean newSegment = true; path.incReserve(numPoints);
boolean lastVisible = false; updatePath(projection, viewRect, path, 0);
// Loop over track points:
for (CachedLocation loc : points) {
// Check if valid, if not then indicate a new segment.
if (!loc.valid) {
newSegment = true;
continue;
}
final GeoPoint geoPoint = loc.geoPoint;
if (firstGeoPoint == null) {
// Found the starting point.
firstGeoPoint = geoPoint;
}
lastGeoPoint = geoPoint;
// Check if this breaks the existing segment.
boolean visible = alwaysVisible || viewRect.contains(
geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
if (!visible && !lastVisible) {
// This is a point outside view not connected to a visible one.
newSegment = true;
}
lastVisible = visible;
// Either move to beginning of a new segment or continue the old one.
projection.toPixels(geoPoint, pt);
if (newSegment) {
path.moveTo(pt.x, pt.y);
newSegment = false;
} else {
path.lineTo(pt.x, pt.y);
}
} }
lastPath = path; lastPath = path;
lastViewRect = viewRect;
} }
lastReferencePoint = referencePoint;
lastViewRect = viewRect;
}
if (path != null) {
canvas.drawPath(path, selectedTrackPaint);
} }
canvas.drawPath(path, selectedTrackPaint);
// Draw the "End" marker. // Draw the "End" marker.
if (showEndMarker && lastGeoPoint != null) { if (showEndMarker) {
drawElement(canvas, projection, lastGeoPoint, endMarker, -markerWidth / 2, for (int i = points.size() - 1; i >= 0; --i) {
-markerHeight); if (points.get(i).valid) {
drawElement(canvas, projection, points.get(i).geoPoint, endMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
} }
// Draw the "Start" marker: // Draw the "Start" marker.
if (firstGeoPoint != null) { for (int i = 0; i < points.size(); ++i) {
drawElement(canvas, projection, firstGeoPoint, if (points.get(i).valid) {
startMarker, -markerWidth / 2, -markerHeight); drawElement(canvas, projection, points.get(i).geoPoint, startMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
private void updatePath(Projection projection, Rect viewRect, Path path,
int startLocationIdx) {
// Whether to start a new segment on new valid and visible point.
boolean newSegment =
startLocationIdx > 0 ? !points.get(startLocationIdx - 1).valid : true;
boolean lastVisible = !newSegment;
final Point pt = new Point();
// Loop over track points.
for (int i = startLocationIdx; i < points.size(); ++i) {
CachedLocation loc = points.get(i);
// Check if valid, if not then indicate a new segment.
if (!loc.valid) {
newSegment = true;
continue;
}
final GeoPoint geoPoint = loc.geoPoint;
// Check if this breaks the existing segment.
boolean visible = alwaysVisible || viewRect.contains(
geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
if (!visible && !lastVisible) {
// This is a point outside view not connected to a visible one.
newSegment = true;
}
lastVisible = visible;
// Either move to beginning of a new segment or continue the old one.
projection.toPixels(geoPoint, pt);
if (newSegment) {
path.moveTo(pt.x, pt.y);
newSegment = false;
} else {
path.lineTo(pt.x, pt.y);
}
} }
} }
@@ -45,6 +45,10 @@ public class MyTracksOverlayTest extends AndroidTestCase {
private MapView mockView; private MapView mockView;
private Projection mockProjection; private Projection mockProjection;
/**
* A mock version of {@code MyTracksOverlay} that does not use
* {@class MapView}.
*/
private class MockMyTracksOverlay extends MyTracksOverlay { private class MockMyTracksOverlay extends MyTracksOverlay {
public MockMyTracksOverlay(Context context) { public MockMyTracksOverlay(Context context) {
super(context); super(context);
@@ -63,9 +67,16 @@ public class MyTracksOverlayTest extends AndroidTestCase {
} }
} }
/**
* A mock class that intercepts {@code Path}'s and records calls to
* {@code #moveTo()} and {@code #lineTo()}.
*/
private static class MockPath extends Path { private static class MockPath extends Path {
/** A list of disjoined path segments. */
public final List<List<PointF>> segments = new LinkedList<List<PointF>>(); public final List<List<PointF>> segments = new LinkedList<List<PointF>>();
/** The total number of points in this path. */
public int totalPoints; public int totalPoints;
private List<PointF> currentSegment; private List<PointF> currentSegment;
@Override @Override
@@ -83,6 +94,24 @@ public class MyTracksOverlayTest extends AndroidTestCase {
totalPoints++; totalPoints++;
} }
} }
/**
* A mock {@code Projection} that acts as the identity matrix.
*/
private static class MockProjection implements Projection {
@Override
public Point toPixels(GeoPoint in, Point out) {
return out;
}
@Override
public float metersToEquatorPixels(float meters) {
return meters;
}
@Override
public GeoPoint fromPixels(int x, int y) {
return new GeoPoint(y, x);
}
}
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
@@ -92,20 +121,7 @@ public class MyTracksOverlayTest extends AndroidTestCase {
// Enable drawing. // Enable drawing.
myTracksOverlay.setTrackDrawingEnabled(true); myTracksOverlay.setTrackDrawingEnabled(true);
mockView = null; mockView = null;
mockProjection = new Projection() { mockProjection = new MockProjection();
@Override
public Point toPixels(GeoPoint in, Point out) {
return out;
}
@Override
public float metersToEquatorPixels(float meters) {
return meters;
}
@Override
public GeoPoint fromPixels(int x, int y) {
return new GeoPoint(y, x);
}
};
} }
public void testAddLocation() throws Exception { public void testAddLocation() throws Exception {