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;
// TODO: Remove it completely after completing performance tests.
private boolean alwaysVisible = true;
private GeoPoint lastReferencePoint;
private Rect lastViewRect;
private Path lastPath;
/**
* 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 {
public final boolean valid;
public final GeoPoint geoPoint;
// Cached point in the screen's coordinate system.
// TODO: Use it in drawTrack.
public Point point;
public CachedLocation(Location location) {
this.valid = MyTracksUtils.isValidLocation(location);
@@ -285,76 +283,91 @@ public class MyTracksOverlay extends Overlay {
private void drawTrack(Canvas canvas, Projection projection, Rect viewRect) {
Path path;
final Point pt = new Point();
GeoPoint firstGeoPoint = null;
GeoPoint lastGeoPoint = null;
synchronized (points) {
// Merge the pending points with the list of cached locations.
if (pendingPoints.drainTo(points) == 0 && lastPath != null &&
viewRect.equals(lastViewRect)) {
// Merge the pending points with the list of cached locations.
final GeoPoint referencePoint = projection.fromPixels(0, 0);
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).
path = lastPath;
} else {
// Regenerate the whole path.
int numPoints = points.size();
if (numPoints < 2) {
return;
}
path = newPath();
path.incReserve(numPoints);
// Whether to start a new segment on new valid and visible point.
boolean newSegment = true;
boolean lastVisible = false;
// 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);
}
path = null;
} else if (lastPath != null && !newProjection) {
path = lastPath;
updatePath(projection, viewRect, path, numPoints - newPoints);
} else {
path = newPath();
path.incReserve(numPoints);
updatePath(projection, viewRect, path, 0);
}
lastPath = path;
lastViewRect = viewRect;
}
lastReferencePoint = referencePoint;
lastViewRect = viewRect;
}
if (path != null) {
canvas.drawPath(path, selectedTrackPaint);
}
canvas.drawPath(path, selectedTrackPaint);
// Draw the "End" marker.
if (showEndMarker && lastGeoPoint != null) {
drawElement(canvas, projection, lastGeoPoint, endMarker, -markerWidth / 2,
-markerHeight);
if (showEndMarker) {
for (int i = points.size() - 1; i >= 0; --i) {
if (points.get(i).valid) {
drawElement(canvas, projection, points.get(i).geoPoint, endMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
// Draw the "Start" marker:
if (firstGeoPoint != null) {
drawElement(canvas, projection, firstGeoPoint,
startMarker, -markerWidth / 2, -markerHeight);
// Draw the "Start" marker.
for (int i = 0; i < points.size(); ++i) {
if (points.get(i).valid) {
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 Projection mockProjection;
/**
* A mock version of {@code MyTracksOverlay} that does not use
* {@class MapView}.
*/
private class MockMyTracksOverlay extends MyTracksOverlay {
public MockMyTracksOverlay(Context 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 {
/** A list of disjoined path segments. */
public final List<List<PointF>> segments = new LinkedList<List<PointF>>();
/** The total number of points in this path. */
public int totalPoints;
private List<PointF> currentSegment;
@Override
@@ -83,6 +94,24 @@ public class MyTracksOverlayTest extends AndroidTestCase {
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
protected void setUp() throws Exception {
@@ -92,20 +121,7 @@ public class MyTracksOverlayTest extends AndroidTestCase {
// Enable drawing.
myTracksOverlay.setTrackDrawingEnabled(true);
mockView = null;
mockProjection = new 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);
}
};
mockProjection = new MockProjection();
}
public void testAddLocation() throws Exception {